diff --git a/.codespell-ignore-lines b/.codespell-ignore-lines index 75a07676e..5902decae 100644 --- a/.codespell-ignore-lines +++ b/.codespell-ignore-lines @@ -1,2 +1,16 @@ mynewt-nimble/nimble/host/services/ans/src/ble_svc_ans.c mynewt-nimble/nimble/host/services/ans/include +#include "services/ans/ble_svc_ans.h" +#include "crypto/controlse/ccertificate.hxx" + * notifications for the ANS Unread Alert Status characteristic + object = new Controlse::CCertificate( + Controlse::CCertificate cert(se, settings->key_id); + auto certificate = Controlse::CCertificate( + * |---------- [-rw-r--r-- 15] afile.txt + g_afile.name = "afile.txt"; + "*.deh", "*.hhe", "*.seh", NULL); + MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), + MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), + MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), + MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), +/* ANS service */ diff --git a/.codespellrc b/.codespellrc index 442f4656c..743ab2a75 100644 --- a/.codespellrc +++ b/.codespellrc @@ -8,3 +8,9 @@ exclude-file = .codespell-ignore-lines # Ignore complete files (e.g. legal text or other immutable material). skip = LICENSE, + examples/webpanel/content/www/xterm.min.js, + **/games/NXDoom/src/doom/d_englsh.h, + **/games/NXDoom/src/doom/d_french.h + +# Ignore words list (FTP protocol commands and technical terms) +ignore-words-list = ALLO, ARCHTYPE, parm, shiftIn diff --git a/.github/labeler.yml b/.github/labeler.yml index 88336e8ae..278cc551a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -17,6 +17,9 @@ # specific language governing permissions and limitations # under the License. # +# Note: NuttX PR Labeler only supports a subset of the +# `actions/labeler` syntax: `changed-files` and +# `any-glob-to-any-file`. See .github/workflows/labeler.yml # add arch labels diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml index 278d5f666..02e39ab2b 100644 --- a/.github/workflows/arch.yml +++ b/.github/workflows/arch.yml @@ -230,7 +230,7 @@ jobs: skip_build=1 fi - # For "Arch / Board: xtensa": Build xtensa-01, xtensa-02 + # For "Arch / Board: xtensa": Build xtensa-01, xtensa-02, xtensa-03 elif [[ "$arch_contains_xtensa" == "1" || "$board_contains_xtensa" == "1" ]]; then if [[ "$board" != *"xtensa-"* ]]; then skip_build=1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab5cd6875..f9ac66a20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,7 +85,7 @@ jobs: echo "apps_ref=$APPS_REF" >> $GITHUB_OUTPUT - name: Checkout nuttx repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: apache/nuttx ref: ${{ steps.gittargets.outputs.os_ref }} @@ -95,7 +95,7 @@ jobs: run: git -C sources/nuttx fetch --tags - name: Checkout apps repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: apache/nuttx-apps ref: ${{ steps.gittargets.outputs.apps_ref }} @@ -106,7 +106,7 @@ jobs: run: tar zcf sources.tar.gz sources - name: Archive Source Bundle - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7.0.1 with: name: source-bundle path: sources.tar.gz @@ -121,7 +121,7 @@ jobs: [ "arm-01", "risc-v-01", "sim-01", "xtensa-01", "arm64-01", "x86_64-01", "other", "arm-02", "risc-v-02", "sim-02", "xtensa-02", - "arm-03", "risc-v-03", "sim-03", + "arm-03", "risc-v-03", "sim-03", "xtensa-03", "arm-04", "risc-v-04", "arm-05", "risc-v-05", "arm-06", "risc-v-06", @@ -135,6 +135,10 @@ jobs: runs-on: ubuntu-latest env: DOCKER_BUILDKIT: 1 + # Documented sim/login CI test credential (not a production secret). + # Used when CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE=y and defconfig omits + # the password. See nuttx tools/update_romfs_password.sh. + NUTTX_ROMFS_PASSWD_PASSWORD: NuttXSimLogin1! strategy: max-parallel: 12 @@ -143,8 +147,18 @@ jobs: steps: + - name: Show Disk Space + run: df -h + + - name: Free Disk Space (Ubuntu) + run: | + sudo rm -rf /usr/local/lib/android + + - name: After CLEAN-UP Disk Space + run: df -h + - name: Download Source Artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: source-bundle path: . @@ -153,7 +167,7 @@ jobs: run: tar zxf sources.tar.gz - name: Docker Login - uses: docker/login-action@v3 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.actor }} @@ -169,13 +183,45 @@ jobs: uses: ./sources/nuttx/.github/actions/ci-container env: BLOBDIR: /tools/blobs + NUTTX_ROMFS_PASSWD_PASSWORD: NuttXSimLogin1! with: run: | - echo "::add-matcher::sources/nuttx/.github/gcc.json" + export NUTTX_ROMFS_PASSWD_PASSWORD=NuttXSimLogin1! export ARTIFACTDIR=`pwd`/buildartifacts + + for i in 1 2 3; do + python -m pip install \ + --default-timeout=100 \ + --retries 10 \ + ntfc==0.0.1 && break + echo "Retry $i failed..." + sleep 5 + done + + mkdir /github/workspace/nuttx-ntfc + mkdir /github/workspace/nuttx-ntfc/external + cd /github/workspace/nuttx-ntfc + # get NTFC test cases + cd external + for i in 1 2 3 4 5; do + git clone -b release-0.0.1 https://github.com/apache/nuttx-ntfc-testing && break + if [ "$i" -eq 5 ]; then + echo "Failed to clone nuttx-ntfc-testing after $i attempts" + exit 1 + fi + + delay=$((i * 10)) + echo "Clone attempt $i failed; retrying in ${delay}s..." + rm -rf nuttx-ntfc-testing + sleep "$delay" + done + mv nuttx-ntfc-testing nuttx-testing + export NTFCDIR=/github/workspace/nuttx-ntfc + + echo "::add-matcher::sources/nuttx/.github/gcc.json" git config --global --add safe.directory /github/workspace/sources/nuttx git config --global --add safe.directory /github/workspace/sources/apps - cd sources/nuttx/tools/ci + cd /github/workspace/sources/nuttx/tools/ci if [ "X${{matrix.boards}}" = "Xcodechecker" ]; then ./cibuild.sh -c -A -N -R --codechecker testlist/${{matrix.boards}}.dat else @@ -183,7 +229,11 @@ jobs: ./cibuild.sh -c -A -N -R -S testlist/${{matrix.boards}}.dat fi - - uses: actions/upload-artifact@v5 + - name: Post-build Disk Space + if: always() + run: df -h + + - uses: actions/upload-artifact@v7.0.1 if: ${{ always() }} with: name: linux-${{matrix.boards}}-builds @@ -198,7 +248,7 @@ jobs: DOCKER_BUILDKIT: 1 steps: - name: Download Source Artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: source-bundle path: . @@ -207,7 +257,7 @@ jobs: run: tar zxf sources.tar.gz - name: Docker Login - uses: docker/login-action@v3 + uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1 with: registry: ghcr.io username: ${{ github.actor }} @@ -228,7 +278,7 @@ jobs: cd sources/nuttx ./tools/ci/cibuild-oot.sh - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v7.0.1 if: ${{ always() }} with: name: oot-build-artifacts @@ -251,13 +301,15 @@ jobs: runs-on: macos-15-intel needs: macOS-Arch if: ${{ needs.macOS-Arch.outputs.skip_all_builds != '1' }} + env: + NUTTX_ROMFS_PASSWD_PASSWORD: NuttXSimLogin1! strategy: max-parallel: 2 matrix: boards: ${{ fromJSON(needs.macOS-Arch.outputs.selected_builds) }} steps: - name: Download Source Artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: source-bundle path: . @@ -267,7 +319,7 @@ jobs: - name: Restore Tools Cache id: cache-tools - uses: actions/cache@v4 + uses: actions/cache@v6 env: cache-name: ${{ runner.os }}-cache-tools with: @@ -279,17 +331,18 @@ jobs: # Released version of Cython has issues with Python 11. Set runner to use Python 3.10 # https://github.com/cython/cython/issues/4500 - - uses: actions/setup-python@v6 + - uses: actions/setup-python@v7 with: python-version: '3.10' - name: Run Builds run: | echo "::add-matcher::sources/nuttx/.github/gcc.json" + export NUTTX_ROMFS_PASSWD_PASSWORD=NuttXSimLogin1! export ARTIFACTDIR=`pwd`/buildartifacts cd sources/nuttx/tools/ci ./cibuild.sh -i -c -A -R testlist/${{matrix.boards}}.dat - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v7.0.1 with: name: macos-${{matrix.boards}}-builds path: buildartifacts/ @@ -319,7 +372,7 @@ jobs: run: shell: msys2 {0} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: msys2/setup-msys2@v2 with: msystem: MSYS @@ -352,7 +405,7 @@ jobs: - run: git config --global core.autocrlf false - name: Download Source Artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: source-bundle path: . @@ -372,7 +425,7 @@ jobs: cd sources/nuttx/tools/ci ./cibuild.sh -g -i -A -C -N -R testlist/${{matrix.boards}}.dat - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v7.0.1 with: name: msys2-${{matrix.boards}}-builds path: buildartifacts/ @@ -391,12 +444,12 @@ jobs: msvc: needs: msvc-Arch if: ${{ needs.msvc-Arch.outputs.skip_all_builds != '1' }} - runs-on: windows-latest + runs-on: windows-2022 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 # Set up Python environment and install kconfiglib - name: Set up Python and install kconfiglib - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: '3.10' - name: Install kconfiglib @@ -406,7 +459,7 @@ jobs: - run: git config --global core.autocrlf false - name: Download Source Artifact - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: name: source-bundle path: . @@ -424,7 +477,7 @@ jobs: cd sources\nuttx\tools\ci .\cibuild.ps1 -n -i -A -C -N testlist\windows.dat - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v7.0.1 with: name: msvc-builds path: ./sources/buildartifacts/ diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 67b8d17ec..a183d0c12 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -28,14 +28,14 @@ jobs: steps: - name: Checkout nuttx repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: apache/nuttx path: nuttx fetch-depth: 0 - name: Checkout apps repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: apache/nuttx-apps path: apps diff --git a/.github/workflows/issue_labeler.yml b/.github/workflows/issue_labeler.yml index 2363ece67..2965bf5a6 100644 --- a/.github/workflows/issue_labeler.yml +++ b/.github/workflows/issue_labeler.yml @@ -23,7 +23,7 @@ jobs: issues: write steps: - name: Add labels issues automatically based on their body. - uses: actions/github-script@v8 + uses: actions/github-script@v9.0.0 with: script: | const body = context.payload.issue.body; diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 1c38d38da..4c6bb3ef7 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,34 +12,139 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# This workflow will fetch the updated PR filenames, compute the Size Label +# and Arch Labels, then save the PR Labels into a PR Artifact. The +# PR Labels will be written to the PR inside the "workflow_run" trigger +# (pr_labeler.yml), because this "pull_request" trigger has read-only +# permission. Don't use "pull_request_target", it's unsafe. +# See https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=321719166#GitHubActionsSecurity-Buildstriggeredwithpull_request_target name: "Pull Request Labeler" on: - - pull_request_target + - pull_request jobs: labeler: permissions: contents: read - pull-requests: write - issues: write + pull-requests: read + issues: read runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Assign labels based on paths - uses: actions/labeler@main + # Checkout one file from our trusted source: .github/labeler.yml + # Never checkout and execute any untrusted code from the PR. + - name: Checkout labeler config + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - sync-labels: true + repository: apache/nuttx-apps + ref: master + path: labeler + fetch-depth: 1 + persist-credentials: false + sparse-checkout: .github/labeler.yml + sparse-checkout-cone-mode: false - - name: Assign labels based on the PR's size - uses: codelytv/pr-size-labeler@v1.10.3 + # Fetch the updated PR filenames. Compute the Size Label and Arch Labels. + - name: Compute PR labels + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ignore_file_deletions: true - xs_label: 'Size: XS' - s_label: 'Size: S' - m_label: 'Size: M' - l_label: 'Size: L' - xl_label: 'Size: XL' + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const pull_number = context.issue.number; + + // Fetch the array of updated PR filenames: + // { status: 'added', filename: 'arch/arm/test.txt', additions: 3, deletions: 0, changes: 3 } + // { status: 'removed', filename: 'Documentation/legacy_README.md', additions: 0, deletions: 2531, changes: 2531 } + // { status: 'modified', filename: 'Documentation/security.rst', additions: 1, deletions: 0, changes: 1 } + const listFilesOptions = github.rest.pulls.listFiles + .endpoint.merge({ owner, repo, pull_number }); + const listFilesResponse = await github.paginate(listFilesOptions); + + // Sum up the number of lines changed + const sizeFiles = listFilesResponse + .filter(f => (f.status != 'removed')); // Ignore deleted files + var linesChanged = 0; + for (const file of sizeFiles) { + linesChanged += file.changes; + } + console.log({ linesChanged }); + + // Compute the Size Label + const sizeLabel = + (linesChanged <= 10) ? 'Size: XS' + : (linesChanged <= 100) ? 'Size: S' + : (linesChanged <= 500) ? 'Size: M' + : (linesChanged <= 1000) ? 'Size: L' + : 'Size: XL'; + var prLabels = [ sizeLabel ]; + + // Parse the Arch Label Patterns in .github/labeler.yml. Condense into: + // "Arch: arm": + // - any-glob-to-any-file: 'arch/arm/**' + // - any-glob-to-any-file: ... + const fs = require('fs'); + const config = fs.readFileSync('labeler/.github/labeler.yml', 'utf8') + .split('\n') // Split by newline + .map(s => s.trim()) // Remove leading and trailing spaces + .filter(s => (s != '')) // Remove empty lines + .filter(s => !s.startsWith('#')) // Remove comments + .filter(s => !s.startsWith('- changed-files:')); // Remove "changed-files" + + // Convert the Arch Label Patterns from config to archLabels. + // archLabels will contain the mappings for Arch Label and Filename Pattern: + // { label: "Arch: arm", pattern: "arch/arm/.*" }, + // { label: "Arch: arm64", pattern: "arch/arm64/.*" }, ... + var archLabels = []; + var label = ""; + for (const c of config) { + // Get the Arch Label + if (c.startsWith('"')) { // "Arch: arm": + label = c.split('"')[1]; // Arch: arm + + } else if (c.startsWith('- any-glob-to-any-file:')) { // - any-glob-to-any-file: 'arch/arm/**' + // Convert the Glob Pattern to Regex Pattern + const pattern = c.split("'")[1] // arch/arm/** + .split('.').join('\\.') // . becomes \. + .split('*').join('[^/]*') // * becomes [^/]* + .split('[^/]*[^/]*').join('.*'); // ** becomes .* + archLabels.push({ + label, // Arch: arm + pattern: '^' + pattern + '$' // Match the Line Start and Line End + }); + + } else { + // We don't support all rules of `actions/labeler` + throw new Error('.github/labeler.yml should contain only changed-files and any-glob-to-any-file, not: ' + c); + } + } + + // Search the filenames for matching Arch Labels + for (const archLabel of archLabels) { + if (prLabels.includes(archLabel.label)) { + break; + } + for (const file of listFilesResponse) { + const re = new RegExp(archLabel.pattern); + const match = re.test(file.filename); + if (match && !prLabels.includes(archLabel.label)) { + prLabels.push(archLabel.label); + break; + } + } + } + console.log({ prLabels }); + + // Save the PR Number and PR Labels into a PR Artifact + // e.g. 'Size: XS\nArch: avr\n' + const dir = 'pr'; + fs.mkdirSync(dir); + fs.writeFileSync(dir + '/pr-id.txt', pull_number + '\n'); + fs.writeFileSync(dir + '/pr-labels.txt', prLabels.join('\n') + '\n'); + + # Upload the PR Artifact as pr.zip + - name: Upload PR artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pr + path: pr/ diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index fbebfdc0a..9a82b0362 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,7 +17,7 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-depth: 0 - run: mkdir super-linter.report diff --git a/.github/workflows/pr_labeler.yml b/.github/workflows/pr_labeler.yml new file mode 100644 index 000000000..26ed16441 --- /dev/null +++ b/.github/workflows/pr_labeler.yml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This workflow will fetch the PR Labels from the PR Artifact, and write +# the PR Labels into the PR. The workflow is called after the +# "pull_request" trigger (labeler.yml). This "workflow_run" trigger uses a +# GitHub Token with Write Permission, so we must never run any untrusted +# code from the PR, and we must always extract and use the PR Artifact +# safely. See https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=321719166#GitHubActionsSecurity-Buildstriggeredwithworkflow_run +name: "Set Pull Request Labels" +on: + workflow_run: + workflows: ["Pull Request Labeler"] + types: + - completed + +jobs: + pr_labeler: + permissions: + contents: read + pull-requests: write + issues: write + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + # Download the PR Artifact, containing PR Number and PR Labels + - name: Download PR artifact + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }}, + }); + const matchArtifact = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr" + })[0]; + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + const fs = require('fs'); + fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); + + # Unzip the PR Artifact + - name: Unzip PR artifact + run: unzip pr.zip + + # Write the PR Labels into the PR + - name: Write PR labels + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const fs = require('fs'); + + // Read the PR Number and PR Labels from the PR Artifact + // e.g. 'Size: XS\nArch: avr\n' + const issue_number = Number(fs.readFileSync('pr-id.txt')); + const labels = fs.readFileSync('pr-labels.txt', 'utf8') + .split('\n') // Split by newline + .filter(s => (s != '')); // Remove empty lines + console.log({ issue_number, labels }); + + // Write the PR Labels into the PR + // e.g. [ 'Size: XS', 'Arch: avr' ] + await github.rest.issues.setLabels({ + owner, + repo, + issue_number, + labels + }); diff --git a/Application.mk b/Application.mk index e99300644..bc83121a9 100644 --- a/Application.mk +++ b/Application.mk @@ -122,6 +122,7 @@ ifneq ($(strip $(PROGNAME)),) NLIST := $(shell seq 1 $(words $(PROGNAME))) $(foreach i, $(NLIST), \ $(eval PROGNAME_$(word $i,$(PROGOBJ)) := $(word $i,$(PROGNAME))) \ + $(eval PROGSYM_$(word $i,$(PROGOBJ)) := $(subst -,_,$(word $i,$(PROGNAME)))) \ $(eval PROGOBJ_$(word $i,$(PROGLIST)) := $(word $i,$(PROGOBJ))) \ $(eval PRIORITY_$(word $i,$(REGLIST)) := \ $(if $(word $i,$(PRIORITY)),$(word $i,$(PRIORITY)),$(lastword $(PRIORITY)))) \ @@ -133,7 +134,31 @@ ifneq ($(strip $(PROGNAME)),) $(if $(word $i,$(GID)),$(word $i,$(GID)),$(lastword $(GID)))) \ $(eval MODE_$(word $i,$(REGLIST)) := \ $(if $(word $i,$(MODE)),$(word $i,$(MODE)),$(lastword $(MODE)))) \ + $(eval STACKSIZE_$(word $i,$(PROGLIST)) := $(STACKSIZE_$(word $i,$(REGLIST)))) \ + $(eval PRIORITY_$(word $i,$(PROGLIST)) := $(PRIORITY_$(word $i,$(REGLIST)))) \ + $(eval UID_$(word $i,$(PROGLIST)) := $(UID_$(word $i,$(REGLIST)))) \ + $(eval GID_$(word $i,$(PROGLIST)) := $(GID_$(word $i,$(REGLIST)))) \ + $(eval MODE_$(word $i,$(PROGLIST)) := $(MODE_$(word $i,$(REGLIST)))) \ ) + + # Emit the application's configured attributes as absolute ELF symbols + # (nx_stacksize, nx_priority, and nx_uid/nx_gid/nx_mode under + # CONFIG_SCHED_USER_IDENTITY) so the binary loader can recover them. + # Per-target so each PROGLIST entry picks up its own STACKSIZE_/PRIORITY_. + + $(PROGLIST): MODLDFLAGS += \ + $(if $(STACKSIZE_$@),--defsym nx_stacksize=$(STACKSIZE_$@)) \ + $(if $(filter-out SCHED_PRIORITY_DEFAULT,$(PRIORITY_$@)),--defsym nx_priority=$(PRIORITY_$@)) +ifeq ($(CONFIG_SCHED_USER_IDENTITY),y) + $(PROGLIST): MODLDFLAGS += \ + $(if $(UID_$@),--defsym nx_uid=$(UID_$@)) \ + $(if $(GID_$@),--defsym nx_gid=$(GID_$@)) \ + $(if $(MODE_$@),--defsym nx_mode=$(MODE_$@)) +endif + + # Keep the attribute symbols through --strip-unneeded so the binary loader + # can still read them from the stripped runtime image in bin/. + $(PROGLIST): NX_KEEP = $(if $(STACKSIZE_$@),-K nx_stacksize) $(if $(filter-out SCHED_PRIORITY_DEFAULT,$(PRIORITY_$@)),-K nx_priority) $(if $(UID_$@),-K nx_uid) $(if $(GID_$@),-K nx_gid) $(if $(MODE_$@),-K nx_mode) endif # Condition flags @@ -148,7 +173,7 @@ ifeq ($(WASM_BUILD),y) DO_REGISTRATION = n endif -ifeq ($(DYNLIB),y) +ifeq ($(BUILD_MODULE),y) DO_REGISTRATION = n endif @@ -223,7 +248,7 @@ endef # rename "main()" in $1 to "xxx_main()" and save to $2 define RENAMEMAIN $(ECHO_BEGIN)"Rename main() in $1 and save to $2" - $(Q) ${shell cat $1 | sed -e "s/fn[ ]\+main/fn $(addsuffix _main,$(PROGNAME_$@))/" > $2} + $(Q) ${shell cat $1 | sed -e "s/fn[ ]\+main/fn $(addsuffix _main,$(PROGSYM_$@))/" > $2} $(ECHO_END) endef @@ -310,7 +335,7 @@ $(PROGLIST): $(MAINCOBJ) $(MAINCXXOBJ) $(MAINRUSTOBJ) $(MAINZIGOBJ) $(MAINDOBJ) ifneq ($(CONFIG_DEBUG_SYMBOLS),) $(Q) mkdir -p $(BINDIR_DEBUG) $(Q) cp $@ $(BINDIR_DEBUG) - $(Q) $(MODULESTRIP) $@ + $(Q) $(MODULESTRIP) $(NX_KEEP) $@ endif install:: $(PROGLIST) @@ -319,14 +344,14 @@ install:: $(PROGLIST) else $(MAINCXXOBJ): $(PREFIX)%$(CXXEXT)$(SUFFIX)$(OBJEXT): %$(CXXEXT) - $(eval $<_CXXFLAGS += ${shell $(DEFINE) "$(CXX)" main=$(addsuffix _main,$(PROGNAME_$@))}) - $(eval $<_CXXELFFLAGS += ${shell $(DEFINE) "$(CXX)" main=$(addsuffix _main,$(PROGNAME_$@))}) + $(eval $<_CXXFLAGS += ${shell $(DEFINE) "$(CXX)" main=$(addsuffix _main,$(PROGSYM_$@))}) + $(eval $<_CXXELFFLAGS += ${shell $(DEFINE) "$(CXX)" main=$(addsuffix _main,$(PROGSYM_$@))}) $(if $(and $(CONFIG_MODULES),$(MODCXXFLAGS)), \ $(call ELFCOMPILEXX, $<, $@), $(call COMPILEXX, $<, $@)) $(MAINCOBJ): $(PREFIX)%.c$(SUFFIX)$(OBJEXT): %.c - $(eval $<_CFLAGS += ${DEFINE_PREFIX}main=$(addsuffix _main,$(PROGNAME_$@))) - $(eval $<_CELFFLAGS += ${DEFINE_PREFIX}main=$(addsuffix _main,$(PROGNAME_$@))) + $(eval $<_CFLAGS += ${DEFINE_PREFIX}main=$(addsuffix _main,$(PROGSYM_$@))) + $(eval $<_CELFFLAGS += ${DEFINE_PREFIX}main=$(addsuffix _main,$(PROGSYM_$@))) $(if $(and $(CONFIG_MODULES),$(MODCFLAGS)), \ $(call ELFCOMPILE, $<, $@), $(call COMPILE, $<, $@)) @@ -362,10 +387,11 @@ ifeq ($(DO_REGISTRATION),y) $(REGLIST): $(DEPCONFIG) Makefile $(eval PROGNAME_$@ := $(basename $(notdir $@))) + $(eval PROGSYM_$@ := $(subst -,_,$(PROGNAME_$@))) ifeq ($(CONFIG_SCHED_USER_IDENTITY),y) - $(call REGISTER,$(PROGNAME_$@),$(PRIORITY_$@),$(STACKSIZE_$@),$(if $(BUILD_MODULE),,$(PROGNAME_$@)_main),$(UID_$@),$(GID_$@),$(MODE_$@)) + $(call REGISTER,$(PROGNAME_$@),$(PRIORITY_$@),$(STACKSIZE_$@),$(if $(BUILD_MODULE),,$(PROGSYM_$@)_main),$(UID_$@),$(GID_$@),$(MODE_$@)) else - $(call REGISTER,$(PROGNAME_$@),$(PRIORITY_$@),$(STACKSIZE_$@),$(if $(BUILD_MODULE),,$(PROGNAME_$@)_main)) + $(call REGISTER,$(PROGNAME_$@),$(PRIORITY_$@),$(STACKSIZE_$@),$(if $(BUILD_MODULE),,$(PROGSYM_$@)_main)) endif register:: $(REGLIST) @@ -393,6 +419,7 @@ clean:: $(call CLEANAROBJS) $(call CLEAN) distclean:: clean + $(call DELFILE, .kconfig) $(call DELFILE, Make.dep) $(call DELFILE, .depend) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84844e700..55110defb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,12 +55,12 @@ add_subdirectory(fsutils) add_subdirectory(games) add_subdirectory(graphics) add_subdirectory(industry) +add_subdirectory(inertial) add_subdirectory(interpreters) add_subdirectory(logging) add_subdirectory(lte) add_subdirectory(math) add_subdirectory(mlearning) -add_subdirectory(modbus) add_subdirectory(netutils) add_subdirectory(nshlib) add_subdirectory(platform) diff --git a/LICENSE b/LICENSE index 61fb28ab4..84b5055da 100644 --- a/LICENSE +++ b/LICENSE @@ -1282,7 +1282,7 @@ apps/system/telnet/telnet_client.c Copyright (C) 2017 Gregory Nutt. All rights reserved. Author: Gregory Nutt - The original authors of libtelnet are listed below. Per their licesne, + The original authors of libtelnet are listed below. Per their license, "The author or authors of this code dedicate any and all copyright interest in this code to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and @@ -2248,3 +2248,29 @@ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +netutils/bare/ +============== + +MIT License + +Copyright (c) 2022 Frank Smit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index dc179d7c9..204931977 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ NuttX directory. Like: If all of the above conditions are TRUE, then NuttX will be able to find the application directory. If your application directory has a different name or is -location at a different position, then you will have to inform the NuttX build +located at a different position, then you will have to inform the NuttX build system of that location. There are several ways to do that: 1) You can define `CONFIG_APPS_DIR` to be the full path to your application @@ -140,7 +140,7 @@ project. One must: main() ``` - 4. Set the requirements in the file: `Makefile`, specially the lines: + 4. Set the requirements in the file: `Makefile`, specifically the lines: ```makefile PROGNAME = progname diff --git a/audioutils/lame/CMakeLists.txt b/audioutils/lame/CMakeLists.txt index 26899b986..f963cbf3d 100644 --- a/audioutils/lame/CMakeLists.txt +++ b/audioutils/lame/CMakeLists.txt @@ -36,11 +36,20 @@ if(CONFIG_AUDIOUTILS_LAME) list(APPEND CFG_CMDS "--cross-prefix=${CROSSDEV}") endif() + # The revision to check out. See the comment in Makefile: an unpinned + # checkout stops linking on the day lame's trunk grows its next vector tier. + # Raise this deliberately, together with the vector source list below. + + if(NOT DEFINED LAME_VERSION) + set(LAME_VERSION 6720) + endif() + # # Download lame if no lame/configure found if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/lame/configure") execute_process( - COMMAND "svn" "checkout" "https://svn.code.sf.net/p/lame/svn/trunk/lame" - "lame" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + COMMAND "svn" "checkout" "-r" "${LAME_VERSION}" + "https://svn.code.sf.net/p/lame/svn/trunk/lame" "lame" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() # Set source path @@ -75,7 +84,6 @@ if(CONFIG_AUDIOUTILS_LAME) "${SRC_PATH}/libmp3lame/newmdct.c" "${SRC_PATH}/libmp3lame/psymodel.c" "${SRC_PATH}/libmp3lame/quantize.c" - "${SRC_PATH}/libmp3lame/vector/xmm_quantize_sub.c" "${SRC_PATH}/libmp3lame/quantize_pvt.c" "${SRC_PATH}/libmp3lame/set_get.c" "${SRC_PATH}/libmp3lame/vbrquantize.c" @@ -87,6 +95,21 @@ if(CONFIG_AUDIOUTILS_LAME) "${SRC_PATH}/libmp3lame/version.c" "${SRC_PATH}/libmp3lame/presets.c") + if(CONFIG_HOST_X86 OR CONFIG_HOST_X86_64) + list( + APPEND + CSRCS + "${SRC_PATH}/libmp3lame/vector/xmm_quantize_sub.c" + "${SRC_PATH}/libmp3lame/vector/xmm_choose_table.c" + "${SRC_PATH}/libmp3lame/vector/xmm_quantize_lines.c" + "${SRC_PATH}/libmp3lame/vector/xmm_calc_sfb_noise.c" + "${SRC_PATH}/libmp3lame/vector/avx2_choose_table.c" + "${SRC_PATH}/libmp3lame/vector/avx2_quantize_lines.c" + "${SRC_PATH}/libmp3lame/vector/avx512_choose_table.c" + "${SRC_PATH}/libmp3lame/vector/avx512_quantize_lines.c" + "${SRC_PATH}/libmp3lame/vector/avx512_calc_sfb_noise.c") + endif() + # Add custom target to generate config_h set(CONFIG_H_FILE "${CMAKE_CURRENT_SOURCE_DIR}/lame/configure") set(CONFIG_OPTIONS diff --git a/audioutils/lame/Makefile b/audioutils/lame/Makefile index 99526d63b..cf2daf24e 100644 --- a/audioutils/lame/Makefile +++ b/audioutils/lame/Makefile @@ -20,10 +20,19 @@ # ############################################################################ +# The revision to check out. lame's trunk grows vector tiers over time -- +# SSE2, then AVX2, then AVX-512 -- and each one adds sources that this file +# and CMakeLists.txt have to name, so an unpinned checkout stops linking on +# the day upstream adds the next one. Raise this deliberately, together with +# the vector source list below. + +LAME_VERSION ?= 6720 + # Download lame if no lame/configure found lame-svn: $(Q) echo "svn checkout lame ..." - $(Q) svn checkout https://svn.code.sf.net/p/lame/svn/trunk/lame lame + $(Q) svn checkout -r $(LAME_VERSION) \ + https://svn.code.sf.net/p/lame/svn/trunk/lame lame ifeq ($(wildcard lame/configure),) context:: lame-svn @@ -72,7 +81,6 @@ CSRCS += $(SRC_PATH)/libmp3lame/bitstream.c \ $(SRC_PATH)/libmp3lame/newmdct.c \ $(SRC_PATH)/libmp3lame/psymodel.c \ $(SRC_PATH)/libmp3lame/quantize.c \ - $(SRC_PATH)/libmp3lame/vector/xmm_quantize_sub.c \ $(SRC_PATH)/libmp3lame/quantize_pvt.c \ $(SRC_PATH)/libmp3lame/set_get.c \ $(SRC_PATH)/libmp3lame/vbrquantize.c \ @@ -84,6 +92,23 @@ CSRCS += $(SRC_PATH)/libmp3lame/bitstream.c \ $(SRC_PATH)/libmp3lame/version.c \ $(SRC_PATH)/libmp3lame/presets.c +# lame's configure enables the SSE2, AVX2 and AVX-512 dispatch paths whenever +# the host compiler accepts their per-function target attributes, and the +# scalar code then calls into them, so the whole group has to be built on an +# x86 host. Other hosts keep lame's scalar implementation. + +ifneq ($(CONFIG_HOST_X86)$(CONFIG_HOST_X86_64),) +CSRCS += $(SRC_PATH)/libmp3lame/vector/xmm_quantize_sub.c \ + $(SRC_PATH)/libmp3lame/vector/xmm_choose_table.c \ + $(SRC_PATH)/libmp3lame/vector/xmm_quantize_lines.c \ + $(SRC_PATH)/libmp3lame/vector/xmm_calc_sfb_noise.c \ + $(SRC_PATH)/libmp3lame/vector/avx2_choose_table.c \ + $(SRC_PATH)/libmp3lame/vector/avx2_quantize_lines.c \ + $(SRC_PATH)/libmp3lame/vector/avx512_choose_table.c \ + $(SRC_PATH)/libmp3lame/vector/avx512_quantize_lines.c \ + $(SRC_PATH)/libmp3lame/vector/avx512_calc_sfb_noise.c +endif + LAME_CONFIG_SCRIPT := $(CURDIR)$(DELIM)lame$(DELIM)configure $(DST_PATH)/config.h: @@ -102,6 +127,6 @@ ifneq ($(PREFIX),) endif distclean:: - $(Q)cd $(DST_PATH) && make distclean + $(Q)if [ -d $(DST_PATH) ]; then cd $(DST_PATH) && $(MAKE) distclean; fi include $(APPDIR)/Application.mk diff --git a/audioutils/mml_parser/mml_parser.c b/audioutils/mml_parser/mml_parser.c index de7450f71..033bbbfde 100644 --- a/audioutils/mml_parser/mml_parser.c +++ b/audioutils/mml_parser/mml_parser.c @@ -169,15 +169,33 @@ static int calc_samples(int fs, int tempo, int num, int dots) switch (num) { - case 0: n = 3; break; - case 1: n = 2; break; - case 2: n = 1; break; - case 4: n = 0; break; - case 8: div = 1; break; - case 16: div = 2; break; - case 32: div = 3; break; - case 64: div = 4; break; - default: div = -1; break; + case 0: + n = 3; + break; + case 1: + n = 2; + break; + case 2: + n = 1; + break; + case 4: + n = 0; + break; + case 8: + div = 1; + break; + case 16: + div = 2; + break; + case 32: + div = 3; + break; + case 64: + div = 4; + break; + default: + div = -1; + break; } if (dots <= 4) diff --git a/audioutils/morsey/.gitignore b/audioutils/morsey/.gitignore new file mode 100644 index 000000000..26b43c946 --- /dev/null +++ b/audioutils/morsey/.gitignore @@ -0,0 +1 @@ +morsey diff --git a/audioutils/morsey/Kconfig b/audioutils/morsey/Kconfig new file mode 100644 index 000000000..4f8b2f98d --- /dev/null +++ b/audioutils/morsey/Kconfig @@ -0,0 +1,6 @@ +config AUDIOUTILS_MORSEY + bool "Morse code playing library" + default n + ---help--- + Simple library for parsing ASCII text into playable Morse code. Not + necessarily restricted to audio. diff --git a/audioutils/morsey/Make.defs b/audioutils/morsey/Make.defs new file mode 100644 index 000000000..9fab9a304 --- /dev/null +++ b/audioutils/morsey/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/audioutils/morsey/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_AUDIOUTILS_MORSEY),) +CONFIGURED_APPS += $(APPDIR)/audioutils/morsey +endif diff --git a/audioutils/morsey/Makefile b/audioutils/morsey/Makefile new file mode 100644 index 000000000..04f454e64 --- /dev/null +++ b/audioutils/morsey/Makefile @@ -0,0 +1,50 @@ +############################################################################ +# apps/audioutils/morsey/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +CSRCS = $(MORSEY_DIR)/morsey.c + +MORSEY_DIR = morsey +MORSEY_VERSION = v1.0.1 +MORSEY_MORSEY_H_SHA256 = 55f45e7b9a4d810f928ffe3ee9bea4cc318e28b1f49abf9512aee2638bd1dbfa +MORSEY_MORSEY_C_SHA256 = 12babc90d3518e026c344c38dc3701287c41fd6b41a3ec96fb36c77907be78f8 + +$(MORSEY_DIR): + $(Q) echo "Cloning Morsey repo..." + $(Q) git clone --depth=1 --branch=$(MORSEY_VERSION) https://github.com/linguini1/morsey.git + $(Q) touch $(MORSEY_DIR) + $(Q) echo "Checking Morsey hashes..." + $(Q) $(APPDIR)/tools/check-hash.sh sha256 $(MORSEY_MORSEY_H_SHA256) $@/morsey.h + $(Q) $(APPDIR)/tools/check-hash.sh sha256 $(MORSEY_MORSEY_C_SHA256) $@/morsey.c + +create_includes: $(MORSEY_DIR)/morsey.h + $(Q) cp $< $(APPDIR)/include/audioutils + +context:: $(MORSEY_DIR) + $(Q) $(MAKE) create_includes + +distclean:: + $(call DELFILE, $(APPDIR)/include/audioutils/morsey.h) + $(call DELDIR, morsey) + +include $(APPDIR)/Application.mk diff --git a/audioutils/rtttl-c/.gitignore b/audioutils/rtttl-c/.gitignore new file mode 100644 index 000000000..554f93513 --- /dev/null +++ b/audioutils/rtttl-c/.gitignore @@ -0,0 +1 @@ +/rtttl-c diff --git a/audioutils/rtttl-c/Kconfig b/audioutils/rtttl-c/Kconfig new file mode 100644 index 000000000..8ea3355d5 --- /dev/null +++ b/audioutils/rtttl-c/Kconfig @@ -0,0 +1,21 @@ +config AUDIOUTILS_RTTTL_C + bool "RTTTL parsing library" + default n + ---help--- + Simple library for parsing Ring Tone Text Transfer Language (RTTTL) + + Define how to make a sound: + + void + play_tone(struct rtttl_tone tone) + { + /* TODO: Make a sound with + * tone.frequency_100hz + * tone.period_us + * tone.duration_us + */ + } + + and then play RTTTL string: + + rtttl_play("...", play_tone); diff --git a/audioutils/rtttl-c/Make.defs b/audioutils/rtttl-c/Make.defs new file mode 100644 index 000000000..fa9aa98f8 --- /dev/null +++ b/audioutils/rtttl-c/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/audioutils/rtttl-c/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_AUDIOUTILS_RTTTL_C),) +CONFIGURED_APPS += $(APPDIR)/audioutils/rtttl-c +endif diff --git a/audioutils/rtttl-c/Makefile b/audioutils/rtttl-c/Makefile new file mode 100644 index 000000000..aa290169d --- /dev/null +++ b/audioutils/rtttl-c/Makefile @@ -0,0 +1,50 @@ +############################################################################ +# apps/audioutils/rtttl-c/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +CSRCS = $(RTTTL_C_DIR)/rtttl.c + +RTTTL_C_DIR = rtttl-c +RTTTL_C_VERSION = v0.1.0 +RTTTL_C_RTTTL_H_SHA256 = 6331174ae34a419ad427d9755f8ba9d3b6a92c09ecc4f36bbe07ec5e4d0cc007 +RTTTL_C_RTTTL_C_SHA256 = f0db64ef0b68136bac24616a3d62508bcc54ccdd68265d6447d99b264653b7b9 + +$(RTTTL_C_DIR): + $(Q) echo "Cloning rtttl-c repo..." + $(Q) git clone --depth=1 --branch=$(RTTTL_C_VERSION) https://git.sr.ht/~qeef/rtttl-c + $(Q) touch $(RTTTL_C_DIR) + $(Q) echo "Checking rtttl-c hashes..." + $(Q) $(APPDIR)/tools/check-hash.sh sha256 $(RTTTL_C_RTTTL_H_SHA256) $@/rtttl.h + $(Q) $(APPDIR)/tools/check-hash.sh sha256 $(RTTTL_C_RTTTL_C_SHA256) $@/rtttl.c + +create_includes: $(RTTTL_C_DIR)/rtttl.h + $(Q) cp $< $(APPDIR)/include/audioutils + +context:: $(RTTTL_C_DIR) + $(Q) $(MAKE) create_includes + +distclean:: + $(call DELFILE, $(APPDIR)/include/audioutils/rtttl.h) + $(call DELDIR, rtttl-c) + +include $(APPDIR)/Application.mk diff --git a/benchmarks/cyclictest/cyclictest.c b/benchmarks/cyclictest/cyclictest.c index 1727c54f1..fb9382dd5 100644 --- a/benchmarks/cyclictest/cyclictest.c +++ b/benchmarks/cyclictest/cyclictest.c @@ -443,8 +443,8 @@ static inline void tsnorm(struct timespec *ts) static inline int64_t timediff_us(struct timespec t1, struct timespec t2) { int64_t ret; - ret = 1000000 * (int64_t) ((int) t1.tv_sec - (int) t2.tv_sec); - ret += (int64_t) ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000; + ret = 1000000 * (t1.tv_sec - t2.tv_sec); + ret += (t1.tv_nsec - t2.tv_nsec) / 1000; return ret; } @@ -458,11 +458,11 @@ static inline int64_t timediff_us_timer(struct timer_status_s after, t2 = after.timeleft; if (t2 < t1) { - ret = (int64_t) (t1 - t2); + ret = t1 - t2; } else { - ret = (int64_t) (after.timeout - (t2 - t1)); + ret = after.timeout - (t2 - t1); } return ret; diff --git a/benchmarks/mtd/mtd.c b/benchmarks/mtd/mtd.c index 475fd1c66..b9d3d8a98 100644 --- a/benchmarks/mtd/mtd.c +++ b/benchmarks/mtd/mtd.c @@ -102,10 +102,10 @@ int main(int argc, FAR char *argv[]) /* Report the device structure */ printf("FLASH device parameters:\n"); - printf(" Sector size: %10d\n", info.sectorsize); - printf(" Sector count: %10d\n", info.numsectors); - printf(" Erase block: %10" PRIx32 "\n", geo.erasesize); - printf(" Total size: %10d\n", info.sectorsize * info.numsectors); + printf(" Sector size: %10d\n", info.sectorsize); + printf(" Sector count: %10d\n", info.numsectors); + printf(" Erase block size: %10" PRIu32 "\n", geo.erasesize); + printf(" Total size: %10d\n", info.sectorsize * info.numsectors); if (info.sectorsize != geo.erasesize) { @@ -124,7 +124,7 @@ int main(int argc, FAR char *argv[]) goto errout_with_driver; } - /* Fill the buffer with known data and print it in hex format */ + /* Fill the buffer with known data */ for (int i = 0; i < info.sectorsize; i++) { diff --git a/benchmarks/osperf/osperf.c b/benchmarks/osperf/osperf.c index 0e71a4e05..757fb4216 100644 --- a/benchmarks/osperf/osperf.c +++ b/benchmarks/osperf/osperf.c @@ -39,6 +39,7 @@ #include #include +#include /**************************************************************************** * Private Types @@ -129,7 +130,7 @@ static size_t performance_gettime(FAR struct performance_time_s *result) } /**************************************************************************** - * Pthread swtich performance + * Pthread switch performance ****************************************************************************/ static FAR void *pthread_switch_task(FAR void *arg) diff --git a/benchmarks/ramspeed/CMakeLists.txt b/benchmarks/ramspeed/CMakeLists.txt new file mode 100644 index 000000000..a8b0df091 --- /dev/null +++ b/benchmarks/ramspeed/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/benchmarks/ramspeed/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_BENCHMARK_RAMSPEED) + nuttx_add_application( + NAME + ${CONFIG_BENCHMARK_RAMSPEED_PROGNAME} + SRCS + ramspeed_main.c + STACKSIZE + ${CONFIG_BENCHMARK_RAMSPEED_STACKSIZE} + PRIORITY + ${CONFIG_BENCHMARK_RAMSPEED_PRIORITY}) +endif() diff --git a/benchmarks/sd_bench/sd_bench_main.c b/benchmarks/sd_bench/sd_bench_main.c index 8a48a9128..9822ac748 100644 --- a/benchmarks/sd_bench/sd_bench_main.c +++ b/benchmarks/sd_bench/sd_bench_main.c @@ -130,8 +130,8 @@ static uint64_t get_time_delta_us(const struct timespec *start, const struct timespec *end) { uint64_t elapsed; - elapsed = (((uint64_t)end->tv_sec * NSEC_PER_SEC) + end->tv_nsec); - elapsed -= (((uint64_t)start->tv_sec * NSEC_PER_SEC) + start->tv_nsec); + elapsed = (end->tv_sec * NSEC_PER_SEC) + end->tv_nsec; + elapsed -= (start->tv_sec * NSEC_PER_SEC) + start->tv_nsec; return elapsed / 1000.; } @@ -451,7 +451,7 @@ int main(int argc, char *argv[]) cfg.run_duration *= 1000; bench_fd = open(BENCHMARK_FILE, - O_CREAT | (verify ? O_RDWR : O_WRONLY) | O_TRUNC); + O_CREAT | (verify ? O_RDWR : O_WRONLY) | O_TRUNC, 0666); if (bench_fd < 0) { diff --git a/benchmarks/tacle-bench/0001-tacle-bench-add-makefile-and-all-in-one-main-file.patch b/benchmarks/tacle-bench/0001-tacle-bench-add-makefile-and-all-in-one-main-file.patch index 4958ac5a8..fb1e2e400 100644 --- a/benchmarks/tacle-bench/0001-tacle-bench-add-makefile-and-all-in-one-main-file.patch +++ b/benchmarks/tacle-bench/0001-tacle-bench-add-makefile-and-all-in-one-main-file.patch @@ -1,10 +1,11 @@ -From 71f51cd5ca4f492a464fb12d5bce91e5fbba300a Mon Sep 17 00:00:00 2001 +From 6d0be04a4b4208447e75c369e9811d089f466739 Mon Sep 17 00:00:00 2001 From: ouyangxiangzhen -Date: Tue, 11 Jun 2024 16:01:23 +0800 +Date: Thu, 3 Apr 2025 11:59:02 +0800 Subject: [PATCH] tacle-bench: add makefile and all-in-one main file The original taclebench is used for WCET analysis. This commit allows most taclebench test cases (except parallel test cases) to be compiled and executed. +Change-Id: I707b8ac58d3ddc4b7974c5bedecac1a7b5c887f9 Signed-off-by: ouyangxiangzhen --- Makefile | 12 + @@ -65,14 +66,14 @@ Signed-off-by: ouyangxiangzhen bench/test/cover/cover.c | 3 + bench/test/duff/duff.c | 3 + bench/test/test3/test3.c | 3 + - taclebench.c | 349 ++++++++++++++++++ - 59 files changed, 532 insertions(+) + taclebench.c | 366 ++++++++++++++++++ + 59 files changed, 549 insertions(+) create mode 100644 Makefile create mode 100644 taclebench.c diff --git a/Makefile b/Makefile new file mode 100644 -index 0000000..2385c61 +index 0000000..75ca0a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ @@ -83,7 +84,7 @@ index 0000000..2385c61 +TEST_SRCS := $(shell find bench/test -name "*.c") + +all: -+ cc -DALL_IN_ONE ${APP_SRCS} ${KERNEL_SRCS} ${SEQUENTIAL_SRCS} ${TEST_SRCS} bench/kernel/cosf/wcclibm.c taclebench.c -static -o taclebench ++ cc -DALL_IN_ONE ${APP_SRCS} ${KERNEL_SRCS} ${SEQUENTIAL_SRCS} ${TEST_SRCS} bench/kernel/cosf/wcclibm.c taclebench.c -static -o taclebench -O3 + +clean: + rm -f taclebench @@ -888,11 +889,12 @@ index 0235738..6eaf8c2 100755 diff --git a/taclebench.c b/taclebench.c new file mode 100644 -index 0000000..1231b87 +index 0000000..aaff1bb --- /dev/null +++ b/taclebench.c -@@ -0,0 +1,349 @@ +@@ -0,0 +1,366 @@ +#include ++#include + +int main_epic(void); +int main_mpeg2(void); @@ -954,6 +956,10 @@ index 0000000..1231b87 + +int main(void) +{ ++ struct timespec start_ts; ++ struct timespec end_ts; ++ clock_gettime(CLOCK_MONOTONIC, &start_ts); ++ + if (main_epic() != 0) + { + printf("main_epic error\n"); @@ -1239,6 +1245,18 @@ index 0000000..1231b87 + printf("main_bitonic error\n"); + } + ++ clock_gettime(CLOCK_MONOTONIC, &end_ts); ++ long sec_diff = end_ts.tv_sec - start_ts.tv_sec; ++ long nsec_diff = end_ts.tv_nsec - start_ts.tv_nsec; ++ ++ if (nsec_diff < 0) ++ { ++ sec_diff -= 1; ++ nsec_diff += 1000000000; ++ } ++ ++ printf("%ld.%09ld seconds\n", sec_diff, nsec_diff); ++ + return 0; +} -- diff --git a/benchmarks/whetstone/CMakeLists.txt b/benchmarks/whetstone/CMakeLists.txt new file mode 100644 index 000000000..1bcc0f256 --- /dev/null +++ b/benchmarks/whetstone/CMakeLists.txt @@ -0,0 +1,31 @@ +# ############################################################################## +# apps/benchmarks/whetstone/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_BENCHMARK_WHETSTONE) + nuttx_add_application( + NAME + ${CONFIG_BENCHMARK_WHETSTONE_PROGNAME} + SRCS + whetstone.c + STACKSIZE + ${CONFIG_BENCHMARK_WHETSTONE_STACKSIZE} + PRIORITY + ${CONFIG_BENCHMARK_WHETSTONE_PRIORITY}) +endif() diff --git a/benchmarks/whetstone/Kconfig b/benchmarks/whetstone/Kconfig new file mode 100644 index 000000000..10df1429e --- /dev/null +++ b/benchmarks/whetstone/Kconfig @@ -0,0 +1,34 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config BENCHMARK_WHETSTONE + tristate "Double Precision Benchmark" + default n + depends on ALLOW_CUSTOM_PERMISSIVE_COMPONENTS + ---help--- + Enable a Whetstone test. + + This benchmark is based on the C converted version from + https://www.netlib.org/benchmark/whetstone.c which has a + custom permissive license requiring attribution. + +if BENCHMARK_WHETSTONE + +config BENCHMARK_WHETSTONE_PROGNAME + string "Program name" + default "whetstone" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config BENCHMARK_WHETSTONE_PRIORITY + int "Whetstone test task priority" + default 100 + +config BENCHMARK_WHETSTONE_STACKSIZE + int "Whetstone test stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/benchmarks/whetstone/Make.defs b/benchmarks/whetstone/Make.defs new file mode 100644 index 000000000..d9fa9f1af --- /dev/null +++ b/benchmarks/whetstone/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/benchmarks/whetstone/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_BENCHMARK_WHETSTONE),) +CONFIGURED_APPS += $(APPDIR)/benchmarks/whetstone +endif diff --git a/benchmarks/whetstone/Makefile b/benchmarks/whetstone/Makefile new file mode 100644 index 000000000..1bdf6dfd6 --- /dev/null +++ b/benchmarks/whetstone/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/benchmarks/whetstone/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# WHETSTONE speed test + +PROGNAME = $(CONFIG_BENCHMARK_WHETSTONE_PROGNAME) +PRIORITY = $(CONFIG_BENCHMARK_WHETSTONE_PRIORITY) +STACKSIZE = $(CONFIG_BENCHMARK_WHETSTONE_STACKSIZE) +MODULE = $(CONFIG_BENCHMARK_WHETSTONE) + +MAINSRC = whetstone.c + +include $(APPDIR)/Application.mk diff --git a/benchmarks/whetstone/whetstone.c b/benchmarks/whetstone/whetstone.c new file mode 100644 index 000000000..dbc8247fd --- /dev/null +++ b/benchmarks/whetstone/whetstone.c @@ -0,0 +1,520 @@ +/**************************************************************************** + * apps/benchmarks/whetstone/whetstone.c + * + * SPDX-License-Identifier: LicenseRef-Painter-Engineering-Whetstone + * + * Converted Whetstone Double Precision Benchmark + * Version 1.2 22 March 1998 + * + * (c) Copyright 1998 Painter Engineering, Inc. + * All Rights Reserved. + * + * Permission is granted to use, duplicate, and + * publish this text and program as long as it + * includes this entire comment block and limited + * rights reference. + * + * Converted by Rich Painter, Painter Engineering, Inc. based on the + * www.netlib.org benchmark/whetstoned version obtained 16 March 1998. + * + * A novel approach was used here to keep the look and feel of the + * FORTRAN version. Altering the FORTRAN-based array indices, + * starting at element 1, to start at element 0 for C, would require + * numerous changes, including decrementing the variable indices by 1. + * Instead, the array e1[] was declared 1 element larger in C. This + * allows the FORTRAN index range to function without any literal or + * variable indices changes. The array element e1[0] is simply never + * used and does not alter the benchmark results. + * + * The major FORTRAN comment blocks were retained to minimize + * differences between versions. Modules N5 and N12, like in the + * FORTRAN version, have been eliminated here. + * + * An optional command-line argument has been provided [-c] to + * offer continuous repetition of the entire benchmark. + * An optional argument for setting an alternate loop count is also + * provided. Define PRINTOUT to cause the pout() function to print + * outputs at various stages. Final timing measurements should be + * made with the PRINTOUT undefined. + * + * Questions and comments may be directed to the author at + * r.painter@ieee.org + ****************************************************************************/ + +/**************************************************************************** + * Benchmark #2 -- Double Precision Whetstone (A001) + * + * o This is a REAL*8 version of + * the Whetstone benchmark program. + * + * o DO-loop semantics are ANSI-66 compatible. + * + * o Final measurements are to be made with all + * WRITE statements and FORMAT sttements removed. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* standard C library headers required */ + +#include +#include +#include +#include + +/* the following is optional depending on the timing function used */ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* map the FORTRAN math functions, etc. to the C versions */ + +#define DSIN sin +#define DCOS cos +#define DATAN atan +#define DLOG log +#define DEXP exp +#define DSQRT sqrt +#define IF if +#define USAGE "usage: whetdc [-c] [loops]\n" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* function prototypes */ + +void pout(long n, long j, long k, double x1, + double x2, double x3, double x4); +void pa(double e[]); +void p0(void); +void p3(double x, double y, FAR double *z); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* COMMON t,t1,t2,e1(4),j,k,l */ + +double t; +double t1; +double t2; +double e1[5]; +int j; +int k; +int l; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + /* used in the FORTRAN version */ + + long loop; + long i; + long n1; + long n2; + long n3; + long n4; + long n6; + long n7; + long n8; + long n9; + long n10; + long n11; + double x1; + double x2; + double x3; + double x4; + double x; + double y; + double z; + int ii; + int jj; + + /* added for this version */ + + long loopstart; + long startmsec; + long finimsec; + float KIPS; + int continuous; + struct timespec ts; + + loopstart = 1000; /* see the note about loop below */ + continuous = 0; + + ii = 1; /* start at the first arg (temp use of ii here) */ + while (ii < argc) + { + if (strncmp(argv[ii], "-c", 2) == 0 || argv[ii][0] == 'c') + { + continuous = 1; + } + else if (atol(argv[ii]) > 0) + { + loopstart = atol(argv[ii]); + } + else + { + fprintf(stderr, USAGE); + return 1; + } + + ii++; + } + +LCONT: + + /* Start benchmark timing at this point. */ + + clock_gettime(CLOCK_REALTIME, &ts); + startmsec = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + + /* The actual benchmark starts here. */ + + t = .499975; + t1 = 0.50025; + t2 = 2.0; + + /* With loopcount loop=10, one million Whetstone instructions + * will be executed in EACH MAJOR loop..A MAJOR loop IS EXECUTED + * 'ii' TIMES TO INCREASE WALL-CLOCK TIMING ACCURACY. + * + * loop = 1000; + */ + + loop = loopstart; + ii = 1; + + jj = 1; + +IILOOP: + n1 = 0; + n2 = 12 * loop; + n3 = 14 * loop; + n4 = 345 * loop; + n6 = 210 * loop; + n7 = 32 * loop; + n8 = 899 * loop; + n9 = 616 * loop; + n10 = 0; + n11 = 93 * loop; + + /* Module 1: Simple identifiers */ + + x1 = 1.0; + x2 = -1.0; + x3 = -1.0; + x4 = -1.0; + + for (i = 1; i <= n1; i++) + { + x1 = (x1 + x2 + x3 - x4) * t; + x2 = (x1 + x2 - x3 + x4) * t; + x3 = (x1 - x2 + x3 + x4) * t; + x4 = (-x1 + x2 + x3 + x4) * t; + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n1, n1, n1, x1, x2, x3, x4); + } +#endif + + /* Module 2: Array elements */ + + e1[1] = 1.0; + e1[2] = -1.0; + e1[3] = -1.0; + e1[4] = -1.0; + + for (i = 1; i <= n2; i++) + { + e1[1] = (e1[1] + e1[2] + e1[3] - e1[4]) * t; + e1[2] = (e1[1] + e1[2] - e1[3] + e1[4]) * t; + e1[3] = (e1[1] - e1[2] + e1[3] + e1[4]) * t; + e1[4] = (-e1[1] + e1[2] + e1[3] + e1[4]) * t; + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n2, n3, n2, e1[1], e1[2], e1[3], e1[4]); + } +#endif + + /* Module 3: Array as parameter */ + + for (i = 1; i <= n3; i++) + { + pa(e1); + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n3, n2, n2, e1[1], e1[2], e1[3], e1[4]); + } +#endif + + /* Module 4: Conditional jumps */ + + j = 1; + for (i = 1; i <= n4; i++) + { + if (j == 1) + { + j = 2; + } + else + { + j = 3; + } + + if (j > 2) + { + j = 0; + } + else + { + j = 1; + } + + if (j < 1) + { + j = 1; + } + else + { + j = 0; + } + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n4, j, j, x1, x2, x3, x4); + } +#endif + +/* Module 5: Omitted + * Module 6: Integer arithmetic + */ + + j = 1; + k = 2; + l = 3; + + for (i = 1; i <= n6; i++) + { + j = j * (k - j) * (l - k); + k = l * k - (l - j) * k; + l = (l - k) * (k + j); + e1[l - 1] = j + k + l; + e1[k - 1] = j * k * l; + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n6, j, k, e1[1], e1[2], e1[3], e1[4]); + } +#endif + + /* Module 7: Trigonometric functions */ + + x = 0.5; + y = 0.5; + + for (i = 1; i <= n7; i++) + { + x = t * DATAN(t2 * DSIN(x) * DCOS(x) / + (DCOS(x + y) + DCOS(x - y) - 1.0)); + y = t * DATAN(t2 * DSIN(y) * DCOS(y) / + (DCOS(x + y) + DCOS(x - y) - 1.0)); + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n7, j, k, x, x, y, y); + } +#endif + + /* Module 8: Procedure calls */ + + x = 1.0; + y = 1.0; + z = 1.0; + + for (i = 1; i <= n8; i++) + { + p3(x, y, &z); + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n8, j, k, x, y, z, z); + } +#endif + + /* Module 9: Array references */ + + j = 1; + k = 2; + l = 3; + e1[1] = 1.0; + e1[2] = 2.0; + e1[3] = 3.0; + + for (i = 1; i <= n9; i++) + { + p0(); + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n9, j, k, e1[1], e1[2], e1[3], e1[4]); + } +#endif + + /* Module 10: Integer arithmetic */ + + j = 2; + k = 3; + + for (i = 1; i <= n10; i++) + { + j = j + k; + k = j + k; + j = k - j; + k = k - j - j; + } + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n10, j, k, x1, x2, x3, x4); + } +#endif + + /* Module 11: Standard functions */ + + x = 0.75; + + for (i = 1; i <= n11; i++) + x = DSQRT(DEXP(DLOG(x) / t1)); + +#ifdef PRINTOUT + IF (jj == ii) + { + pout(n11, j, k, x, x, x, x); + } +#endif + + /* THIS IS THE END OF THE MAJOR loop. */ + + if (++jj <= ii) + { + goto IILOOP; + } + + /* Stop benchmark timing at this point. */ + + clock_gettime(CLOCK_REALTIME, &ts); + finimsec = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + + /* Performance in Whetstone KIP's per second is given by + * + * (100*loop*ii)/TIME + * + * where TIME is in seconds. + */ + + printf("\n"); + if (finimsec - startmsec <= 0) + { + printf("Insufficient duration- Increase the loop count\n"); + return 1; + } + + printf("Loops: %ld, Iterations: %d, Duration: %ld millisecond.\n", + loop, ii, finimsec - startmsec); + + KIPS = (100.0 * loop * ii) / ((float)(finimsec - startmsec) * 1000); + if (KIPS >= 1000.0) + { + printf("C Converted Double Precision Whetstones: %.1f MIPS\n", + KIPS / 1000.0); + } + else + { + printf("C Converted Double Precision Whetstones: %.1f KIPS\n", KIPS); + } + + if (continuous) + { + goto LCONT; + } + + return 0; +} + +void +pa(double e[]) +{ + j = 0; + +L10: + e[1] = (e[1] + e[2] + e[3] - e[4]) * t; + e[2] = (e[1] + e[2] - e[3] + e[4]) * t; + e[3] = (e[1] - e[2] + e[3] + e[4]) * t; + e[4] = (-e[1] + e[2] + e[3] + e[4]) / t2; + j += 1; + + if (j < 6) + { + goto L10; + } +} + +void +p0(void) +{ + e1[j] = e1[k]; + e1[k] = e1[l]; + e1[l] = e1[j]; +} + +void p3(double x, double y, FAR double *z) +{ + double x1; + double y1; + + x1 = x; + y1 = y; + x1 = t * (x1 + y1); + y1 = t * (x1 + y1); + *z = (x1 + y1) / t2; +} + +#ifdef PRINTOUT +void +pout(long n, long j, long k, double x1, double x2, double x3, double x4) +{ + printf("%7ld %7ld %7ld %12.4e %12.4e %12.4e %12.4e\n", + n, j, k, x1, x2, x3, x4); +} +#endif diff --git a/boot/mcuboot/CMakeLists.txt b/boot/mcuboot/CMakeLists.txt index 2ea3e7137..49c02a133 100644 --- a/boot/mcuboot/CMakeLists.txt +++ b/boot/mcuboot/CMakeLists.txt @@ -45,6 +45,9 @@ if(CONFIG_BOOT_MCUBOOT) set(SRCS mcuboot/boot/bootutil/src/boot_record.c + mcuboot/boot/bootutil/src/bootutil_area.c + mcuboot/boot/bootutil/src/bootutil_img_hash.c + mcuboot/boot/bootutil/src/bootutil_loader.c mcuboot/boot/bootutil/src/bootutil_misc.c mcuboot/boot/bootutil/src/bootutil_public.c mcuboot/boot/bootutil/src/caps.c diff --git a/boot/mcuboot/Kconfig b/boot/mcuboot/Kconfig index f5c904a7a..656861d73 100644 --- a/boot/mcuboot/Kconfig +++ b/boot/mcuboot/Kconfig @@ -22,11 +22,11 @@ config MCUBOOT_REPOSITORY config MCUBOOT_VERSION string "MCUboot version" - default "fefc398cc13ebbc527e297fe9df78cd98a359d75" + default "e36d3c8d6ca5e0ed1f510f2a7d110f405a7c396b" ---help--- Defines MCUboot version to be downloaded. Either release tag or commit hash should be specified. Using newer MCUboot version - may cause compatability issues. + may cause compatibility issues. config MCUBOOT_ENABLE_LOGGING bool "Enable MCUboot logging" diff --git a/boot/mcuboot/Makefile b/boot/mcuboot/Makefile index e6851be93..6c036147f 100644 --- a/boot/mcuboot/Makefile +++ b/boot/mcuboot/Makefile @@ -44,6 +44,9 @@ endif CFLAGS += -Wno-undef -Wno-unused-but-set-variable CSRCS := $(MCUBOOT_UNPACK)/boot/bootutil/src/boot_record.c \ + $(MCUBOOT_UNPACK)/boot/bootutil/src/bootutil_area.c \ + $(MCUBOOT_UNPACK)/boot/bootutil/src/bootutil_img_hash.c \ + $(MCUBOOT_UNPACK)/boot/bootutil/src/bootutil_loader.c \ $(MCUBOOT_UNPACK)/boot/bootutil/src/bootutil_misc.c \ $(MCUBOOT_UNPACK)/boot/bootutil/src/bootutil_public.c \ $(MCUBOOT_UNPACK)/boot/bootutil/src/caps.c \ diff --git a/boot/miniboot/miniboot_main.c b/boot/miniboot/miniboot_main.c index 63f47465c..ce490c45b 100644 --- a/boot/miniboot/miniboot_main.c +++ b/boot/miniboot/miniboot_main.c @@ -39,7 +39,7 @@ * Name: miniboot_main * * Description: - * Minimal bootlaoder entry point. + * Minimal bootloader entry point. * ****************************************************************************/ @@ -49,10 +49,6 @@ int main(int argc, FAR char *argv[]) syslog(LOG_INFO, "*** miniboot ***\n"); - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - #ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ diff --git a/boot/nxboot/include/nxboot.h b/boot/nxboot/include/nxboot.h index 6821cc703..3d92e9753 100644 --- a/boot/nxboot/include/nxboot.h +++ b/boot/nxboot/include/nxboot.h @@ -169,7 +169,7 @@ enum progress_msg_e ****************************************************************************/ #ifdef CONFIG_NXBOOT_PROGRESS -void nxboot_progress(enum progress_type_e type, ...); +void nxboot_progress(int type, ...); #else #define nxboot_progress(type, ...) do {} while (0) #endif diff --git a/boot/nxboot/loader/boot.c b/boot/nxboot/loader/boot.c index 6de9659ac..c4f936d23 100644 --- a/boot/nxboot/loader/boot.c +++ b/boot/nxboot/loader/boot.c @@ -417,6 +417,7 @@ static int perform_update(struct nxboot_state *state, bool check_only) syslog(LOG_INFO, "Creating recovery image.\n"); nxboot_progress(nxboot_progress_start, recovery_create); copy_partition(primary, recovery, state, false); + flash_partition_flush(recovery); nxboot_progress(nxboot_progress_end); nxboot_progress(nxboot_progress_start, validate_recovery); successful = validate_image(recovery); @@ -444,6 +445,8 @@ static int perform_update(struct nxboot_state *state, bool check_only) nxboot_progress(nxboot_progress_start, update_from_update); if (copy_partition(update, primary, state, true) >= 0) { + flash_partition_flush(primary); + /* Erase the first sector of update partition. This marks the * partition as updated so we don't end up in an update loop. * The sector is written back again during the image @@ -884,7 +887,6 @@ int nxboot_perform_update(bool check_only) int ret; int primary; struct nxboot_state state; - struct nxboot_img_header header; ret = nxboot_get_state(&state); if (ret < 0) @@ -908,9 +910,9 @@ int nxboot_perform_update(bool check_only) } } - /* Check whether there is a valid image in the primary slot. This just - * checks whether the header is valid, but does not calculate the CRC - * of the image as this would prolong the boot process. + /* Check whether there is a valid image in the primary slot. Validates + * both the header and the full image CRC to ensure integrity before + * booting. */ primary = flash_partition_open(CONFIG_NXBOOT_PRIMARY_SLOT_PATH); @@ -919,8 +921,7 @@ int nxboot_perform_update(bool check_only) return ERROR; } - get_image_header(primary, &header); - if (!validate_image_header(&header)) + if (!validate_image(primary)) { ret = ERROR; } diff --git a/boot/nxboot/loader/flash.c b/boot/nxboot/loader/flash.c index 9142b25d9..ab6a24320 100644 --- a/boot/nxboot/loader/flash.c +++ b/boot/nxboot/loader/flash.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,26 @@ int flash_partition_open(const char *path) return fd; } +/**************************************************************************** + * Name: flash_partition_flush + * + * Description: + * Flushes any buffered writes to the underlying storage. This ensures + * data is physically committed to flash before the caller proceeds. + * + * Input parameters: + * fd: Valid file descriptor. + * + * Returned Value: + * 0 on success, -1 on failure. + * + ****************************************************************************/ + +int flash_partition_flush(int fd) +{ + return fsync(fd); +} + /**************************************************************************** * Name: flash_partition_close * @@ -137,14 +158,15 @@ int flash_partition_write(int fd, const void *buf, size_t count, off_t off) pos = lseek(fd, off, SEEK_SET); if (pos != off) { - syslog(LOG_ERR, "Could not seek to %ld: %s\n", off, strerror(errno)); + syslog(LOG_ERR, "Could not seek to %" PRIdOFF ": %s\n", + off, strerror(errno)); return ERROR; } nbytes = write(fd, buf, count); if (nbytes != count) { - syslog(LOG_ERR, "Write to offset %ld failed %s\n", + syslog(LOG_ERR, "Write to offset %" PRIdOFF " failed %s\n", off, strerror(errno)); return ERROR; } @@ -195,15 +217,16 @@ int flash_partition_read(int fd, void *buf, size_t count, off_t off) pos = lseek(fd, off, SEEK_SET); if (pos != off) { - syslog(LOG_ERR, "Could not seek to %ld: %s\n", off, strerror(errno)); + syslog(LOG_ERR, "Could not seek to %" PRIdOFF ": %s\n", + off, strerror(errno)); return ERROR; } nbytes = read(fd, buf, count); if (nbytes != count) { - syslog(LOG_ERR, "Read from offset %ld failed %s\n", off, - strerror(errno)); + syslog(LOG_ERR, "Read from offset %" PRIdOFF " failed %s\n", + off, strerror(errno)); return ERROR; } diff --git a/boot/nxboot/loader/flash.h b/boot/nxboot/loader/flash.h index ef1dee09d..69906490d 100644 --- a/boot/nxboot/loader/flash.h +++ b/boot/nxboot/loader/flash.h @@ -79,6 +79,22 @@ int flash_partition_open(const char *path); int flash_partition_close(int fd); +/**************************************************************************** + * Name: flash_partition_flush + * + * Description: + * Flushes any buffered writes to the underlying storage. + * + * Input parameters: + * fd: Valid file descriptor. + * + * Returned Value: + * 0 on success, -1 on failure. + * + ****************************************************************************/ + +int flash_partition_flush(int fd); + /**************************************************************************** * Name: flash_partition_write * diff --git a/boot/nxboot/nxboot_main.c b/boot/nxboot/nxboot_main.c index ddc2f2284..0682b92c7 100644 --- a/boot/nxboot/nxboot_main.c +++ b/boot/nxboot/nxboot_main.c @@ -124,7 +124,7 @@ static const char *progress_msgs[] = ****************************************************************************/ #ifdef CONFIG_NXBOOT_PROGRESS -void nxboot_progress(enum progress_type_e type, ...) +void nxboot_progress(int type, ...) { #ifdef CONFIG_NXBOOT_PRINTF_PROGRESS va_list arg; @@ -264,16 +264,10 @@ int main(int argc, FAR char *argv[]) FAR struct boardioc_reset_cause_s cause; #endif -#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_NSH_ARCHINIT) - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - #ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -#endif #endif syslog(LOG_INFO, "*** nxboot ***\n"); diff --git a/builtin/exec_builtin.c b/builtin/exec_builtin.c index 325d594cf..b87133243 100644 --- a/builtin/exec_builtin.c +++ b/builtin/exec_builtin.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "builtin/builtin.h" diff --git a/canutils/candump/Kconfig b/canutils/candump/Kconfig index 4359ec287..45a46118b 100644 --- a/canutils/candump/Kconfig +++ b/canutils/candump/Kconfig @@ -6,7 +6,7 @@ config CANUTILS_CANDUMP tristate "SocketCAN candump tool" default n - depends on NET_CAN && CANUTILS_LIBCANUTILS + depends on NET_CAN && CANUTILS_LIBCANUTILS && ENABLE_ALL_SIGNALS ---help--- Enable the SocketCAN candump tool ported from https://github.com/linux-can/can-utils diff --git a/canutils/canlib/canlib_getbaud.c b/canutils/canlib/canlib_getbaud.c index af123edea..61ebb4de9 100644 --- a/canutils/canlib/canlib_getbaud.c +++ b/canutils/canlib/canlib_getbaud.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/canlib/canlib_getloopback.c b/canutils/canlib/canlib_getloopback.c index 443161e96..b931301d5 100644 --- a/canutils/canlib/canlib_getloopback.c +++ b/canutils/canlib/canlib_getloopback.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/canlib/canlib_getsilent.c b/canutils/canlib/canlib_getsilent.c index 868072eb1..18e2b0637 100644 --- a/canutils/canlib/canlib_getsilent.c +++ b/canutils/canlib/canlib_getsilent.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/canlib/canlib_setbaud.c b/canutils/canlib/canlib_setbaud.c index b3ee68f26..f8bbb23ff 100644 --- a/canutils/canlib/canlib_setbaud.c +++ b/canutils/canlib/canlib_setbaud.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/canlib/canlib_setloopback.c b/canutils/canlib/canlib_setloopback.c index 7e78b1a34..18164210c 100644 --- a/canutils/canlib/canlib_setloopback.c +++ b/canutils/canlib/canlib_setloopback.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/canlib/canlib_setsilent.c b/canutils/canlib/canlib_setsilent.c index 0900f9261..bf8ef1722 100644 --- a/canutils/canlib/canlib_setsilent.c +++ b/canutils/canlib/canlib_setsilent.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/canutils/lely-canopen/Kconfig b/canutils/lely-canopen/Kconfig index 5718356e6..fe665a757 100644 --- a/canutils/lely-canopen/Kconfig +++ b/canutils/lely-canopen/Kconfig @@ -51,6 +51,7 @@ config CANUTILS_LELYCANOPEN_OBJUPLOAD config CANUTILS_LELYCANOPEN_SDEV bool "Lely CANopen enable static device description support" default n + depends on CANUTILS_LELYCANOPEN_OBJNAME config CANUTILS_LELYCANOPEN_CSDO bool "Lely CANopen enable Client-SDO support" @@ -67,6 +68,7 @@ config CANUTILS_LELYCANOPEN_TPDO config CANUTILS_LELYCANOPEN_MPDO bool "Lely CANopen enable Multiplex PDO support" default n + depends on CANUTILS_LELYCANOPEN_TPDO config CANUTILS_LELYCANOPEN_SYNC bool "Lely CANopen enable SYNC support" @@ -95,14 +97,17 @@ config CANUTILS_LELYCANOPEN_MASTER config CANUTILS_LELYCANOPEN_NG bool "Lely CANopen enable node guardian support" default n + depends on CANUTILS_LELYCANOPEN_EMCY config CANUTILS_LELYCANOPEN_NMTBOOT bool "Lely CANopen enable NMT boot slave support" default n + depends on CANUTILS_LELYCANOPEN_MASTER && CANUTILS_LELYCANOPEN_CSDO config CANUTILS_LELYCANOPEN_NMTCFG bool "Lely CANopen enable NMT configuration request support" default n + depends on CANUTILS_LELYCANOPEN_MASTER && CANUTILS_LELYCANOPEN_CSDO config CANUTILS_LELYCANOPEN_GW bool "Lely CANopen enable gateway support" diff --git a/canutils/lely-canopen/Makefile b/canutils/lely-canopen/Makefile index ca124b833..22c6a85d6 100644 --- a/canutils/lely-canopen/Makefile +++ b/canutils/lely-canopen/Makefile @@ -31,11 +31,16 @@ WD := ${shell echo $(CURDIR) | sed -e 's/ /\\ /g'} LELYCANOPEN_VERSION = $(patsubst "%",%,$(strip $(CONFIG_CANUTILS_LELYCANOPEN_VERSION))) LELYCANOPEN_TARBALL = lely-core-$(LELYCANOPEN_VERSION).tar.gz -LELYCANOPEN_UNPACKNAME = lely-core-master-$(LELYCANOPEN_VERSION) LELYCANOPEN_SRCNAME = lely-core UNPACK ?= tar -zxf LELYCANOPEN_SRCDIR = $(WD)/$(LELYCANOPEN_SRCNAME) +# The GitLab "archive/master" tarball unpacks to a directory named +# lely-core-master-, where is the current tip of master + +LELYCANOPEN_UNPACKGLOB = lely-core-master-* +LELYCANOPEN_PATCHES = $(sort $(wildcard *.patch)) + # CAN network object CSRCS += $(LELYCANOPEN_SRCDIR)/src/can/buf.c @@ -169,37 +174,34 @@ STACKSIZE = $(CONFIG_CANUTILS_LELYCANOPEN_TOOLS_COCTL_STACKSIZE) MAINSRC = $(LELYCANOPEN_SRCDIR)/tools/coctl.c endif -# Download and unpack tarball if no git repo found +# Download, unpack and patch the Lely CANopen sources. + ifeq ($(wildcard $(LELYCANOPEN_SRCNAME)/.git),) + $(LELYCANOPEN_TARBALL): @echo "Downloading: $(LELYCANOPEN_TARBALL)" - $(Q) curl -L -O $(CONFIG_CANUTILS_LELYCANOPEN_URL)/$(LELYCANOPEN_TARBALL) + $(Q) curl -fL -o $@.tmp $(CONFIG_CANUTILS_LELYCANOPEN_URL)/$(LELYCANOPEN_TARBALL) + $(Q) mv $@.tmp $@ -$(LELYCANOPEN_SRCNAME): $(LELYCANOPEN_TARBALL) - @echo "Unpacking: $(LELYCANOPEN_TARBALL) -> $(LELYCANOPEN_UNPACKNAME)" +$(LELYCANOPEN_SRCNAME)/.patched: $(LELYCANOPEN_TARBALL) $(LELYCANOPEN_PATCHES) + @echo "Unpacking: $(LELYCANOPEN_TARBALL) -> $(LELYCANOPEN_SRCNAME)" + $(Q) rm -rf $(LELYCANOPEN_SRCNAME) $(LELYCANOPEN_UNPACKGLOB) $(Q) $(UNPACK) $(LELYCANOPEN_TARBALL) + $(Q) mv $(LELYCANOPEN_UNPACKGLOB) $(LELYCANOPEN_SRCNAME) + $(Q) for patch in $(LELYCANOPEN_PATCHES); do \ + echo "Patching: $$patch"; \ + patch -s -N -p1 -d $(LELYCANOPEN_SRCNAME) < $$patch || exit 1; \ + done + $(Q) touch $@ -patch_src: $(LELYCANOPEN_SRCNAME) - # Get the name of the directory created by the tar command - $(eval LELYCANOPEN_UNPACKNAME := $(shell ls -d lely-core-master*)) - $(Q) mv $(LELYCANOPEN_UNPACKNAME) $(LELYCANOPEN_SRCNAME) - $(Q) cat 0001-tools-eliminate-multiple-definitions-of-poll-compile.patch | patch -s -N -d $(LELYCANOPEN_SRCNAME) -p1 - $(Q) cat 0002-tools-coctl.c-fix-printf-issues.patch | patch -s -N -d $(LELYCANOPEN_SRCNAME) -p1 - $(Q) cat 0003-src-co-nmt.c-fix-compilation.patch | patch -s -N -d $(LELYCANOPEN_SRCNAME) -p1 - $(Q) cat 0004-tools-coctl.c-add-missing-mutex-init.patch | patch -s -N -d $(LELYCANOPEN_SRCNAME) -p1 - $(Q) cat 0005-add-NuttX-support.patch | patch -s -N -d $(LELYCANOPEN_SRCNAME) -p1 - $(Q) echo "Patching $(LELYCANOPEN_SRCNAME)" +context:: $(LELYCANOPEN_SRCNAME)/.patched + +distclean:: + $(Q) rm -rf $(LELYCANOPEN_SRCNAME) $(LELYCANOPEN_UNPACKGLOB) + $(call DELFILE, $(LELYCANOPEN_TARBALL)) -context:: patch_src else context:: $(LELYCANOPEN_SRCNAME) endif - -distclean:: -ifeq ($(wildcard $(LELYCANOPEN_SRCNAME)/.git),) - $(call DELDIR, $(LELYCANOPEN_SRCNAME)) - $(call DELFILE, $(LELYCANOPEN_TARBALL)) -endif - include $(APPDIR)/Application.mk diff --git a/cmake/nuttx_add_rust.cmake b/cmake/nuttx_add_rust.cmake index 97ecdcec7..cfc527b9b 100644 --- a/cmake/nuttx_add_rust.cmake +++ b/cmake/nuttx_add_rust.cmake @@ -34,7 +34,10 @@ include(nuttx_parse_function_args) # - riscv32: riscv32imc/imac/imafc-unknown-nuttx-elf # - riscv64: riscv64imac/imafdc-unknown-nuttx-elf # - x86: i686-unknown-nuttx -# - x86_64: x86_64-unknown-nuttx +# - x86_64: x86_64-unknown-nuttx-macho for sim on macOS, +# x86_64-unknown-nuttx otherwise +# - aarch64: aarch64-unknown-nuttx-macho for sim on macOS, +# aarch64-unknown-nuttx otherwise # # Inputs: # ARCHTYPE - Architecture type (e.g. thumbv7m, riscv32) @@ -48,9 +51,22 @@ include(nuttx_parse_function_args) function(nuttx_rust_target_triple ARCHTYPE ABITYPE CPUTYPE OUTPUT) if(ARCHTYPE STREQUAL "x86_64") - set(TARGET_TRIPLE "x86_64-unknown-nuttx") + if(CONFIG_ARCH_SIM AND CONFIG_HOST_MACOS) + set(TARGET_TRIPLE + "${PROJECT_SOURCE_DIR}/tools/x86_64-unknown-nuttx-macho.json") + else() + set(TARGET_TRIPLE "${PROJECT_SOURCE_DIR}/tools/x86_64-unknown-nuttx.json") + endif() elseif(ARCHTYPE STREQUAL "x86") - set(TARGET_TRIPLE "i686-unknown-nuttx") + set(TARGET_TRIPLE "${PROJECT_SOURCE_DIR}/tools/i486-unknown-nuttx.json") + elseif(ARCHTYPE STREQUAL "aarch64") + if(CONFIG_ARCH_SIM AND CONFIG_HOST_MACOS) + set(TARGET_TRIPLE + "${PROJECT_SOURCE_DIR}/tools/aarch64-unknown-nuttx-macho.json") + else() + set(TARGET_TRIPLE + "${PROJECT_SOURCE_DIR}/tools/aarch64-unknown-nuttx.json") + endif() elseif(ARCHTYPE MATCHES "thumb") if(ARCHTYPE MATCHES "thumbv8m") # Extract just the base architecture type (thumbv8m.main or thumbv8m.base) @@ -90,6 +106,13 @@ function(nuttx_rust_target_triple ARCHTYPE ABITYPE CPUTYPE OUTPUT) set(TARGET_TRIPLE "riscv64imac-unknown-nuttx-elf") endif() endif() + + if(NOT TARGET_TRIPLE) + message( + FATAL_ERROR + "Unsupported Rust target: LLVM_ARCHTYPE=${ARCHTYPE}, LLVM_ABITYPE=${ABITYPE}, LLVM_CPUTYPE=${CPUTYPE}" + ) + endif() set(${OUTPUT} ${TARGET_TRIPLE} PARENT_SCOPE) @@ -107,6 +130,8 @@ endfunction() # hello # CRATE_PATH # ${CMAKE_CURRENT_SOURCE_DIR}/hello +# FEATURES +# sim # ) # ~~~ @@ -119,6 +144,8 @@ function(nuttx_add_rust) ONE_VALUE CRATE_NAME CRATE_PATH + MULTI_VALUE + FEATURES REQUIRED CRATE_NAME CRATE_PATH @@ -127,34 +154,60 @@ function(nuttx_add_rust) # Determine build profile based on CONFIG_DEBUG_FULLOPT if(CONFIG_DEBUG_FULLOPT) + set(RUST_PROFILE_FLAG "--release") set(RUST_PROFILE "release") - set(RUST_DEBUG_FLAGS "-Zbuild-std-features=panic_immediate_abort") + set(RUST_PANIC_FLAGS "-Zunstable-options -Cpanic=immediate-abort") else() + set(RUST_PROFILE_FLAG "") set(RUST_PROFILE "debug") - set(RUST_DEBUG_FLAGS "") + set(RUST_PANIC_FLAGS "") endif() + foreach(feature ${FEATURES}) + list(APPEND RUST_FEATURE_FLAGS --features ${feature}) + endforeach() + # Get the Rust target triple nuttx_rust_target_triple(${LLVM_ARCHTYPE} ${LLVM_ABITYPE} ${LLVM_CPUTYPE} RUST_TARGET) - # Set up build directory in current binary dir - set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CRATE_NAME}) + # Get binary directory path using target triple base name if it's a JSON file + if(RUST_TARGET MATCHES ".json$") + get_filename_component(TARGET_BASE ${RUST_TARGET} NAME_WE) + else() + set(TARGET_BASE ${RUST_TARGET}) + endif() + + set(RUST_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CRATE_NAME}/target) set(RUST_LIB_PATH - ${RUST_BUILD_DIR}/${RUST_TARGET}/${RUST_PROFILE}/lib${CRATE_NAME}.a) + ${RUST_BUILD_DIR}/${TARGET_BASE}/${RUST_PROFILE}/lib${CRATE_NAME}.a) # Create build directory file(MAKE_DIRECTORY ${RUST_BUILD_DIR}) + # Collect Rust source files and manifests as dependencies so that changes in + # the crate trigger a rebuild via CMake/Ninja. + file( + GLOB_RECURSE + RUST_CRATE_SOURCES + CONFIGURE_DEPENDS + "${CRATE_PATH}/Cargo.toml" + "${CRATE_PATH}/Cargo.lock" + "${CRATE_PATH}/build.rs" + "${CRATE_PATH}/src/*.rs" + "${CRATE_PATH}/src/**/*.rs") + # Add a custom command to build the Rust crate add_custom_command( OUTPUT ${RUST_LIB_PATH} COMMAND ${CMAKE_COMMAND} -E env NUTTX_INCLUDE_DIR=${PROJECT_SOURCE_DIR}/include:${CMAKE_BINARY_DIR}/include:${CMAKE_BINARY_DIR}/include/arch - cargo build --${RUST_PROFILE} -Zbuild-std=std,panic_abort - ${RUST_DEBUG_FLAGS} --manifest-path ${CRATE_PATH}/Cargo.toml --target - ${RUST_TARGET} --target-dir ${RUST_BUILD_DIR} + RUSTFLAGS=${RUST_PANIC_FLAGS} cargo build ${RUST_PROFILE_FLAG} + -Zbuild-std=std,panic_abort -Zjson-target-spec --manifest-path + ${CRATE_PATH}/Cargo.toml --target ${RUST_TARGET} --target-dir + ${RUST_BUILD_DIR} ${RUST_FEATURE_FLAGS} + DEPENDS ${RUST_CRATE_SOURCES} COMMENT "Building Rust crate ${CRATE_NAME}" VERBATIM) diff --git a/crypto/controlse/controlse_main.cxx b/crypto/controlse/controlse_main.cxx index 8c0f07cbc..a2b5dddef 100644 --- a/crypto/controlse/controlse_main.cxx +++ b/crypto/controlse/controlse_main.cxx @@ -701,8 +701,12 @@ int main(int argc, FAR char *argv[]) #ifdef CONFIG_STACK_COLORATION FAR struct tcb_s *tcb; tcb = nxsched_get_tcb(getpid()); - fprintf(stderr, "\nStack used: %zu / %zu\n", up_check_tcbstack(tcb), - tcb->adj_stack_size); + if (tcb != NULL) + { + fprintf(stderr, "\nStack used: %zu / %zu\n", up_check_tcbstack(tcb), + tcb->adj_stack_size); + nxsched_put_tcb(tcb); + } #else fprintf(stderr, "\nStack used: unknown" " (STACK_COLORATION must be enabled)\n"); diff --git a/crypto/libtomcrypt/Makefile b/crypto/libtomcrypt/Makefile index 68219a8cf..b4fe79fd6 100644 --- a/crypto/libtomcrypt/Makefile +++ b/crypto/libtomcrypt/Makefile @@ -451,6 +451,7 @@ ifneq ($(CONFIG_LIBTOMCRYPT_DEMOS),) ifneq ($(CONFIG_LIBTOMCRYPT_LTCRYPT),) MAINSRC += $(LIBTOMCRYPT_UNPACKNAME)/demos/ltcrypt.c +MODULE += $(CONFIG_LIBTOMCRYPT_LTCRYPT) PROGNAME += $(CONFIG_LIBTOMCRYPT_LTCRYPT_PROGNAME) PRIORITY += $(CONFIG_LIBTOMCRYPT_LTCRYPT_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMCRYPT_LTCRYPT_STACKSIZE) @@ -459,6 +460,7 @@ endif ifneq ($(CONFIG_LIBTOMCRYPT_HASHSUM),) MAINSRC += $(LIBTOMCRYPT_UNPACKNAME)/demos/hashsum.c +MODULE += $(CONFIG_LIBTOMCRYPT_HASHSUM) PROGNAME += $(CONFIG_LIBTOMCRYPT_HASHSUM_PROGNAME) PRIORITY += $(CONFIG_LIBTOMCRYPT_HASHSUM_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMCRYPT_HASHSUM_STACKSIZE) @@ -478,6 +480,7 @@ NEWSRC := $(wildcard libtomcrypt/tests/*.c) CSRCS += $(filter-out $(LIBTOMCRYPT_UNPACKNAME)/tests/test.c, $(NEWSRC)) MAINSRC += $(LIBTOMCRYPT_UNPACKNAME)/tests/test.c +MODULE += $(CONFIG_LIBTOMCRYPT_TEST) PROGNAME += $(CONFIG_LIBTOMCRYPT_TEST_PROGNAME) PRIORITY += $(CONFIG_LIBTOMCRYPT_TEST_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMCRYPT_TEST_STACKSIZE) diff --git a/crypto/mbedtls/CMakeLists.txt b/crypto/mbedtls/CMakeLists.txt index 9a80ffab9..478a257bd 100644 --- a/crypto/mbedtls/CMakeLists.txt +++ b/crypto/mbedtls/CMakeLists.txt @@ -129,18 +129,16 @@ if(CONFIG_CRYPTO_MBEDTLS) # Library Configuration # ############################################################################ - set_property( - TARGET nuttx - APPEND - PROPERTY NUTTX_INCLUDE_DIRECTORIES ${INCDIR}) - set_property( - TARGET nuttx - APPEND - PROPERTY NUTTX_CXX_INCLUDE_DIRECTORIES ${INCDIR}) - nuttx_add_library(mbedtls STATIC) target_sources(mbedtls PRIVATE ${CSRCS}) - target_include_directories(mbedtls PRIVATE ${INCDIR}) + target_include_directories(mbedtls PUBLIC ${INCDIR}) + nuttx_export_header(TARGET mbedtls INCLUDE_DIRECTORIES ${MBEDTLS_DIR}/include) + # Overlay mbedtls_config.h cannot be in the same nuttx_export_header() as + # ${MBEDTLS_DIR}/include (duplicate path). Alt headers need no export; INCDIR + # supplies them for this library only. + nuttx_create_symlink( + ${CMAKE_CURRENT_LIST_DIR}/include/mbedtls/mbedtls_config.h + ${NUTTX_APPS_BINDIR}/include/mbedtls/mbedtls/mbedtls_config.h) target_compile_definitions(mbedtls PRIVATE unix) if(CONFIG_ARCH_SIM) diff --git a/crypto/mbedtls/Makefile b/crypto/mbedtls/Makefile index 330384111..241e076ea 100644 --- a/crypto/mbedtls/Makefile +++ b/crypto/mbedtls/Makefile @@ -48,6 +48,7 @@ MBEDTLS_FRAMEWORK_UNPACKNAME = mbedtls-framework CFLAGS += ${DEFINE_PREFIX}unix mbedtls/library/bignum.c_CFLAGS += -fno-lto +mbedtls/library/ctr_drbg.c_CFLAGS += -Wno-array-bounds # Build break on Assemble compiler if -fno-omit-frame-pointer and -O3 enabled at same time # {standard input}: Assembler messages: diff --git a/crypto/openssl_mbedtls_wrapper/Kconfig b/crypto/openssl_mbedtls_wrapper/Kconfig index 197adbc3e..3a4ee11a2 100644 --- a/crypto/openssl_mbedtls_wrapper/Kconfig +++ b/crypto/openssl_mbedtls_wrapper/Kconfig @@ -7,3 +7,25 @@ config OPENSSL_MBEDTLS_WRAPPER depends on CRYPTO_MBEDTLS bool "openssl mbedtls wrapper" default n + +if OPENSSL_MBEDTLS_WRAPPER + +choice + prompt "Openssl Mbedtls Wrapper Assert Debug" + default OPENSSL_ASSERT_EXIT + +config OPENSSL_ASSERT_DEBUG + bool "SSL_ASSERT* will show error file name and line" + +config OPENSSL_ASSERT_EXIT + bool "SSL_ASSERT* will just return error code" + +config OPENSSL_ASSERT_DEBUG_EXIT + bool "SSL_ASSERT* will show error file name and line, then return error code" + +config OPENSSL_ASSERT_DEBUG_BLOCK + bool "SSL_ASSERT* will show error file name and line, then block here with 'while (1)'" + +endchoice + +endif # OPENSSL_MBEDTLS_WRAPPER diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/bio.h b/crypto/openssl_mbedtls_wrapper/include/openssl/bio.h index be8acf198..7bfab1eb9 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/bio.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/bio.h @@ -27,16 +27,86 @@ ****************************************************************************/ #include +#include /**************************************************************************** - * Public Function Prototypes + * Pre-processor Definitions ****************************************************************************/ +/* There are the classes of BIOs */ + +#define BIO_TYPE_DESCRIPTOR 0x0100 /* socket, fd, connect or accept */ +#define BIO_TYPE_FILTER 0x0200 +#define BIO_TYPE_SOURCE_SINK 0x0400 + +#define BIO_FLAGS_BASE64_NO_NL 0x100 + +/* This is used with memory BIOs: BIO_FLAGS_MEM_RDONLY + * means we shouldn't free up or change the data in any way; + * BIO_FLAGS_NONCLEAR_RST means we shouldn't clear data on reset. + */ + +#define BIO_FLAGS_MEM_RDONLY 0x200 +#define BIO_FLAGS_NONCLEAR_RST 0x400 +#define BIO_FLAGS_IN_EOF 0x800 + +#define BIO_TYPE_NONE 0 +#define BIO_TYPE_MEM (1 | BIO_TYPE_SOURCE_SINK) +#define BIO_TYPE_FILE (2 | BIO_TYPE_SOURCE_SINK) +#define BIO_TYPE_BASE64 (11 | BIO_TYPE_FILTER) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct bio_method_st +{ + int type; + char *name; + int (*bwrite_old) (BIO *, const char *, int); + int (*bread_old) (BIO *, char *, int); + int (*create) (BIO *); + int (*destroy) (BIO *); +}; + +struct bio_st +{ + const BIO_METHOD *method; + int flags; /* extra storage */ + void *ptr; + struct bio_st *next_bio; /* used by filter BIOs */ + struct bio_st *prev_bio; /* used by filter BIOs */ +}; + #ifdef __cplusplus extern "C" { #endif +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +const BIO_METHOD *BIO_f_base64(void); + +const BIO_METHOD *BIO_s_mem(void); + +BIO *BIO_new(const BIO_METHOD *method); + +BIO *BIO_push(BIO *b, BIO *bio); + +void BIO_set_flags(BIO *b, int flags); + +int BIO_write(BIO *b, const void *data, int dlen); + +int BIO_read(BIO *b, void *data, int dlen); + +void BIO_free_all(BIO *bio); + +BIO *BIO_next(BIO *b); + +int BIO_flush(BIO *b); + #ifdef __cplusplus } #endif diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/crypto.h b/crypto/openssl_mbedtls_wrapper/include/openssl/crypto.h new file mode 100644 index 000000000..b573b2949 --- /dev/null +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/crypto.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * apps/crypto/openssl_mbedtls_wrapper/include/openssl/crypto.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + ****************************************************************************/ + +#ifndef OPENSSL_MBEDTLS_WRAPPER_CRYPTO_H +#define OPENSSL_MBEDTLS_WRAPPER_CRYPTO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CRYPTO_num_locks() 1 +#define CRYPTO_set_locking_callback(func) + +#define CRYPTO_set_id_callback(func) + +/* These defines where used in combination with the old locking callbacks, + * they are not called anymore, but old code that's not called might still + * use them. + */ + +#define CRYPTO_LOCK 1 +#define CRYPTO_UNLOCK 2 +#define CRYPTO_READ 4 +#define CRYPTO_WRITE 8 + +#endif /* OPENSSL_MBEDTLS_WRAPPER_CRYPTO_H */ diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/err.h b/crypto/openssl_mbedtls_wrapper/include/openssl/err.h index f6637eca4..1453be16e 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/err.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/err.h @@ -49,6 +49,12 @@ unsigned long ERR_peek_last_error(void); void ERR_error_string_n(unsigned long e, char *buf, size_t len); void ERR_free_strings(void); char *ERR_error_string(unsigned long e, char *buf); +void ERR_clear_error(void); +unsigned long ERR_get_error(void); +void ERR_remove_state(unsigned long pid); +int ERR_load_crypto_strings(void); +void ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), + void *u); #ifdef __cplusplus } diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/evp.h b/crypto/openssl_mbedtls_wrapper/include/openssl/evp.h index f65a51d88..e1696bfa6 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/evp.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/evp.h @@ -44,6 +44,10 @@ #define EVP_PKEY_X25519 NID_X25519 +/**************************************************************************** + * Public Types + ****************************************************************************/ + struct evp_pkey_st { void *pkey_pm; @@ -166,6 +170,10 @@ int PKCS5_PBKDF2_HMAC(const char *password, size_t password_len, const PKEY_METHOD *EVP_PKEY_method(void); +int OpenSSL_add_all_algorithms(void); + +void EVP_cleanup(void); + #ifdef __cplusplus } #endif diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/ssl.h b/crypto/openssl_mbedtls_wrapper/include/openssl/ssl.h index 937c1ab95..d9b2298b9 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/ssl.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/ssl.h @@ -25,13 +25,19 @@ ****************************************************************************/ #include +#include #include +#include #include #include +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + /* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ -#define SSL_SENT_SHUTDOWN 1 -#define SSL_RECEIVED_SHUTDOWN 2 +#define SSL_SENT_SHUTDOWN 1 +#define SSL_RECEIVED_SHUTDOWN 2 #define SSL_VERIFY_NONE 0x00 #define SSL_VERIFY_PEER 0x01 @@ -57,52 +63,134 @@ #define SSL_ERROR_WANT_READ 2 #define SSL_ERROR_WANT_WRITE 3 #define SSL_ERROR_WANT_X509_LOOKUP 4 -#define SSL_ERROR_SYSCALL 5/* look at error stack/return value/errno */ +#define SSL_ERROR_SYSCALL 5 /* look at error stack/return value/errno */ #define SSL_ERROR_ZERO_RETURN 6 #define SSL_ERROR_WANT_CONNECT 7 #define SSL_ERROR_WANT_ACCEPT 8 #define SSL_ERROR_WANT_ASYNC 9 -#define SSL_ERROR_WANT_ASYNC_JOB 10 +#define SSL_ERROR_WANT_ASYNC_JOB 10 + +#define SSL_ST_CONNECT 0x1000 +#define SSL_ST_ACCEPT 0x2000 + +#define SSL_ST_MASK 0x0FFF + +#define SSL_CB_LOOP 0x01 +#define SSL_CB_EXIT 0x02 +#define SSL_CB_READ 0x04 +#define SSL_CB_WRITE 0x08 +#define SSL_CB_ALERT 0x4000 /* used in callback */ +#define SSL_CB_READ_ALERT (SSL_CB_ALERT | SSL_CB_READ) +#define SSL_CB_WRITE_ALERT (SSL_CB_ALERT | SSL_CB_WRITE) +#define SSL_CB_ACCEPT_LOOP (SSL_ST_ACCEPT | SSL_CB_LOOP) +#define SSL_CB_ACCEPT_EXIT (SSL_ST_ACCEPT | SSL_CB_EXIT) +#define SSL_CB_CONNECT_LOOP (SSL_ST_CONNECT | SSL_CB_LOOP) +#define SSL_CB_CONNECT_EXIT (SSL_ST_CONNECT | SSL_CB_EXIT) +#define SSL_CB_HANDSHAKE_START 0x10 +#define SSL_CB_HANDSHAKE_DONE 0x20 + +#define SSL_FILETYPE_ASN1 X509_FILETYPE_ASN1 +#define SSL_FILETYPE_PEM X509_FILETYPE_PEM + +/* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success + * when just a single record has been written): + */ + +#define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001U + +/* Make it possible to retry SSL_write() with changed buffer location (buffer + * contents must stay the same!); this is not the default to avoid the + * misconception that non-blocking SSL_write() behaves like non-blocking + * write(): + */ + +#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002U + +#define SSLEAY_VERSION 0 +#define SSLEAY_CFLAGS 1 +#define SSLEAY_BUILT_ON 2 +#define SSLEAY_PLATFORM 3 +#define SSLEAY_DIR 4 + +#define SSL2_VERSION 0x0002 + +#define SSL2_MT_CLIENT_HELLO 1 + +#define SSLv23_client_method TLS_client_method +#define SSL_library_init() 1 +#define SSL_load_error_strings() + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef unsigned int (*SSL_psk_client_cb_func)(SSL *ssl, + const char *hint, + char *identity, + unsigned int max_identity_len, + unsigned char *psk, + unsigned int max_psk_len); + +typedef void (*ossl_msg_cb)( + int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg); typedef enum { - TLS_ST_BEFORE, - TLS_ST_OK, - DTLS_ST_CR_HELLO_VERIFY_REQUEST, - TLS_ST_CR_SRVR_HELLO, - TLS_ST_CR_CERT, - TLS_ST_CR_CERT_STATUS, - TLS_ST_CR_KEY_EXCH, - TLS_ST_CR_CERT_REQ, - TLS_ST_CR_SRVR_DONE, - TLS_ST_CR_SESSION_TICKET, - TLS_ST_CR_CHANGE, - TLS_ST_CR_FINISHED, - TLS_ST_CW_CLNT_HELLO, - TLS_ST_CW_CERT, - TLS_ST_CW_KEY_EXCH, - TLS_ST_CW_CERT_VRFY, - TLS_ST_CW_CHANGE, - TLS_ST_CW_NEXT_PROTO, - TLS_ST_CW_FINISHED, - TLS_ST_SW_HELLO_REQ, - TLS_ST_SR_CLNT_HELLO, - DTLS_ST_SW_HELLO_VERIFY_REQUEST, - TLS_ST_SW_SRVR_HELLO, - TLS_ST_SW_CERT, - TLS_ST_SW_KEY_EXCH, - TLS_ST_SW_CERT_REQ, - TLS_ST_SW_SRVR_DONE, - TLS_ST_SR_CERT, - TLS_ST_SR_KEY_EXCH, - TLS_ST_SR_CERT_VRFY, - TLS_ST_SR_NEXT_PROTO, - TLS_ST_SR_CHANGE, - TLS_ST_SR_FINISHED, - TLS_ST_SW_SESSION_TICKET, - TLS_ST_SW_CERT_STATUS, - TLS_ST_SW_CHANGE, - TLS_ST_SW_FINISHED + TLS_ST_BEFORE, + TLS_ST_OK, + DTLS_ST_CR_HELLO_VERIFY_REQUEST, + TLS_ST_CR_SRVR_HELLO, + TLS_ST_CR_CERT, + TLS_ST_CR_COMP_CERT, + TLS_ST_CR_CERT_STATUS, + TLS_ST_CR_KEY_EXCH, + TLS_ST_CR_CERT_REQ, + TLS_ST_CR_SRVR_DONE, + TLS_ST_CR_SESSION_TICKET, + TLS_ST_CR_CHANGE, + TLS_ST_CR_FINISHED, + TLS_ST_CW_CLNT_HELLO, + TLS_ST_CW_CERT, + TLS_ST_CW_COMP_CERT, + TLS_ST_CW_KEY_EXCH, + TLS_ST_CW_CERT_VRFY, + TLS_ST_CW_CHANGE, + TLS_ST_CW_NEXT_PROTO, + TLS_ST_CW_FINISHED, + TLS_ST_SW_HELLO_REQ, + TLS_ST_SR_CLNT_HELLO, + DTLS_ST_SW_HELLO_VERIFY_REQUEST, + TLS_ST_SW_SRVR_HELLO, + TLS_ST_SW_CERT, + TLS_ST_SW_COMP_CERT, + TLS_ST_SW_KEY_EXCH, + TLS_ST_SW_CERT_REQ, + TLS_ST_SW_SRVR_DONE, + TLS_ST_SR_CERT, + TLS_ST_SR_COMP_CERT, + TLS_ST_SR_KEY_EXCH, + TLS_ST_SR_CERT_VRFY, + TLS_ST_SR_NEXT_PROTO, + TLS_ST_SR_CHANGE, + TLS_ST_SR_FINISHED, + TLS_ST_SW_SESSION_TICKET, + TLS_ST_SW_CERT_STATUS, + TLS_ST_SW_CHANGE, + TLS_ST_SW_FINISHED, + TLS_ST_SW_ENCRYPTED_EXTENSIONS, + TLS_ST_CR_ENCRYPTED_EXTENSIONS, + TLS_ST_CR_CERT_VRFY, + TLS_ST_SW_CERT_VRFY, + TLS_ST_CR_HELLO_REQ, + TLS_ST_SW_KEY_UPDATE, + TLS_ST_CW_KEY_UPDATE, + TLS_ST_SR_KEY_UPDATE, + TLS_ST_CR_KEY_UPDATE, + TLS_ST_EARLY_DATA, + TLS_ST_PENDING_EARLY_DATA_END, + TLS_ST_CW_END_OF_EARLY_DATA, + TLS_ST_SR_END_OF_EARLY_DATA } OSSL_HANDSHAKE_STATE; @@ -141,6 +229,8 @@ int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); + int SSL_use_certificate_file(SSL *ssl, const char *file, int type); X509 *SSL_get_peer_certificate(const SSL *ssl); @@ -161,7 +251,7 @@ int SSL_get_error(const SSL *ssl, int ret_code); OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); -SSL_CTX *SSL_CTX_new(const SSL_METHOD *method, void *rngctx); +SSL_CTX *SSL_CTX_new(const SSL_METHOD *method); void SSL_CTX_free(SSL_CTX *ctx); @@ -299,6 +389,78 @@ int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); +const SSL_METHOD *TLS_method(void); +const SSL_METHOD *TLS_server_method(void); +const SSL_METHOD *TLS_client_method(void); + +const SSL_METHOD *TLSv1_method(void); /* TLSv1.0 */ +const SSL_METHOD *TLSv1_server_method(void); +const SSL_METHOD *TLSv1_client_method(void); + +const SSL_METHOD *TLSv1_1_method(void); /* TLSv1.1 */ +const SSL_METHOD *TLSv1_1_server_method(void); +const SSL_METHOD *TLSv1_1_client_method(void); + +const SSL_METHOD *TLSv1_2_method(void); /* TLSv1.2 */ +const SSL_METHOD *TLSv1_2_server_method(void); +const SSL_METHOD *TLSv1_2_client_method(void); + +SSL_SESSION *SSL_get1_session(SSL *ssl); + +void SSL_SESSION_free(SSL_SESSION *session); + +int SSL_set_session(SSL *s, SSL_SESSION *session); + +const char *SSLeay_version(int t); + +const char *SSL_state_string_long(const SSL *s); + +const char *SSL_get_cipher_name(const SSL *s); + +const char *SSL_alert_type_string_long(int value); + +const char *SSL_alert_desc_string_long(int value); + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u); + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); + +void SSL_CTX_set_psk_client_callback(SSL_CTX *ctx, + SSL_psk_client_cb_func cb); + +long SSL_CTX_set_mode(SSL_CTX *ctx, long larg); + +void SSL_CTX_set_info_callback(SSL_CTX *ctx, + void (*cb)(const SSL *ssl, int type, int val)); + +void SSL_CTX_set_msg_callback(SSL_CTX *ctx, + void (*cb)(int write_p, int version, + int content_type, const void *buf, + size_t len, SSL *ssl, void *arg)); + +const char *SSL_get_cipher_list(const SSL *s, int n); + +long SSL_set_tlsext_host_name(SSL *s, void *parg); + +int SSL_get_ex_new_index(long argl, void *argp, + CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func); + +const char *SSL_get_cipher_list(const SSL *s, int n); + +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); + +int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg); + +int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile); + +int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath); + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath); + #ifdef __cplusplus } #endif diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/ssl_local.h b/crypto/openssl_mbedtls_wrapper/include/openssl/ssl_local.h index b9295d1c9..053da27de 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/ssl_local.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/ssl_local.h @@ -30,11 +30,6 @@ #include #include -#ifdef __cplusplus -extern "C" -{ -#endif - /**************************************************************************** * Public Types ****************************************************************************/ @@ -61,6 +56,24 @@ struct ssl_ctx_st int read_ahead; int read_buffer_len; X509_VERIFY_PARAM param; + + /* Default password callback. */ + + pem_password_cb *default_passwd_callback; + + /* Default password callback user data. */ + + void *default_passwd_callback_userdata; + uint32_t mode; + + /* optional informational callback */ + + void (*info_callback)(const SSL *ssl, int type, int val); + + /* callback that allows applications to peek at protocol messages */ + + ossl_msg_cb msg_callback; + SSL_psk_client_cb_func psk_client_callback; }; struct ssl_method_func_st @@ -97,16 +110,22 @@ struct ssl_session_st long timeout; long time; X509 *peer; + const SSL_CIPHER *cipher; + int references; + struct + { + char hostname[TLSEXT_MAXLEN_host_name]; + } ext; }; struct ssl_st { -/* protocol version(one of SSL3.0, TLS1.0, etc.) */ + /* protocol version(one of SSL3.0, TLS1.0, etc.) */ int version; unsigned long options; -/* shut things down(0x01 : sent, 0x02 : received) */ + /* shut things down(0x01 : sent, 0x02 : received) */ int shutdown; CERT *cert; @@ -116,7 +135,7 @@ struct ssl_st const char **alpn_protos; RECORD_LAYER rlayer; -/* where we are */ + /* where we are */ OSSL_STATEM statem; SSL_SESSION *session; @@ -129,11 +148,22 @@ struct ssl_st int err; void (*info_callback) (const SSL *ssl, int type, int val); -/* SSL low-level system arch point */ + /* SSL low-level system arch point */ void *ssl_pm; + SSL_CIPHER *cipher_list; }; +struct ssl_cipher_st +{ + const char *name; /* text name */ +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/tls1.h b/crypto/openssl_mbedtls_wrapper/include/openssl/tls1.h index 57fd4fa26..6f0ff49cb 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/tls1.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/tls1.h @@ -20,42 +20,63 @@ #ifndef OPENSSL_MBEDTLS_WRAPPER_TLS1_H #define OPENSSL_MBEDTLS_WRAPPER_TLS1_H -#ifdef __cplusplus -extern "C" -{ -#endif +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ #define TLS1_AD_DECRYPTION_FAILED 21 #define TLS1_AD_RECORD_OVERFLOW 22 -#define TLS1_AD_UNKNOWN_CA 48/* fatal */ -#define TLS1_AD_ACCESS_DENIED 49/* fatal */ -#define TLS1_AD_DECODE_ERROR 50/* fatal */ +#define TLS1_AD_UNKNOWN_CA 48 /* fatal */ +#define TLS1_AD_ACCESS_DENIED 49 /* fatal */ +#define TLS1_AD_DECODE_ERROR 50 /* fatal */ #define TLS1_AD_DECRYPT_ERROR 51 -#define TLS1_AD_EXPORT_RESTRICTION 60/* fatal */ -#define TLS1_AD_PROTOCOL_VERSION 70/* fatal */ -#define TLS1_AD_INSUFFICIENT_SECURITY 71/* fatal */ -#define TLS1_AD_INTERNAL_ERROR 80/* fatal */ -#define TLS1_AD_INAPPROPRIATE_FALLBACK 86/* fatal */ +#define TLS1_AD_EXPORT_RESTRICTION 60 /* fatal */ +#define TLS1_AD_PROTOCOL_VERSION 70 /* fatal */ +#define TLS1_AD_INSUFFICIENT_SECURITY 71 /* fatal */ +#define TLS1_AD_INTERNAL_ERROR 80 /* fatal */ +#define TLS1_AD_INAPPROPRIATE_FALLBACK 86 /* fatal */ #define TLS1_AD_USER_CANCELLED 90 #define TLS1_AD_NO_RENEGOTIATION 100 + /* codes 110-114 are from RFC3546 */ + #define TLS1_AD_UNSUPPORTED_EXTENSION 110 #define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111 #define TLS1_AD_UNRECOGNIZED_NAME 112 #define TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE 113 #define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114 -#define TLS1_AD_UNKNOWN_PSK_IDENTITY 115/* fatal */ -#define TLS1_AD_NO_APPLICATION_PROTOCOL 120/* fatal */ +#define TLS1_AD_UNKNOWN_PSK_IDENTITY 115 /* fatal */ +#define TLS1_AD_NO_APPLICATION_PROTOCOL 120 /* fatal */ /* Special value for method supporting multiple versions */ + #define TLS_ANY_VERSION 0x10000 #define TLS1_VERSION 0x0301 #define TLS1_1_VERSION 0x0302 #define TLS1_2_VERSION 0x0303 -#define SSL_TLSEXT_ERR_OK 0 -#define SSL_TLSEXT_ERR_NOACK 3 +#define SSL_TLSEXT_ERR_OK 0 +#define SSL_TLSEXT_ERR_NOACK 3 + +#define TLSEXT_MAXLEN_host_name 255 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +long SSL_set_tlsext_host_name(SSL *s, void *parg); #ifdef __cplusplus } diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/types.h b/crypto/openssl_mbedtls_wrapper/include/openssl/types.h index 679fb94c5..294ef20f6 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/types.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/types.h @@ -20,19 +20,22 @@ #ifndef OPENSSL_MBEDTLS_WRAPPER_TYPES_H #define OPENSSL_MBEDTLS_WRAPPER_TYPES_H -#ifdef __cplusplus -extern "C" -{ -#endif +/**************************************************************************** + * Public Types + ****************************************************************************/ -typedef void SSL_CIPHER; +typedef void CRYPTO_EX_new; +typedef void CRYPTO_THREADID; typedef void X509_STORE; - typedef void RSA; -typedef void BIO; typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); +typedef int pem_password_cb(char *buf, int size, + int rwflag, void *userdata); +typedef struct bio_buf_mem_st BIO_BUF_MEM; +typedef struct bio_method_st BIO_METHOD; +typedef struct bio_st BIO; typedef struct stack_st OPENSSL_STACK; typedef struct ssl_method_st SSL_METHOD; typedef struct ssl_method_func_st SSL_METHOD_FUNC; @@ -41,6 +44,7 @@ typedef struct ossl_statem_st OSSL_STATEM; typedef struct ssl_session_st SSL_SESSION; typedef struct ssl_ctx_st SSL_CTX; typedef struct ssl_st SSL; +typedef struct ssl_cipher_st SSL_CIPHER; typedef struct cert_st CERT; typedef struct x509_st X509; typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; @@ -66,8 +70,4 @@ struct pkey_method_st int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); }; -#ifdef __cplusplus -} -#endif - #endif /* OPENSSL_MBEDTLS_WRAPPER_TYPES_H */ diff --git a/crypto/openssl_mbedtls_wrapper/include/openssl/x509.h b/crypto/openssl_mbedtls_wrapper/include/openssl/x509.h index 847aaa4bc..cda221276 100644 --- a/crypto/openssl_mbedtls_wrapper/include/openssl/x509.h +++ b/crypto/openssl_mbedtls_wrapper/include/openssl/x509.h @@ -34,10 +34,13 @@ #include #include -#ifdef __cplusplus -extern "C" -{ -#endif +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define X509_FILETYPE_PEM 1 +#define X509_FILETYPE_ASN1 2 +#define X509_FILETYPE_DEFAULT 3 /**************************************************************************** * Public Types @@ -45,7 +48,7 @@ extern "C" struct x509_st { -/* X509 certification platform private point */ + /* X509 certification platform private point */ void *x509_pm; const X509_METHOD *method; @@ -56,6 +59,8 @@ struct x509_method_st int (*x509_new)(X509 *x, X509 *m_x); void (*x509_free)(X509 *x); int (*x509_load)(X509 *x, const unsigned char *buf, int len); + int (*x509_load_file)(X509 *x, const char *file); + int (*x509_load_path)(X509 *x, const char *path); int (*x509_show_info)(X509 *x); }; @@ -66,6 +71,11 @@ struct cert_st EVP_PKEY *pkey; }; +#ifdef __cplusplus +extern "C" +{ +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/bio_b64.c b/crypto/openssl_mbedtls_wrapper/mbedtls/bio_b64.c new file mode 100644 index 000000000..a13d4f26c --- /dev/null +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/bio_b64.c @@ -0,0 +1,147 @@ +/**************************************************************************** + * apps/crypto/openssl_mbedtls_wrapper/mbedtls/bio_b64.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +#define BIO_B64_BUFSIZE ((BIO_B64_ENC_LEN / 3) * 4 + 1) +#define BIO_B64_ENC_LEN 192 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int b64_write(BIO *h, const char *buf, int num); +static int b64_read(BIO *h, char *buf, int size); +static int b64_new(BIO *h); +static int b64_free(BIO *data); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const BIO_METHOD g_methods_b64 = +{ + BIO_TYPE_BASE64, + "base64 encoding", + b64_write, + b64_read, + b64_new, + b64_free, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int b64_write(BIO *b, const char *in, int inl) +{ + BIO *next; + int len; + int offset = 0; + int ret = 0; + size_t n; + unsigned char out[BIO_B64_BUFSIZE]; + + next = BIO_next(b); + if (next == NULL) + { + return 0; + } + + while (inl > 0) + { + len = MIN(inl, BIO_B64_ENC_LEN); + ret = mbedtls_base64_encode(out, BIO_B64_BUFSIZE, &n, + (const unsigned char *)in + offset, len); + if (ret < 0) + { + break; + } + + ret = BIO_write(next, out, n); + inl -= len; + offset += len; + } + + return ret; +} + +static int b64_read(BIO *b, char *out, int outl) +{ + BIO *next; + int ret; + size_t n; + + if (out == NULL) + { + return 0; + } + + next = BIO_next(b); + if (next == NULL) + { + return 0; + } + + ret = BIO_read(next, out, outl); + if (ret > 0) + { + ret = mbedtls_base64_decode((unsigned char *)out, outl, &n, + (const unsigned char *)out, ret); + if (ret == 0) + { + ret = n; + } + } + + return ret; +} + +static int b64_new(BIO *bi) +{ + return 1; +} + +static int b64_free(BIO *a) +{ + return 1; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const BIO_METHOD *BIO_f_base64(void) +{ + return &g_methods_b64; +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/bio_lib.c b/crypto/openssl_mbedtls_wrapper/mbedtls/bio_lib.c new file mode 100644 index 000000000..36d08403a --- /dev/null +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/bio_lib.c @@ -0,0 +1,151 @@ +/**************************************************************************** + * apps/crypto/openssl_mbedtls_wrapper/mbedtls/bio_lib.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +BIO *BIO_new(const BIO_METHOD *method) +{ + BIO *bio = zalloc(sizeof(*bio)); + + if (bio == NULL) + { + return NULL; + } + + bio->method = method; + + if (method->create != NULL && !method->create(bio)) + { + free(bio); + return NULL; + } + + return bio; +} + +BIO *BIO_push(BIO *b, BIO *bio) +{ + BIO *lb; + + if (b == NULL) + { + return bio; + } + else if (bio == NULL) + { + return b; + } + + lb = b; + while (lb->next_bio != NULL) + { + lb = lb->next_bio; + } + + lb->next_bio = bio; + bio->prev_bio = lb; + return b; +} + +void BIO_set_flags(BIO *b, int flags) +{ + b->flags |= flags; +} + +int BIO_write(BIO *b, const void *data, int dlen) +{ + if (b == NULL || dlen <= 0) + { + return 0; + } + + if (b->method == NULL || b->method->bwrite_old == NULL) + { + return -2; + } + + return b->method->bwrite_old(b, data, dlen); +} + +int BIO_read(BIO *b, void *data, int dlen) +{ + if (b == NULL || dlen <= 0) + { + return 0; + } + + if (b->method == NULL || b->method->bread_old == NULL) + { + return -2; + } + + return b->method->bread_old(b, data, dlen); +} + +int BIO_free(BIO *a) +{ + if (a == NULL) + { + return 0; + } + + if (a->method != NULL && a->method->destroy != NULL) + { + a->method->destroy(a); + } + + free(a); + return 1; +} + +void BIO_free_all(BIO *bio) +{ + while (bio != NULL) + { + BIO *b = bio; + bio = bio->next_bio; + BIO_free(b); + } +} + +BIO *BIO_next(BIO *b) +{ + if (b == NULL) + { + return NULL; + } + + return b->next_bio; +} + +int BIO_flush(BIO *b) +{ + return 1; +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/bss_mem.c b/crypto/openssl_mbedtls_wrapper/mbedtls/bss_mem.c new file mode 100644 index 000000000..40f884135 --- /dev/null +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/bss_mem.c @@ -0,0 +1,236 @@ +/**************************************************************************** + * apps/crypto/openssl_mbedtls_wrapper/mbedtls/bss_mem.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/* LIMIT_BEFORE_EXPANSION is the maximum n such that (n+3)/3*4 < 2**31. That + * function is applied in several functions in this file and this limit + * ensures that the result fits in an int. + */ + +#define LIMIT_BEFORE_EXPANSION 0x5ffffffc + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int mem_write(BIO *h, const char *buf, int num); +static int mem_read(BIO *h, char *buf, int size); +static int mem_new(BIO *h); +static int mem_free(BIO *data); + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct bio_buf_mem_st +{ + unsigned long flags; + char *data; + size_t length; /* current number of bytes */ + size_t max; /* size of buffer */ + size_t offset; /* has been read */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const BIO_METHOD g_mem_method = +{ + BIO_TYPE_MEM, + "memory buffer", + mem_write, + mem_read, + mem_new, + mem_free, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int mem_buf_sync(BIO *b) +{ + if (b != NULL && b->ptr != NULL) + { + BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr; + + if (bbm->offset) + { + bbm->length = bbm->length - bbm->offset; + memmove(bbm->data, bbm->data + bbm->offset, bbm->length); + bbm->offset = 0; + } + } + + return 0; +} + +static size_t BUF_MEM_grow_clean(BIO_BUF_MEM *bbm, size_t len) +{ + char *ret; + size_t n; + + if (bbm->length >= len) + { + if (bbm->data != NULL) + { + memset(&bbm->data[len], 0, bbm->length - len); + } + + bbm->length = len; + return len; + } + + if (bbm->max >= len) + { + memset(&bbm->data[bbm->length], 0, len - bbm->length); + bbm->length = len; + return len; + } + + /* This limit is sufficient to ensure (len+3)/3*4 < 2**31 */ + + if (len > LIMIT_BEFORE_EXPANSION) + { + return 0; + } + + n = MAX((len + 3) / 3 * 4, bbm->max); + ret = realloc(bbm->data, n); + if (ret == NULL) + { + return 0; + } + + bbm->data = ret; + bbm->max = n; + bbm->length = len; + return len; +} + +static int mem_write(BIO *b, const char *in, int inl) +{ + BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr; + int blen; + + if (b->flags & BIO_FLAGS_MEM_RDONLY) + { + return -1; + } + + if (inl <= 0) + { + return 0; + } + else if (in == NULL) + { + return -1; + } + + blen = bbm->length - bbm->offset; + mem_buf_sync(b); + if (BUF_MEM_grow_clean(bbm, blen + inl) == 0) + { + return -1; + } + + memcpy(bbm->data + blen, in, inl); + return inl; +} + +static int mem_read(BIO *b, char *out, int outl) +{ + BIO_BUF_MEM *bbm = (BIO_BUF_MEM *)b->ptr; + + if (outl <= 0) + { + return 0; + } + + if (out == NULL) + { + return -1; + } + + if (outl > bbm->length - bbm->offset) + { + outl = bbm->length - bbm->offset; + } + + memcpy(out, bbm->data + bbm->offset, outl); + bbm->offset += outl; + return outl; +} + +static int mem_new(BIO *bi) +{ + BIO_BUF_MEM *bbm = zalloc(sizeof(*bbm)); + + if (bbm == NULL) + { + return 0; + } + + bi->ptr = bbm; + return 1; +} + +static int mem_free(BIO *a) +{ + BIO_BUF_MEM *bbm; + + if (a == NULL) + { + return 0; + } + + bbm = (BIO_BUF_MEM *)a->ptr; + if (bbm->data) + { + free(bbm->data); + } + + free(bbm); + return 1; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const BIO_METHOD *BIO_s_mem(void) +{ + return &g_mem_method; +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/err.c b/crypto/openssl_mbedtls_wrapper/mbedtls/err.c index 34dad8dc5..4657259f7 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/err.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/err.c @@ -38,7 +38,34 @@ unsigned long ERR_peek_last_error(void) return errno; } +void ERR_free_strings(void) +{ +} + void ERR_error_string_n(unsigned long e, char *buf, size_t len) { mbedtls_strerror(e, buf, len); } + +void ERR_clear_error(void) +{ +} + +unsigned long ERR_get_error(void) +{ + return errno; +} + +void ERR_remove_state(unsigned long pid) +{ +} + +int ERR_load_crypto_strings(void) +{ + return 1; +} + +void ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), + void *u) +{ +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/evp.c b/crypto/openssl_mbedtls_wrapper/mbedtls/evp.c index 3045c495a..b1feae343 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/evp.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/evp.c @@ -344,3 +344,12 @@ int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, { return 0; } + +int OpenSSL_add_all_algorithms(void) +{ + return 0; +} + +void EVP_cleanup(void) +{ +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_lib.c b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_lib.c index a4f0eea40..b240d38d7 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_lib.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_lib.c @@ -21,13 +21,22 @@ * Included Files ****************************************************************************/ +#include #include #include #include #include "ssl_port.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + #define SSL_SEND_DATA_MAX_LENGTH 1460 +/**************************************************************************** + * Private Types + ****************************************************************************/ + struct alpn_ctx { unsigned char data[23]; @@ -56,6 +65,7 @@ static SSL_SESSION *SSL_SESSION_new(void) goto failed2; } + session->references = 1; return session; failed2: @@ -64,12 +74,6 @@ failed1: return NULL; } -static void SSL_SESSION_free(SSL_SESSION *session) -{ - X509_free(session->peer); - ssl_mem_free(session); -} - static void _openssl_alpn_to_mbedtls(struct alpn_ctx *ac, char ***palpn_protos) { @@ -256,7 +260,7 @@ int SSL_get_error(const SSL *ssl, int ret_code) return ret; } -SSL_CTX *SSL_CTX_new(const SSL_METHOD *method, void *rngctx) +SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { SSL_CTX *ctx; CERT *cert; @@ -1014,3 +1018,389 @@ void SSL_set_alpn_select_cb(SSL *ssl, void *arg) _ssl_set_alpn_list(ssl); } + +SSL_SESSION *SSL_get1_session(SSL *ssl) +{ + SSL_ASSERT2(ssl); + + atomic_fetch_add(&ssl->session->references, 1); + return ssl->session; +} + +void SSL_SESSION_free(SSL_SESSION *session) +{ + SSL_ASSERT3(session); + + if (atomic_fetch_sub(&session->references, 1) == 1) + { + X509_free(session->peer); + ssl_mem_free(session); + } +} + +int SSL_set_session(SSL *s, SSL_SESSION *session) +{ + SSL_ASSERT1(s); + SSL_ASSERT1(session); + + SSL_SESSION_free(s->session); + s->session = session; + return 1; +} + +const char *SSLeay_version(int t) +{ + return "not available"; +} + +const char *SSL_state_string_long(const SSL *s) +{ + SSL_ASSERT2(s); + + switch (SSL_get_state(s)) + { + case TLS_ST_CR_CERT_STATUS: + return "SSLv3/TLS read certificate status"; + case TLS_ST_CW_NEXT_PROTO: + return "SSLv3/TLS write next proto"; + case TLS_ST_SR_NEXT_PROTO: + return "SSLv3/TLS read next proto"; + case TLS_ST_SW_CERT_STATUS: + return "SSLv3/TLS write certificate status"; + case TLS_ST_BEFORE: + return "before SSL initialization"; + case TLS_ST_OK: + return "SSL negotiation finished successfully"; + case TLS_ST_CW_CLNT_HELLO: + return "SSLv3/TLS write client hello"; + case TLS_ST_CR_SRVR_HELLO: + return "SSLv3/TLS read server hello"; + case TLS_ST_CR_CERT: + return "SSLv3/TLS read server certificate"; + case TLS_ST_CR_COMP_CERT: + return "TLSv1.3 read server compressed certificate"; + case TLS_ST_CR_KEY_EXCH: + return "SSLv3/TLS read server key exchange"; + case TLS_ST_CR_CERT_REQ: + return "SSLv3/TLS read server certificate request"; + case TLS_ST_CR_SESSION_TICKET: + return "SSLv3/TLS read server session ticket"; + case TLS_ST_CR_SRVR_DONE: + return "SSLv3/TLS read server done"; + case TLS_ST_CW_CERT: + return "SSLv3/TLS write client certificate"; + case TLS_ST_CW_COMP_CERT: + return "TLSv1.3 write client compressed certificate"; + case TLS_ST_CW_KEY_EXCH: + return "SSLv3/TLS write client key exchange"; + case TLS_ST_CW_CERT_VRFY: + return "SSLv3/TLS write certificate verify"; + case TLS_ST_CW_CHANGE: + case TLS_ST_SW_CHANGE: + return "SSLv3/TLS write change cipher spec"; + case TLS_ST_CW_FINISHED: + case TLS_ST_SW_FINISHED: + return "SSLv3/TLS write finished"; + case TLS_ST_CR_CHANGE: + case TLS_ST_SR_CHANGE: + return "SSLv3/TLS read change cipher spec"; + case TLS_ST_CR_FINISHED: + case TLS_ST_SR_FINISHED: + return "SSLv3/TLS read finished"; + case TLS_ST_SR_CLNT_HELLO: + return "SSLv3/TLS read client hello"; + case TLS_ST_SW_HELLO_REQ: + return "SSLv3/TLS write hello request"; + case TLS_ST_SW_SRVR_HELLO: + return "SSLv3/TLS write server hello"; + case TLS_ST_SW_CERT: + return "SSLv3/TLS write certificate"; + case TLS_ST_SW_COMP_CERT: + return "TLSv1.3 write server compressed certificate"; + case TLS_ST_SW_KEY_EXCH: + return "SSLv3/TLS write key exchange"; + case TLS_ST_SW_CERT_REQ: + return "SSLv3/TLS write certificate request"; + case TLS_ST_SW_SESSION_TICKET: + return "SSLv3/TLS write session ticket"; + case TLS_ST_SW_SRVR_DONE: + return "SSLv3/TLS write server done"; + case TLS_ST_SR_CERT: + return "SSLv3/TLS read client certificate"; + case TLS_ST_SR_COMP_CERT: + return "TLSv1.3 read client compressed certificate"; + case TLS_ST_SR_KEY_EXCH: + return "SSLv3/TLS read client key exchange"; + case TLS_ST_SR_CERT_VRFY: + return "SSLv3/TLS read certificate verify"; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + return "DTLS1 read hello verify request"; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + return "DTLS1 write hello verify request"; + case TLS_ST_SW_ENCRYPTED_EXTENSIONS: + return "TLSv1.3 write encrypted extensions"; + case TLS_ST_CR_ENCRYPTED_EXTENSIONS: + return "TLSv1.3 read encrypted extensions"; + case TLS_ST_CR_CERT_VRFY: + return "TLSv1.3 read server certificate verify"; + case TLS_ST_SW_CERT_VRFY: + return "TLSv1.3 write server certificate verify"; + case TLS_ST_CR_HELLO_REQ: + return "SSLv3/TLS read hello request"; + case TLS_ST_SW_KEY_UPDATE: + return "TLSv1.3 write server key update"; + case TLS_ST_CW_KEY_UPDATE: + return "TLSv1.3 write client key update"; + case TLS_ST_SR_KEY_UPDATE: + return "TLSv1.3 read client key update"; + case TLS_ST_CR_KEY_UPDATE: + return "TLSv1.3 read server key update"; + case TLS_ST_EARLY_DATA: + return "TLSv1.3 early data"; + case TLS_ST_PENDING_EARLY_DATA_END: + return "TLSv1.3 pending early data end"; + case TLS_ST_CW_END_OF_EARLY_DATA: + return "TLSv1.3 write end of early data"; + case TLS_ST_SR_END_OF_EARLY_DATA: + return "TLSv1.3 read end of early data"; + default: + return "unknown state"; + } +} + +const char *SSL_get_cipher_name(const SSL *s) +{ + SSL_ASSERT2(s); + SSL_ASSERT2(s->session); + + return s->session->cipher->name ? s->session->cipher->name : "(NONE)"; +} + +const char *SSL_alert_type_string_long(int value) +{ + switch (value >> 8) + { + case SSL3_AL_WARNING: + return "warning"; + case SSL3_AL_FATAL: + return "fatal"; + } + + return "unknown"; +} + +const char *SSL_alert_desc_string_long(int value) +{ + switch (value & 0xff) + { + case SSL3_AD_CLOSE_NOTIFY: + return "close notify"; + case SSL3_AD_UNEXPECTED_MESSAGE: + return "unexpected message"; + case SSL3_AD_BAD_RECORD_MAC: + return "bad record mac"; + case SSL3_AD_DECOMPRESSION_FAILURE: + return "decompression failure"; + case SSL3_AD_HANDSHAKE_FAILURE: + return "handshake failure"; + case SSL3_AD_NO_CERTIFICATE: + return "no certificate"; + case SSL3_AD_BAD_CERTIFICATE: + return "bad certificate"; + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + return "unsupported certificate"; + case SSL3_AD_CERTIFICATE_REVOKED: + return "certificate revoked"; + case SSL3_AD_CERTIFICATE_EXPIRED: + return "certificate expired"; + case SSL3_AD_CERTIFICATE_UNKNOWN: + return "certificate unknown"; + case SSL3_AD_ILLEGAL_PARAMETER: + return "illegal parameter"; + case TLS1_AD_DECRYPTION_FAILED: + return "decryption failed"; + case TLS1_AD_RECORD_OVERFLOW: + return "record overflow"; + case TLS1_AD_UNKNOWN_CA: + return "unknown CA"; + case TLS1_AD_ACCESS_DENIED: + return "access denied"; + case TLS1_AD_DECODE_ERROR: + return "decode error"; + case TLS1_AD_DECRYPT_ERROR: + return "decrypt error"; + case TLS1_AD_EXPORT_RESTRICTION: + return "export restriction"; + case TLS1_AD_PROTOCOL_VERSION: + return "protocol version"; + case TLS1_AD_INSUFFICIENT_SECURITY: + return "insufficient security"; + case TLS1_AD_INTERNAL_ERROR: + return "internal error"; + case TLS1_AD_USER_CANCELLED: + return "user canceled"; + case TLS1_AD_NO_RENEGOTIATION: + return "no renegotiation"; + case TLS1_AD_UNSUPPORTED_EXTENSION: + return "unsupported extension"; + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + return "certificate unobtainable"; + case TLS1_AD_UNRECOGNIZED_NAME: + return "unrecognized name"; + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + return "bad certificate status response"; + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + return "bad certificate hash value"; + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + return "unknown PSK identity"; + case TLS1_AD_NO_APPLICATION_PROTOCOL: + return "no application protocol"; + default: + return "unknown"; + } +} + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) +{ + ctx->default_passwd_callback = cb; +} + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u) +{ + ctx->default_passwd_callback_userdata = u; +} + +void SSL_CTX_set_psk_client_callback(SSL_CTX *ctx, SSL_psk_client_cb_func cb) +{ + ctx->psk_client_callback = cb; +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) +{ + return 0; +} + +long SSL_CTX_set_mode(SSL_CTX *ctx, long larg) +{ + return ctx->mode |= larg; +} + +void SSL_CTX_set_info_callback(SSL_CTX *ctx, + void (*cb)(const SSL *ssl, int type, int val)) +{ + ctx->info_callback = cb; +} + +void SSL_CTX_set_msg_callback(SSL_CTX *ctx, + void (*cb)(int write_p, int version, + int content_type, const void *buf, + size_t len, SSL *ssl, void *arg)) +{ + ctx->msg_callback = cb; +} + +long SSL_set_tlsext_host_name(SSL *s, void *parg) +{ + size_t len; + + SSL_ASSERT1(s); + SSL_ASSERT1(s->session); + + if (parg == NULL) + { + return 1; + } + + len = strlen(parg); + if (len == 0 || len > TLSEXT_MAXLEN_host_name) + { + return 0; + } + + memset(s->session->ext.hostname, 0, TLSEXT_MAXLEN_host_name); + memcpy(s->session->ext.hostname, parg, len); + return 1; +} + +int SSL_CTX_load_verify_file(SSL_CTX *ctx, const char *CAfile) +{ + X509 *x; + int ret; + + SSL_ASSERT1(ctx); + SSL_ASSERT1(CAfile); + + x = X509_new(); + ret = X509_METHOD_CALL(load_file, x, CAfile); + if (ret) + { + X509_free(x); + return 0; + } + + SSL_CTX_add_client_CA(ctx, x); + return 1; +} + +int SSL_CTX_load_verify_dir(SSL_CTX *ctx, const char *CApath) +{ + X509 *x; + int ret; + + SSL_ASSERT1(ctx); + SSL_ASSERT1(CApath); + + x = X509_new(); + ret = X509_METHOD_CALL(load_path, x, CApath); + if (ret) + { + X509_free(x); + return 0; + } + + SSL_CTX_add_client_CA(ctx, x); + return 1; +} + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath) +{ + if (CAfile == NULL && CApath == NULL) + { + return 0; + } + + if (CAfile != NULL && !SSL_CTX_load_verify_file(ctx, CAfile)) + { + return 0; + } + + if (CApath != NULL && !SSL_CTX_load_verify_dir(ctx, CApath)) + { + return 0; + } + + return 1; +} + +int SSL_get_ex_new_index(long argl, void *argp, + CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func) +{ + return 0; +} + +const char *SSL_get_cipher_list(const SSL *s, int n) +{ + return NULL; +} + +int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg) +{ + return 0; +} + +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) +{ + return 0; +} diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.c b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.c index 7abddee9f..a5b8e5bb0 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.c @@ -95,7 +95,8 @@ IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); IMPLEMENT_X509_METHOD(X509_method, x509_pm_new, x509_pm_free, - x509_pm_load, x509_pm_show_info); + x509_pm_load, x509_pm_load_file, + x509_pm_load_path, x509_pm_show_info); /** * @brief get private key object method diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.h b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.h index 3c2647814..c93546fc7 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.h +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_methods.h @@ -27,10 +27,9 @@ #include #include "ssl_pm.h" -#ifdef __cplusplus -extern "C" -{ -#endif +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ /* TLS method function implement */ @@ -42,7 +41,7 @@ extern "C" set_bufflen, \ get_verify_result, \ get_state) \ - static const SSL_METHOD_FUNC func_name LOCAL_ATRR = \ + static const SSL_METHOD_FUNC func_name = \ { \ new, \ free, \ @@ -62,7 +61,7 @@ extern "C" #define IMPLEMENT_TLS_METHOD(ver, mode, fun, func_name) \ const SSL_METHOD* func_name(void) \ { \ - static const SSL_METHOD func_name##_data LOCAL_ATRR = \ + static const SSL_METHOD func_name##_data = \ { \ ver, \ mode, \ @@ -74,7 +73,7 @@ extern "C" #define IMPLEMENT_SSL_METHOD(ver, mode, fun, func_name) \ const SSL_METHOD* func_name(void) \ { \ - static const SSL_METHOD func_name##_data LOCAL_ATRR = \ + static const SSL_METHOD func_name##_data = \ { \ ver, \ mode, \ @@ -87,14 +86,18 @@ extern "C" new, \ free, \ load, \ + load_file, \ + load_path, \ show_info) \ const X509_METHOD* func_name(void) \ { \ - static const X509_METHOD func_name##_data LOCAL_ATRR = \ + static const X509_METHOD func_name##_data = \ { \ new, \ free, \ load, \ + load_file, \ + load_path, \ show_info \ }; \ return &func_name##_data; \ @@ -106,7 +109,7 @@ extern "C" load) \ const PKEY_METHOD* func_name(void) \ { \ - static const PKEY_METHOD func_name##_data LOCAL_ATRR = \ + static const PKEY_METHOD func_name##_data = \ { \ new, \ free, \ @@ -115,6 +118,15 @@ extern "C" return &func_name##_data; \ } +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + /** * @brief get X509 object method * diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.c b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.c index 95bec900e..4924969c9 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.c @@ -38,7 +38,16 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/error.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + #define X509_INFO_STRING_LENGTH 8192 +#define READ_TIMEOUT_MS 50000 /* 50 seconds */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ struct ssl_pm { @@ -58,7 +67,7 @@ struct ssl_pm struct x509_pm { - mbedtls_x509_crt *x509_crt; + mbedtls_x509_crt x509_crt; mbedtls_x509_crt *ex_crt; }; @@ -68,10 +77,15 @@ struct pkey_pm mbedtls_pk_context *ex_pkey; }; +/**************************************************************************** + * Public Data + ****************************************************************************/ + unsigned int max_content_len; -/* mbedtls debug level */ -#define MBEDTLS_DEBUG_LEVEL 4 +/**************************************************************************** + * Private Functions + ****************************************************************************/ /** * @brief mbedtls debug function @@ -134,6 +148,7 @@ int ssl_pm_new(SSL *ssl) mbedtls_ctr_drbg_init(&ssl_pm->ctr_drbg); mbedtls_entropy_init(&ssl_pm->entropy); mbedtls_ssl_init(&ssl_pm->ssl); + mbedtls_ssl_conf_read_timeout(&ssl_pm->conf, READ_TIMEOUT_MS); ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); @@ -210,7 +225,8 @@ int ssl_pm_new(SSL *ssl) } mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, - mbedtls_net_send, mbedtls_net_recv, NULL); + mbedtls_net_send, mbedtls_net_recv, + mbedtls_net_recv_timeout); ssl->ssl_pm = ssl_pm; @@ -249,7 +265,7 @@ void ssl_pm_free(SSL *ssl) static int ssl_pm_reload_crt(SSL *ssl) { - int ret; + int ret = 0; int mode; struct ssl_pm *ssl_pm = ssl->ssl_pm; struct x509_pm *ca_pm = (struct x509_pm *)ssl->client_CA->x509_pm; @@ -275,29 +291,16 @@ static int ssl_pm_reload_crt(SSL *ssl) } mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - - if (ca_pm->x509_crt) - { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); - } - else if (ca_pm->ex_crt) + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &ca_pm->x509_crt, NULL); + if (ca_pm->ex_crt) { mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->ex_crt, NULL); } - if (crt_pm->x509_crt && pkey_pm->pkey) + if (pkey_pm->pkey) { ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, - crt_pm->x509_crt, pkey_pm->pkey); - } - else if (crt_pm->ex_crt && pkey_pm->ex_pkey) - { - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, - crt_pm->ex_crt, pkey_pm->ex_pkey); - } - else - { - ret = 0; + &crt_pm->x509_crt, pkey_pm->pkey); } if (ret) @@ -342,7 +345,8 @@ int ssl_pm_handshake(SSL *ssl) ret = ssl_pm_reload_crt(ssl); if (ret) { - printf("%s: cert reload failed\n", __func__); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "%s: cert reload failed\n", __func__); return 0; } @@ -364,17 +368,21 @@ int ssl_pm_handshake(SSL *ssl) } /* OpenSSL return codes: - * 0 = did not complete, but may be retried + * 0 = The TLS/SSL handshake was not successful but was shut down + * controlled and by the specifications of the TLS/SSL protocol. * 1 = successfully completed - * <0 = death + * <0 = The TLS/SSL handshake was not successful because a fatal error + * occurred either at the protocol level or a connection failure + * occurred. */ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - ssl->err = ret; + ssl->err = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? SSL_ERROR_WANT_READ : + SSL_ERROR_WANT_WRITE; SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); - return 0; /* OpenSSL: did not complete but may be retried */ + return -1; } if (ret == 0) @@ -393,10 +401,11 @@ int ssl_pm_handshake(SSL *ssl) { ssl->err = ret == MBEDTLS_ERR_SSL_WANT_READ; - return 0; + return -1; } - printf("%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "%s: mbedtls_ssl_handshake() returned -0x%x\n", __func__, -ret); /* it's had it */ @@ -415,7 +424,7 @@ ssl_ctx_get_mbedtls_x509_crt(SSL_CTX *ssl_ctx) return NULL; } - return x509_pm->x509_crt; + return &x509_pm->x509_crt; } mbedtls_x509_crt * @@ -451,7 +460,7 @@ int ssl_pm_shutdown(SSL *ssl) else { struct x509_pm *x509_pm = - (struct x509_pm *)ssl->session->peer->x509_pm; + (struct x509_pm *)ssl->session->peer->x509_pm; x509_pm->ex_crt = NULL; ret = 1; /* OpenSSL: "The shutdown was successfully completed" @@ -481,6 +490,22 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len) { ssl->err = SSL_ERROR_SYSCALL; } + else if (ret == MBEDTLS_ERR_SSL_WANT_READ) + { + ssl->err = SSL_ERROR_WANT_READ; + } + else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) + { + ssl->err = SSL_ERROR_WANT_WRITE; + } + else if (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) + { + ssl->err = SSL_ERROR_WANT_ASYNC; + } + else + { + ssl->err = SSL_ERROR_SSL; + } ret = -1; } @@ -613,7 +638,7 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) case MBEDTLS_SSL_HANDSHAKE_OVER: state = TLS_ST_OK; break; - default : + default: state = TLS_ST_BEFORE; break; } @@ -628,20 +653,8 @@ int x509_pm_show_info(X509 *x) mbedtls_x509_crt *x509_crt; struct x509_pm *x509_pm = x->x509_pm; - if (x509_pm->x509_crt) - { - x509_crt = x509_pm->x509_crt; - } - else if (x509_pm->ex_crt) - { - x509_crt = x509_pm->ex_crt; - } - else - { - x509_crt = NULL; - } - - if (!x509_crt) + x509_crt = &x509_pm->x509_crt; + if (x509_crt->version == 0) { return -1; } @@ -693,7 +706,7 @@ int x509_pm_new(X509 *x, X509 *m_x) { struct x509_pm *m_x509_pm = (struct x509_pm *)m_x->x509_pm; - x509_pm->ex_crt = m_x509_pm->x509_crt; + x509_pm->ex_crt = &m_x509_pm->x509_crt; } return 0; @@ -706,14 +719,7 @@ void x509_pm_free(X509 *x) { struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - if (x509_pm->x509_crt) - { - mbedtls_x509_crt_free(x509_pm->x509_crt); - - ssl_mem_free(x509_pm->x509_crt); - x509_pm->x509_crt = NULL; - } - + mbedtls_x509_crt_free(&x509_pm->x509_crt); ssl_mem_free(x->x509_pm); x->x509_pm = NULL; } @@ -724,23 +730,8 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) unsigned char *load_buf; struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - if (x509_pm->x509_crt) - { - mbedtls_x509_crt_free(x509_pm->x509_crt); - } - - if (!x509_pm->x509_crt) - { - x509_pm->x509_crt = ssl_mem_malloc(sizeof(mbedtls_x509_crt)); - if (!x509_pm->x509_crt) - { - SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, - "no enough memory > (x509_pm->x509_crt)"); - goto no_mem; - } - } - - mbedtls_x509_crt_init(x509_pm->x509_crt); + mbedtls_x509_crt_free(&x509_pm->x509_crt); + mbedtls_x509_crt_init(&x509_pm->x509_crt); if (buffer[0] != 0x30) { load_buf = ssl_mem_malloc(len + 1); @@ -754,32 +745,67 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; - ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); + ret = mbedtls_x509_crt_parse(&x509_pm->x509_crt, load_buf, len + 1); ssl_mem_free(load_buf); } else { - printf("parsing as der\n"); - - ret = mbedtls_x509_crt_parse_der(x509_pm->x509_crt, buffer, len); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "parsing as der\n"); + ret = mbedtls_x509_crt_parse_der(&x509_pm->x509_crt, buffer, len); } if (ret) { - printf("mbedtls_x509_crt_parse return -0x%x", -ret); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_x509_crt_parse return -0x%x", -ret); goto failed; } return 0; failed: - mbedtls_x509_crt_free(x509_pm->x509_crt); - ssl_mem_free(x509_pm->x509_crt); - x509_pm->x509_crt = NULL; -no_mem: + mbedtls_x509_crt_free(&x509_pm->x509_crt); return -1; } +int x509_pm_load_file(X509 *x, const char *path) +{ + int ret; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + mbedtls_x509_crt_free(&x509_pm->x509_crt); + mbedtls_x509_crt_init(&x509_pm->x509_crt); + ret = mbedtls_x509_crt_parse_file(&x509_pm->x509_crt, path); + if (ret) + { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_x509_crt_parse_file return -0x%x", -ret); + mbedtls_x509_crt_free(&x509_pm->x509_crt); + return -1; + } + + return 0; +} + +int x509_pm_load_path(X509 *x, const char *path) +{ + int ret; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + mbedtls_x509_crt_free(&x509_pm->x509_crt); + mbedtls_x509_crt_init(&x509_pm->x509_crt); + ret = mbedtls_x509_crt_parse_path(&x509_pm->x509_crt, path); + if (ret) + { + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_x509_crt_parse_file return -0x%x", -ret); + mbedtls_x509_crt_free(&x509_pm->x509_crt); + return -1; + } + + return 0; +} + int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pkey) { struct pkey_pm *pkey_pm; @@ -977,7 +1003,8 @@ void _ssl_set_alpn_list(const SSL *ssl) &((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->alpn_protos)) { - fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n"); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_ssl_conf_alpn_protocols failed\n"); } return; @@ -992,7 +1019,8 @@ void _ssl_set_alpn_list(const SSL *ssl) &((struct ssl_pm *)(ssl->ssl_pm))->conf, ssl->ctx->alpn_protos)) { - fprintf(stderr, "mbedtls_ssl_conf_alpn_protocols failed\n"); + SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, + "mbedtls_ssl_conf_alpn_protocols failed\n"); } } @@ -1070,8 +1098,8 @@ void SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) ssl->verify_mode = ctx->verify_mode; - mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, x509_pm_ca->x509_crt, NULL); - mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, x509_pm->x509_crt, + mbedtls_ssl_set_hs_ca_chain(&ssl_pm->ssl, &x509_pm_ca->x509_crt, NULL); + mbedtls_ssl_set_hs_own_cert(&ssl_pm->ssl, &x509_pm->x509_crt, pkey_pm->pkey); mbedtls_ssl_set_hs_authmode(&ssl_pm->ssl, mode); } diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.h b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.h index 22e6eda4a..0d2065024 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.h +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_pm.h @@ -33,7 +33,9 @@ extern "C" { #endif -#define LOCAL_ATRR +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ int ssl_pm_new(SSL *ssl); void ssl_pm_free(SSL *ssl); @@ -57,6 +59,8 @@ int x509_pm_show_info(X509 *x); int x509_pm_new(X509 *x, X509 *m_x); void x509_pm_free(X509 *x); int x509_pm_load(X509 *x, const unsigned char *buffer, int len); +int x509_pm_load_file(X509 *x, const char *path); +int x509_pm_load_path(X509 *x, const char *path); int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pk); void pkey_pm_free(EVP_PKEY *pk); diff --git a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_rsa.c b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_rsa.c index 0a754317d..7f21912ea 100644 --- a/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_rsa.c +++ b/crypto/openssl_mbedtls_wrapper/mbedtls/ssl_rsa.c @@ -124,6 +124,11 @@ int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) return 0; } +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) +{ + return 0; +} + int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { return 0; diff --git a/crypto/tinycrypt/CMakeLists.txt b/crypto/tinycrypt/CMakeLists.txt index a44ed6234..c75ae88c6 100644 --- a/crypto/tinycrypt/CMakeLists.txt +++ b/crypto/tinycrypt/CMakeLists.txt @@ -64,6 +64,7 @@ if(CONFIG_TINYCRYPT) # Sources # ############################################################################ + list(APPEND CFLAGS -Dunix) set(CSRCS ${TINYCRYPT_DIR}/lib/source/utils.c ${TINYCRYPT_DIR}/lib/source/ecc.c ${TINYCRYPT_DIR}/lib/source/ecc_platform_specific.c) @@ -120,7 +121,7 @@ if(CONFIG_TINYCRYPT) if(CONFIG_TINYCRYPT_TEST) list(APPEND CSRCS ${TINYCRYPT_DIR}/tests/test_ecc_utils.c) list(APPEND INCDIR ${TINYCRYPT_DIR}/tests/include) - set(CFLAGS -Dhex2bin=ltp_hex2bin -DENABLE_TESTS) + list(APPEND CFLAGS -DENABLE_TESTS) nuttx_add_application( NAME diff --git a/crypto/tinycrypt/Makefile b/crypto/tinycrypt/Makefile index 6b3930a91..d5c2c0e9f 100644 --- a/crypto/tinycrypt/Makefile +++ b/crypto/tinycrypt/Makefile @@ -48,6 +48,7 @@ distclean:: $(Q) rm -rf $(TINYCRYPT_UNPACKNAME) endif +CFLAGS += ${DEFINE_PREFIX}unix CSRCS += tinycrypt/lib/source/utils.c ifeq ($(CONFIG_TINYCRYPT_ECC),y) @@ -103,11 +104,11 @@ ifeq ($(CONFIG_TINYCRYPT_CTR_PRNG),y) CSRCS += tinycrypt/lib/source/ctr_prng.c endif -ifeq ($(CONFIG_TINYCRYPT_TEST),y) +ifneq ($(CONFIG_TINYCRYPT_TEST),) CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/crypto/tinycrypt/tinycrypt/tests/include - CFLAGS +=-Dhex2bin=ltp_hex2bin - CFLAGS +=-DENABLE_TESTS + CFLAGS += -DENABLE_TESTS CSRCS += tinycrypt/tests/test_ecc_utils.c + MODULE += $(CONFIG_TINYCRYPT_TEST) PRIORITY = $(CONFIG_TINYCRYPT_TEST_PRIORITY) STACKSIZE = $(CONFIG_TINYCRYPT_TEST_STACKSIZE) diff --git a/crypto/wolfssl/Makefile b/crypto/wolfssl/Makefile index 77bc546a5..90b0f6390 100644 --- a/crypto/wolfssl/Makefile +++ b/crypto/wolfssl/Makefile @@ -168,7 +168,7 @@ distclean:: $(call DELDIR, $(WOLFSSL_UNPACKNAME)) $(call DELDIR, $(WOLFSSL_EXAMPLESNAME)) else -distclean: +distclean:: $(call DELDIR, $(WOLFSSL_UNPACKNAME)) endif diff --git a/database/sqlite/Makefile b/database/sqlite/Makefile index cc1e6b460..3a109672f 100644 --- a/database/sqlite/Makefile +++ b/database/sqlite/Makefile @@ -36,6 +36,7 @@ ifneq ($(CONFIG_UTILS_SQLITE),) PROGNAME = sqlite3 PRIORITY = 100 STACKSIZE = ${CONFIG_UTILS_SQLITE_STACKSIZE} +MODULE = $(CONFIG_UTILS_SQLITE) MAINSRC = ${BUILDDIR}/shell.c endif @@ -67,4 +68,3 @@ ifeq ($(wildcard sqlite/.git),) endif include $(APPDIR)/Application.mk - diff --git a/examples/adc/adc_main.c b/examples/adc/adc_main.c index 68f4f1955..e5678e123 100644 --- a/examples/adc/adc_main.c +++ b/examples/adc/adc_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -95,7 +95,7 @@ static void adc_help(FAR struct adc_state_s *adc) printf("Usage: adc [OPTIONS]\n"); printf("\nArguments are \"sticky\". " "For example, once the ADC device is\n"); - printf("specified, that device will be re-used until it is changed.\n"); + printf("specified, that device will be reused until it is changed.\n"); printf("\n\"sticky\" OPTIONS include:\n"); printf(" [-p devpath] selects the ADC device. " "Default: %s Current: %s\n", diff --git a/examples/adjtime/adjtime_main.c b/examples/adjtime/adjtime_main.c index 17eda10f5..fefe80567 100644 --- a/examples/adjtime/adjtime_main.c +++ b/examples/adjtime/adjtime_main.c @@ -25,6 +25,7 @@ ****************************************************************************/ #include +#include #include #include @@ -149,8 +150,8 @@ int main(int argc, FAR char *argv[]) parse_args(&delta, argc, argv); - printf("Delta time is %ld seconds and %ld micro seconds.\n", - (long)delta.tv_sec, delta.tv_usec); + printf("Delta time is %jd seconds and %ld micro seconds.\n", + (intmax_t)delta.tv_sec, delta.tv_usec); /* Call adjtime function. */ @@ -161,8 +162,8 @@ int main(int argc, FAR char *argv[]) } else { - printf("Returned olddelta is %ld seconds and %ld micro seconds.\n", - (long)olddelta.tv_sec, olddelta.tv_usec); + printf("Returned olddelta is %jd seconds and %ld micro seconds.\n", + (intmax_t)olddelta.tv_sec, olddelta.tv_usec); } return ret; diff --git a/examples/ajoystick/ajoy_main.c b/examples/ajoystick/ajoy_main.c index 178608b2f..8a58b66a9 100644 --- a/examples/ajoystick/ajoy_main.c +++ b/examples/ajoystick/ajoy_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/alarm/alarm_main.c b/examples/alarm/alarm_main.c index d4ab70a80..4b5296f73 100644 --- a/examples/alarm/alarm_main.c +++ b/examples/alarm/alarm_main.c @@ -393,7 +393,7 @@ int main(int argc, FAR char *argv[]) setrel.id = alarmid; setrel.pid = g_alarm_daemon_pid; - setrel.reltime = (time_t)seconds; + setrel.reltime = seconds; setrel.event.sigev_notify = SIGEV_SIGNAL; setrel.event.sigev_signo = CONFIG_EXAMPLES_ALARM_SIGNO; diff --git a/examples/audio_rttl/CMakeLists.txt b/examples/audio_rttl/CMakeLists.txt index ec7709bd3..3553cfec6 100644 --- a/examples/audio_rttl/CMakeLists.txt +++ b/examples/audio_rttl/CMakeLists.txt @@ -29,5 +29,5 @@ if(CONFIG_EXAMPLES_AUDIO_SOUND) STACKSIZE ${CONFIG_EXAMPLES_AUDIO_SOUND_STACKSIZE} SRCS - audio_rttl.cxx) + audio_rttl_main.c) endif() diff --git a/examples/audio_rttl/Kconfig b/examples/audio_rttl/Kconfig index 2dae260b0..3a594e867 100644 --- a/examples/audio_rttl/Kconfig +++ b/examples/audio_rttl/Kconfig @@ -5,9 +5,11 @@ config EXAMPLES_AUDIO_SOUND bool "Audio tone generator example (RTTL player)" + depends on AUDIO + depends on AUDIOUTILS_RTTTL_C default n ---help--- - Enable the audio RTTL player example + Enable the audio RTTL player example. if EXAMPLES_AUDIO_SOUND @@ -23,4 +25,12 @@ config EXAMPLES_AUDIO_SOUND_STACKSIZE int "Audio sound stack size" default DEFAULT_TASK_STACKSIZE +comment "Program options" + +config EXAMPLES_AUDIO_SOUND_MAXLEN + int "Maximum RTTL string length" + default 256 + ---help--- + The maximum length of an RTTL string that can be read from a file. + endif diff --git a/examples/audio_rttl/Makefile b/examples/audio_rttl/Makefile index 1816513a7..b2a4630b3 100644 --- a/examples/audio_rttl/Makefile +++ b/examples/audio_rttl/Makefile @@ -30,6 +30,6 @@ STACKSIZE = $(CONFIG_EXAMPLES_AUDIO_SOUND_STACKSIZE) # Audio Example -MAINSRC = audio_rttl.cxx +MAINSRC = audio_rttl_main.c include $(APPDIR)/Application.mk diff --git a/examples/audio_rttl/audio_rttl.cxx b/examples/audio_rttl/audio_rttl.cxx deleted file mode 100644 index 3533258c1..000000000 --- a/examples/audio_rttl/audio_rttl.cxx +++ /dev/null @@ -1,361 +0,0 @@ -/**************************************************************************** - * apps/examples/audio_rttl/audio_rttl.cxx - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include -#include -#include -#include -#include - -#include "audio_rttl.h" - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* Note - note freq on octave 1 */ - -#define C 16.35 -#define CD 17.32 -#define D 18.35 -#define DE 19.45 -#define E 20.60 -#define F 21.83 -#define FG 23.12 -#define G 24.50 -#define A 27.50 -#define AB 29.14 -#define B 30.84 - -/* Octave - base freq multiplier for the base freq */ - -#define OCT2 4 -#define OCT3 8 -#define OCT4 16 -#define OCT5 32 -#define OCT6 64 -#define OCT7 128 -#define OCT8 256 - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -extern "C" int main(int argc, FAR char *argv[]) -{ - int freq; - float base_freq; - char temp_note; - int octave; - int duration; - int note_duration; - int base_duration; - int bpm; - int cnt; - - /* Set I/O parameters for power on. */ - - if (!board_audio_power_control(true)) - { - printf("Error: board_audio_power_control() failure.\n"); - return 1; - } - - printf("Start Audio RTTL player\n"); - - bpm = 0; - octave = 0; - base_duration = 0; - note_duration = 0; - - while (*g_song != ':') - { - g_song++; - } - - g_song++; - - if (*g_song == 'd') - { - g_song = g_song + 2; - base_duration = *g_song - '0'; - g_song++; - if ((*g_song >= '0') && (*g_song <= '9')) - { - base_duration = (base_duration * 10) + (*g_song - '0'); - g_song++; - } - - g_song++; - } - - if (*g_song == 'o') - { - g_song = g_song + 2; - octave = *g_song - '0'; - g_song = g_song + 2; - } - - if (octave == 2) - { - octave = OCT2; - } - else if (octave == 3) - { - octave = OCT3; - } - else if (octave == 4) - { - octave = OCT4; - } - else if (octave == 5) - { - octave = OCT5; - } - else if (octave == 6) - { - octave = OCT6; - } - else if (octave == 7) - { - octave = OCT7; - } - else if (octave == 8) - { - octave = OCT8; - } - - if (*g_song == 'b') - { - g_song = g_song + 2; - bpm = 0; - for (cnt = 1; cnt <= 3; cnt++) - { - if (*g_song != ':') - { - bpm = bpm * 10 + (*g_song - '0'); - g_song++; - } - - } - - g_song++; - } - - do - { - if ((*g_song >= '0') && (*g_song <= '9')) - { - note_duration = *g_song - '0'; - g_song++; - } - - if ((*g_song >= '0') && (*g_song <= '9')) - { - note_duration = (note_duration * 10) + (*g_song - '0'); - g_song++; - } - - if (note_duration > 0) - { - duration = ((60 * 1000L / bpm) * 4) / note_duration; - } - else - { - duration = ((60 * 1000L / bpm) * 4) / base_duration; - } - - base_freq = 0; - temp_note = ' '; - switch (*g_song) - { - case 'c': - { - temp_note = 'c'; - base_freq = C; - break; - } - - case 'd': - { - temp_note = 'd'; - base_freq = D; - break; - } - - case 'e': - { - temp_note = 'e'; - base_freq = E; - break; - } - - case 'f': - { - temp_note = 'f'; - base_freq = F; - break; - } - - case 'g': - { - temp_note = 'g'; - base_freq = G; - break; - } - - case 'a': - { - temp_note = 'a'; - base_freq = A; - break; - } - - case 'b': - { - temp_note = 'b'; - base_freq = B; - break; - } - - case 'p': - { - temp_note = 'p'; - break; - } - - default: - break; - } - - g_song++; - - if (*g_song == '.') - { - duration += duration / 2; - g_song++; - } - - if (*g_song == '#') - { - if (temp_note == 'c') - { - base_freq = CD; - } - - if (temp_note == 'd') - { - base_freq = DE; - } - - if (temp_note == 'f') - { - base_freq = FG; - } - - if (temp_note == 'a') - { - base_freq = AB; - } - - g_song++; - } - - if ((*g_song - '0') == '2') - { - octave = OCT2; - } - - if ((*g_song - '0') == '3') - { - octave = OCT3; - } - - if ((*g_song - '0') == '4') - { - octave = OCT4; - } - - if ((*g_song - '0') == '5') - { - octave = OCT5; - } - - if ((*g_song - '0') == '6') - { - octave = OCT6; - } - - if ((*g_song - '0') == '7') - { - octave = OCT7; - } - - if ((*g_song - '0') == '8') - { - octave = OCT8; - } - - g_song++; - - if (*g_song == ',') - { - g_song++; - } - - /* play the sound */ - - freq = ceil(base_freq * octave); - - if (!board_audio_tone_generator(1, -40, freq)) - { - break; - } - - usleep(duration * 1000L); - } - while (*g_song); - - /* Sound off. */ - - if (!board_audio_tone_generator(0, 0, 0)) - { - printf("Error: board_audio_tone_generator() failure.\n"); - return 1; - } - - /* Set I/O parameters for power off. */ - - if (!board_audio_power_control(false)) - { - printf("Error: board_audio_power_control() failure.\n"); - return 1; - } - - printf("Done.\n"); - - return 0; -} diff --git a/examples/audio_rttl/audio_rttl_main.c b/examples/audio_rttl/audio_rttl_main.c new file mode 100644 index 000000000..5e9e7e960 --- /dev/null +++ b/examples/audio_rttl/audio_rttl_main.c @@ -0,0 +1,305 @@ +/**************************************************************************** + * apps/examples/audio_rttl/audio_rttl_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_PWM +#include +#endif + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct player_s +{ + void (*play)(struct rtttl_tone); /* Play RTTTL sound function */ + void (*teardown)(void); /* Clean-up function */ + void *arg; /* Private data for functions */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Default song if none was passed. Otherwise, used as buffer for reading + * from a file. + */ + +static char g_default_song[CONFIG_EXAMPLES_AUDIO_SOUND_MAXLEN] = + "Test:d=4,o=6,b=90:c,c#,d,d#,e,f,f#,g,a,a#,b,p,b,a#,a,g," + "f#,f,e,d#,d,c#,c"; + +/* The selected player is a global variable so it can be accessed from within + * the player functions properly. + */ + +static struct player_s g_player; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: play_default + * + * Description: + * The default player choice; prints an error and exits. + * + * Input Parameters: + * sound - The sound to play + * + ****************************************************************************/ + +static void play_default(struct rtttl_tone sound) +{ + fprintf(stderr, "No player selected!\n"); + return; +} + +/**************************************************************************** + * Name: teardown_default + * + * Description: + * Default tear-down function. Does nothing. + * + ****************************************************************************/ + +static void teardown_default(void) +{ + /* No-op */ + + return; +} + +#ifdef CONFIG_PWM + +/**************************************************************************** + * Name: play_pwm + * + * Description: + * RTTTL player for audio sinks using PWM devices + * + * Input Parameters: + * sound - The sound to play + * + ****************************************************************************/ + +static void play_pwm(struct rtttl_tone sound) +{ + int err; + int i; + struct pwm_info_s info; + int fd = *((int *)g_player.arg); + + info.channels[0].channel = 1; + info.channels[0].duty = b16divi(uitoub16(50) - 1, 100); /* 50% duty cycle */ + info.channels[0].cpol = PWM_CPOL_NDEF; /* Default */ + info.channels[0].dcpol = PWM_DCPOL_NDEF; /* Default */ + info.frequency = sound.frequency_100hz / 100; + + /* Copy settings to all channels */ + + for (i = 1; i < CONFIG_PWM_NCHANNELS; i++) + { + info.channels[i] = info.channels[0]; + info.channels[i].channel = i + 1; + } + + err = ioctl(fd, PWMIOC_SETCHARACTERISTICS, &info); + if (err < 0) + { + fprintf(stderr, "Failed to set PWM characteristics: %d\n", errno); + return; + } + + err = ioctl(fd, PWMIOC_START, 0); + if (err < 0) + { + fprintf(stderr, "Failed to start PWM: %d\n", errno); + return; + } + + usleep(sound.duration_us); /* Wait */ + + err = ioctl(fd, PWMIOC_STOP, 0); + if (err < 0) + { + fprintf(stderr, "Failed to stop PWM: %d\n", errno); + } +} + +/**************************************************************************** + * Name: teardown_pwm + * + * Description: + * Teardown function for PWM players + * + ****************************************************************************/ + +static void teardown_pwm(void) +{ + int fd = *((int *)g_player.arg); + close(fd); +} +#endif /* CONFIG_PWM */ + +/**************************************************************************** + * Name: print_usage + * + * Description: + * Prints out the usage information for the program. + * + * Input Parameters: + * sink - The stream to output the usage information + * + ****************************************************************************/ + +static void print_usage(FILE *sink) +{ + fprintf(sink, "USAGE: audio_rttl [-s string] [-f file]\n"); + fprintf(sink, "Ex: audio_rttl /dev/tone0\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + int err; + int fd; + int c; + FILE *fptr; + size_t bread; + const char *song = g_default_song; + const char *device = NULL; + const char *file = NULL; + + g_player.play = play_default; + g_player.teardown = teardown_default; + g_player.arg = NULL; + + while ((c = getopt(argc, argv, ":hs:f:")) != -1) + { + switch (c) + { + case 'h': + print_usage(stdout); + break; + case 's': + song = optarg; + break; + case 'f': + file = optarg; + break; + case '?': + fprintf(stderr, "Unknown option -%c\n", optopt); + print_usage(stderr); + return EXIT_FAILURE; + } + } + + if (argc <= optind) + { + fprintf(stderr, "Missing argument for device path.\n"); + return EXIT_FAILURE; + } + + device = argv[optind++]; + + /* Based on the device path, we choose which player should be used and + * perform the setup. + */ + +#ifdef CONFIG_PWM + if (strstr(device, "pwm")) + { + fd = open(device, O_RDWR); + if (fd < 0) + { + fprintf(stderr, "Failed to open %s: %d\n", device, errno); + return EXIT_FAILURE; + } + else + { + g_player.arg = &fd; + } + + g_player.play = play_pwm; + g_player.teardown = teardown_pwm; + } +#endif /* CONFIG_PWM */ + + /* If the user passed a file and a song string, use the string. Otherwise, + * read from the file + */ + + if (file != NULL && song == g_default_song) + { + fptr = fopen(file, "r"); + if (fptr == NULL) + { + fprintf(stderr, "Couldn't open file '%s': %d", file, errno); + g_player.teardown(); + return EXIT_FAILURE; + } + + bread = + fread(g_default_song, sizeof(char), sizeof(g_default_song), fptr); + + /* Ensure null termination of the string. */ + + if (bread <= sizeof(g_default_song)) + { + g_default_song[bread - 1] = '\0'; + } + + fclose(fptr); + } + + /* Play the song */ + + err = rtttl_play(song, g_player.play); + if (err != 0) + { + fprintf(stderr, "Failed to play song: %d\n", err); + return err; + } + + /* Tear down the player and exit */ + + g_player.teardown(); + return EXIT_SUCCESS; +} diff --git a/examples/baromonitor/CMakeLists.txt b/examples/baromonitor/CMakeLists.txt new file mode 100644 index 000000000..e047922f8 --- /dev/null +++ b/examples/baromonitor/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/baromonitor/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_BAROMONITOR) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_BAROMONITOR_PROGNAME} + PRIORITY + ${CONFIG_EXAMPLES_BAROMONITOR_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_BAROMONITOR_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_BAROMONITOR} + SRCS + baromonitor_main.c) +endif() diff --git a/examples/baromonitor/Kconfig b/examples/baromonitor/Kconfig new file mode 100644 index 000000000..7ef4f17bc --- /dev/null +++ b/examples/baromonitor/Kconfig @@ -0,0 +1,38 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_BAROMONITOR + tristate "Barometer monitoring dashboard example" + default n + depends on UORB + depends on LIBC_FLOATINGPOINT + depends on GRAPHICS_LVGL + depends on LV_USE_NUTTX + depends on LV_FONT_MONTSERRAT_24 + depends on LV_FONT_MONTSERRAT_32 + ---help--- + Enable the barometer monitoring dashboard. + +if EXAMPLES_BAROMONITOR + +config EXAMPLES_BAROMONITOR_PROGNAME + string "Program name" + default "baromonitor" + +config EXAMPLES_BAROMONITOR_PRIORITY + int "Barometer monitor task priority" + default 100 + +config EXAMPLES_BAROMONITOR_STACKSIZE + int "Baromonitor stack size" + default DEFAULT_TASK_STACKSIZE + +config EXAMPLES_BAROMONITOR_SAMPLERATE + int "Sample rate (ms)" + default 50 + ---help--- + Sample rate in milliseconds of barometer data. + +endif # EXAMPLES_BARO_DASHBOARD diff --git a/examples/baromonitor/Make.defs b/examples/baromonitor/Make.defs new file mode 100644 index 000000000..206ea679f --- /dev/null +++ b/examples/baromonitor/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/baromonitor/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_BAROMONITOR),) +CONFIGURED_APPS += $(APPDIR)/examples/baromonitor +endif diff --git a/examples/baromonitor/Makefile b/examples/baromonitor/Makefile new file mode 100644 index 000000000..8fe37e36f --- /dev/null +++ b/examples/baromonitor/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/examples/baromonitor/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# BAROMONITOR Barometer sensor example built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_BAROMONITOR_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_BAROMONITOR_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_BAROMONITOR_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_BAROMONITOR) + +# BAROMONITOR Barometer sensor example + +MAINSRC = baromonitor_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/baromonitor/baromonitor_main.c b/examples/baromonitor/baromonitor_main.c new file mode 100644 index 000000000..c3a93d6b9 --- /dev/null +++ b/examples/baromonitor/baromonitor_main.c @@ -0,0 +1,419 @@ +/**************************************************************************** + * apps/examples/baromonitor/baromonitor_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PRESSURE_COLOUR 0xef4444 +#define TEMPERATURE_COLOUR 0x6366f1 + +#define LINE_WIDTH (8) +#define GAUGE_SIZE (600) + +/* Axis limits */ + +#define PRESS_MIN (960) +#define PRESS_MAX (1010) + +#define TEMP_MIN (-30) +#define TEMP_MAX (60) +#define TEMP_THRESH (TEMP_MAX - 10) + +#define PRESS_MAJOR_TICKS_EVERY (5) +#define TEMP_MAJOR_TICKS_EVERY (5) + +#define NUM_TEMP_LABELS ((TEMP_MAX - TEMP_MIN) / TEMP_MAJOR_TICKS_EVERY) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char *g_temp_labels[NUM_TEMP_LABELS + 1]; +static char g_label_buf[NUM_TEMP_LABELS][8]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV +static void lv_nuttx_uv_loop(uv_loop_t *loop, lv_nuttx_result_t *result) +{ + lv_nuttx_uv_t uv_info; + void *data; + + uv_loop_init(loop); + + lv_memset(&uv_info, 0, sizeof(uv_info)); + uv_info.loop = loop; + uv_info.disp = result->disp; + uv_info.indev = result->indev; +#ifdef CONFIG_UINPUT_TOUCH + uv_info.uindev = result->utouch_indev; +#endif + + data = lv_nuttx_uv_init(&uv_info); + uv_run(loop, UV_RUN_DEFAULT); + lv_nuttx_uv_deinit(&data); +} +#endif + +static void set_column_flex(lv_obj_t *obj) +{ + lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_flex_main_place(obj, LV_FLEX_ALIGN_CENTER, 0); + lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, 0); + lv_obj_set_style_flex_track_place(obj, LV_FLEX_ALIGN_CENTER, 0); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * baromonitor_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + lv_nuttx_dsc_t info; + lv_nuttx_result_t result; + lv_obj_t *screen; + lv_obj_t *gauge_cont; + lv_obj_t *press_gauge; + lv_obj_t *press_line; + lv_obj_t *press_cont; + lv_obj_t *press_label; + lv_obj_t *temp_gauge; + lv_obj_t *temp_line; + lv_obj_t *temp_cont; + lv_obj_t *temp_label; + lv_obj_t *title_cont; + lv_obj_t *title; + lv_scale_section_t *section; + lv_style_t indicator_style; + lv_style_t minor_ticks_style; + lv_style_t main_line_style; + lv_style_t section_label_style; + lv_style_t section_minor_ticks_style; + lv_style_t section_main_line_style; + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV + uv_loop_t ui_loop; +#endif + + int err; + int fd; + bool got_data; + struct sensor_baro sensor_data; + unsigned instance = 0; + char topic[32]; + + if (argc == 2) + { + instance = atoi(argv[1]); + } + + snprintf(topic, sizeof(topic), "sensor_baro%u", instance); + + fd = orb_open("sensor_baro", instance, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + fprintf(stderr, "Could not open %s: %d\n", topic, errno); + return EXIT_FAILURE; + } + + printf("Subscribed to %s\n", topic); + + /* LVGL setup */ + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV + lv_memzero(&ui_loop, sizeof(ui_loop)); +#endif + + if (!lv_is_initialized()) + { + printf("Initializing LVGL...\n"); + lv_init(); + printf("LVGL initialized...\n"); + } + + lv_nuttx_dsc_init(&info); + lv_nuttx_init(&info, &result); + + if (result.disp == NULL) + { + fprintf(stderr, "Failed to initialize LVGL.\n"); + close(fd); + return EXIT_FAILURE; + } + + /* Set up stuff to render */ + + screen = lv_screen_active(); + set_column_flex(screen); + + /* Create containers for layout. We want a spanning title, followed by two + * side-by-side containers for the gauges. + * + * Gauge container is therefore a ROW flex, while its inner containers are + * column flex (scale followed by a label below) + */ + + title_cont = lv_obj_create(screen); + lv_obj_set_size(title_cont, LV_PCT(100), LV_PCT(10)); + lv_obj_align(title_cont, LV_ALIGN_CENTER, 0, 0); + title = lv_label_create(title_cont); + lv_label_set_text(title, "Barometer monitor"); + lv_obj_set_style_text_font(title, &lv_font_montserrat_32, 0); + lv_obj_align(title, LV_ALIGN_CENTER, 0, 0); + + gauge_cont = lv_obj_create(screen); + lv_obj_set_size(gauge_cont, LV_PCT(100), LV_PCT(90)); + lv_obj_set_flex_flow(gauge_cont, LV_FLEX_FLOW_ROW); + lv_obj_set_style_flex_main_place(gauge_cont, LV_FLEX_ALIGN_CENTER, 0); + lv_obj_set_style_flex_cross_place(gauge_cont, LV_FLEX_ALIGN_CENTER, 0); + lv_obj_set_style_flex_track_place(gauge_cont, LV_FLEX_ALIGN_CENTER, 0); + + press_cont = lv_obj_create(gauge_cont); + temp_cont = lv_obj_create(gauge_cont); + set_column_flex(press_cont); + set_column_flex(temp_cont); + lv_obj_set_size(press_cont, LV_PCT(50), LV_PCT(100)); + lv_obj_set_size(temp_cont, LV_PCT(50), LV_PCT(100)); + + /* TODO: Get the text labels on the gauges to be padded better */ + + /* Pressure gauge */ + + press_gauge = lv_scale_create(press_cont); + + lv_obj_set_size(press_gauge, GAUGE_SIZE, GAUGE_SIZE); + lv_scale_set_mode(press_gauge, LV_SCALE_MODE_ROUND_INNER); + lv_obj_set_style_bg_opa(press_gauge, LV_OPA_COVER, 0); + lv_obj_set_style_bg_color(press_gauge, + lv_palette_lighten(LV_PALETTE_RED, 4), 0); + lv_obj_set_style_radius(press_gauge, LV_RADIUS_CIRCLE, 0); + lv_obj_set_style_clip_corner(press_gauge, true, 0); + lv_obj_set_style_text_font(press_gauge, &lv_font_montserrat_24, 0); + lv_obj_align(press_gauge, LV_ALIGN_LEFT_MID, LV_PCT(5), 0); + + lv_scale_set_label_show(press_gauge, true); + + lv_scale_set_total_tick_count(press_gauge, (PRESS_MAX - PRESS_MIN)); + lv_scale_set_major_tick_every(press_gauge, PRESS_MAJOR_TICKS_EVERY); + + lv_obj_set_style_length(press_gauge, 5, LV_PART_ITEMS); + lv_obj_set_style_length(press_gauge, 10, LV_PART_INDICATOR); + lv_scale_set_range(press_gauge, PRESS_MIN, PRESS_MAX); + + lv_scale_set_angle_range(press_gauge, 270); + lv_scale_set_rotation(press_gauge, 135); + + press_line = lv_line_create(press_gauge); + + lv_obj_set_style_line_width(press_line, LINE_WIDTH, LV_PART_MAIN); + lv_obj_set_style_line_rounded(press_line, true, LV_PART_MAIN); + lv_obj_set_style_line_color(press_line, lv_color_black(), LV_PART_MAIN); + lv_obj_center(press_line); + + /* Temperature gauge */ + + temp_gauge = lv_scale_create(temp_cont); + lv_obj_set_size(temp_gauge, GAUGE_SIZE, GAUGE_SIZE); + lv_scale_set_label_show(temp_gauge, true); + lv_scale_set_mode(temp_gauge, LV_SCALE_MODE_ROUND_OUTER); + lv_obj_align(temp_gauge, LV_ALIGN_RIGHT_MID, LV_PCT(-5), 0); + + lv_scale_set_total_tick_count(temp_gauge, (TEMP_MAX - TEMP_MIN)); + lv_scale_set_major_tick_every(temp_gauge, TEMP_MAJOR_TICKS_EVERY); + lv_obj_set_style_length(temp_gauge, 5, LV_PART_ITEMS); + lv_obj_set_style_length(temp_gauge, 10, LV_PART_INDICATOR); + lv_scale_set_range(temp_gauge, TEMP_MIN, TEMP_MAX); + + lv_scale_set_angle_range(temp_gauge, 270); + lv_scale_set_rotation(temp_gauge, 135); + + temp_line = lv_line_create(temp_gauge); + + lv_obj_set_style_line_width(temp_line, LINE_WIDTH, LV_PART_MAIN); + lv_obj_set_style_line_rounded(temp_line, true, LV_PART_MAIN); + lv_obj_center(temp_line); + + /* Generate labels */ + + for (unsigned i = 0; i < NUM_TEMP_LABELS; i++) + { + snprintf(g_label_buf[i], sizeof(g_label_buf[i]), "%d °C", + TEMP_MIN + i * (TEMP_MAX - TEMP_MIN) / NUM_TEMP_LABELS); + g_temp_labels[i] = (const char *)g_label_buf[i]; + } + + g_temp_labels[NUM_TEMP_LABELS] = NULL; + + lv_scale_set_text_src(temp_gauge, g_temp_labels); + + /* Temp gauge styles */ + + lv_style_init(&indicator_style); + + lv_style_set_text_font(&indicator_style, &lv_font_montserrat_24); + lv_style_set_text_color(&indicator_style, + lv_palette_darken(LV_PALETTE_BLUE, 3)); + + lv_style_set_line_color(&indicator_style, + lv_palette_darken(LV_PALETTE_BLUE, 3)); + lv_style_set_width(&indicator_style, 10U); /* Tick length */ + lv_style_set_line_width(&indicator_style, 2U); /* Tick width */ + lv_obj_add_style(temp_gauge, &indicator_style, LV_PART_INDICATOR); + + lv_style_init(&minor_ticks_style); + lv_style_set_line_color(&minor_ticks_style, + lv_palette_lighten(LV_PALETTE_BLUE, 2)); + lv_style_set_width(&minor_ticks_style, 5U); /* Tick length */ + lv_style_set_line_width(&minor_ticks_style, 2U); /* Tick width */ + lv_obj_add_style(temp_gauge, &minor_ticks_style, LV_PART_ITEMS); + + lv_style_init(&main_line_style); + lv_style_set_arc_color(&main_line_style, + lv_palette_darken(LV_PALETTE_BLUE, 3)); + lv_style_set_arc_width(&main_line_style, 2U); /* Tick width */ + + /* Section on temperature gauge */ + + lv_style_init(§ion_label_style); + lv_style_init(§ion_minor_ticks_style); + lv_style_init(§ion_main_line_style); + + lv_style_set_text_font(§ion_label_style, &lv_font_montserrat_24); + lv_style_set_text_color(§ion_label_style, + lv_palette_darken(LV_PALETTE_RED, 3)); + + lv_style_set_line_color(§ion_label_style, + lv_palette_darken(LV_PALETTE_RED, 3)); + lv_style_set_line_width(§ion_label_style, 5U); /* Tick width */ + + lv_style_set_line_color(§ion_minor_ticks_style, + lv_palette_lighten(LV_PALETTE_RED, 2)); + lv_style_set_line_width(§ion_minor_ticks_style, 4U); /* Tick width */ + + lv_style_set_arc_color(§ion_main_line_style, + lv_palette_darken(LV_PALETTE_RED, 3)); + lv_style_set_arc_width(§ion_main_line_style, 4U); /* Tick width */ + + section = lv_scale_add_section(temp_gauge); + lv_scale_section_set_range(section, TEMP_THRESH, TEMP_MAX); + lv_scale_section_set_style(section, LV_PART_INDICATOR, + §ion_label_style); + lv_scale_section_set_style(section, LV_PART_ITEMS, + §ion_minor_ticks_style); + lv_scale_section_set_style(section, LV_PART_MAIN, + §ion_main_line_style); + + /* Data labels */ + + press_label = lv_label_create(press_cont); + lv_obj_align(press_label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_text_font(press_label, &lv_font_montserrat_24, 0); + temp_label = lv_label_create(temp_cont); + lv_obj_align(temp_label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_text_font(temp_label, &lv_font_montserrat_24, 0); + + /* Main loop */ + +#ifdef CONFIG_LV_USE_NUTTX_LIBUV + lv_nuttx_uv_loop(&ui_loop, &result); +#else + for (; ; ) + { + err = read(fd, &sensor_data, sizeof(sensor_data)); + if (err < 0) + { + fprintf(stderr, "Failed read data from %s: %d\n", topic, errno); + } + else if (err == 0) + { + fprintf(stderr, "No data from %s\n", topic); + } + else + { + got_data = true; + } + + if (got_data) + { + /* Update display if we obtained it successfully */ + + lv_label_set_text_fmt(temp_label, "Temperature: %2.f °C", + sensor_data.temperature); + lv_label_set_text_fmt(press_label, "Pressure: %2.f mbar", + sensor_data.pressure); + + lv_scale_set_line_needle_value(press_gauge, press_line, -10, + (int32_t)sensor_data.pressure); + lv_scale_set_line_needle_value(temp_gauge, temp_line, -10, + (int32_t)sensor_data.temperature); + + if (sensor_data.temperature < TEMP_THRESH) + { + lv_obj_set_style_line_color( + temp_line, lv_palette_darken(LV_PALETTE_BLUE, 3), + LV_PART_MAIN); + } + else + { + lv_obj_set_style_line_color( + temp_line, lv_palette_darken(LV_PALETTE_RED, 3), + LV_PART_MAIN); + } + + got_data = false; /* Reset for next loop */ + } + + lv_timer_handler(); + usleep(CONFIG_EXAMPLES_BAROMONITOR_SAMPLERATE * 1000); + + /* TODO: make this equivalent in LIBUV loop */ + } +#endif + + orb_close(fd); + lv_nuttx_deinit(&result); + lv_deinit(); + return EXIT_SUCCESS; +} diff --git a/examples/battery/batt_main.c b/examples/battery/batt_main.c index d88a2885f..384d78147 100644 --- a/examples/battery/batt_main.c +++ b/examples/battery/batt_main.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -117,7 +117,7 @@ void health_report(int health) case BATTERY_HEALTH_GOOD: { - printf("Battery is in good condiction!\n"); + printf("Battery is in good condition!\n"); } break; diff --git a/examples/bridge/bridge_main.c b/examples/bridge/bridge_main.c index 8d2165514..183ccedbe 100644 --- a/examples/bridge/bridge_main.c +++ b/examples/bridge/bridge_main.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/buttons/buttons_main.c b/examples/buttons/buttons_main.c index d4b36431c..13d4b080a 100644 --- a/examples/buttons/buttons_main.c +++ b/examples/buttons/buttons_main.c @@ -219,9 +219,11 @@ static int button_daemon(int argc, char *argv[]) goto errout_with_fd; } +#ifdef CONFIG_ENABLE_ALL_SIGNALS /* Ignore the default signal action */ signal(CONFIG_EXAMPLES_BUTTONS_SIGNO, SIG_IGN); +#endif /* CONFIG_ENABLE_ALL_SIGNALS */ #endif /* Now loop forever, waiting BUTTONs events */ diff --git a/examples/camera/camera_bkgd.c b/examples/camera/camera_bkgd.c index c53b11ab8..a0c0bd2fa 100644 --- a/examples/camera/camera_bkgd.c +++ b/examples/camera/camera_bkgd.c @@ -32,7 +32,8 @@ #include #include #include -#include +#include +#include #include #include @@ -248,7 +249,7 @@ int nximage_initialize(void) } /* Start a separate thread to listen for server events. - * For simplicity, use defaul thread attribute. + * For simplicity, use default thread attribute. */ ret = pthread_create(&thread, NULL, nximage_listener, NULL); @@ -306,21 +307,28 @@ void nximage_draw(FAR void *image, int w, int h) FAR struct nxgl_rect_s dest; FAR const void *src[CONFIG_NX_NPLANES]; int ret; + int dest_w; + int dest_h; origin.x = 0; origin.y = 0; - /* Set up the destination to whole LCD screen */ + /* Clip destination to the smaller of image size and screen size */ + + dest_w = w < g_nximage.xres ? w : g_nximage.xres; + dest_h = h < g_nximage.yres ? h : g_nximage.yres; dest.pt1.x = 0; dest.pt1.y = 0; - dest.pt2.x = g_nximage.xres - 1; - dest.pt2.y = g_nximage.yres - 1; + dest.pt2.x = dest_w - 1; + dest.pt2.y = dest_h - 1; src[0] = image; + /* stride must match the source image width, not the screen width */ + ret = nx_bitmap(g_nximage.hbkgd, &dest, src, &origin, - g_nximage.xres * sizeof(nxgl_mxpixel_t)); + w * sizeof(nxgl_mxpixel_t)); if (ret < 0) { printf("nximage_image: nx_bitmap failed: %d\n", errno); diff --git a/examples/camera/camera_main.c b/examples/camera/camera_main.c index 43f1fff9d..c2c83e762 100644 --- a/examples/camera/camera_main.c +++ b/examples/camera/camera_main.c @@ -38,6 +38,7 @@ #include #include +#include #include "camera_fileutil.h" #include "camera_bkgd.h" @@ -84,13 +85,16 @@ static int camera_prepare(int fd, enum v4l2_buf_type type, uint32_t buf_mode, uint32_t pixformat, uint16_t hsize, uint16_t vsize, FAR struct v_buffer **vbuf, - uint8_t buffernum, int buffersize); + uint8_t buffernum, int buffersize, + FAR uint32_t *memory, + FAR uint32_t *actual_fmt); static void free_buffer(FAR struct v_buffer *buffers, uint8_t bufnum); static int parse_arguments(int argc, FAR char *argv[], FAR int *capture_num, - FAR enum v4l2_buf_type *type); + FAR enum v4l2_buf_type *type, + FAR bool *mirror); static int get_camimage(int fd, FAR struct v4l2_buffer *v4l2_buf, - enum v4l2_buf_type buf_type); + enum v4l2_buf_type buf_type, uint32_t memory); static int release_camimage(int fd, FAR struct v4l2_buffer *v4l2_buf); static int start_stillcapture(int v_fd, enum v4l2_buf_type capture_type); static int stop_stillcapture(int v_fd, enum v4l2_buf_type capture_type); @@ -119,7 +123,9 @@ static int camera_prepare(int fd, enum v4l2_buf_type type, uint32_t buf_mode, uint32_t pixformat, uint16_t hsize, uint16_t vsize, FAR struct v_buffer **vbuf, - uint8_t buffernum, int buffersize) + uint8_t buffernum, int buffersize, + FAR uint32_t *memory, + FAR uint32_t *actual_fmt) { int ret; int cnt; @@ -147,27 +153,51 @@ static int camera_prepare(int fd, enum v4l2_buf_type type, fmt.fmt.pix.pixelformat = pixformat; ret = ioctl(fd, VIDIOC_S_FMT, (uintptr_t)&fmt); + if (ret < 0 && pixformat == V4L2_PIX_FMT_RGB565) + { + /* Some sensors on 8-bit DVP output big-endian RGB565 (RGB565X). + * Retry with RGB565X if RGB565 is not supported. + */ + + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565X; + ret = ioctl(fd, VIDIOC_S_FMT, (uintptr_t)&fmt); + } + if (ret < 0) { printf("Failed to VIDIOC_S_FMT: errno = %d\n", errno); return ret; } - /* VIDIOC_REQBUFS initiate user pointer I/O */ + if (actual_fmt) + { + *actual_fmt = fmt.fmt.pix.pixelformat; + } + + /* VIDIOC_REQBUFS: try MMAP first (driver-managed DMA buffers). + * Fall back to USERPTR if the driver does not support MMAP. + */ req.type = type; - req.memory = V4L2_MEMORY_USERPTR; + req.memory = V4L2_MEMORY_MMAP; req.count = buffernum; req.mode = buf_mode; ret = ioctl(fd, VIDIOC_REQBUFS, (uintptr_t)&req); if (ret < 0) { - printf("Failed to VIDIOC_REQBUFS: errno = %d\n", errno); - return ret; + req.memory = V4L2_MEMORY_USERPTR; + ret = ioctl(fd, VIDIOC_REQBUFS, (uintptr_t)&req); + if (ret < 0) + { + printf("Failed to VIDIOC_REQBUFS: errno = %d\n", errno); + return ret; + } } - /* Prepare video memory to store images */ + *memory = req.memory; + + /* Prepare v_buffer array to track buffer metadata */ *vbuf = malloc(sizeof(v_buffer_t) * buffernum); if (!(*vbuf)) @@ -178,29 +208,54 @@ static int camera_prepare(int fd, enum v4l2_buf_type type, for (cnt = 0; cnt < buffernum; cnt++) { - (*vbuf)[cnt].length = buffersize; - - /* Note: - * VIDIOC_QBUF set buffer pointer. - * Buffer pointer must be 32bytes aligned. - */ - - (*vbuf)[cnt].start = memalign(32, buffersize); - if (!(*vbuf)[cnt].start) + if (req.memory == V4L2_MEMORY_MMAP) { - printf("Out of memory for image buffer of %d/%d\n", - cnt, buffernum); + /* VIDIOC_QUERYBUF: get driver-allocated buffer metadata */ - /* Release allocated memory. */ + memset(&buf, 0, sizeof(v4l2_buffer_t)); + buf.type = type; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = cnt; - while (cnt--) + ret = ioctl(fd, VIDIOC_QUERYBUF, (uintptr_t)&buf); + if (ret < 0) { - free((*vbuf)[cnt].start); + printf("Failed to VIDIOC_QUERYBUF %d: errno = %d\n", + cnt, errno); + free(*vbuf); + *vbuf = NULL; + return ret; } - free(*vbuf); - *vbuf = NULL; - return ERROR; + (*vbuf)[cnt].start = NULL; + (*vbuf)[cnt].length = buf.length; + } + else + { + (*vbuf)[cnt].length = buffersize; + + /* Note: + * VIDIOC_QBUF set buffer pointer. + * Buffer pointer must be 32bytes aligned. + */ + + (*vbuf)[cnt].start = memalign(32, buffersize); + if (!(*vbuf)[cnt].start) + { + printf("Out of memory for image buffer of %d/%d\n", + cnt, buffernum); + + /* Release allocated memory. */ + + while (cnt--) + { + free((*vbuf)[cnt].start); + } + + free(*vbuf); + *vbuf = NULL; + return ERROR; + } } } @@ -210,11 +265,15 @@ static int camera_prepare(int fd, enum v4l2_buf_type type, { memset(&buf, 0, sizeof(v4l2_buffer_t)); buf.type = type; - buf.memory = V4L2_MEMORY_USERPTR; + buf.memory = req.memory; buf.index = cnt; - buf.m.userptr = (uintptr_t)(*vbuf)[cnt].start; buf.length = (*vbuf)[cnt].length; + if (req.memory == V4L2_MEMORY_USERPTR) + { + buf.m.userptr = (uintptr_t)(*vbuf)[cnt].start; + } + ret = ioctl(fd, VIDIOC_QBUF, (uintptr_t)&buf); if (ret) { @@ -272,58 +331,36 @@ static void free_buffer(FAR struct v_buffer *buffers, uint8_t bufnum) static int parse_arguments(int argc, FAR char *argv[], FAR int *capture_num, - FAR enum v4l2_buf_type *type) + FAR enum v4l2_buf_type *type, + FAR bool *mirror) { - if (argc == 1) + int i; + + *capture_num = DEFAULT_CAPTURE_NUM; + *type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + *mirror = false; + + for (i = 1; i < argc; i++) { - *capture_num = DEFAULT_CAPTURE_NUM; - *type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - else if (argc == 2) - { - if (strncmp(argv[1], "-jpg", 5) == 0) + if (strncmp(argv[i], "-jpg", 5) == 0) { - *capture_num = DEFAULT_CAPTURE_NUM; *type = V4L2_BUF_TYPE_STILL_CAPTURE; } + else if (strncmp(argv[i], "-m", 3) == 0) + { + *mirror = true; + } else { - *capture_num = atoi(argv[1]); + *capture_num = atoi(argv[i]); if (*capture_num < 0 || *capture_num > MAX_CAPTURE_NUM) { printf("Invalid capture num(%d). must be >=0 and <=%d\n", *capture_num, MAX_CAPTURE_NUM); return ERROR; } - - *type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } } - else if (argc == 3) - { - if (strncmp(argv[1], "-jpg", 5) == 0) - { - *capture_num = atoi(argv[2]); - if (*capture_num < 0 || *capture_num > MAX_CAPTURE_NUM) - { - printf("Invalid capture num(%d). must be >=0 and <=%d\n", - *capture_num, MAX_CAPTURE_NUM); - return ERROR; - } - - *type = V4L2_BUF_TYPE_STILL_CAPTURE; - } - else - { - printf("Invalid argument 1 : %s\n", argv[1]); - return ERROR; - } - } - else - { - printf("Too many arguments\n"); - return ERROR; - } return OK; } @@ -336,7 +373,7 @@ static int parse_arguments(int argc, FAR char *argv[], ****************************************************************************/ static int get_camimage(int fd, FAR struct v4l2_buffer *v4l2_buf, - enum v4l2_buf_type buf_type) + enum v4l2_buf_type buf_type, uint32_t memory) { int ret; @@ -344,7 +381,7 @@ static int get_camimage(int fd, FAR struct v4l2_buffer *v4l2_buf, memset(v4l2_buf, 0, sizeof(v4l2_buffer_t)); v4l2_buf->type = buf_type; - v4l2_buf->memory = V4L2_MEMORY_USERPTR; + v4l2_buf->memory = memory; ret = ioctl(fd, VIDIOC_DQBUF, (uintptr_t)v4l2_buf); if (ret) @@ -476,13 +513,17 @@ int main(int argc, FAR char *argv[]) FAR struct v_buffer *buffers_video = NULL; FAR struct v_buffer *buffers_still = NULL; + uint32_t video_memory = V4L2_MEMORY_USERPTR; + uint32_t still_memory = V4L2_MEMORY_USERPTR; + uint32_t video_pixfmt = V4L2_PIX_FMT_RGB565; + bool mirror = false; /* ===== Parse and Check arguments ===== */ - ret = parse_arguments(argc, argv, &capture_num, &capture_type); + ret = parse_arguments(argc, argv, &capture_num, &capture_type, &mirror); if (ret != OK) { - printf("usage: %s ([-jpg]) ([capture num])\n", argv[0]); + printf("usage: %s [-jpg] [-m] [capture num]\n", argv[0]); return ERROR; } @@ -522,6 +563,21 @@ int main(int argc, FAR char *argv[]) goto exit_without_cleaning_buffer; } + /* Set horizontal mirror if requested */ + + if (mirror) + { + struct v4l2_control ctrl; + + ctrl.id = V4L2_CID_HFLIP; + ctrl.value = 1; + ret = ioctl(v_fd, VIDIOC_S_CTRL, (uintptr_t)&ctrl); + if (ret < 0) + { + printf("WARNING: VIDIOC_S_CTRL HFLIP failed: %d\n", errno); + } + } + /* Prepare for STILL_CAPTURE stream. * * The video buffer mode is V4L2_BUF_MODE_FIFO mode. @@ -534,10 +590,10 @@ int main(int argc, FAR char *argv[]) * Set FULLHD size in ISX012 case, QUADVGA size in ISX019 case or other * image sensors, * Number of frame buffers is defined as STILL_BUFNUM(1). - * And all allocated memorys are VIDIOC_QBUFed. + * And all allocated memories are VIDIOC_QBUFed. */ - if (capture_num != 0) + if (capture_num != 0 && capture_type == V4L2_BUF_TYPE_STILL_CAPTURE) { /* Determine image size from connected image sensor name, * because video driver does not support VIDIOC_ENUM_FRAMESIZES @@ -564,7 +620,8 @@ int main(int argc, FAR char *argv[]) ret = camera_prepare(v_fd, V4L2_BUF_TYPE_STILL_CAPTURE, V4L2_BUF_MODE_FIFO, V4L2_PIX_FMT_JPEG, w, h, - &buffers_still, STILL_BUFNUM, IMAGE_JPG_SIZE); + &buffers_still, STILL_BUFNUM, IMAGE_JPG_SIZE, + &still_memory, NULL); if (ret != OK) { goto exit_this_app; @@ -580,15 +637,16 @@ int main(int argc, FAR char *argv[]) * order from the captured frame buffer and a new camera image is * recaptured. * - * Allocate freame buffers for QVGA RGB565 size (320x240x2=150KB). + * Allocate frame buffers for QVGA RGB565 size (320x240x2=150KB). * Number of frame buffers is defined as VIDEO_BUFNUM(3). - * And all allocated memorys are VIDIOC_QBUFed. + * And all allocated memories are VIDIOC_QBUFed. */ ret = camera_prepare(v_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_MODE_RING, V4L2_PIX_FMT_RGB565, VIDEO_HSIZE_QVGA, VIDEO_VSIZE_QVGA, - &buffers_video, VIDEO_BUFNUM, IMAGE_RGB_SIZE); + &buffers_video, VIDEO_BUFNUM, IMAGE_RGB_SIZE, + &video_memory, &video_pixfmt); if (ret != OK) { goto exit_this_app; @@ -603,7 +661,7 @@ int main(int argc, FAR char *argv[]) * * APP_STATE_UNDER_CAPTURE: * This state will start taking picture and store the image into files. - * Number of taking pictures is set capture_num valiable. + * Number of taking pictures is set capture_num variable. * It can be changed by command line argument. * After finishing taking pictures, the state will be changed to * APP_STATE_AFTER_CAPTURE. @@ -659,13 +717,36 @@ int main(int argc, FAR char *argv[]) case APP_STATE_BEFORE_CAPTURE: case APP_STATE_AFTER_CAPTURE: - ret = get_camimage(v_fd, &v4l2_buf, V4L2_BUF_TYPE_VIDEO_CAPTURE); + ret = get_camimage(v_fd, &v4l2_buf, + V4L2_BUF_TYPE_VIDEO_CAPTURE, video_memory); if (ret != OK) { goto exit_this_app; } #ifdef CONFIG_EXAMPLES_CAMERA_OUTPUT_LCD + /* If the sensor outputs RGB565X (big-endian), invalidate + * D-Cache so we read fresh DMA data from PSRAM, then + * byte-swap in place for display and swap back before + * returning the buffer to the driver. + */ + + if (video_pixfmt == V4L2_PIX_FMT_RGB565X) + { + FAR uint16_t *p = (FAR uint16_t *)v4l2_buf.m.userptr; + uint32_t npixels = v4l2_buf.bytesused / 2; + uint32_t i; + + up_invalidate_dcache((uintptr_t)p, + (uintptr_t)p + v4l2_buf.bytesused); + + for (i = 0; i < npixels; i++) + { + uint16_t v = p[i]; + p[i] = (v >> 8) | (v << 8); + } + } + nximage_draw((FAR void *)v4l2_buf.m.userptr, VIDEO_HSIZE_QVGA, VIDEO_VSIZE_QVGA); #endif @@ -712,7 +793,10 @@ int main(int argc, FAR char *argv[]) while (capture_num) { - ret = get_camimage(v_fd, &v4l2_buf, capture_type); + ret = get_camimage(v_fd, &v4l2_buf, capture_type, + capture_type == + V4L2_BUF_TYPE_STILL_CAPTURE ? + still_memory : video_memory); if (ret != OK) { goto exit_this_app; diff --git a/examples/can/can_main.c b/examples/can/can_main.c index b62f565fe..422458a33 100644 --- a/examples/can/can_main.c +++ b/examples/can/can_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include diff --git a/examples/capture/cap.h b/examples/capture/cap.h index 2b1c6f5cf..98c3a5e27 100644 --- a/examples/capture/cap.h +++ b/examples/capture/cap.h @@ -42,6 +42,7 @@ struct cap_example_s FAR char *devpath; /* Path to the capture device */ unsigned int nloops; /* Collect this number of samples */ unsigned int delay; /* Delay this number of seconds between samples */ + int signo; /* Signal number to deliver on isr occur */ }; /**************************************************************************** diff --git a/examples/capture/cap_main.c b/examples/capture/cap_main.c index 3cbe9e8c2..1c44e0383 100644 --- a/examples/capture/cap_main.c +++ b/examples/capture/cap_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include @@ -98,6 +98,10 @@ static void cap_help(void) printf(" [-p devpath] Capture device path\n"); printf(" [-n samples] Number of samples\n"); printf(" [-t msec] Delay between samples (msec)\n"); +#ifdef CONFIG_CAPTURE_NOTIFY + printf(" [-w signo]: Wait for a signal if given " + "otherwise POLL mode if this is an interrupt pin.\n"); +#endif printf(" [-h] Shows this message and exits\n\n"); } @@ -149,6 +153,7 @@ static void parse_args(int argc, FAR char **argv) g_capexample.nloops = CONFIG_EXAMPLES_CAPTURE_NSAMPLES; g_capexample.delay = CONFIG_EXAMPLES_CAPTURE_DELAY; + g_capexample.signo = 1; for (index = 1; index < argc; ) { @@ -191,6 +196,20 @@ static void parse_args(int argc, FAR char **argv) index += nargs; break; +#ifdef CONFIG_CAPTURE_NOTIFY + case 'w': + nargs = arg_decimal(&argv[index], &value); + if (value < MIN_SIGNO || value > MAX_SIGNO) + { + printf("Wait signo out of range: %ld\n", value); + exit(1); + } + + g_capexample.signo = (int)value; + index += nargs; + break; +#endif + case 'h': cap_help(); exit(EXIT_SUCCESS); @@ -214,12 +233,17 @@ static void parse_args(int argc, FAR char **argv) int main(int argc, FAR char *argv[]) { int8_t dutycycle; - int32_t frequence; + int32_t frequency; int32_t edges; int fd; int exitval = EXIT_SUCCESS; int ret; int nloops; +#ifdef CONFIG_CAPTURE_NOTIFY + struct cap_notify_s notify; + struct timespec ts; + sigset_t set; +#endif /* Set the default values */ @@ -242,6 +266,56 @@ int main(int argc, FAR char *argv[]) goto errout; } +#ifdef CONFIG_CAPTURE_NOTIFY + notify.chan = 0; + notify.type = CAP_TYPE_BOTH; + + notify.event.sigev_notify = SIGEV_SIGNAL; + notify.event.sigev_signo = g_capexample.signo; + + ret = ioctl(fd, CAPIOC_REGISTER, (unsigned long)¬ify); + if (ret < 0) + { + printf("cap_main: ioctl(GPIOC_REGISTER) failed: %d\n", errno); + exitval = EXIT_FAILURE; + goto errout_with_dev; + } + + /* Wait up to 5 seconds for the signal */ + + sigemptyset(&set); + sigaddset(&set, g_capexample.signo); + + ts.tv_sec = 5; + ts.tv_nsec = 0; + + ret = sigtimedwait(&set, NULL, &ts); + ioctl(fd, CAPIOC_UNREGISTER, notify.chan); + if (ret < 0) + { + int errcode = errno; + if (errcode == EAGAIN) + { + printf("cpu_main: Five second timeout with no signal\n"); + exitval = EXIT_FAILURE; + goto errout_with_dev; + } + else + { + printf("cpu_main: ERROR: Failed to wait signal %d " + "from %s: %d\n", g_capexample.signo, g_capexample.devpath, + errcode); + exitval = EXIT_FAILURE; + goto errout_with_dev; + } + } + else + { + printf("cap_main: Received signal %d from %s\n", + g_capexample.signo, g_capexample.devpath); + } +#endif + /* Now loop the appropriate number of times, displaying the collected * encoder samples. */ @@ -275,13 +349,13 @@ int main(int argc, FAR char *argv[]) printf("pwm duty cycle: %d %% \n", dutycycle); } - /* Get the frequence data using the ioctl */ + /* Get the frequency data using the ioctl */ - ret = ioctl(fd, CAPIOC_FREQUENCE, - (unsigned long)((uintptr_t)&frequence)); + ret = ioctl(fd, CAPIOC_FREQUENCY, + (unsigned long)((uintptr_t)&frequency)); if (ret < 0) { - printf("cap_main: ioctl(CAPIOC_FREQUENCE) failed: %d\n", errno); + printf("cap_main: ioctl(CAPIOC_FREQUENCY) failed: %d\n", errno); exitval = EXIT_FAILURE; goto errout_with_dev; } @@ -290,7 +364,7 @@ int main(int argc, FAR char *argv[]) else { - printf("pwm frequence: %"PRId32" Hz \n", frequence); + printf("pwm frequency: %"PRId32" Hz \n", frequency); } /* Get the edges data using the ioctl */ diff --git a/examples/charger/charger_main.c b/examples/charger/charger_main.c index baac2f27e..2184ea9d5 100644 --- a/examples/charger/charger_main.c +++ b/examples/charger/charger_main.c @@ -231,8 +231,8 @@ int main(int argc, FAR char *argv[]) } gettimeofday(&tv, NULL); - printf("%ju.%06ld: %d mV, %d mA\n", - (uintmax_t)tv.tv_sec, tv.tv_usec, voltage, current); + printf("%jd.%06ld: %d mV, %d mA\n", + (intmax_t)tv.tv_sec, tv.tv_usec, voltage, current); close(fd); diff --git a/examples/chat/chat_main.c b/examples/chat/chat_main.c index caf6cca28..291e88824 100644 --- a/examples/chat/chat_main.c +++ b/examples/chat/chat_main.c @@ -45,7 +45,7 @@ #include #include -#include +#include #include #include #include diff --git a/examples/chrono/Kconfig b/examples/chrono/Kconfig index 866bc660a..324f0c452 100644 --- a/examples/chrono/Kconfig +++ b/examples/chrono/Kconfig @@ -6,7 +6,7 @@ config EXAMPLES_CHRONO tristate "Chronometer example to use with STM32LDiscover" default n - depends on SLCD + depends on SLCD && ENABLE_ALL_SIGNALS ---help--- Enable the Chronometer example diff --git a/examples/chrono/chrono_main.c b/examples/chrono/chrono_main.c index ee6658591..9ce5dbb58 100644 --- a/examples/chrono/chrono_main.c +++ b/examples/chrono/chrono_main.c @@ -165,9 +165,11 @@ static int chrono_daemon(int argc, char *argv[]) goto errout_with_fd; } +#ifdef CONFIG_ENABLE_ALL_SIGNALS /* Ignore the default signal action */ signal(BUTTON_SIGNO, SIG_IGN); +#endif /* Now loop forever, waiting BUTTONs events */ diff --git a/examples/configdata/configdata_main.c b/examples/configdata/configdata_main.c index 236d8e267..007a9d162 100644 --- a/examples/configdata/configdata_main.c +++ b/examples/configdata/configdata_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -450,7 +450,7 @@ static int configdata_verifyconfig(void) { g_nverified++; #if CONFIG_EXAMPLES_CONFIGDATA_VERBOSE != 0 - printf(" Verifed entry %04X, %d\n", + printf(" Verified entry %04X, %d\n", entry->id, entry->instance); #endif } @@ -664,7 +664,7 @@ int main(int argc, FAR char *argv[]) /* Open the /dev/config device */ - g_fd = open("/dev/config", O_RDOK); + g_fd = open("/dev/config", O_RDONLY); if (g_fd == -1) { printf("ERROR: Failed to open /dev/config %d\n", -errno); diff --git a/examples/cordic/cordic_main.c b/examples/cordic/cordic_main.c index 204a755ac..3d4aede0c 100644 --- a/examples/cordic/cordic_main.c +++ b/examples/cordic/cordic_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/dhcpd/CMakeLists.txt b/examples/dhcpd/CMakeLists.txt index 0305ab59c..5b2ba7cd2 100644 --- a/examples/dhcpd/CMakeLists.txt +++ b/examples/dhcpd/CMakeLists.txt @@ -25,10 +25,32 @@ if(CONFIG_EXAMPLES_DHCPD) NAME dhcpd SRCS - host.c target.c + dhcpd_daemon.c STACKSIZE - 2048) + ${CONFIG_DEFAULT_TASK_STACKSIZE}) + + if(NOT CONFIG_BUILD_KERNEL) + nuttx_add_application( + NAME + dhcpd_start + SRCS + dhcpd_start.c + dhcpd_daemon.c + STACKSIZE + ${CONFIG_DEFAULT_TASK_STACKSIZE}) + endif() + + if(CONFIG_SCHED_WAITPID) + nuttx_add_application( + NAME + dhcpd_stop + SRCS + dhcpd_stop.c + dhcpd_daemon.c + STACKSIZE + ${CONFIG_DEFAULT_TASK_STACKSIZE}) + endif() add_definitions(-DCONFIG_NETUTILS_DHCPD_HOST=1 -DHAVE_SO_REUSEADDR=1 -DHAVE_SO_BROADCAST=1) diff --git a/examples/dhcpd/Makefile b/examples/dhcpd/Makefile index 84ce2efc4..6c6ab3ba8 100644 --- a/examples/dhcpd/Makefile +++ b/examples/dhcpd/Makefile @@ -26,13 +26,23 @@ include $(APPDIR)/Make.defs CSRCS = dhcpd_daemon.c -MAINSRC = dhcpd_start.c dhcpd_stop.c target.c +MAINSRC = target.c # DHCPD built-in application info -PROGNAME = dhcpd_start dhcpd_stop dhcpd +PROGNAME = dhcpd PRIORITY = SCHED_PRIORITY_DEFAULT STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_DHCPD) +ifneq ($(CONFIG_BUILD_KERNEL),y) +MAINSRC += dhcpd_start.c +PROGNAME += dhcpd_start +endif + +ifeq ($(CONFIG_SCHED_WAITPID),y) +MAINSRC += dhcpd_stop.c +PROGNAME += dhcpd_stop +endif + include $(APPDIR)/Application.mk diff --git a/examples/dhcpd/dhcpd_daemon.c b/examples/dhcpd/dhcpd_daemon.c index 5b8f52186..b4f16e787 100644 --- a/examples/dhcpd/dhcpd_daemon.c +++ b/examples/dhcpd/dhcpd_daemon.c @@ -128,10 +128,12 @@ int dhcpd_daemon(int argc, FAR char *argv[], bool daemon) /* Then start the dhcpd */ - if (daemon) +#ifndef CONFIG_BUILD_KERNEL + if (!daemon) { - return dhcpd_run(devname); + return dhcpd_start(devname); } +#endif - return dhcpd_start(devname); + return dhcpd_run(devname); } diff --git a/examples/discover/discover_main.c b/examples/discover/discover_main.c index cd14720ed..9f5941587 100644 --- a/examples/discover/discover_main.c +++ b/examples/discover/discover_main.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/djoystick/Kconfig b/examples/djoystick/Kconfig index 7bfad3a2e..68e38ab6e 100644 --- a/examples/djoystick/Kconfig +++ b/examples/djoystick/Kconfig @@ -6,7 +6,7 @@ config EXAMPLES_DJOYSTICK tristate "Discrete joystick example" default n - depends on INPUT_DJOYSTICK + depends on INPUT_DJOYSTICK && ENABLE_ALL_SIGNALS ---help--- Enable the discrete joystick example diff --git a/examples/djoystick/djoy_main.c b/examples/djoystick/djoy_main.c index 110de4531..b3d1134c9 100644 --- a/examples/djoystick/djoy_main.c +++ b/examples/djoystick/djoy_main.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/dronecan/Kconfig b/examples/dronecan/Kconfig index f005a37f2..4ad761eb1 100644 --- a/examples/dronecan/Kconfig +++ b/examples/dronecan/Kconfig @@ -6,7 +6,7 @@ config EXAMPLES_DRONECAN tristate "DroneCAN example" default n - depends on CANUTILS_LIBDRONECAN && SYSTEM_TIME64 + depends on CANUTILS_LIBDRONECAN ---help--- Enable the LIBDRONECAN example diff --git a/examples/elf/.gitignore b/examples/elf/.gitignore new file mode 100644 index 000000000..9e1d2593e --- /dev/null +++ b/examples/elf/.gitignore @@ -0,0 +1 @@ +/Kconfig diff --git a/examples/elf/CMakeLists.txt b/examples/elf/CMakeLists.txt index 3ca9eeb4b..f5b2b0ee6 100644 --- a/examples/elf/CMakeLists.txt +++ b/examples/elf/CMakeLists.txt @@ -20,9 +20,5 @@ # # ############################################################################## -if(CONFIG_EXAMPLES_ELF) - nuttx_add_application(NAME elf SRCS elf_main.c) - - # TODO: tests - -endif() +nuttx_add_subdirectory() +nuttx_generate_kconfig(MENUDESC "Elf") diff --git a/examples/elf/Make.defs b/examples/elf/Make.defs index e01a9d0dd..fe9092e3b 100644 --- a/examples/elf/Make.defs +++ b/examples/elf/Make.defs @@ -20,6 +20,4 @@ # ############################################################################ -ifneq ($(CONFIG_EXAMPLES_ELF),) -CONFIGURED_APPS += $(APPDIR)/examples/elf -endif +include $(wildcard $(APPDIR)/examples/elf/*/Make.defs) diff --git a/examples/elf/Makefile b/examples/elf/Makefile index 8d1e32f13..cde6ca47d 100644 --- a/examples/elf/Makefile +++ b/examples/elf/Makefile @@ -20,47 +20,6 @@ # ############################################################################ -include $(APPDIR)/Make.defs +MENUDESC = "Elf example" -# ELF Example - -ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) -CSRCS = romfs.c -endif -ifeq ($(CONFIG_EXAMPLES_ELF_CROMFS),y) -CSRCS = cromfs.c -endif -CSRCS += dirlist.c -CSRCS += symtab.c -tests$(DELIM)symtab.c_CFLAGS = -fno-builtin -tests$(DELIM)symtab.c_CELFFLAGS = -fno-builtin -MAINSRC = elf_main.c - -PROGNAME = elf -PRIORITY = SCHED_PRIORITY_DEFAULT -STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) -MODULE = $(CONFIG_EXAMPLES_ELF) - -DEPPATH := --dep-path tests - -# Build targets - -VPATH += :tests - -ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) -tests/romfs.c: build -endif -ifeq ($(CONFIG_EXAMPLES_ELF_CROMFS),y) -tests/cromfs.c: build -endif -tests/dirlist.c: build -tests/symtab.c: build - -.PHONY: build -build: - +$(Q) $(MAKE) -C tests TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV) - -clean:: - +$(Q) $(MAKE) -C tests TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV) clean - -include $(APPDIR)/Application.mk +include $(APPDIR)/Directory.mk diff --git a/examples/elf/main/.gitignore b/examples/elf/main/.gitignore new file mode 100644 index 000000000..17739289c --- /dev/null +++ b/examples/elf/main/.gitignore @@ -0,0 +1,4 @@ +/elf_romfs.c +/elf_romfs.img +/cromfs.c +/test_symtab.c diff --git a/examples/elf/main/CMakeLists.txt b/examples/elf/main/CMakeLists.txt new file mode 100644 index 000000000..3b354fe21 --- /dev/null +++ b/examples/elf/main/CMakeLists.txt @@ -0,0 +1,134 @@ +# ############################################################################## +# apps/examples/elf/main/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_ELF) + set(ELF_SYMTAB ${CMAKE_CURRENT_BINARY_DIR}/test_symtab.c) + set(ELF_ROMFS_IMG ${CMAKE_CURRENT_BINARY_DIR}/elf_romfs.img) + set(ELF_ROMFS_SRC ${CMAKE_CURRENT_BINARY_DIR}/elf_romfs.c) + + set(ELFNAME_BASE hello) + + set(ELF_BINARY_FILES) + set(ELF_BINARY_TARGETS) + foreach(ELFNAME IN LISTS ELFNAME_BASE) + list(APPEND ELF_BINARY_FILES ${CMAKE_BINARY_DIR}/bin/${ELFNAME}) + + if(CMAKE_C_ELF_COMPILER) + list(APPEND ELF_BINARY_TARGETS ${ELFNAME}) + else() + list(APPEND ELF_BINARY_TARGETS ELF_${ELFNAME}) + endif() + endforeach() + + add_custom_command( + OUTPUT ${ELF_SYMTAB} + COMMAND ${NUTTX_APPS_DIR}/tools/mksymtab.sh ${ELF_BINARY_FILES} g_elf > + ${ELF_SYMTAB} + DEPENDS ${ELF_BINARY_TARGETS}) + + set_source_files_properties(${ELF_SYMTAB} PROPERTIES GENERATED TRUE) + + set(ELF_GENERATED_OUTPUTS ${ELF_SYMTAB}) + set(ELF_SRCS elf_main.c ${ELF_SYMTAB}) + + if(CONFIG_EXAMPLES_ELF_ROMFS) + set(ELF_ROMFS_DEPS ${ELF_BINARY_TARGETS}) + + if(CONFIG_SYSTEM_NXPKG) + set(ELF_INDEX_JSON ${CMAKE_BINARY_DIR}/bin/index.json) + set(ELF_BAD_INDEX_JSON ${CMAKE_BINARY_DIR}/bin/bad-index.json) + set(ELF_PKGTEST ${CMAKE_BINARY_DIR}/bin/pkgtest.nsh) + set(ELF_PKGFAIL ${CMAKE_BINARY_DIR}/bin/pkgfail.nsh) + set(ELF_HELLO_BIN ${CMAKE_BINARY_DIR}/bin/hello) + set(ELF_FIXTURE_HOST_DIR ${CMAKE_CURRENT_BINARY_DIR}/host) + set(ELF_FIXTURE_HOST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/host/CMakeLists.txt) + set(ELF_FIXTURE_TOOL + ${ELF_FIXTURE_HOST_DIR}/mk_pkg_fixture${CMAKE_EXECUTABLE_SUFFIX}) + set(ELF_HELLO_TARGET hello) + + if(CONFIG_ARCH_BOARD) + set(ELF_FIXTURE_COMPAT ${CONFIG_ARCH_BOARD}) + else() + set(ELF_FIXTURE_COMPAT ${CONFIG_ARCH_BOARD_CUSTOM_NAME}) + endif() + + if(NOT CMAKE_C_ELF_COMPILER) + set(ELF_HELLO_TARGET ELF_hello) + endif() + + add_custom_command( + OUTPUT ${ELF_FIXTURE_TOOL} + COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/host -B + ${ELF_FIXTURE_HOST_DIR} + COMMAND ${CMAKE_COMMAND} --build ${ELF_FIXTURE_HOST_DIR} --target + mk_pkg_fixture + DEPENDS ${ELF_FIXTURE_HOST_SRC} + ${CMAKE_CURRENT_SOURCE_DIR}/mk_pkg_fixture.c + VERBATIM) + + add_custom_command( + OUTPUT ${ELF_INDEX_JSON} ${ELF_BAD_INDEX_JSON} ${ELF_PKGTEST} + ${ELF_PKGFAIL} + COMMAND + ${ELF_FIXTURE_TOOL} ${ELF_HELLO_BIN} ${ELF_INDEX_JSON} + ${ELF_BAD_INDEX_JSON} ${ELF_PKGTEST} ${ELF_PKGFAIL} ${CONFIG_ARCH} + ${ELF_FIXTURE_COMPAT} + DEPENDS ${ELF_HELLO_TARGET} ${ELF_FIXTURE_TOOL} + VERBATIM) + + list(APPEND ELF_ROMFS_DEPS ${ELF_INDEX_JSON} ${ELF_BAD_INDEX_JSON} + ${ELF_PKGTEST} ${ELF_PKGFAIL}) + endif() + + add_custom_command( + OUTPUT ${ELF_ROMFS_IMG} + COMMAND genromfs -f ${ELF_ROMFS_IMG} -d ${CMAKE_BINARY_DIR}/bin + DEPENDS ${ELF_ROMFS_DEPS} + VERBATIM) + + get_filename_component(ELF_ROMFS_IMG_NAME ${ELF_ROMFS_IMG} NAME) + + # xxd -i derives the generated array/length symbol names from the exact path + # it is given - elf_main.c declares them as plain + # "elf_romfs_img"/"elf_romfs_img_len", so xxd must be run against just the + # bare filename (via WORKING_DIRECTORY) rather than the absolute + # ${ELF_ROMFS_IMG} path, or every non-alphanumeric character in that path + # (e.g. every "/" in the build directory) gets folded into the symbol name + # instead, leaving the names elf_main.c expects undefined at link time. + + add_custom_command( + OUTPUT ${ELF_ROMFS_SRC} + COMMAND xxd -i ${ELF_ROMFS_IMG_NAME} > ${ELF_ROMFS_SRC} + DEPENDS ${ELF_ROMFS_IMG} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + set_source_files_properties(${ELF_ROMFS_SRC} PROPERTIES GENERATED TRUE) + + list(APPEND ELF_GENERATED_OUTPUTS ${ELF_ROMFS_SRC}) + list(APPEND ELF_SRCS ${ELF_ROMFS_SRC}) + endif() + + add_custom_target(elf_artifacts DEPENDS ${ELF_GENERATED_OUTPUTS}) + + nuttx_add_application(NAME elf SRCS ${ELF_SRCS} DEPENDS elf_artifacts) + + # TODO: tests + +endif() diff --git a/examples/elf/Kconfig b/examples/elf/main/Kconfig similarity index 100% rename from examples/elf/Kconfig rename to examples/elf/main/Kconfig diff --git a/examples/elf/main/Make.defs b/examples/elf/main/Make.defs new file mode 100644 index 000000000..bc5af5300 --- /dev/null +++ b/examples/elf/main/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/elf/main/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_ELF),) +CONFIGURED_APPS += $(APPDIR)/examples/elf/main +endif diff --git a/examples/elf/main/Makefile b/examples/elf/main/Makefile new file mode 100644 index 000000000..5cc8db511 --- /dev/null +++ b/examples/elf/main/Makefile @@ -0,0 +1,143 @@ +############################################################################ +# apps/examples/elf/main/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Module example built-in application info + +PROGNAME = elf +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_ELF) + +MAINSRC = elf_main.c + +SYMTABSRC = test_symtab.c +SYMTABOBJ = $(SYMTABSRC:.c=$(OBJEXT)) + +ELFNAME_BASE = errno hello +ifneq ($(CONFIG_DISABLE_SIGNALS),y) + ELFNAME_BASE += signal +endif + +ifeq ($(CONFIG_HAVE_CXX),y) + ELFNAME_BASE += hello++1 hello++2 + ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y) + ELFNAME_BASE += hello++3 + endif + ifeq ($(CONFIG_EXAMPLES_ELF_CXX),y) + ELFNAME_BASE += hello++4 hello++5 + endif +endif +ifeq ($(CONFIG_EXAMPLES_ELF_LONGJMP),y) + ELFNAME_BASE += longjmp +endif +ifneq ($(CONFIG_DISABLE_PTHREAD),y) + ELFNAME_BASE += pthread mutex +endif +ifneq ($(CONFIG_ARCH_ADDRENV),y) + ELFNAME_BASE += task +endif + +ELFNAME = $(addprefix $(BINDIR)$(DELIM),$(ELFNAME_BASE)) + +$(SYMTABSRC): + $(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh $(ELFNAME) g_elf >$@.tmp + $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) + + +$(SYMTABOBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@, -fno-lto -fno-builtin) + +ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) + +ROMFSIMG = elf_romfs.img +ROMFSSRC = elf_romfs.c +ROMFSOBJ = $(ROMFSSRC:.c=$(OBJEXT)) +ifdef CONFIG_SYSTEM_NXPKG +HOSTOBJSEXT ?= hobj +PKGINDEX = $(BINDIR)/index.json +PKGBADINDEX = $(BINDIR)/bad-index.json +PKGTEST = $(BINDIR)/pkgtest.nsh +PKGFAIL = $(BINDIR)/pkgfail.nsh +PKG_FIXTURE_GEN = mk_pkg_fixture$(HOSTEXEEXT) +PKG_FIXTURE_SRCS = mk_pkg_fixture.c +PKG_FIXTURE_OBJS = $(PKG_FIXTURE_SRCS:.c=.$(HOSTOBJSEXT)) +PKG_FIXTURE_OUTPUTS = $(PKGINDEX) $(PKGBADINDEX) $(PKGTEST) $(PKGFAIL) +PKG_FIXTURE_INPUTS = $(BINDIR)/hello +PKG_FIXTURE_STAMP = $(BINDIR)/.pkgtest.stamp +PKG_FIXTURE_COMPAT = $(if $(CONFIG_ARCH_BOARD),$(CONFIG_ARCH_BOARD),$(CONFIG_ARCH_BOARD_CUSTOM_NAME)) +PKG_FIXTURE_ARGS = \ + $(BINDIR)/hello $(PKGINDEX) $(PKGBADINDEX) $(PKGTEST) $(PKGFAIL) \ + $(CONFIG_ARCH) $(PKG_FIXTURE_COMPAT) + +$(PKG_FIXTURE_OBJS): %.$(HOSTOBJSEXT): %.c + @echo "CC: $<" + $(Q) $(HOSTCC) -c $(HOSTCFLAGS) $< -o $@ + +$(PKG_FIXTURE_GEN): $(PKG_FIXTURE_OBJS) + $(Q) $(HOSTCC) $(HOSTLDFLAGS) $(PKG_FIXTURE_OBJS) -o $@ + +$(PKG_FIXTURE_OUTPUTS): $(PKG_FIXTURE_STAMP) + +$(PKG_FIXTURE_STAMP): $(PKG_FIXTURE_INPUTS) $(PKG_FIXTURE_GEN) + $(Q) ./$(PKG_FIXTURE_GEN) \ + $(PKG_FIXTURE_ARGS) + $(Q) touch $@ +endif + +$(ROMFSIMG): $(PKG_FIXTURE_OUTPUTS) + $(Q) genromfs -d $(BINDIR) -f $@ + +$(ROMFSSRC): $(ROMFSIMG) + $(Q) (echo "#include " >$@ && \ + xxd -i $(ROMFSIMG) | sed -e "s/^unsigned char/const unsigned char aligned_data(4)/g" >>$@) + +$(ROMFSOBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@, -fno-lto -fno-builtin) + +else ifeq ($(CONFIG_EXAMPLES_ELF_CROMFS),y) + NXTOOLDIR = $(TOPDIR)/tools + GENCROMFSSRC = gencromfs.c + GENCROMFSEXE = gencromfs$(HOSTEXEEXT) + + FSIMG_SRC = cromfs.c + FSIMG_OBJ = $(FSIMG_SRC:.c=$(OBJEXT)) + +$(NXTOOLDIR)/$(GENCROMFSEXE): $(NXTOOLDIR)/$(GENCROMFSSRC) + $(Q) $(MAKE) -C $(NXTOOLDIR) -f Makefile.host $(GENCROMFSEXE) + +# Create the cromfs.c file from the populated cromfs directory + +$(FSIMG_SRC):$(NXTOOLDIR)/$(GENCROMFSEXE) + $(Q) $(NXTOOLDIR)/$(GENCROMFSEXE) $(BINDIR) $@.tmp + $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) + +$(FSIMG_OBJ): %$(OBJEXT): %.c + $(call COMPILE, $<, $@, -fno-lto -fno-builtin) + +endif + +postinstall:: $(ROMFSOBJ) $(SYMTABOBJ) $(FSIMG_OBJ) + $(call ARLOCK, $(call CONVERT_PATH,$(BIN)), $^) + +distclean:: + $(Q) $(call DELFILE, $(SYMTABSRC) $(SYMTABOBJ) $(ROMFSSRC) $(ROMFSIMG) $(ROMFSOBJ) $(MODLUE_NAME) $(PKG_FIXTURE_GEN) $(PKG_FIXTURE_OBJS) $(PKG_FIXTURE_STAMP)) + + +include $(APPDIR)/Application.mk diff --git a/examples/elf/elf_main.c b/examples/elf/main/elf_main.c similarity index 95% rename from examples/elf/elf_main.c rename to examples/elf/main/elf_main.c index d6950c1c8..000339e00 100644 --- a/examples/elf/elf_main.c +++ b/examples/elf/main/elf_main.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/examples/elf/elf_main.c + * apps/examples/elf/main/elf_main.c * * SPDX-License-Identifier: Apache-2.0 * @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -132,8 +132,37 @@ extern const unsigned int elf_romfs_img_len; # error "No file system selected" #endif -extern const char *dirlist[]; - +const char *dirlist[] = +{ + "errno", + "hello", +#ifndef CONFIG_DISABLE_SIGNALS + "signal", +#endif + "struct", +#ifdef CONFIG_HAVE_CXX + "hello++1", + "hello++2", +# ifdef CONFIG_HAVE_CXXINITIALIZE + "hello++3", +# endif +# ifdef CONFIG_EXAMPLES_ELF_CXX + "hello++4", + "hello++5", +# endif +#endif +#ifdef CONFIG_EXAMPLES_ELF_LONGJMP + "longjmp" +#endif +#ifndef CONFIG_DISABLE_PTHREAD + "mutex", + "pthread", +#endif +#ifndef CONFIG_ARCH_ADDRENV + "task", +#endif + NULL +}; extern const struct symtab_s g_elf_exports[]; extern const int g_elf_nexports; diff --git a/examples/elf/main/host/CMakeLists.txt b/examples/elf/main/host/CMakeLists.txt new file mode 100644 index 000000000..a2474cb7a --- /dev/null +++ b/examples/elf/main/host/CMakeLists.txt @@ -0,0 +1,26 @@ +# ############################################################################## +# apps/examples/elf/main/host/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +cmake_minimum_required(VERSION 3.16) +project(elf_fixture_host LANGUAGES C) + +add_executable(mk_pkg_fixture ../mk_pkg_fixture.c) diff --git a/examples/elf/main/mk_pkg_fixture.c b/examples/elf/main/mk_pkg_fixture.c new file mode 100644 index 000000000..c3799b519 --- /dev/null +++ b/examples/elf/main/mk_pkg_fixture.c @@ -0,0 +1,463 @@ +/**************************************************************************** + * apps/examples/elf/main/mk_pkg_fixture.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SHA256_BLOCK_SIZE 64 +#define SHA256_DIGEST_SIZE 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sha256_state_s +{ + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_SIZE]; + size_t buffer_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint32_t g_sha256_init[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +static const uint32_t g_sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t sha256_rotr(uint32_t value, unsigned int bits) +{ + return (value >> bits) | (value << (32 - bits)); +} + +static uint32_t sha256_ch(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (~x & z); +} + +static uint32_t sha256_maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (x & z) ^ (y & z); +} + +static uint32_t sha256_bs0(uint32_t x) +{ + return sha256_rotr(x, 2) ^ sha256_rotr(x, 13) ^ sha256_rotr(x, 22); +} + +static uint32_t sha256_bs1(uint32_t x) +{ + return sha256_rotr(x, 6) ^ sha256_rotr(x, 11) ^ sha256_rotr(x, 25); +} + +static uint32_t sha256_ss0(uint32_t x) +{ + return sha256_rotr(x, 7) ^ sha256_rotr(x, 18) ^ (x >> 3); +} + +static uint32_t sha256_ss1(uint32_t x) +{ + return sha256_rotr(x, 17) ^ sha256_rotr(x, 19) ^ (x >> 10); +} + +static uint32_t sha256_load32(const uint8_t *src) +{ + return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | + ((uint32_t)src[2] << 8) | (uint32_t)src[3]; +} + +static void sha256_store32(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)(value >> 24); + dst[1] = (uint8_t)(value >> 16); + dst[2] = (uint8_t)(value >> 8); + dst[3] = (uint8_t)value; +} + +static void sha256_transform(struct sha256_state_s *ctx, + const uint8_t block[SHA256_BLOCK_SIZE]) +{ + uint32_t schedule[64]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + int i; + + for (i = 0; i < 16; i++) + { + schedule[i] = sha256_load32(block + i * 4); + } + + for (; i < 64; i++) + { + schedule[i] = sha256_ss1(schedule[i - 2]) + schedule[i - 7] + + sha256_ss0(schedule[i - 15]) + schedule[i - 16]; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; i++) + { + uint32_t t1 = h + sha256_bs1(e) + sha256_ch(e, f, g) + g_sha256_k[i] + + schedule[i]; + uint32_t t2 = sha256_bs0(a) + sha256_maj(a, b, c); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +static void sha256_init(struct sha256_state_s *ctx) +{ + memcpy(ctx->state, g_sha256_init, sizeof(g_sha256_init)); + ctx->bitcount = 0; + ctx->buffer_len = 0; +} + +static void sha256_update(struct sha256_state_s *ctx, const uint8_t *data, + size_t len) +{ + ctx->bitcount += (uint64_t)len * 8; + + while (len > 0) + { + size_t space = SHA256_BLOCK_SIZE - ctx->buffer_len; + size_t chunk = len < space ? len : space; + + memcpy(ctx->buffer + ctx->buffer_len, data, chunk); + ctx->buffer_len += chunk; + data += chunk; + len -= chunk; + + if (ctx->buffer_len == SHA256_BLOCK_SIZE) + { + sha256_transform(ctx, ctx->buffer); + ctx->buffer_len = 0; + } + } +} + +static void sha256_final(struct sha256_state_s *ctx, + uint8_t digest[SHA256_DIGEST_SIZE]) +{ + uint8_t length_bytes[8]; + uint8_t padding[SHA256_BLOCK_SIZE] = + { + 0x80 + }; + + int i; + + for (i = 0; i < 8; i++) + { + length_bytes[7 - i] = (uint8_t)(ctx->bitcount >> (i * 8)); + } + + if (ctx->buffer_len < 56) + { + sha256_update(ctx, padding, 56 - ctx->buffer_len); + } + else + { + sha256_update(ctx, padding, SHA256_BLOCK_SIZE - ctx->buffer_len); + sha256_update(ctx, padding + 1, 56); + } + + sha256_update(ctx, length_bytes, sizeof(length_bytes)); + + for (i = 0; i < 8; i++) + { + sha256_store32(digest + i * 4, ctx->state[i]); + } +} + +static int sha256_file(const char *path, char *digest_hex, size_t digest_len) +{ + struct sha256_state_s ctx; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t buffer[4096]; + static const char g_hex[] = "0123456789abcdef"; + FILE *stream; + size_t nread; + int i; + + if (digest_len < SHA256_DIGEST_SIZE * 2 + 1) + { + fprintf(stderr, "digest buffer is too small\n"); + return -1; + } + + stream = fopen(path, "rb"); + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + sha256_init(&ctx); + + while ((nread = fread(buffer, 1, sizeof(buffer), stream)) > 0) + { + sha256_update(&ctx, buffer, nread); + } + + if (ferror(stream) != 0) + { + fprintf(stderr, "failed to read %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + fclose(stream); + sha256_final(&ctx, digest); + + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + { + digest_hex[i * 2] = g_hex[digest[i] >> 4]; + digest_hex[i * 2 + 1] = g_hex[digest[i] & 0x0f]; + } + + digest_hex[SHA256_DIGEST_SIZE * 2] = '\0'; + return 0; +} + +static int write_text_file(const char *path, const char *content) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fputs(content, stream) == EOF) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static int write_index_file(const char *path, const char *arch, + const char *compat, const char *digest) +{ + const char *artifact = "hello"; + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fprintf(stream, + "{\"_nxpkg_source\":\"/etc/nxpkg/index.json\"," + "\"packages\":[" + "{\"name\":\"hello\",\"version\":\"1.0.0\",\"arch\":\"%s\"," + "\"compat\":\"%s\",\"artifact\":\"%s\",\"sha256\":\"%s\"," + "\"type\":\"elf\"}," + "{\"name\":\"hello\",\"version\":\"9.9.9\",\"arch\":\"arm\"," + "\"compat\":\"stm32f4discovery\",\"artifact\":\"%s\"," + "\"sha256\":\"%s\",\"type\":\"elf\"}" + "]}\n", + arch, compat, artifact, digest, artifact, digest) < 0) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static int write_bad_index_file(const char *path, const char *arch, + const char *compat) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fprintf(stream, + "{\"_nxpkg_source\":\"/etc/nxpkg/index.json\"," + "\"packages\":[" + "{\"name\":\"hello-missing\",\"version\":\"1.0.0\"," + "\"arch\":\"%s\",\"compat\":\"%s\"," + "\"artifact\":\"hello-missing\"," + "\"sha256\":\"" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "\",\"type\":\"elf\"}" + "]}\n", + arch, compat) < 0) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s " + " \n", + progname); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + char digest[SHA256_DIGEST_SIZE * 2 + 1]; + static const char g_pkgtest[] = + "mount -t tmpfs /etc\n" + "mount -t tmpfs /var\n" + "mkdir /etc/nxpkg\n" + "cp /mnt/elf/romfs/index.json /etc/nxpkg/index.json\n" + "cp /mnt/elf/romfs/hello /etc/nxpkg/hello\n" + "mkdir /var/lib\n" + "mkdir /var/lib/nxpkg\n" + "cp /mnt/elf/romfs/index.json /var/lib/nxpkg/index.jsn\n" + "nxpkg install hello\n" + "nxpkg list\n"; + static const char g_pkgfail[] = + "mount -t tmpfs /etc\n" + "mount -t tmpfs /var\n" + "mkdir /etc/nxpkg\n" + "cp /mnt/elf/romfs/bad-index.json /etc/nxpkg/index.json\n" + "mkdir /var/lib\n" + "mkdir /var/lib/nxpkg\n" + "cp /mnt/elf/romfs/bad-index.json /var/lib/nxpkg/index.jsn\n" + "nxpkg install hello-missing\n"; + + if (argc != 8) + { + usage(argv[0]); + return EXIT_FAILURE; + } + + if (sha256_file(argv[1], digest, sizeof(digest)) < 0 || + write_index_file(argv[2], argv[6], argv[7], digest) < 0 || + write_bad_index_file(argv[3], argv[6], argv[7]) < 0 || + write_text_file(argv[4], g_pkgtest) < 0 || + write_text_file(argv[5], g_pkgfail) < 0) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/examples/elf/tests/.gitignore b/examples/elf/tests/.gitignore index 416f7e66e..78af96a0d 100644 --- a/examples/elf/tests/.gitignore +++ b/examples/elf/tests/.gitignore @@ -6,3 +6,4 @@ /dirlist.c /extfs /symtab.c +/Kconfig diff --git a/examples/elf/tests/CMakeLists.txt b/examples/elf/tests/CMakeLists.txt new file mode 100644 index 000000000..952929788 --- /dev/null +++ b/examples/elf/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +# ############################################################################## +# apps/examples/elf/tests/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +nuttx_add_subdirectory() diff --git a/examples/elf/tests/Make.defs b/examples/elf/tests/Make.defs new file mode 100644 index 000000000..e29058c49 --- /dev/null +++ b/examples/elf/tests/Make.defs @@ -0,0 +1,41 @@ +############################################################################ +# apps/examples/elf/tests/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifeq ($(CONFIG_EXAMPLES_ELF),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/errno + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/hello + ifneq ($(CONFIG_DISABLE_SIGNALS),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/signal + endif + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/struct + ifeq ($(CONFIG_HAVE_CXX),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/helloxx + endif + ifeq ($(CONFIG_EXAMPLES_ELF_LONGJMP),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/longjmp + endif + ifneq ($(CONFIG_DISABLE_PTHREAD),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/mutex + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/pthread + endif + ifneq ($(CONFIG_ARCH_ADDRENV),y) + CONFIGURED_APPS += $(APPDIR)/examples/elf/tests/task + endif +endif diff --git a/examples/elf/tests/Makefile b/examples/elf/tests/Makefile index f73d31fc9..741d9d7a9 100644 --- a/examples/elf/tests/Makefile +++ b/examples/elf/tests/Makefile @@ -20,134 +20,6 @@ # ############################################################################ -include $(APPDIR)/Make.defs +MENUDESC = "Elf tests example" -ALL_SUBDIRS = errno hello helloxx longjmp mutex pthread signal task struct -BUILD_SUBDIRS = errno hello struct signal - -ifeq ($(CONFIG_HAVE_CXX),y) -BUILD_SUBDIRS += helloxx -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_LONGJMP),y) -BUILD_SUBDIRS += longjmp -endif - -ifneq ($(CONFIG_DISABLE_PTHREAD),y) -BUILD_SUBDIRS += mutex pthread -endif - -ifneq ($(CONFIG_ARCH_ADDRENV),y) -BUILD_SUBDIRS += task -endif - -ELF_DIR = $(APPDIR)/examples/elf -TESTS_DIR = $(ELF_DIR)/tests -DIRLIST_SRC = $(TESTS_DIR)/dirlist.c -SYMTAB_SRC = $(TESTS_DIR)/symtab.c - -ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) - FSIMG_SUBDIR = romfs - FSIMG_DIR = $(TESTS_DIR)/$(FSIMG_SUBDIR) - ROMFS_IMG = $(TESTS_DIR)/romfs.img - FSIMG_SRC = $(TESTS_DIR)/romfs.c -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_CROMFS),y) - NXTOOLDIR = $(TOPDIR)/tools - GENCROMFSSRC = gencromfs.c - GENCROMFSEXE = gencromfs$(HOSTEXEEXT) - - FSIMG_SUBDIR = cromfs - FSIMG_DIR = $(TESTS_DIR)/$(FSIMG_SUBDIR) - FSIMG_SRC = $(TESTS_DIR)/cromfs.c -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_EXTERN),y) - FSIMG_SUBDIR = extfs - FSIMG_DIR = $(TESTS_DIR)/$(FSIMG_SUBDIR) -endif - -# generate the romfs image in case the fs type is romfs -# the FSIMG_SRC is not actually generated. - -ifeq ($(CONFIG_EXAMPLES_ELF_FSTYPE), "romfs") - ROMFS_IMG = $(TESTS_DIR)/romfs.img - FSIMG_SRC = $(TESTS_DIR)/romfs.c -endif - -define DIR_template -$(1)_$(2): - +$(Q) $(MAKE) -C $(1) $(2) TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" FSIMG_DIR="$(FSIMG_DIR)" CROSSDEV=$(CROSSDEV) -endef - -all: $(FSIMG_SRC) $(DIRLIST_SRC) $(SYMTAB_SRC) -.PHONY: all clean install - -$(foreach DIR, $(ALL_SUBDIRS), $(eval $(call DIR_template,$(DIR),clean))) -$(foreach DIR, $(BUILD_SUBDIRS), $(eval $(call DIR_template,$(DIR),install))) - -# Install each program in the file system image directory - -install: $(foreach DIR, $(BUILD_SUBDIRS), $(DIR)_install) - -ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) -# Create the romfs.img file from the populated romfs directory - -$(ROMFS_IMG): install - $(Q) genromfs -f $@.tmp -d $(FSIMG_DIR) -V "ELFTEST" - $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) - -# Create the romfs.c file from the romfs.img file - -$(FSIMG_SRC): $(ROMFS_IMG) - $(Q) (cd $(TESTS_DIR) && echo "#include " >$@ && \ - xxd -i romfs.img | sed -e "s/^unsigned char/const unsigned char aligned_data(4)/g" | \ - sed -e "s/romfs_img/elf_romfs_img/g" >>$@) - -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_CROMFS),y) -# Make sure that the NuttX gencromfs tool has been built - -$(NXTOOLDIR)/$(GENCROMFSEXE): $(NXTOOLDIR)/$(GENCROMFSSRC) - $(Q) $(MAKE) -C $(NXTOOLDIR) -f Makefile.host $(GENCROMFSEXE) - -# Create the cromfs.h header file from the populated cromfs directory - -$(FSIMG_SRC): install $(NXTOOLDIR)/$(GENCROMFSEXE) - $(Q) $(NXTOOLDIR)/$(GENCROMFSEXE) $(FSIMG_DIR) $@.tmp - $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) - -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_FSTYPE),"romfs") -# Create the romfs.img file from the populated romfs directory - -$(ROMFS_IMG): install - $(Q) genromfs -f $@.tmp -d $(FSIMG_DIR) -V "ELFTEST" - $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) - -# Create the romfs.c file from the romfs.img file - -$(FSIMG_SRC): $(ROMFS_IMG) - -endif - -# Create the dirlist.h header file from the file system image directory - -$(DIRLIST_SRC): install - $(Q) $(TESTS_DIR)/mkdirlist.sh $(FSIMG_DIR) >$@.tmp - $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) - -# Create the exported symbol table - -$(SYMTAB_SRC): install - $(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh $(FSIMG_DIR) g_elf >$@.tmp - $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) - -# Clean each subdirectory - -clean: $(foreach DIR, $(ALL_SUBDIRS), $(DIR)_clean) - $(Q) rm -f $(FSIMG_SRC) $(DIRLIST_SRC) $(ROMFS_IMG) $(SYMTAB_SRC) - $(Q) rm -rf $(FSIMG_DIR) +include $(APPDIR)/Directory.mk diff --git a/examples/elf/tests/errno/Kconfig b/examples/elf/tests/errno/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/errno/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/errno/Makefile b/examples/elf/tests/errno/Makefile index 2f9d2a13a..ee383144d 100644 --- a/examples/elf/tests/errno/Makefile +++ b/examples/elf/tests/errno/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = errno +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = errno.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = errno - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/hello/CMakeLists.txt b/examples/elf/tests/hello/CMakeLists.txt new file mode 100644 index 000000000..d3906bfcf --- /dev/null +++ b/examples/elf/tests/hello/CMakeLists.txt @@ -0,0 +1,33 @@ +# ############################################################################## +# apps/examples/elf/tests/hello/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_ELF) + nuttx_add_application( + NAME + hello + SRCS + hello.c + STACKSIZE + ${CONFIG_ELF_STACKSIZE} + MODULE + m) +endif() diff --git a/examples/elf/tests/hello/Kconfig b/examples/elf/tests/hello/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/hello/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/hello/Makefile b/examples/elf/tests/hello/Makefile index 88ec0f0a8..6920b1522 100644 --- a/examples/elf/tests/hello/Makefile +++ b/examples/elf/tests/hello/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = hello +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = hello.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = hello - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/helloxx/Kconfig b/examples/elf/tests/helloxx/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/helloxx/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/helloxx/Makefile b/examples/elf/tests/helloxx/Makefile index b2f276834..667bf453d 100644 --- a/examples/elf/tests/helloxx/Makefile +++ b/examples/elf/tests/helloxx/Makefile @@ -22,156 +22,19 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN1 = hello++1 -ALL_BIN = $(FSIMG_DIR)/$(BIN1) -BIN2 = hello++2 -ALL_BIN += $(FSIMG_DIR)/$(BIN2) +PROGNAME = hello++1 hello++2 +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = hello++1.cxx hello++2.cxx ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y) -BIN3 = hello++3 -ALL_BIN += $(FSIMG_DIR)/$(BIN3) -ifeq ($(CONFIG_EXAMPLES_ELF_CXX),y) -BIN4 = hello++4 -ALL_BIN += $(FSIMG_DIR)/$(BIN4) -BIN5 = hello++5 -ALL_BIN += $(FSIMG_DIR)/$(BIN5) -endif -endif - -SRCS1 = $(BIN1).c -OBJS1 = $(SRCS1:.c=$(OBJEXT)) - -SRCS2 = $(BIN2).c -OBJS2 = $(SRCS2:.c=$(OBJEXT)) - -ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y) -SRCS3 = $(BIN3).c -OBJS3 = $(SRCS3:.c=$(OBJEXT)) -ifeq ($(CONFIG_EXAMPLES_ELF_CXX),y) -SRCS4 = $(BIN4).c -OBJS4 = $(SRCS4:.c=$(OBJEXT)) -SRCS5 = $(BIN5).c -OBJS5 = $(SRCS5:.c=$(OBJEXT)) -endif -endif - -SRCS = $(SRCS1) $(SRCS2) $(SRCS3) $(SRCS4) $(SRCS5) -OBJS = $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) - -LDLIBSTDC_STUBS_DIR = $(TOPDIR)/libxx -LDLIBSTDC_STUBS_LIB = $(LDLIBSTDC_STUBS_DIR)/liblibxx.a - -all: $(BIN1) $(BIN2) $(BIN3) $(BIN4) $(BIN5) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.cxx - @echo "CC: $<" - $(Q) $(CXX) -c $(CXXELFFLAGS) $< -o $@ - -# This contains libstdc++ stubs to that you can build C++ code -# without actually having libstdc++ - -$(LDLIBSTDC_STUBS_LIB): - $(Q) $(MAKE) -C $(LDLIBSTDC_STUBS_DIR) TOPDIR=$(TOPDIR) - -# BIN1 and BIN2 link just like C code because they contain no -# static constructors. BIN1 is equivalent to a C hello world; -# BIN2 contains a class that implements hello world, but it is -# not statically initialized. - -$(BIN1): $(OBJS1) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(BIN2): $(OBJS2) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -# BIN3 is equivalent to BIN2 except that is uses static initializers - -ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y) -$(BIN3): $(OBJS3) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -# BIN4 is similar to BIN3 except that it uses the streams code from libstdc++ -# Both BIN4 and BIN5 use exceptions -# NOTE: libstdc++ is not available for NuttX as of this writing -# -ifeq ($(CONFIG_EXAMPLES_ELF_CXX),y) -ifeq ($(CONFIG_CXX_EXCEPTION),y) -$(BIN4): $(OBJS4) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) -$(BIN5): $(OBJS5) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) -endif -endif -endif - -$(FSIMG_DIR)/$(BIN1): $(BIN1) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN1) $(FSIMG_DIR)/$(BIN1) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN1) -endif - -$(FSIMG_DIR)/$(BIN2): $(BIN2) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN2) $(FSIMG_DIR)/$(BIN2) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN2) -endif - -ifeq ($(CONFIG_HAVE_CXXINITIALIZE),y) -$(FSIMG_DIR)/$(BIN3): $(BIN3) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN3) $(FSIMG_DIR)/$(BIN3) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN3) + PROGNAME += hello++3 + MAINSRC += hello++3.cxx endif ifeq ($(CONFIG_EXAMPLES_ELF_CXX),y) -$(FSIMG_DIR)/$(BIN4): $(BIN4) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN4) $(FSIMG_DIR)/$(BIN4) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN4) + PROGNAME += hello++4 hello++5 + MAINSRC += hello++4.cxx hello++5.cxx endif -$(FSIMG_DIR)/$(BIN5): $(BIN5) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN5) $(FSIMG_DIR)/$(BIN5) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN5) -endif -endif -endif - -install: $(ALL_BIN) - -clean: - $(call DELFILE, $(BIN1)) - $(call DELFILE, $(BIN2)) - $(call DELFILE, $(BIN3)) - $(call DELFILE, $(BIN4)) - $(call DELFILE, $(BIN5)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/longjmp/Kconfig b/examples/elf/tests/longjmp/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/longjmp/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/longjmp/Makefile b/examples/elf/tests/longjmp/Makefile index fa792e6ff..a5e482f28 100644 --- a/examples/elf/tests/longjmp/Makefile +++ b/examples/elf/tests/longjmp/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = longjmp +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = longjmp.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = longjmp - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/mutex/Kconfig b/examples/elf/tests/mutex/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/mutex/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/mutex/Makefile b/examples/elf/tests/mutex/Makefile index 5fec89d3c..fe3619513 100644 --- a/examples/elf/tests/mutex/Makefile +++ b/examples/elf/tests/mutex/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = mutex +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = mutex.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = mutex - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/pthread/Kconfig b/examples/elf/tests/pthread/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/pthread/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/pthread/Makefile b/examples/elf/tests/pthread/Makefile index 61108bc5f..b1ca8c70d 100644 --- a/examples/elf/tests/pthread/Makefile +++ b/examples/elf/tests/pthread/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = pthread +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = pthread.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = pthread - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/signal/Kconfig b/examples/elf/tests/signal/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/signal/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/signal/Makefile b/examples/elf/tests/signal/Makefile index 21df11dd0..64d674033 100644 --- a/examples/elf/tests/signal/Makefile +++ b/examples/elf/tests/signal/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = signal +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = signal.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = signal - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/signal/signal.c b/examples/elf/tests/signal/signal.c index b091602bc..afcdff281 100644 --- a/examples/elf/tests/signal/signal.c +++ b/examples/elf/tests/signal/signal.c @@ -66,8 +66,8 @@ static int sigusr2_rcvd = 0; * Name: siguser_action ****************************************************************************/ -/* NOTE: it is necessary for functions that are referred to by function pointers - * pointer to be declared with global scope (at least for ARM). Otherwise, +/* NOTE: it is necessary for functions that are referred to by function + * pointers to be declared with global scope (at least for ARM). Otherwise, * a relocation type that is not supported by ELF is generated by GCC. */ @@ -225,7 +225,7 @@ int main(int argc, char **argv) /* Verify that the handler that was removed was siguser_action */ - if ((void*)act.sa_handler != (void*)siguser_action) + if ((void *)act.sa_handler != (void *)siguser_action) { fprintf(stderr, "Old SIGUSR2 signal handler (%p) is not siguser_action (%p)\n", diff --git a/examples/elf/tests/struct/Kconfig b/examples/elf/tests/struct/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/struct/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/struct/Makefile b/examples/elf/tests/struct/Makefile index 990d990c2..88ab66f3f 100644 --- a/examples/elf/tests/struct/Makefile +++ b/examples/elf/tests/struct/Makefile @@ -22,48 +22,11 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = struct +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +CSRCS = struct_dummy.c +MAINSRC = struct_main.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = struct -SRCS = struct_main.c struct_dummy.c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/elf/tests/struct/struct_main.c b/examples/elf/tests/struct/struct_main.c index 801d2c5c5..5158c03b7 100644 --- a/examples/elf/tests/struct/struct_main.c +++ b/examples/elf/tests/struct/struct_main.c @@ -35,7 +35,7 @@ const struct struct_dummy_s dummy_struct = { - DUMMY_SCALAR_VALUE3 + DUMMY_SCALAR_VALUE3 }; int dummy_scalar = DUMMY_SCALAR_VALUE2; diff --git a/examples/elf/tests/task/Kconfig b/examples/elf/tests/task/Kconfig new file mode 100644 index 000000000..f72f3c094 --- /dev/null +++ b/examples/elf/tests/task/Kconfig @@ -0,0 +1,4 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# diff --git a/examples/elf/tests/task/Makefile b/examples/elf/tests/task/Makefile index b57b98885..09424ac96 100644 --- a/examples/elf/tests/task/Makefile +++ b/examples/elf/tests/task/Makefile @@ -22,49 +22,10 @@ include $(APPDIR)/Make.defs -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -else -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDELFFLAGS += -Bstatic -LDLIBPATH += -L $(NUTTXLIB) -endif -endif +PROGNAME = task +PRIORITY = 100 +STACKSIZE = $(CONFIG_ELF_STACKSIZE) +MODULE = m +MAINSRC = task.c -ifeq ($(CONFIG_EXAMPLES_ELF_LIBC),y) -LDLIBS += -lc -endif - -ifeq ($(CONFIG_EXAMPLES_ELF_SYSCALL),y) -LDLIBS += -lproxies -endif - -BIN = task - -SRCS = $(BIN).c -OBJS = $(SRCS:.c=$(OBJEXT)) - -all: $(BIN) -.PHONY: all clean install - -$(OBJS): %$(OBJEXT): %.c - @echo "CC: $<" - $(Q) $(CC) -c $(CELFFLAGS) $< -o $@ - -$(BIN): $(OBJS) - @echo "LD: $<" - $(Q) $(LD) $(LDELFFLAGS) $(LDLIBPATH) -o $@ $(ARCHCRT0OBJ) $^ $(LDLIBS) - -$(FSIMG_DIR)/$(BIN): $(BIN) - $(Q) mkdir -p $(FSIMG_DIR) - $(Q) install $(BIN) $(FSIMG_DIR)/$(BIN) -ifneq ($(CONFIG_DEBUG_SYMBOLS),y) - $(Q) $(STRIP) $(FSIMG_DIR)/$(BIN) -endif - -install: $(FSIMG_DIR)/$(BIN) - -clean: - $(call DELFILE, $(BIN)) - $(call CLEAN) +include $(APPDIR)/Application.mk diff --git a/examples/etl/etl_main.cxx b/examples/etl/etl_main.cxx index efb4aa835..f8628cdfc 100644 --- a/examples/etl/etl_main.cxx +++ b/examples/etl/etl_main.cxx @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include diff --git a/examples/fb/fb_main.c b/examples/fb/fb_main.c index bd7d457e1..3d237df05 100644 --- a/examples/fb/fb_main.c +++ b/examples/fb/fb_main.c @@ -43,7 +43,11 @@ * Preprocessor Definitions ****************************************************************************/ -#define NCOLORS 6 +#define NRECTCOLORS 6 +#define NSMPTECOLORS 7 + +#define PATTERN_RECTANGLES 0 +#define PATTERN_SMPTE 1 /**************************************************************************** * Private Types @@ -71,24 +75,51 @@ static const char g_default_fbdev[] = CONFIG_EXAMPLES_FB_DEFAULTFB; /* Violet-Blue-Green-Yellow-Orange-Red */ -static const uint32_t g_rgb24[NCOLORS] = +static const uint32_t g_rgb24[NRECTCOLORS] = { RGB24_VIOLET, RGB24_BLUE, RGB24_GREEN, RGB24_YELLOW, RGB24_ORANGE, RGB24_RED }; -static const uint16_t g_rgb16[NCOLORS] = +static const uint16_t g_rgb16[NRECTCOLORS] = { RGB16_VIOLET, RGB16_BLUE, RGB16_GREEN, RGB16_YELLOW, RGB16_ORANGE, RGB16_RED }; -static const uint8_t g_rgb8[NCOLORS] = +static const uint8_t g_rgb8[NRECTCOLORS] = { RGB8_VIOLET, RGB8_BLUE, RGB8_GREEN, RGB8_YELLOW, RGB8_ORANGE, RGB8_RED }; +/* White-Yellow-Cyan-Green-Magenta-Red-Blue */ + +static const uint32_t g_smpte_rgb24[NSMPTECOLORS] = +{ + RGB24_WHITE, RGB24_YELLOW, RGB24_CYAN, RGB24_GREEN, + RGB24_MAGENTA, RGB24_RED, RGB24_BLUE +}; + +static const uint16_t g_smpte_rgb16[NSMPTECOLORS] = +{ + RGB16_WHITE, RGB16_YELLOW, RGB16_CYAN, RGB16_GREEN, + RGB16_MAGENTA, RGB16_RED, RGB16_BLUE +}; + +static const uint8_t g_smpte_rgb8[NSMPTECOLORS] = +{ + RGB8_WHITE, RGB8_YELLOW, RGB8_CYAN, RGB8_GREEN, + RGB8_MAGENTA, RGB8_RED, RGB8_BLUE +}; + +/* Monochrome equivalents selected using BT.601 luma. */ + +static const uint8_t g_smpte_mono[NSMPTECOLORS] = +{ + 1, 1, 1, 1, 0, 0, 0 +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -270,7 +301,7 @@ static int fb_init_mem2(FAR struct fb_state_s *state) ****************************************************************************/ static void draw_rect32(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color) { FAR uint32_t *dest; FAR uint8_t *row; @@ -283,7 +314,7 @@ static void draw_rect32(FAR struct fb_state_s *state, dest = ((FAR uint32_t *)row) + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb24[color] | 0xff000000; + *dest++ = color | 0xff000000; } row += state->pinfo.stride; @@ -291,7 +322,7 @@ static void draw_rect32(FAR struct fb_state_s *state, } static void draw_rect24(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color) { FAR uint8_t *dest; FAR uint8_t *row; @@ -304,9 +335,9 @@ static void draw_rect24(FAR struct fb_state_s *state, dest = ((FAR uint8_t *)row) + area->x * 3; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb24[color] & 0xff; - *dest++ = (g_rgb24[color] >> 8) & 0xff; - *dest++ = (g_rgb24[color] >> 16) & 0xff; + *dest++ = color & 0xff; + *dest++ = (color >> 8) & 0xff; + *dest++ = (color >> 16) & 0xff; } row += state->pinfo.stride; @@ -314,7 +345,7 @@ static void draw_rect24(FAR struct fb_state_s *state, } static void draw_rect16(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint16_t color) { FAR uint16_t *dest; FAR uint8_t *row; @@ -327,7 +358,7 @@ static void draw_rect16(FAR struct fb_state_s *state, dest = ((FAR uint16_t *)row) + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb16[color]; + *dest++ = color; } row += state->pinfo.stride; @@ -335,7 +366,7 @@ static void draw_rect16(FAR struct fb_state_s *state, } static void draw_rect8(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint8_t color) { FAR uint8_t *dest; FAR uint8_t *row; @@ -348,7 +379,7 @@ static void draw_rect8(FAR struct fb_state_s *state, dest = row + area->x; for (x = 0; x < area->w; x++) { - *dest++ = g_rgb8[color]; + *dest++ = color; } row += state->pinfo.stride; @@ -356,11 +387,11 @@ static void draw_rect8(FAR struct fb_state_s *state, } static void draw_rect1(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint8_t color) { FAR uint8_t *pixel; FAR uint8_t *row; - uint8_t color8 = (color & 1) == 0 ? 0 : 0xff; + uint8_t color8 = color == 0 ? 0 : 0xff; int start_full_x; int end_full_x; @@ -437,43 +468,47 @@ static void draw_rect1(FAR struct fb_state_s *state, } static void draw_rect(FAR struct fb_state_s *state, - FAR struct fb_area_s *area, int color) + FAR struct fb_area_s *area, uint32_t color24, + uint16_t color16, uint8_t color8, uint8_t mono) { -#ifdef CONFIG_FB_UPDATE - int ret; -#endif - switch (state->pinfo.bpp) { case 32: - draw_rect32(state, area, color); + draw_rect32(state, area, color24); break; case 24: - draw_rect24(state, area, color); + draw_rect24(state, area, color24); break; case 16: - draw_rect16(state, area, color); + draw_rect16(state, area, color16); break; case 8: default: - draw_rect8(state, area, color); + draw_rect8(state, area, color8); break; case 1: - draw_rect1(state, area, color); + draw_rect1(state, area, mono); break; } +} +static void present_area(FAR struct fb_state_s *state, + FAR const struct fb_area_s *area) +{ #ifdef CONFIG_FB_UPDATE + struct fb_area_s update = *area; + int ret; + int yoffset = state->act_fbmem == state->fbmem ? 0 : state->mem2_yoffset; - area->y += yoffset; + update.y += yoffset; ret = ioctl(state->fd, FBIO_UPDATE, - (unsigned long)((uintptr_t)area)); + (unsigned long)((uintptr_t)&update)); if (ret < 0) { int errcode = errno; @@ -499,6 +534,7 @@ static void draw_rect(FAR struct fb_state_s *state, int main(int argc, FAR char *argv[]) { FAR const char *fbdev = g_default_fbdev; + int pattern = PATTERN_RECTANGLES; struct fb_state_s state; struct fb_area_s area; int nsteps; @@ -510,20 +546,43 @@ int main(int argc, FAR char *argv[]) int x; int y; int ret; + int opt; - /* There is a single required argument: The path to the framebuffer - * driver. - */ - - if (argc == 2) + while ((opt = getopt(argc, argv, "p:")) != ERROR) { - fbdev = argv[1]; + switch (opt) + { + case 'p': + if (strcmp(optarg, "rectangles") == 0) + { + pattern = PATTERN_RECTANGLES; + } + else if (strcmp(optarg, "smpte") == 0) + { + pattern = PATTERN_SMPTE; + } + else + { + fprintf(stderr, "ERROR: Unknown pattern: %s\n", optarg); + goto usage; + } + + break; + + default: + goto usage; + } } - else if (argc != 1) + + if (optind < argc) { - fprintf(stderr, "ERROR: Single argument required\n"); - fprintf(stderr, "USAGE: %s []\n", argv[0]); - return EXIT_FAILURE; + fbdev = argv[optind++]; + } + + if (optind < argc) + { + fprintf(stderr, "ERROR: Too many framebuffer paths\n"); + goto usage; } /* Open the framebuffer driver */ @@ -652,38 +711,64 @@ int main(int argc, FAR char *argv[]) } } - /* Draw some rectangles */ + /* Draw the selected pattern */ state.act_fbmem = state.fbmem; - nsteps = 2 * (NCOLORS - 1) + 1; - xstep = state.vinfo.xres / nsteps; - ystep = state.vinfo.yres / nsteps; - width = state.vinfo.xres; - height = state.vinfo.yres; - - for (x = 0, y = 0, color = 0; - color < NCOLORS; - x += xstep, y += ystep, color++) + if (pattern == PATTERN_SMPTE) { - area.x = x; - area.y = y; - area.w = width; - area.h = height; - - printf("%2d: (%3d,%3d) (%3d,%3d)\n", - color, area.x, area.y, area.w, area.h); - - draw_rect(&state, &area, color); - usleep(500 * 1000); - - width -= (2 * xstep); - height -= (2 * ystep); - - /* double buffer mode */ - - if (state.pinfo.yres_virtual == (state.vinfo.yres * 2)) + for (color = 0; color < NSMPTECOLORS; color++) { - sync_area(&state); + area.x = color * state.vinfo.xres / NSMPTECOLORS; + area.y = 0; + area.w = (color + 1) * state.vinfo.xres / NSMPTECOLORS - + area.x; + area.h = state.vinfo.yres; + + draw_rect(&state, &area, g_smpte_rgb24[color], + g_smpte_rgb16[color], g_smpte_rgb8[color], + g_smpte_mono[color]); + } + + area.x = 0; + area.y = 0; + area.w = state.vinfo.xres; + area.h = state.vinfo.yres; + present_area(&state, &area); + } + else + { + nsteps = 2 * (NRECTCOLORS - 1) + 1; + xstep = state.vinfo.xres / nsteps; + ystep = state.vinfo.yres / nsteps; + width = state.vinfo.xres; + height = state.vinfo.yres; + + for (x = 0, y = 0, color = 0; + color < NRECTCOLORS; + x += xstep, y += ystep, color++) + { + area.x = x; + area.y = y; + area.w = width; + area.h = height; + + printf("%2d: (%3d,%3d) (%3d,%3d)\n", + color, area.x, area.y, area.w, area.h); + + draw_rect(&state, &area, g_rgb24[color], + g_rgb16[color], g_rgb8[color], color & 1); + present_area(&state, &area); + usleep(500 * 1000); + + width -= (2 * xstep); + height -= (2 * ystep); + + /* double buffer mode */ + + if (state.pinfo.yres_virtual == (state.vinfo.yres * 2)) + { + sync_area(&state); + } } } @@ -693,4 +778,10 @@ out: munmap(state.fbmem, state.pinfo.fblen); close(state.fd); return ret; + +usage: + fprintf(stderr, + "USAGE: %s [-p rectangles|smpte] []\n", + argv[0]); + return EXIT_FAILURE; } diff --git a/examples/fbcon/fbcon_main.c b/examples/fbcon/fbcon_main.c index 2aa95e0c5..da0833800 100644 --- a/examples/fbcon/fbcon_main.c +++ b/examples/fbcon/fbcon_main.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -1913,7 +1913,7 @@ static void fbcon_write(FAR struct fbcon_state_s *st, do { - /* Is the character part of a VT100 escape sequnce? */ + /* Is the character part of a VT100 escape sequence? */ state = fbcon_vt100(st, ch); switch (state) @@ -2652,4 +2652,3 @@ errout: fprintf(stderr, "FBCON exiting with error %d\n", exitcode); return exitcode; } - diff --git a/examples/fdpicxip/CMakeLists.txt b/examples/fdpicxip/CMakeLists.txt new file mode 100644 index 000000000..2af0fc4a8 --- /dev/null +++ b/examples/fdpicxip/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/examples/fdpicxip/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_FDPICXIP) + nuttx_add_application(NAME fdpicxip SRCS fdpicxip_main.c) +endif() diff --git a/examples/fdpicxip/Kconfig b/examples/fdpicxip/Kconfig new file mode 100644 index 000000000..6de10e005 --- /dev/null +++ b/examples/fdpicxip/Kconfig @@ -0,0 +1,39 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_FDPICXIP + tristate "FDPIC modules executed in place from xipfs demo" + depends on FS_XIPFS && FDPIC && LIBC_EXECFUNCS + default n + ---help--- + Write FDPIC modules into xipfs at run time, the way a download + would, then run them. Their text is executed directly out of + flash and shared between concurrent instances, while each + instance gets its own data. + + Subcommands: + + qsort one self-contained module, two instances + solib a shared library across two instances + cxx the same in C++, checking global constructors ran + jmprel a module whose imports are all in DT_JMPREL + + With no subcommand, qsort runs. + + The modules are embedded in the image as C headers. Their + sources are in modules/, which rebuilds the headers on an + explicit 'make regen': linking one needs arm-uclinuxfdpiceabi + binutils, which this build does not. + + This demonstrates; it does not assert. For the assertions see + the fdpic and reject sections of CONFIG_TESTING_FS_XIPFS. + +if EXAMPLES_FDPICXIP + +config EXAMPLES_FDPICXIP_MOUNTPT + string "xipfs mountpoint" + default "/mnt/xipfs" + +endif # EXAMPLES_FDPICXIP diff --git a/examples/fdpicxip/Make.defs b/examples/fdpicxip/Make.defs new file mode 100644 index 000000000..88acd75f8 --- /dev/null +++ b/examples/fdpicxip/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/fdpicxip/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_FDPICXIP),) +CONFIGURED_APPS += $(APPDIR)/examples/fdpicxip +endif diff --git a/examples/rmtchar/Makefile b/examples/fdpicxip/Makefile similarity index 73% rename from examples/rmtchar/Makefile rename to examples/fdpicxip/Makefile index 2c6d454dd..05f90116c 100644 --- a/examples/rmtchar/Makefile +++ b/examples/fdpicxip/Makefile @@ -1,5 +1,5 @@ ############################################################################ -# apps/examples/rmtchar/Makefile +# apps/examples/fdpicxip/Makefile # # SPDX-License-Identifier: Apache-2.0 # @@ -22,22 +22,16 @@ include $(APPDIR)/Make.defs -# rmt character driver test +# FDPIC modules executed in place from xipfs. The modules are embedded as +# headers, built from the sources in modules/ by an explicit 'make regen' +# there: linking one needs arm-uclinuxfdpiceabi binutils, which this build +# does not. -ifeq ($(CONFIG_EXAMPLES_RMTCHAR_TX),y) -CSRCS += rmtchar_transmitter.c -endif -ifeq ($(CONFIG_EXAMPLES_RMTCHAR_RX),y) -CSRCS += rmtchar_receiver.c -endif -CSRCS += rmtchar_common.c -MAINSRC = rmtchar_main.c +MAINSRC = fdpicxip_main.c -# Touchscreen built-in application info - -PROGNAME = rmtchar -PRIORITY = SCHED_PRIORITY_DEFAULT +PROGNAME = fdpicxip +PRIORITY = SCHED_PRIORITY_DEFAULT STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) -MODULE = $(CONFIG_EXAMPLES_RMTCHAR) +MODULE = $(CONFIG_EXAMPLES_FDPICXIP) include $(APPDIR)/Application.mk diff --git a/examples/fdpicxip/cxxuser_bin.h b/examples/fdpicxip/cxxuser_bin.h new file mode 100644 index 000000000..8baafab00 --- /dev/null +++ b/examples/fdpicxip/cxxuser_bin.h @@ -0,0 +1,400 @@ +/**************************************************************************** + * apps/examples/fdpicxip/cxxuser_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from cxxuser.fdpic -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_cxxuser[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x81, 0x04, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x30, 0x0e, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x12, 0x00, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x06, 0x00, 0x00, + 0xf0, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xf0, 0x06, 0x00, 0x00, 0xf0, 0x16, 0x00, 0x00, + 0xf0, 0x16, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xf4, 0x06, 0x00, 0x00, 0xf4, 0x16, 0x00, 0x00, 0xf4, 0x16, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x94, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0c, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, + 0xf0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x81, 0x04, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x61, 0x74, + 0x6f, 0x69, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x6d, 0x61, 0x72, + 0x6b, 0x65, 0x72, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x65, 0x64, 0x00, 0x73, 0x79, + 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x61, + 0x64, 0x64, 0x00, 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x73, 0x68, + 0x61, 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x73, 0x6e, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x00, + 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x00, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x00, 0x6c, 0x69, + 0x62, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x73, 0x6f, 0x00, 0x5f, 0x5f, + 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, + 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, + 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x00, 0xf0, 0x16, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0xa0, 0x17, 0x00, 0x00, 0xa4, 0x07, 0x00, 0x00, 0xa8, 0x17, 0x00, 0x00, + 0xa4, 0x08, 0x00, 0x00, 0xb0, 0x17, 0x00, 0x00, 0xa4, 0x09, 0x00, 0x00, + 0xb8, 0x17, 0x00, 0x00, 0xa4, 0x0a, 0x00, 0x00, 0xc0, 0x17, 0x00, 0x00, + 0xa4, 0x0b, 0x00, 0x00, 0xc8, 0x17, 0x00, 0x00, 0xa4, 0x0c, 0x00, 0x00, + 0xd0, 0x17, 0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0xd8, 0x17, 0x00, 0x00, + 0xa4, 0x11, 0x00, 0x00, 0xe0, 0x17, 0x00, 0x00, 0xa4, 0x12, 0x00, 0x00, + 0xe8, 0x17, 0x00, 0x00, 0xa4, 0x13, 0x00, 0x00, 0xf0, 0x17, 0x00, 0x00, + 0xa4, 0x14, 0x00, 0x00, 0xf8, 0x17, 0x00, 0x00, 0xa4, 0x15, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x0c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x14, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x1c, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x24, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x2c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x34, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x44, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x4c, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x54, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x5c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x64, 0x00, 0x00, 0x00, + 0x2d, 0xe9, 0xf0, 0x45, 0x01, 0x28, 0x4c, 0x46, 0x80, 0x46, 0x0f, 0x46, + 0x89, 0xb0, 0x40, 0xf3, 0x8e, 0x80, 0x48, 0x68, 0xff, 0xf7, 0xcc, 0xff, + 0xb8, 0xf1, 0x02, 0x0f, 0xa1, 0x46, 0x06, 0x46, 0x03, 0xd0, 0xb8, 0x68, + 0xff, 0xf7, 0xd8, 0xff, 0xa1, 0x46, 0x52, 0x4b, 0x4f, 0x4d, 0x59, 0xf8, + 0x03, 0x30, 0x1b, 0x68, 0xab, 0x42, 0x7c, 0xd1, 0xdf, 0xf8, 0x3c, 0xa1, + 0xfa, 0x44, 0xff, 0xf7, 0x8f, 0xff, 0xa1, 0x46, 0x00, 0x28, 0x78, 0xd0, + 0x4c, 0x4b, 0x7b, 0x44, 0x49, 0x4a, 0x59, 0xf8, 0x02, 0x20, 0x12, 0x79, + 0x00, 0x2a, 0x73, 0xd0, 0x49, 0x4a, 0x7a, 0x44, 0x49, 0x49, 0xcd, 0xe9, + 0x00, 0x32, 0x06, 0x20, 0x53, 0x46, 0x32, 0x46, 0x79, 0x44, 0xff, 0xf7, + 0x97, 0xff, 0xa1, 0x46, 0x40, 0x4b, 0x59, 0xf8, 0x03, 0x30, 0x1b, 0x68, + 0x5d, 0x1b, 0x18, 0xbf, 0x01, 0x25, 0xff, 0xf7, 0x6f, 0xff, 0xa1, 0x46, + 0x08, 0xb9, 0x45, 0xf0, 0x02, 0x05, 0x3a, 0x4b, 0x59, 0xf8, 0x03, 0x30, + 0x1b, 0x79, 0x0b, 0xb9, 0x45, 0xf0, 0x04, 0x05, 0x4f, 0xf0, 0x03, 0x0a, + 0x34, 0x4b, 0x03, 0x93, 0x30, 0x46, 0xff, 0xf7, 0x35, 0xff, 0x03, 0x98, + 0xa1, 0x46, 0xff, 0xf7, 0x45, 0xff, 0xba, 0xf1, 0x01, 0x0a, 0xa1, 0x46, + 0xf4, 0xd1, 0xff, 0xf7, 0x49, 0xff, 0x06, 0xeb, 0x46, 0x0a, 0xa1, 0x46, + 0x50, 0x45, 0x18, 0xbf, 0x45, 0xf0, 0x08, 0x05, 0xff, 0xf7, 0x40, 0xff, + 0xa1, 0x46, 0x03, 0x46, 0x00, 0x2d, 0x38, 0xd1, 0x2c, 0x4a, 0x7a, 0x44, + 0x2c, 0x49, 0xcd, 0xe9, 0x00, 0xa2, 0x06, 0x20, 0x32, 0x46, 0x79, 0x44, + 0xff, 0xf7, 0x5a, 0xff, 0xb8, 0xf1, 0x03, 0x0f, 0xa1, 0x46, 0x1a, 0xdd, + 0xff, 0x68, 0xc7, 0xb1, 0x26, 0x4a, 0x2b, 0x46, 0x10, 0x21, 0x7a, 0x44, + 0x04, 0xa8, 0xff, 0xf7, 0x11, 0xff, 0x4f, 0xf4, 0xd2, 0x72, 0xa1, 0x46, + 0x82, 0x46, 0x40, 0xf2, 0x41, 0x21, 0x38, 0x46, 0xff, 0xf7, 0x58, 0xff, + 0x06, 0x1e, 0xa1, 0x46, 0x18, 0xda, 0x1e, 0x49, 0x3a, 0x46, 0x06, 0x20, + 0x79, 0x44, 0xff, 0xf7, 0x3b, 0xff, 0x28, 0x46, 0x09, 0xb0, 0xbd, 0xe8, + 0xf0, 0x85, 0x01, 0x26, 0x7b, 0xe7, 0xdf, 0xf8, 0x64, 0xa0, 0xfa, 0x44, + 0x81, 0xe7, 0x18, 0x4b, 0x7b, 0x44, 0x85, 0xe7, 0x17, 0x4a, 0x7a, 0x44, + 0x8a, 0xe7, 0x17, 0x4a, 0x7a, 0x44, 0xc5, 0xe7, 0x51, 0x46, 0xff, 0xf7, + 0x11, 0xff, 0x00, 0x28, 0xa1, 0x46, 0x05, 0xdb, 0x52, 0x46, 0x30, 0x46, + 0x04, 0xa9, 0xff, 0xf7, 0x13, 0xff, 0xa1, 0x46, 0x30, 0x46, 0xff, 0xf7, + 0x41, 0xff, 0xdc, 0xe7, 0x55, 0x1a, 0x0c, 0x00, 0xa0, 0x86, 0x01, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, + 0x66, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, + 0x34, 0x01, 0x00, 0x00, 0x4e, 0x01, 0x00, 0x00, 0x2b, 0x01, 0x00, 0x00, + 0x8e, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x38, 0xb5, 0x05, 0x4b, 0x59, 0xf8, 0x03, 0x40, + 0x02, 0x4b, 0x23, 0x60, 0xff, 0xf7, 0xd4, 0xfe, 0x20, 0x71, 0x38, 0xbd, + 0x55, 0x1a, 0x0c, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x79, 0x65, 0x73, 0x00, + 0x4e, 0x4f, 0x00, 0x50, 0x41, 0x53, 0x53, 0x00, 0x46, 0x41, 0x49, 0x4c, + 0x00, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x20, 0x25, 0x64, 0x5d, 0x20, 0x6f, + 0x77, 0x6e, 0x20, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x3a, + 0x20, 0x25, 0x73, 0x2c, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, + 0x20, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x72, 0x61, 0x6e, 0x3a, 0x20, 0x25, + 0x73, 0x2c, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x20, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x0a, 0x00, 0x5b, 0x75, + 0x73, 0x65, 0x72, 0x20, 0x25, 0x64, 0x5d, 0x20, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x20, 0x3d, 0x20, + 0x25, 0x64, 0x20, 0x28, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x20, 0x25, 0x64, 0x29, 0x20, 0x2d, 0x2d, 0x20, 0x25, 0x73, 0x0a, 0x00, + 0x25, 0x64, 0x00, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x5d, 0x20, 0x63, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x25, 0x73, 0x0a, 0x00, 0x00, 0x94, 0x17, 0x00, 0x00, + 0x29, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x94, 0x17, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfb, 0xff, 0xff, 0x6f, 0x01, 0x00, 0x00, 0x00, 0xfa, 0xff, 0xff, 0x6f, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, + 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x41, 0x72, 0x6d, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x54, 0x6f, 0x6f, 0x6c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, + 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x52, 0x65, 0x6c, 0x31, 0x20, 0x28, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x20, 0x61, 0x72, 0x6d, 0x2d, 0x31, 0x35, 0x2e, + 0x38, 0x36, 0x29, 0x29, 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x31, 0x20, + 0x32, 0x30, 0x32, 0x35, 0x31, 0x32, 0x30, 0x33, 0x00, 0x41, 0x2c, 0x00, + 0x00, 0x00, 0x61, 0x65, 0x61, 0x62, 0x69, 0x00, 0x01, 0x22, 0x00, 0x00, + 0x00, 0x05, 0x37, 0x2d, 0x4d, 0x00, 0x06, 0x0a, 0x07, 0x4d, 0x09, 0x02, + 0x12, 0x04, 0x14, 0x01, 0x15, 0x01, 0x17, 0x03, 0x18, 0x01, 0x19, 0x01, + 0x1a, 0x01, 0x1e, 0x04, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf4, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x17, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0xec, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x29, 0x06, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xf0, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x22, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x04, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xf1, 0xff, 0x37, 0x00, 0x00, 0x00, 0xf4, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf1, 0xff, 0x40, 0x00, 0x00, 0x00, + 0x94, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xa0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xcc, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xdc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xf4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x18, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x30, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x54, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x6c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, + 0xf0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0xae, 0x00, 0x00, 0x00, 0x81, 0x04, 0x00, 0x00, 0xa8, 0x01, 0x00, 0x00, + 0x12, 0x00, 0x06, 0x00, 0xb3, 0x00, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0xc4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0xe2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x74, 0x00, 0x24, 0x64, 0x00, 0x5f, + 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x5f, 0x73, 0x75, 0x62, 0x5f, + 0x49, 0x5f, 0x63, 0x78, 0x78, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x63, 0x70, + 0x70, 0x00, 0x2e, 0x4c, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x30, 0x00, + 0x5f, 0x5a, 0x4c, 0x36, 0x67, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x00, 0x5f, + 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, 0x47, 0x4c, 0x4f, + 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, + 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, + 0x61, 0x64, 0x64, 0x00, 0x73, 0x6e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, + 0x00, 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x65, + 0x64, 0x00, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x00, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, + 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x6d, 0x61, + 0x69, 0x6e, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, + 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x73, 0x79, 0x73, 0x6c, + 0x6f, 0x67, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, 0x6f, 0x70, 0x65, 0x6e, + 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, + 0x72, 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x00, 0x00, 0x2e, 0x73, 0x79, + 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, + 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, + 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x72, 0x65, + 0x6c, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x70, 0x6c, 0x74, 0x00, 0x2e, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x00, 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, 0x70, 0x00, 0x2e, 0x69, + 0x6e, 0x69, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x2e, 0x64, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, + 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x00, 0x2e, 0x41, 0x52, 0x4d, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x44, 0x06, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, 0xec, 0x06, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x00, 0x00, + 0xf0, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xf4, 0x16, 0x00, 0x00, 0xf4, 0x06, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x94, 0x17, 0x00, 0x00, 0x94, 0x07, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x18, 0x00, 0x00, + 0x04, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x08, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x08, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb8, 0x0c, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x0d, 0x00, 0x00, + 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_cxxuser_len = 4352; diff --git a/examples/fdpicxip/fdpicxip_main.c b/examples/fdpicxip/fdpicxip_main.c new file mode 100644 index 000000000..5c89f2ae0 --- /dev/null +++ b/examples/fdpicxip/fdpicxip_main.c @@ -0,0 +1,538 @@ +/**************************************************************************** + * apps/examples/fdpicxip/fdpicxip_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * End to end demonstration of the FDPIC module loader over a writable + * execute-in-place file system: + * + * 1. write a module into xipfs at run time, the way a download would, + * 2. confirm the file system can hand out a direct flash pointer for it, + * 3. run it, in some cases as two concurrent instances, + * 4. confirm the shared text was pinned in place while they ran and + * released afterwards. + * + * The point of FDPIC here is that text stays in flash and only the writable + * segment is copied, once per running instance -- so a second instance, or + * a second module sharing a library, costs data and no code. + * + * This demonstrates; it does not assert. The assertions live in the fdpic + * and reject sections of apps/testing/fs/xipfs. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "qsorter_bin.h" +#include "libcounter_bin.h" +#include "user_bin.h" +#include "libshape_bin.h" +#include "cxxuser_bin.h" +#include "lazymod_bin.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_EXAMPLES_FDPICXIP_MOUNTPT + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: extent_info_path + * + * Description: + * Where a file physically lies: its extent, its flash address, and how + * many live mappings are pinning it in place. + * + ****************************************************************************/ + +static int extent_info_path(FAR const char *path, + FAR struct xipfs_extent_info_s *info) +{ + int fd; + int ret; + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + return -errno; + } + + ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)info); + close(fd); + + return ret < 0 ? -errno : 0; +} + +/**************************************************************************** + * Shared library demonstration + * + * Stages a library and a module that needs it, then runs two instances. + * Both share one mapped copy of each object's code, executed in place from + * flash, while each instance gets private copies of both the module's data + * and the library's. If the library's state were shared, the two totals + * would interleave instead of each reaching seed*3. + ****************************************************************************/ + +static int stage_blob(FAR const char *path, FAR const unsigned char *data, + unsigned int len) +{ + ssize_t n; + int fd; + + unlink(path); + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0755); + if (fd < 0) + { + return -errno; + } + + if (ftruncate(fd, len) < 0) + { + close(fd); + return -errno; + } + + n = write(fd, data, len); + close(fd); + + return (n == (ssize_t)len) ? 0 : -EIO; +} + +/**************************************************************************** + * Name: qsorter_main + * + * Description: + * The simplest case the loader has: one self-contained FDPIC module, no + * library behind it, run as two concurrent instances. It is the FDPIC + * counterpart of what this demo does by default with NXFLAT, and the + * only subcommand that exercises a module with no second object -- solib + * and cxx both bring a library, jmprel is about how imports are bound. + * + * The two instances overlap deliberately: each fills its array, sleeps, + * then sorts. One shared copy of the text, a private copy of the data + * per instance -- shared data would show up as corrupted output, and the + * pin count while both are alive is what stops the defragmenter + * relocating code that is executing. + * + ****************************************************************************/ + +static int qsorter_main(void) +{ + struct xipfs_extent_info_s info; + FAR char *args[3]; + pid_t pid[2]; + char seed[8]; + int status; + int ret; + int i; + + syslog(LOG_INFO, + "\n=== FDPIC module executed in place from xipfs ===\n\n"); + + ret = stage_blob(MOUNTPT "/qsorter", g_qsorter_nxf, g_qsorter_nxf_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the module failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "staged qsorter (%u bytes)\n", g_qsorter_nxf_len); + + if (extent_info_path(MOUNTPT "/qsorter", &info) == 0) + { + syslog(LOG_INFO, "extent: block %" PRIu32 " x%" PRIu32 ", size %" + PRIu32 ", flash addr 0x%08lx\n", + info.start_block, info.nblocks, info.size, + (unsigned long)info.xipaddr); + + if (info.xipaddr == 0) + { + syslog(LOG_INFO, + "ERROR: filesystem cannot expose a flash pointer; the " + "module would have to be copied to RAM\n"); + return EXIT_FAILURE; + } + } + + syslog(LOG_INFO, "\nspawning two concurrent instances...\n\n"); + + for (i = 0; i < 2; i++) + { + snprintf(seed, sizeof(seed), "%d", i + 1); + args[0] = (FAR char *)"qsorter"; + args[1] = seed; + args[2] = NULL; + + ret = posix_spawn(&pid[i], MOUNTPT "/qsorter", NULL, NULL, args, NULL); + if (ret != 0) + { + syslog(LOG_INFO, "spawn %d failed: %d\n", i + 1, ret); + return EXIT_FAILURE; + } + } + + usleep(150000); + + if (extent_info_path(MOUNTPT "/qsorter", &info) == 0) + { + syslog(LOG_INFO, "[while running] pins on the shared text = %lu\n\n", + (unsigned long)info.pincount); + } + + for (i = 0; i < 2; i++) + { + waitpid(pid[i], &status, 0); + } + + usleep(100000); + + if (extent_info_path(MOUNTPT "/qsorter", &info) == 0) + { + syslog(LOG_INFO, "\n[after exit] pins on the shared text = %lu\n", + (unsigned long)info.pincount); + } + + syslog(LOG_INFO, "\n=== done ===\n"); + return EXIT_SUCCESS; +} + +static int solib_main(void) +{ + struct xipfs_extent_info_s li; + struct xipfs_extent_info_s ui; + FAR char *args[3]; + pid_t pid[2]; + char seed[8]; + int status; + int ret; + int i; + + syslog(LOG_INFO, "\n=== FDPIC shared library, executed in place ===\n\n"); + + ret = stage_blob(MOUNTPT "/libcounter.so", g_libcounter, g_libcounter_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the library failed: %d\n", ret); + return EXIT_FAILURE; + } + + ret = stage_blob(MOUNTPT "/user", g_user, g_user_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the module failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "staged libcounter.so (%u bytes) and user (%u bytes)\n", + g_libcounter_len, g_user_len); + + if (extent_info_path(MOUNTPT "/libcounter.so", &li) == 0 && + extent_info_path(MOUNTPT "/user", &ui) == 0) + { + syslog(LOG_INFO, " libcounter.so text at 0x%08lx\n", + (unsigned long)li.xipaddr); + syslog(LOG_INFO, " user text at 0x%08lx\n", + (unsigned long)ui.xipaddr); + } + + syslog(LOG_INFO, "\nspawning two instances, each bumping by its own" + " seed...\n\n"); + + for (i = 0; i < 2; i++) + { + snprintf(seed, sizeof(seed), "%d", i + 1); + args[0] = (FAR char *)"user"; + args[1] = seed; + args[2] = NULL; + + ret = posix_spawn(&pid[i], MOUNTPT "/user", NULL, NULL, args, NULL); + if (ret != 0) + { + syslog(LOG_INFO, "spawn %d failed: %d\n", i + 1, ret); + return EXIT_FAILURE; + } + } + + usleep(150000); + + if (extent_info_path(MOUNTPT "/libcounter.so", &li) == 0) + { + syslog(LOG_INFO, "[while running] pins on the library's shared text" + " = %lu\n\n", (unsigned long)li.pincount); + } + + for (i = 0; i < 2; i++) + { + waitpid(pid[i], &status, 0); + } + + usleep(100000); + + if (extent_info_path(MOUNTPT "/libcounter.so", &li) == 0) + { + syslog(LOG_INFO, "\n[after exit] pins on the library's shared text" + " = %lu\n", (unsigned long)li.pincount); + } + + syslog(LOG_INFO, "\n=== done ===\n"); + return EXIT_SUCCESS; +} + +/**************************************************************************** + * Name: cxx_main + * + * Description: + * The same shape as solib_main(), in C++, and with one thing extra to + * prove: that global constructors ran. + * + * That is worth a demo of its own because of how it fails. A loader that + * ignores DT_INIT_ARRAY still loads a C++ module, still resolves every + * symbol, and still runs it -- the globals are simply left as .bss and + * every field reads zero. Nothing faults and nothing is logged; the + * module just answers wrong. So both objects here carry a magic number + * that only a constructor writes, and the module reports whether it found + * them. + * + * The ordering is checked too. The module's constructor samples whether + * the library was already constructed when it ran, which is the only + * moment the answer is still interesting -- by the time main() is entered + * both have run either way. + * + ****************************************************************************/ + +static int cxx_main(void) +{ + struct xipfs_extent_info_s li; + struct xipfs_extent_info_s ui; + FAR char *args[3]; + pid_t pid[2]; + char seed[8]; + int status; + int ret; + int i; + + syslog(LOG_INFO, "\n=== C++ module and shared library, executed in" + " place ===\n\n"); + + ret = stage_blob(MOUNTPT "/libshape.so", g_libshape, g_libshape_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the library failed: %d\n", ret); + return EXIT_FAILURE; + } + + ret = stage_blob(MOUNTPT "/cxxuser", g_cxxuser, g_cxxuser_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the module failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "staged libshape.so (%u bytes) and cxxuser (%u bytes)\n", + g_libshape_len, g_cxxuser_len); + + if (extent_info_path(MOUNTPT "/libshape.so", &li) == 0 && + extent_info_path(MOUNTPT "/cxxuser", &ui) == 0) + { + syslog(LOG_INFO, " libshape.so text at 0x%08lx\n", + (unsigned long)li.xipaddr); + syslog(LOG_INFO, " cxxuser text at 0x%08lx\n", + (unsigned long)ui.xipaddr); + } + + syslog(LOG_INFO, "\nspawning two instances, each adding its own seed...\n" + "(each instance's constructors run at load time, before main)\n\n"); + + for (i = 0; i < 2; i++) + { + snprintf(seed, sizeof(seed), "%d", i + 1); + args[0] = (FAR char *)"cxxuser"; + args[1] = seed; + args[2] = NULL; + + ret = posix_spawn(&pid[i], MOUNTPT "/cxxuser", NULL, NULL, args, NULL); + if (ret != 0) + { + syslog(LOG_INFO, "spawn %d failed: %d\n", i + 1, ret); + return EXIT_FAILURE; + } + } + + usleep(150000); + + if (extent_info_path(MOUNTPT "/libshape.so", &li) == 0) + { + syslog(LOG_INFO, "[while running] pins on the library's shared text" + " = %lu\n\n", (unsigned long)li.pincount); + } + + /* Only a barrier. The exit status is deliberately not read: waitpid() + * retains it for an already-exited child only under + * CONFIG_SCHED_CHILD_STATUS, and both instances run for the same time, so + * the second is always gone by the time the first wait returns. Checking + * `status` after a failed wait reads whatever the previous iteration left + * there, which looks like a pass. The suite in apps/testing/fs/xipfs + * asserts the real result via files; this demo reports through the log. + */ + + ret = EXIT_SUCCESS; + + for (i = 0; i < 2; i++) + { + waitpid(pid[i], &status, 0); + } + + /* The destructors run on unload, which happens as each task is reaped -- + * after waitpid() has returned, so give them a moment to be logged. + */ + + usleep(100000); + + if (extent_info_path(MOUNTPT "/libshape.so", &li) == 0) + { + syslog(LOG_INFO, "\n[after exit] pins on the library's shared text" + " = %lu\n", (unsigned long)li.pincount); + } + + syslog(LOG_INFO, "\n=== %s ===\n", + ret == EXIT_SUCCESS ? "done" : "FAILED"); + return ret; +} + +/**************************************************************************** + * Name: jmprel_main + * + * Description: + * Load a module whose imports the linker put in DT_JMPREL. + * + * Every other module here is linked with -z now, which leaves the imported + * descriptors in DT_REL. This one empties BINDNOW in modules/Makefile, so + * it has no DT_REL at all -- both of its imports are in the PLT relocation + * table. + * + * There is no lazy resolver in the loader, so a DT_JMPREL table that went + * unwalked was not deferred work: the descriptors stayed unrelocated and + * the module's first call into the firmware branched to whatever the + * unrelocated word held. It loaded cleanly and printed nothing, then + * took an INVSTATE UsageFault that escalated to a HardFault -- no + * console, no crash dump. If this demo prints its line, both tables are + * being bound. + * + ****************************************************************************/ + +static int jmprel_main(void) +{ + FAR char *args[3]; + pid_t pid; + int status; + int ret; + + syslog(LOG_INFO, "\n=== FDPIC module bound through DT_JMPREL ===\n\n"); + + ret = stage_blob(MOUNTPT "/lazymod", g_lazymod, g_lazymod_len); + if (ret < 0) + { + syslog(LOG_INFO, "staging the module failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "staged lazymod (%u bytes), all imports in DT_JMPREL\n\n", + g_lazymod_len); + + args[0] = (FAR char *)"lazymod"; + args[1] = (FAR char *)"42"; + args[2] = NULL; + + ret = posix_spawn(&pid, MOUNTPT "/lazymod", NULL, NULL, args, NULL); + if (ret != 0) + { + syslog(LOG_INFO, "spawn failed: %d\n", ret); + return EXIT_FAILURE; + } + + waitpid(pid, &status, 0); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) + { + syslog(LOG_INFO, "\n=== FAILED: module did not exit cleanly ===\n"); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "\n=== done: the module reached the firmware and came" + " back ===\n"); + return EXIT_SUCCESS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + if (argc > 1 && strcmp(argv[1], "solib") == 0) + { + return solib_main(); + } + + if (argc > 1 && strcmp(argv[1], "cxx") == 0) + { + return cxx_main(); + } + + if (argc > 1 && strcmp(argv[1], "jmprel") == 0) + { + return jmprel_main(); + } + + if (argc > 1 && strcmp(argv[1], "qsort") != 0) + { + fprintf(stderr, "usage: fdpicxip [qsort|solib|cxx|jmprel]\n"); + return EXIT_FAILURE; + } + + return qsorter_main(); +} diff --git a/examples/fdpicxip/lazymod_bin.h b/examples/fdpicxip/lazymod_bin.h new file mode 100644 index 000000000..b2d514312 --- /dev/null +++ b/examples/fdpicxip/lazymod_bin.h @@ -0,0 +1,220 @@ +/**************************************************************************** + * apps/examples/fdpicxip/lazymod_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from lazymod.fdpic -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_lazymod[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd5, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x3c, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, + 0x3c, 0x12, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x3c, 0x02, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd5, 0x01, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, + 0x6e, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, 0x73, 0x79, 0x73, 0x6c, 0x6f, + 0x67, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, + 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, + 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x00, + 0xc0, 0x12, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x00, 0xc8, 0x12, 0x00, 0x00, + 0xa4, 0x05, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0xf8, 0x08, 0xc0, 0x4d, 0xf8, 0x04, 0xcd, + 0xd9, 0xf8, 0x04, 0xc0, 0xd9, 0xf8, 0x00, 0xf0, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5f, 0xf8, 0x08, 0xc0, + 0x4d, 0xf8, 0x04, 0xcd, 0xd9, 0xf8, 0x04, 0xc0, 0xd9, 0xf8, 0x00, 0xf0, + 0x01, 0x28, 0x10, 0xb5, 0x4c, 0x46, 0x0b, 0xdd, 0x48, 0x68, 0xff, 0xf7, + 0xe5, 0xff, 0xa1, 0x46, 0x02, 0x46, 0x04, 0x49, 0x06, 0x20, 0x79, 0x44, + 0xff, 0xf7, 0xca, 0xff, 0x00, 0x20, 0x10, 0xbd, 0x01, 0x22, 0xf6, 0xe7, + 0x0e, 0x00, 0x00, 0x00, 0x5b, 0x6c, 0x61, 0x7a, 0x79, 0x6d, 0x6f, 0x64, + 0x5d, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x74, 0x68, 0x72, + 0x6f, 0x75, 0x67, 0x68, 0x20, 0x61, 0x20, 0x44, 0x54, 0x5f, 0x4a, 0x4d, + 0x50, 0x52, 0x45, 0x4c, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x73, 0x65, 0x65, 0x64, 0x20, 0x25, 0x64, + 0x0a, 0x00, 0x00, 0x00, 0xb4, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xb4, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc4, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x41, 0x72, 0x6d, 0x20, 0x47, 0x4e, + 0x55, 0x20, 0x54, 0x6f, 0x6f, 0x6c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, + 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x52, 0x65, 0x6c, 0x31, 0x20, 0x28, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x20, 0x61, 0x72, 0x6d, 0x2d, 0x31, 0x35, 0x2e, + 0x38, 0x36, 0x29, 0x29, 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x31, 0x20, + 0x32, 0x30, 0x32, 0x35, 0x31, 0x32, 0x30, 0x33, 0x00, 0x41, 0x2c, 0x00, + 0x00, 0x00, 0x61, 0x65, 0x61, 0x62, 0x69, 0x00, 0x01, 0x22, 0x00, 0x00, + 0x00, 0x05, 0x37, 0x2d, 0x4d, 0x00, 0x06, 0x0a, 0x07, 0x4d, 0x09, 0x02, + 0x12, 0x04, 0x14, 0x01, 0x15, 0x01, 0x17, 0x03, 0x18, 0x01, 0x19, 0x01, + 0x1a, 0x01, 0x1e, 0x04, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb4, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xf1, 0xff, 0x0b, 0x00, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xf1, 0xff, 0x11, 0x00, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf1, 0xff, 0x1a, 0x00, 0x00, 0x00, + 0xb4, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x94, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0xbc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xd5, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x6c, 0x61, 0x7a, 0x79, 0x6d, 0x6f, 0x64, 0x2e, 0x63, 0x00, 0x24, + 0x74, 0x00, 0x24, 0x64, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, + 0x43, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, + 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, + 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, + 0x44, 0x5f, 0x5f, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x5f, 0x52, + 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, + 0x5f, 0x00, 0x73, 0x79, 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x61, 0x74, 0x6f, + 0x69, 0x00, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, + 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, + 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, + 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, + 0x74, 0x72, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x2e, 0x70, 0x6c, 0x74, 0x00, + 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x00, 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, 0x70, 0x00, 0x2e, + 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, + 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 0x41, + 0x52, 0x4d, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, + 0x74, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x84, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, 0xd4, 0x01, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x00, + 0xfc, 0x01, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x38, 0x02, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x3c, 0x12, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xb4, 0x12, 0x00, 0x00, + 0xb4, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x03, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x05, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x05, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_lazymod_len = 2192; diff --git a/examples/fdpicxip/libcounter_bin.h b/examples/fdpicxip/libcounter_bin.h new file mode 100644 index 000000000..9ef92cae7 --- /dev/null +++ b/examples/fdpicxip/libcounter_bin.h @@ -0,0 +1,208 @@ +/**************************************************************************** + * apps/examples/fdpicxip/libcounter_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from libcounter.so -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_libcounter[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0xac, 0x05, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, + 0xf8, 0x11, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd1, 0x01, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0xe5, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x05, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x00, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x75, 0x6d, 0x70, 0x00, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, + 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, + 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, + 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x73, 0x6f, 0x00, 0x00, + 0x8c, 0x12, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x4b, 0x59, 0xf8, + 0x03, 0x30, 0x1a, 0x68, 0x10, 0x44, 0x18, 0x60, 0x70, 0x47, 0x00, 0xbf, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x4b, 0x59, 0xf8, 0x03, 0x30, 0x18, 0x68, + 0x70, 0x47, 0x00, 0xbf, 0x0c, 0x00, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xff, 0xff, 0x6f, + 0x01, 0x00, 0x00, 0x00, 0xfa, 0xff, 0xff, 0x6f, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, + 0x20, 0x28, 0x41, 0x72, 0x6d, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x54, 0x6f, + 0x6f, 0x6c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x32, + 0x2e, 0x52, 0x65, 0x6c, 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x20, 0x61, 0x72, 0x6d, 0x2d, 0x31, 0x35, 0x2e, 0x38, 0x36, 0x29, 0x29, + 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x32, 0x35, + 0x31, 0x32, 0x30, 0x33, 0x00, 0x41, 0x2c, 0x00, 0x00, 0x00, 0x61, 0x65, + 0x61, 0x62, 0x69, 0x00, 0x01, 0x22, 0x00, 0x00, 0x00, 0x05, 0x37, 0x2d, + 0x4d, 0x00, 0x06, 0x0a, 0x07, 0x4d, 0x09, 0x02, 0x12, 0x04, 0x14, 0x01, + 0x15, 0x01, 0x17, 0x03, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1e, 0x04, + 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x0e, 0x00, 0x00, 0x00, + 0xd0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x14, 0x00, 0x00, 0x00, 0x90, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x90, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0xf1, 0xff, 0x26, 0x00, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf1, 0xff, 0x2f, 0x00, 0x00, 0x00, + 0x80, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x00, 0xd1, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x05, 0x00, 0x52, 0x00, 0x00, 0x00, 0xe5, 0x01, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x05, 0x00, 0x60, 0x00, 0x00, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, + 0x70, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x06, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x65, 0x72, 0x2e, 0x63, 0x00, 0x24, 0x74, 0x00, 0x24, 0x64, 0x00, + 0x2e, 0x4c, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x30, 0x00, 0x67, 0x5f, + 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, + 0x49, 0x43, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, + 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, + 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x75, 0x6d, + 0x70, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, + 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, + 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, + 0x00, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, + 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, + 0x74, 0x61, 0x62, 0x00, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, + 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, + 0x72, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, + 0x70, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, + 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 0x41, 0x52, 0x4d, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, + 0xec, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x7c, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, + 0xd0, 0x01, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xf4, 0x01, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xf8, 0x11, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x12, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x90, 0x12, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd5, 0x02, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x04, 0x00, 0x00, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x35, 0x05, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_libcounter_len = 2052; diff --git a/examples/fdpicxip/libshape_bin.h b/examples/fdpicxip/libshape_bin.h new file mode 100644 index 000000000..178153308 --- /dev/null +++ b/examples/fdpicxip/libshape_bin.h @@ -0,0 +1,389 @@ +/**************************************************************************** + * apps/examples/fdpicxip/libshape_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from libshape.so -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_libshape[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x88, 0x0d, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x13, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00, + 0xbc, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00, 0xbc, 0x15, 0x00, 0x00, + 0xbc, 0x15, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xc4, 0x05, 0x00, 0x00, 0xc4, 0x15, 0x00, 0x00, 0xc4, 0x15, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x74, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc4, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0d, 0x00, 0x3d, 0x00, 0x00, 0x00, 0xc5, 0x04, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x06, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0x6d, 0x04, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc5, 0x04, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x06, 0x00, 0x6d, 0x00, 0x00, 0x00, + 0x99, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x79, 0x00, 0x00, 0x00, 0xa9, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x8b, 0x00, 0x00, 0x00, + 0xb8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x85, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5a, 0x4e, 0x38, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x44, 0x32, 0x45, 0x76, 0x00, 0x73, 0x79, + 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x73, 0x6e, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x66, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x66, 0x74, 0x72, 0x75, 0x6e, + 0x63, 0x61, 0x74, 0x65, 0x00, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x00, 0x5f, 0x5a, 0x4e, 0x38, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x44, 0x31, 0x45, 0x76, 0x00, 0x73, 0x68, + 0x61, 0x70, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x00, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x00, 0x73, 0x74, 0x72, + 0x6c, 0x63, 0x70, 0x79, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x65, 0x64, 0x00, 0x5f, + 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, + 0x54, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, + 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x73, + 0x68, 0x61, 0x70, 0x65, 0x2e, 0x73, 0x6f, 0x00, 0xbc, 0x15, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0xc0, 0x15, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0xc0, 0x16, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x80, 0x16, 0x00, 0x00, + 0xa4, 0x07, 0x00, 0x00, 0x88, 0x16, 0x00, 0x00, 0xa4, 0x09, 0x00, 0x00, + 0x90, 0x16, 0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0x98, 0x16, 0x00, 0x00, + 0xa4, 0x0e, 0x00, 0x00, 0xa0, 0x16, 0x00, 0x00, 0xa4, 0x0f, 0x00, 0x00, + 0xa8, 0x16, 0x00, 0x00, 0xa4, 0x12, 0x00, 0x00, 0xb0, 0x16, 0x00, 0x00, + 0xa4, 0x13, 0x00, 0x00, 0xb8, 0x16, 0x00, 0x00, 0xa4, 0x15, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x0c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x14, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x1c, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x24, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x2c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x34, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x44, 0x00, 0x00, 0x00, 0x10, 0xb5, 0x03, 0x4b, 0x59, 0xf8, 0x03, 0x00, + 0xff, 0xf7, 0xaa, 0xff, 0x10, 0xbd, 0x00, 0xbf, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x22, 0x07, 0x4b, 0x05, 0x49, 0x59, 0xf8, 0x03, 0x30, 0x06, 0x20, + 0xc3, 0xe9, 0x00, 0x12, 0x04, 0x49, 0x9a, 0x60, 0x79, 0x44, 0x1a, 0x73, + 0xff, 0xf7, 0xca, 0xbf, 0xe0, 0xa9, 0x5b, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x3e, 0x01, 0x00, 0x00, 0x04, 0x4b, 0x59, 0xf8, 0x03, 0x30, 0x9a, 0x68, + 0x01, 0x32, 0x9a, 0x60, 0x5a, 0x68, 0x10, 0x44, 0x58, 0x60, 0x70, 0x47, + 0x4c, 0x00, 0x00, 0x00, 0x03, 0x4b, 0x01, 0x46, 0x59, 0xf8, 0x03, 0x00, + 0x40, 0x22, 0x0c, 0x30, 0xff, 0xf7, 0xa6, 0xbf, 0x4c, 0x00, 0x00, 0x00, + 0x02, 0x4b, 0x59, 0xf8, 0x03, 0x30, 0x58, 0x68, 0x70, 0x47, 0x00, 0xbf, + 0x4c, 0x00, 0x00, 0x00, 0x05, 0x4b, 0x59, 0xf8, 0x03, 0x30, 0x18, 0x68, + 0x02, 0x4b, 0xc3, 0x1a, 0x58, 0x42, 0x58, 0x41, 0x70, 0x47, 0x00, 0xbf, + 0xe0, 0xa9, 0x5b, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x2d, 0xe9, 0xf0, 0x45, + 0x1d, 0x49, 0x05, 0x46, 0x82, 0x68, 0x85, 0xb0, 0x06, 0x20, 0x79, 0x44, + 0x4c, 0x46, 0xff, 0xf7, 0x8d, 0xff, 0x2b, 0x7b, 0xa1, 0x46, 0x1b, 0xb3, + 0x18, 0x4a, 0x10, 0x21, 0x6b, 0x68, 0x7a, 0x44, 0x68, 0x46, 0xff, 0xf7, + 0x5b, 0xff, 0x05, 0xf1, 0x0c, 0x07, 0xa1, 0x46, 0x82, 0x46, 0x4f, 0xf4, + 0xd2, 0x72, 0x40, 0xf2, 0x41, 0x21, 0x38, 0x46, 0xff, 0xf7, 0x82, 0xff, + 0x06, 0x1e, 0xa1, 0x46, 0x12, 0xdb, 0x51, 0x46, 0xff, 0xf7, 0x54, 0xff, + 0x00, 0x28, 0xa1, 0x46, 0x05, 0xdb, 0x52, 0x46, 0x69, 0x46, 0x30, 0x46, + 0xff, 0xf7, 0x56, 0xff, 0xa1, 0x46, 0x30, 0x46, 0xff, 0xf7, 0x7a, 0xff, + 0x28, 0x46, 0x05, 0xb0, 0xbd, 0xe8, 0xf0, 0x85, 0x05, 0x49, 0x3a, 0x46, + 0x06, 0x20, 0x79, 0x44, 0xff, 0xf7, 0x5c, 0xff, 0xf4, 0xe7, 0x00, 0xbf, + 0x76, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, + 0x20, 0x20, 0x5b, 0x6c, 0x69, 0x62, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5d, + 0x20, 0x7e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x28, 0x29, + 0x20, 0x72, 0x61, 0x6e, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x25, + 0x64, 0x20, 0x61, 0x64, 0x64, 0x73, 0x0a, 0x00, 0x25, 0x64, 0x00, 0x20, + 0x20, 0x5b, 0x6c, 0x69, 0x62, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5d, 0x20, + 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x00, 0x20, 0x20, 0x5b, 0x6c, 0x69, 0x62, + 0x73, 0x68, 0x61, 0x70, 0x65, 0x5d, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x28, 0x29, 0x20, 0x72, 0x61, 0x6e, 0x0a, 0x00, 0x00, + 0x74, 0x16, 0x00, 0x00, 0x45, 0x04, 0x00, 0x00, 0x31, 0x04, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0xbc, 0x15, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0xc0, 0x15, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x74, 0x16, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfb, 0xff, 0xff, 0x6f, 0x01, 0x00, 0x00, 0x00, 0xfa, 0xff, 0xff, 0x6f, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0x16, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x41, 0x72, + 0x6d, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x54, 0x6f, 0x6f, 0x6c, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x52, 0x65, 0x6c, + 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x61, 0x72, 0x6d, + 0x2d, 0x31, 0x35, 0x2e, 0x38, 0x36, 0x29, 0x29, 0x20, 0x31, 0x35, 0x2e, + 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x32, 0x35, 0x31, 0x32, 0x30, 0x33, + 0x00, 0x41, 0x2c, 0x00, 0x00, 0x00, 0x61, 0x65, 0x61, 0x62, 0x69, 0x00, + 0x01, 0x22, 0x00, 0x00, 0x00, 0x05, 0x37, 0x2d, 0x4d, 0x00, 0x06, 0x0a, + 0x07, 0x4d, 0x09, 0x02, 0x12, 0x04, 0x14, 0x01, 0x15, 0x01, 0x17, 0x03, + 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1e, 0x04, 0x22, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x74, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0f, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xc4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6c, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x98, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xbc, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x44, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x45, 0x04, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xbc, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x31, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc0, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xc4, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0xc4, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00, 0x00, 0x00, 0xc4, 0x16, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, + 0x59, 0x00, 0x00, 0x00, 0xc4, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xf1, 0xff, 0x62, 0x00, 0x00, 0x00, 0x74, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x4c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xb4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xc8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xcc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x2c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x78, 0x00, 0x00, 0x00, 0xc5, 0x04, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x06, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x6d, 0x04, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0xc5, 0x04, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x06, 0x00, 0xad, 0x00, 0x00, 0x00, + 0x99, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0xb9, 0x00, 0x00, 0x00, 0xa9, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x06, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, 0xbc, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0xf3, 0x00, 0x00, 0x00, + 0xb8, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, + 0x85, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x1d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x24, 0x74, 0x00, 0x24, 0x64, 0x00, 0x5f, + 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x5f, 0x73, 0x75, 0x62, 0x5f, + 0x49, 0x5f, 0x6c, 0x69, 0x62, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x63, + 0x70, 0x70, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x5f, + 0x73, 0x75, 0x62, 0x5f, 0x44, 0x5f, 0x6c, 0x69, 0x62, 0x73, 0x68, 0x61, + 0x70, 0x65, 0x2e, 0x63, 0x70, 0x70, 0x00, 0x2e, 0x4c, 0x41, 0x4e, 0x43, + 0x48, 0x4f, 0x52, 0x30, 0x00, 0x5f, 0x5a, 0x4c, 0x31, 0x30, 0x67, 0x5f, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x00, 0x5f, 0x44, 0x59, + 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, + 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x41, 0x42, + 0x4c, 0x45, 0x5f, 0x00, 0x5f, 0x5a, 0x4e, 0x38, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x44, 0x31, 0x45, 0x76, 0x00, 0x73, 0x68, 0x61, + 0x70, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x00, 0x73, 0x6e, 0x70, 0x72, 0x69, + 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5a, 0x4e, 0x38, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x44, 0x32, 0x45, 0x76, 0x00, 0x73, 0x68, 0x61, + 0x70, 0x65, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x73, 0x68, 0x61, + 0x70, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x65, 0x64, 0x00, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, + 0x00, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x73, 0x74, 0x72, 0x6c, 0x63, + 0x70, 0x79, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, + 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, + 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, + 0x73, 0x79, 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x00, + 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, + 0x00, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x00, 0x00, 0x2e, 0x73, 0x79, 0x6d, + 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, + 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, + 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x72, 0x65, 0x6c, + 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x70, 0x6c, 0x74, 0x00, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, + 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, 0x70, 0x00, 0x2e, 0x69, 0x6e, + 0x69, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x2e, 0x66, 0x69, + 0x6e, 0x69, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x2e, 0x64, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, 0x2e, + 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, + 0x00, 0x2e, 0x41, 0x52, 0x4d, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, + 0x60, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x38, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, + 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, + 0x30, 0x04, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x4c, 0x05, 0x00, 0x00, 0x4c, 0x05, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xb8, 0x05, 0x00, 0x00, 0xb8, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xbc, 0x15, 0x00, 0x00, + 0xbc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xc0, 0x15, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xc4, 0x15, 0x00, 0x00, 0xc4, 0x05, 0x00, 0x00, + 0xb0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x16, 0x00, 0x00, + 0x74, 0x06, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xc4, 0x16, 0x00, 0x00, 0xc4, 0x06, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x06, 0x00, 0x00, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x07, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x07, 0x00, 0x00, 0x90, 0x04, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x0b, 0x00, 0x00, + 0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xeb, 0x0c, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_libshape_len = 4224; diff --git a/examples/fdpicxip/modules/.gitignore b/examples/fdpicxip/modules/.gitignore new file mode 100644 index 000000000..8c3555579 --- /dev/null +++ b/examples/fdpicxip/modules/.gitignore @@ -0,0 +1,5 @@ +/*.fdpic +/*.so +/.nuttx-exports +/manyneeded.c +/need*.c diff --git a/examples/fdpicxip/modules/Makefile b/examples/fdpicxip/modules/Makefile new file mode 100644 index 000000000..56dfd7ebb --- /dev/null +++ b/examples/fdpicxip/modules/Makefile @@ -0,0 +1,205 @@ +############################################################################ +# apps/examples/fdpicxip/modules/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +# The FDPIC modules that examples/fdpicxip and testing/fs/xipfs carry inside +# their images, and the sources they are built from. +# +# This is NOT part of the application build. Nothing here runs unless it is +# asked for: the *_bin.h headers are committed, so both apps build with a +# plain toolchain and CI covers them, while the link needs +# arm-uclinuxfdpiceabi binutils, which the tree does not require. See +# nuttx/tools/fdpic/README.md and Documentation/components/fdpic.rst. +# +# To rebuild the headers from these sources: +# +# make -C apps/examples/fdpicxip/modules regen NUTTX_DIR=/path/to/nuttx +# +# NUTTX_DIR must be a configured, built tree with CONFIG_FDPIC and a system +# symbol table: the compile needs its headers and the verify step needs +# libs/libc/exec_symtab.c. +# +# CPU is cortex-m3 rather than the toolchain default, deliberately. A v7-M +# module runs on both the v7-M and v8-M targets, so one set of blobs serves +# the RP2350 (Cortex-M33) and mps2-an500 (Cortex-M7) alike. Building for +# cortex-m33 produces blobs the M7 cannot execute at all. + +CPU ?= cortex-m3 + +FDPICDIR = $(NUTTX_DIR)/tools/fdpic +MODULE_MK = $(FDPICDIR)/nuttx-fdpic.mk +EMBED = $(FDPICDIR)/fdpic-embed + +# Where each generated header goes, and its path from the repository root -- +# fdpic-embed puts that on line 2, which is what nxstyle wants. + +DEMODIR = .. +DEMOREL = apps/examples/fdpicxip +TESTDIR = ../../../testing/fs/xipfs +TESTREL = apps/testing/fs/xipfs + +BUILD = $(MAKE) -f $(MODULE_MK) NUTTX_DIR=$(NUTTX_DIR) CPU=$(CPU) + +# manyneeded needs one library per DT_NEEDED entry, one more than the loader +# will follow. They exist only to make the linker record nine entries; the +# module is refused while its dynamic section is parsed, before any of them +# would be loaded, so none of them is embedded. + +NEEDLIBS := $(foreach n,0 1 2 3 4 5 6 7 8,need$(n).so) + +DEMO_HDRS = $(DEMODIR)/qsorter_bin.h $(DEMODIR)/libcounter_bin.h \ + $(DEMODIR)/user_bin.h $(DEMODIR)/libshape_bin.h \ + $(DEMODIR)/cxxuser_bin.h $(DEMODIR)/lazymod_bin.h + +TEST_HDRS = $(TESTDIR)/callback_bin.h $(TESTDIR)/counteruser_bin.h \ + $(TESTDIR)/cxxuser_bin.h $(TESTDIR)/funcdesc_bin.h \ + $(TESTDIR)/lazymod_bin.h $(TESTDIR)/libcounter_bin.h \ + $(TESTDIR)/libshape_bin.h $(TESTDIR)/manyneeded_bin.h \ + $(TESTDIR)/missingsym_bin.h + +.PHONY: all regen check-nuttx-dir clean + +all: + @echo "Nothing to do: the *_bin.h headers are committed." + @echo "To rebuild them: make regen NUTTX_DIR=/path/to/nuttx" + +regen: check-nuttx-dir $(DEMO_HDRS) $(TEST_HDRS) + @echo "Regenerated the embedded modules for $(DEMOREL) and $(TESTREL)." + +check-nuttx-dir: +ifeq ($(NUTTX_DIR),) + $(error Set NUTTX_DIR to a configured, built NuttX tree) +endif + +# The modules. Libraries have no entry point and are named by their SONAME, +# which is what a consumer records in DT_NEEDED and what the loader searches +# for at run time. + +qsorter.fdpic: qsorter.c + $(BUILD) MODULE=qsorter SRCS=qsorter.c + +callback.fdpic: callback.c + $(BUILD) MODULE=callback SRCS=callback.c + +funcdesc.fdpic: funcdesc.c + $(BUILD) MODULE=funcdesc SRCS=funcdesc.c + +libcounter.so: libcounter.c + $(BUILD) MODULE=libcounter SRCS=libcounter.c ENTRY=0 \ + EXTRA_LDFLAGS="-soname libcounter.so" + mv libcounter.fdpic libcounter.so + +user.fdpic: user.c libcounter.so + $(BUILD) MODULE=user SRCS=user.c LIBS=libcounter.so + +# C++ compiles with the stock arm-none-eabi-g++; only the link is FDPIC. + +libshape.so: libshape.cpp + $(BUILD) MODULE=libshape CXXSRCS=libshape.cpp ENTRY=0 \ + EXTRA_LDFLAGS="-soname libshape.so" + mv libshape.fdpic libshape.so + +cxxuser.fdpic: cxxuser.cpp libshape.so + $(BUILD) MODULE=cxxuser CXXSRCS=cxxuser.cpp LIBS=libshape.so + +# BINDNOW is emptied so the imported descriptors stay in the lazy binding +# table, which is the case this module exists to cover. + +lazymod.fdpic: lazymod.c + $(BUILD) MODULE=lazymod SRCS=lazymod.c BINDNOW= + +# missingsym is linked with the bare .fdpic target rather than the default +# 'verify' one, because it is exactly what fdpic-verify is meant to catch. + +missingsym.fdpic: missingsym.c + $(BUILD) MODULE=missingsym SRCS=missingsym.c missingsym.fdpic + +need%.so: + echo "int need_leaf_$*(void){return $*;}" > need$*.c + $(BUILD) MODULE=need$* SRCS=need$*.c ENTRY=0 \ + EXTRA_LDFLAGS="-soname need$*.so" + mv need$*.fdpic need$*.so + +manyneeded.c: $(NEEDLIBS) + { for n in 0 1 2 3 4 5 6 7 8; do \ + echo "extern int need_leaf_$$n(void);"; done; \ + echo "int main(int argc, char *argv[]) {"; \ + echo " return need_leaf_0() + need_leaf_1() + need_leaf_2() +"; \ + echo " need_leaf_3() + need_leaf_4() + need_leaf_5() +"; \ + echo " need_leaf_6() + need_leaf_7() + need_leaf_8();"; \ + echo "}"; } > manyneeded.c + +manyneeded.fdpic: manyneeded.c $(NEEDLIBS) + $(BUILD) MODULE=manyneeded SRCS=manyneeded.c LIBS="$(NEEDLIBS)" \ + manyneeded.fdpic + +# The headers. One artifact can be embedded under more than one name: the +# demo's user_bin.h and the suite's counteruser_bin.h are the same module. + +$(DEMODIR)/qsorter_bin.h: qsorter.fdpic + $(EMBED) $< g_qsorter_nxf $(DEMOREL)/$(@F) > $@ + +$(DEMODIR)/libcounter_bin.h: libcounter.so + $(EMBED) $< g_libcounter $(DEMOREL)/$(@F) > $@ + +$(DEMODIR)/user_bin.h: user.fdpic + $(EMBED) $< g_user $(DEMOREL)/$(@F) > $@ + +$(DEMODIR)/libshape_bin.h: libshape.so + $(EMBED) $< g_libshape $(DEMOREL)/$(@F) > $@ + +$(DEMODIR)/cxxuser_bin.h: cxxuser.fdpic + $(EMBED) $< g_cxxuser $(DEMOREL)/$(@F) > $@ + +$(DEMODIR)/lazymod_bin.h: lazymod.fdpic + $(EMBED) $< g_lazymod $(DEMOREL)/$(@F) > $@ + +$(TESTDIR)/callback_bin.h: callback.fdpic + $(EMBED) $< g_callback $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/counteruser_bin.h: user.fdpic + $(EMBED) $< g_counteruser $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/cxxuser_bin.h: cxxuser.fdpic + $(EMBED) $< g_cxxuser $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/funcdesc_bin.h: funcdesc.fdpic + $(EMBED) $< g_funcdesc $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/lazymod_bin.h: lazymod.fdpic + $(EMBED) $< g_lazymod $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/libcounter_bin.h: libcounter.so + $(EMBED) $< g_libcounter $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/libshape_bin.h: libshape.so + $(EMBED) $< g_libshape $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/manyneeded_bin.h: manyneeded.fdpic + $(EMBED) $< g_manyneeded $(TESTREL)/$(@F) > $@ + +$(TESTDIR)/missingsym_bin.h: missingsym.fdpic + $(EMBED) $< g_missingsym $(TESTREL)/$(@F) > $@ + +# No NUTTX_DIR needed to clean. + +clean: + $(Q) rm -f *.o *.fdpic *.so .nuttx-exports need*.c manyneeded.c diff --git a/examples/fdpicxip/modules/callback.c b/examples/fdpicxip/modules/callback.c new file mode 100644 index 000000000..1a1b33325 --- /dev/null +++ b/examples/fdpicxip/modules/callback.c @@ -0,0 +1,517 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/callback.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Hands a module function to the firmware every way the firmware will + * accept one. + * + * In FDPIC a function pointer is the address of a two-word descriptor, not + * a code address, and that descriptor lives in the module's writable + * segment in RAM. Firmware that stores such a pointer and later branches to + * it therefore jumps into the module's *data* and faults -- unless the entry + * point it came through resolved the descriptor first. + * + * qsort and bsearch have always done that. pthread_create, signal and + * task_create did not, which made them look usable and then take the board + * down; pthread_once, task_spawn, scandir's filter and sigaction followed + * later. This module is what keeps all of them honest. + * + * scandir is one worth noting: its filter is called by scandir itself and + * must be resolved there, while its comparison function is handed to qsort, + * which resolves it. Resolving both would be a bug, so both are passed here + * at once. + * + * mq_notify and timer_create with SIGEV_THREAD are the interesting ones: + * their callback runs on a work-queue worker that carries no module data + * base. The base is captured at registration and installed around the call, + * so the callback still reaches the module's own globals. Each is checked + * by having the callback write a distinctive value into a module global. + * + * Everything is checked by observing a side effect the callback itself + * produces, because a callback that never runs is the failure being tested + * for -- and on this target the alternative failure is a HardFault, which + * reports nothing at all. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FAIL_QSORT 0x01 +#define FAIL_PTHREAD 0x02 +#define FAIL_SIGNAL 0x04 +#define FAIL_TASK 0x08 +#define FAIL_ONCE 0x10 +#define FAIL_SPAWN 0x20 +#define FAIL_SCANDIR 0x40 +#define FAIL_SIGACTION 0x80 + +/* The mq checks run under "callback mq" and report in their own byte, so the + * exit status -- only eight bits survive waitpid -- never has to hold more + * than eight flags at once. + */ + +#define FAIL_MQ_THREAD 0x01 +#define FAIL_MQ_SIGNAL 0x02 +#define FAIL_TIMER_THREAD 0x04 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Written by the callbacks, read by main. These live in the module's + * writable segment, which is what the callback can only reach if it was + * entered with this module's data base in the FDPIC register. + */ + +static volatile int g_qsort_calls; +static volatile int g_thread_ran; +static volatile int g_signal_ran; +static volatile int g_task_ran; +static volatile int g_once_ran; +static volatile int g_spawn_ran; +static volatile int g_filter_calls; +static volatile int g_compar_calls; +static volatile int g_sigaction_ran; +static volatile int g_mq_signal_ran; +static volatile int g_mq_thread_ran; +static volatile int g_timer_thread_ran; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int cmp_int(const void *a, const void *b) +{ + g_qsort_calls++; + return *(const int *)a - *(const int *)b; +} + +static void *thread_main(void *arg) +{ + g_thread_ran = (int)(intptr_t)arg; + return NULL; +} + +static void sig_handler(int signo) +{ + g_signal_ran = signo; +} + +static void sigaction_handler(int signo, siginfo_t *info, void *context) +{ + g_sigaction_ran = signo; +} + +static void mq_signal_handler(int signo, siginfo_t *info, void *context) +{ + g_mq_signal_ran = signo; +} + +static void mq_thread_notify(union sigval value) +{ + /* Runs on a work-queue worker, which carries no module data base of its + * own. Writing this module global proves the base was installed around + * the call -- without it this store would fault or land nowhere. + */ + + g_mq_thread_ran = value.sival_int; +} + +static void timer_thread_notify(union sigval value) +{ + g_timer_thread_ran = value.sival_int; +} + +static int task_main(int argc, char *argv[]) +{ + g_task_ran = 1; + return 0; +} + +static void once_routine(void) +{ + g_once_ran++; +} + +static int spawn_main(int argc, char *argv[]) +{ + g_spawn_ran = 1; + return 0; +} + +static int dir_filter(const struct dirent *entry) +{ + g_filter_calls++; + return 1; +} + +static int dir_compar(const struct dirent **a, const struct dirent **b) +{ + g_compar_calls++; + return strcmp((*a)->d_name, (*b)->d_name); +} + +/* The message-queue checks run as a second invocation, "callback mq", so + * their two flags report in a byte of their own rather than overflowing the + * eight-bit exit status the other checks already fill. + */ + +static int run_mq(void) +{ + struct mq_attr attr; + struct sigaction sa; + struct sigevent ev; + struct itimerspec its; + timer_t timer; + mqd_t mqd; + int fails = 0; + int i; + + attr.mq_maxmsg = 1; + attr.mq_msgsize = 1; + attr.mq_flags = 0; + + mqd = mq_open("/cbmq", O_CREAT | O_RDWR, 0644, &attr); + if (mqd == (mqd_t)-1) + { + return FAIL_MQ_THREAD | FAIL_MQ_SIGNAL | FAIL_TIMER_THREAD; + } + + /* mq_notify with SIGEV_THREAD. The callback runs on a work-queue worker + * that carries no module data base of its own; the base captured at + * registration is installed around the call, so the callback reaches this + * module's globals. A distinctive sival marks that the right callback + * ran with the right argument. + */ + + memset(&ev, 0, sizeof(ev)); + ev.sigev_notify = SIGEV_THREAD; + ev.sigev_notify_function = mq_thread_notify; + ev.sigev_value.sival_int = 0x5a; + + if (mq_notify(mqd, &ev) < 0) + { + fails |= FAIL_MQ_THREAD; + } + else + { + char buf[1]; + + /* The notification fires on a send to an empty queue; drain the + * message afterwards so the queue is empty for the next test and the + * next send does not block on a full queue. + */ + + mq_send(mqd, "x", 1, 0); + + for (i = 0; i < 50 && g_mq_thread_ran == 0; i++) + { + usleep(10000); + } + + mq_receive(mqd, buf, sizeof(buf), NULL); + + if (g_mq_thread_ran != 0x5a) + { + fails |= FAIL_MQ_THREAD; + } + } + + /* mq_notify with SIGEV_SIGNAL, delivered to this task and caught by a + * handler installed through sigaction. + */ + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = mq_signal_handler; + sa.sa_flags = SA_SIGINFO; + sigaction(SIGUSR1, &sa, NULL); + + memset(&ev, 0, sizeof(ev)); + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGUSR1; + + if (mq_notify(mqd, &ev) < 0) + { + fails |= FAIL_MQ_SIGNAL; + } + else + { + char buf[1]; + + mq_send(mqd, "x", 1, 0); + + for (i = 0; i < 50 && g_mq_signal_ran == 0; i++) + { + usleep(10000); + } + + mq_receive(mqd, buf, sizeof(buf), NULL); + + if (g_mq_signal_ran != SIGUSR1) + { + fails |= FAIL_MQ_SIGNAL; + } + } + + mq_close(mqd); + mq_unlink("/cbmq"); + + /* timer_create with SIGEV_THREAD reaches the module the same way: the + * expiry callback runs on the worker with the module's base installed. + */ + + memset(&ev, 0, sizeof(ev)); + ev.sigev_notify = SIGEV_THREAD; + ev.sigev_notify_function = timer_thread_notify; + ev.sigev_value.sival_int = 0xa5; + + if (timer_create(CLOCK_MONOTONIC, &ev, &timer) < 0) + { + fails |= FAIL_TIMER_THREAD; + } + else + { + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 20 * 1000 * 1000; + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + timer_settime(timer, 0, &its, NULL); + + for (i = 0; i < 50 && g_timer_thread_ran == 0; i++) + { + usleep(10000); + } + + if (g_timer_thread_ran != 0xa5) + { + fails |= FAIL_TIMER_THREAD; + } + + timer_delete(timer); + } + + syslog(LOG_INFO, + "[callback] mq_thread=%#x mq_signal=%d timer_thread=%#x -- %s\n", + g_mq_thread_ran, g_mq_signal_ran, g_timer_thread_ran, + fails == 0 ? "PASS" : "FAIL"); + + return fails; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int v[5] = + { + 5, 3, 1, 4, 2 + }; + + struct dirent **namelist; + pthread_once_t once = PTHREAD_ONCE_INIT; + struct sigaction sa; + pthread_t tid; + int fails = 0; + int n; + int i; + + if (argc > 1 && strcmp(argv[1], "mq") == 0) + { + return run_mq(); + } + + /* qsort -- the path that always worked */ + + qsort(v, 5, sizeof(int), cmp_int); + if (g_qsort_calls == 0 || v[0] != 1 || v[4] != 5) + { + fails |= FAIL_QSORT; + } + + /* pthread_create. The thread inherits this module's D-Space, so it can + * reach g_thread_ran only if it was entered at the right address. + */ + + if (pthread_create(&tid, NULL, thread_main, (void *)(intptr_t)7) != 0) + { + fails |= FAIL_PTHREAD; + } + else + { + pthread_join(tid, NULL); + if (g_thread_ran != 7) + { + fails |= FAIL_PTHREAD; + } + } + + /* signal, delivered to this task */ + + if (signal(SIGUSR1, sig_handler) == SIG_ERR) + { + fails |= FAIL_SIGNAL; + } + else + { + /* raise() rather than kill(getpid(), ...): getpid is not in the + * firmware's export list, and raise targets the calling thread, which + * is exactly what this is checking. + */ + + raise(SIGUSR1); + + for (i = 0; i < 50 && g_signal_ran == 0; i++) + { + usleep(10000); + } + + if (g_signal_ran != SIGUSR1) + { + fails |= FAIL_SIGNAL; + } + } + + /* task_create. A separate task, but the same D-Space. */ + + if (task_create("cbtask", SCHED_PRIORITY_DEFAULT, 2048, task_main, + NULL) < 0) + { + fails |= FAIL_TASK; + } + else + { + for (i = 0; i < 50 && g_task_ran == 0; i++) + { + usleep(10000); + } + + if (g_task_ran == 0) + { + fails |= FAIL_TASK; + } + } + + /* sigaction, delivered to this task. signal() above resolves the handler + * before calling sigaction; this reaches sigaction directly, which is the + * path signal() does not cover. + */ + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigaction_handler; + sa.sa_flags = SA_SIGINFO; + + if (sigaction(SIGUSR2, &sa, NULL) < 0) + { + fails |= FAIL_SIGACTION; + } + else + { + raise(SIGUSR2); + + for (i = 0; i < 50 && g_sigaction_ran == 0; i++) + { + usleep(10000); + } + + if (g_sigaction_ran != SIGUSR2) + { + fails |= FAIL_SIGACTION; + } + } + + /* pthread_once. Runs on this thread, so only the code address matters. */ + + if (pthread_once(&once, once_routine) != 0 || g_once_ran != 1) + { + fails |= FAIL_ONCE; + } + + /* task_spawn. Like task_create, a separate task with the same D-Space. */ + + if (task_spawn("cbspawn", spawn_main, NULL, NULL, NULL, NULL) < 0) + { + fails |= FAIL_SPAWN; + } + else + { + for (i = 0; i < 50 && g_spawn_ran == 0; i++) + { + usleep(10000); + } + + if (g_spawn_ran == 0) + { + fails |= FAIL_SPAWN; + } + } + + /* scandir takes two callbacks that reach the module by different routes: + * the filter is called by scandir itself, while the comparison function is + * handed to qsort. Passing both at once is the point -- it is what would + * catch the comparison function being resolved twice. + */ + + namelist = NULL; + n = scandir("/mnt/xipfs", &namelist, dir_filter, dir_compar); + if (n < 0 || g_filter_calls == 0) + { + fails |= FAIL_SCANDIR; + } + + if (namelist != NULL) + { + while (n-- > 0) + { + free(namelist[n]); + } + + free(namelist); + } + + syslog(LOG_INFO, + "[callback] qsort=%d pthread=%d signal=%d task=%d sigaction=%d " + "once=%d spawn=%d filter=%d compar=%d -- %s\n", + g_qsort_calls, g_thread_ran, g_signal_ran, g_task_ran, + g_sigaction_ran, g_once_ran, g_spawn_ran, g_filter_calls, + g_compar_calls, fails == 0 ? "PASS" : "FAIL"); + + return fails; +} diff --git a/examples/fdpicxip/modules/cxxuser.cpp b/examples/fdpicxip/modules/cxxuser.cpp new file mode 100644 index 000000000..5c27cf575 --- /dev/null +++ b/examples/fdpicxip/modules/cxxuser.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/cxxuser.cpp + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* A C++ module using a C++ shared library. + * + * Two instances of this run at once in the demo. Between them they show + * the three things that have to hold: + * + * - constructors ran, in both objects. Neither magic can be right by + * accident: an unconstructed global is zero. + * - the library's constructors ran before this module's, which is the + * ordering DT_NEEDED implies and the loader has to honour. + * - the library's data is private per instance. One copy of its code + * sits in flash; the totals do not interleave. + */ + +#include +#include +#include +#include +#include + +extern "C" +{ + int shape_add(int by); + int shape_total(void); + bool shape_constructed(void); + void shape_marker(const char *path); +} + +#define USER_MAGIC 0x0c1a55 + +class Instance +{ +public: + Instance() : m_magic(USER_MAGIC), m_libwasready(shape_constructed()) + { + } + + bool constructed() const + { + return m_magic == USER_MAGIC; + } + + /* Whether the library was already constructed when this object's own + * constructor ran. Sampling it here is the only way to observe the + * ordering: by the time main() runs, both have been constructed either + * way. + */ + + bool libwasready() const + { + return m_libwasready; + } + +private: + int m_magic; + bool m_libwasready; +}; + +static Instance g_self; + +/* Exit status is a bitmask of what failed, so one run reports on all four + * properties independently. Kept in step with the reader in + * apps/testing/fs/xipfs/. + */ + +#define USER_FAIL_OWN_CTOR 0x01 +#define USER_FAIL_LIB_CTOR 0x02 +#define USER_FAIL_ORDER 0x04 +#define USER_FAIL_PRIVATE 0x08 + +/**************************************************************************** + * Name: record + * + * Description: + * Write a number to a file, for a caller that cannot rely on this task's + * exit status. + * + * It cannot, in general. waitpid() only retains the status of a child + * that has already exited when CONFIG_SCHED_CHILD_STATUS is set, and that + * depends on CONFIG_SCHED_HAVE_PARENT. With two instances running for + * the same length of time, the second has always exited by the time the + * first waitpid() returns, so its status is simply gone. A file survives + * the task, and survives the module being unloaded. + * + ****************************************************************************/ + +static void record(const char *path, int value) +{ + char text[16]; + int len; + int fd; + + if (path == NULL) + { + return; + } + + len = snprintf(text, sizeof(text), "%d", value); + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + { + syslog(LOG_INFO, "[user] cannot record to %s\n", path); + return; + } + + /* xipfs wants the size declared up front so it can reserve one + * contiguous extent. + */ + + if (ftruncate(fd, len) >= 0) + { + write(fd, text, len); + } + + close(fd); +} + +extern "C" int main(int argc, char *argv[]) +{ + int seed = (argc > 1) ? atoi(argv[1]) : 1; + int fails = 0; + int i; + + /* Optional second argument: a path for the library's destructor to record + * its final total in. A destructor runs at unload, after this task is + * gone, so leaving a file behind is the only way a test can observe that + * it ran at all. The demo omits it and reads the log instead. + */ + + if (argc > 2) + { + shape_marker(argv[2]); + } + + syslog(LOG_INFO, "[user %d] own ctor ran: %s, library ctor ran: %s, " + "library first: %s\n", + seed, + g_self.constructed() ? "yes" : "NO", + shape_constructed() ? "yes" : "NO", + g_self.libwasready() ? "yes" : "NO"); + + /* Every check runs and contributes a bit, rather than the first failure + * returning early. A caller that reports these individually can then say + * which property broke instead of just that something did -- and none of + * its assertions pass merely because an earlier one stopped the run. + */ + + if (!g_self.constructed()) + { + fails |= USER_FAIL_OWN_CTOR; + } + + if (!shape_constructed()) + { + fails |= USER_FAIL_LIB_CTOR; + } + + if (!g_self.libwasready()) + { + fails |= USER_FAIL_ORDER; + } + + for (i = 0; i < 3; i++) + { + shape_add(seed); + usleep(100000); + } + + if (shape_total() != seed * 3) + { + fails |= USER_FAIL_PRIVATE; + } + + syslog(LOG_INFO, "[user %d] library total = %d (expected %d) -- %s\n", + seed, shape_total(), seed * 3, + fails == 0 ? "PASS" : "FAIL"); + + /* Optional third argument: where to record the bitmask, for a caller that + * cannot rely on the exit status surviving. See record() above. + */ + + if (argc > 3) + { + record(argv[3], fails); + } + + return fails; +} diff --git a/examples/fdpicxip/modules/funcdesc.c b/examples/fdpicxip/modules/funcdesc.c new file mode 100644 index 000000000..850715474 --- /dev/null +++ b/examples/fdpicxip/modules/funcdesc.c @@ -0,0 +1,156 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/funcdesc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Exercises R_ARM_FUNCDESC -- the relocation that asks the loader to + * manufacture a function descriptor and hand back its address. + * + * It is emitted for an *address-taken imported* function, which is a + * different thing from a called one. A call gets a PLT entry and an + * R_ARM_FUNCDESC_VALUE relocation on a descriptor the linker already laid + * out; taking the address instead needs a pointer to a descriptor that does + * not exist yet, so the loader carves one out of the pool it reserves + * behind the writable segment. No other module here emits one, which is + * why this exists. + * + * Every pointer here is called *through*, not merely printed: a descriptor + * with the wrong entry or the wrong GOT half only shows up when it is + * branched through. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef int (*atoifn)(const char *); +typedef void *(*memcpyfn)(void *, const void *, size_t); +typedef int (*strcmpfn)(const char *, const char *); +typedef void (*qsortfn)(void *, size_t, size_t, + int (*)(const void *, const void *)); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* volatile is load bearing, and the reason is worth knowing before anyone + * tidies it away. + * + * Without it these are static pointers that are never written, so GCC + * constant-propagates them, turns every indirect call back into a direct + * one, and deletes the variables. The descriptor address then never has to + * materialise, the linker emits R_ARM_FUNCDESC_VALUE for the direct calls + * instead, and not one R_ARM_FUNCDESC survives -- the module still builds, + * still runs, still passes, and tests nothing it claims to. + * + * volatile forces each pointer to be loaded from memory at the point of + * call, which is what makes the slot genuinely hold the address of a + * descriptor the loader has to supply. + */ + +static atoifn volatile g_atoi = atoi; +static memcpyfn volatile g_memcpy = memcpy; +static strcmpfn volatile g_strcmp = strcmp; +static qsortfn volatile g_qsort = qsort; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* A local function's address is R_ARM_FUNCDESC_VALUE, not FUNCDESC -- the + * linker can lay that descriptor out itself. Kept here so the module + * exercises both kinds side by side. + */ + +static int cmp_int(const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int v[5] = + { + 5, 3, 1, 4, 2 + }; + + int copy[5]; + char buf[8]; + int seed; + int fails = 0; + int i; + + seed = g_atoi((argc > 1) ? argv[1] : "7"); + if (seed != 7 && seed != 1 && seed != 2) + { + syslog(LOG_INFO, "[funcdesc] FAIL atoi through a descriptor: %d\n", + seed); + fails++; + } + + g_memcpy(copy, v, sizeof(v)); + for (i = 0; i < 5; i++) + { + if (copy[i] != v[i]) + { + syslog(LOG_INFO, "[funcdesc] FAIL memcpy through a descriptor\n"); + fails++; + break; + } + } + + g_memcpy(buf, "abcdef", 7); + if (g_strcmp(buf, "abcdef") != 0) + { + syslog(LOG_INFO, "[funcdesc] FAIL strcmp through a descriptor\n"); + fails++; + } + + /* A manufactured descriptor for qsort, called with a descriptor for a + * function of our own -- so the firmware calls back into module code + * that has to arrive with this module's data base in the FDPIC register. + */ + + g_qsort(copy, 5, sizeof(int), cmp_int); + for (i = 0; i < 5; i++) + { + if (copy[i] != i + 1) + { + syslog(LOG_INFO, "[funcdesc] FAIL qsort through a descriptor\n"); + fails++; + break; + } + } + + syslog(LOG_INFO, "[funcdesc] seed %d, %d failures\n", seed, fails); + return fails == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/examples/fdpicxip/modules/lazymod.c b/examples/fdpicxip/modules/lazymod.c new file mode 100644 index 000000000..40a21d6fc --- /dev/null +++ b/examples/fdpicxip/modules/lazymod.c @@ -0,0 +1,45 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/lazymod.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Linked without -z now (BINDNOW= in the makefile), so its imported function + * descriptors land in DT_JMPREL instead of DT_REL. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int seed = (argc > 1) ? atoi(argv[1]) : 1; + + syslog(LOG_INFO, "[lazymod] called through a DT_JMPREL descriptor, " + "seed %d\n", seed); + return 0; +} diff --git a/examples/fdpicxip/modules/libcounter.c b/examples/fdpicxip/modules/libcounter.c new file mode 100644 index 000000000..7d4c16677 --- /dev/null +++ b/examples/fdpicxip/modules/libcounter.c @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/libcounter.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* A shared library with its own state. + * + * g_calls is the interesting part: it lives in the library's writable + * segment, and each task that loads the library gets a private copy. The + * library's code, meanwhile, is mapped once and executed in place. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_calls; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int counter_bump(int by); +int counter_total(void); + +int counter_bump(int by) +{ + g_calls += by; + return g_calls; +} + +int counter_total(void) +{ + return g_calls; +} diff --git a/examples/fdpicxip/modules/libshape.cpp b/examples/fdpicxip/modules/libshape.cpp new file mode 100644 index 000000000..1cab840ef --- /dev/null +++ b/examples/fdpicxip/modules/libshape.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/libshape.cpp + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* A C++ shared library, executed in place out of flash. + * + * The point of this file is g_registry. It is a global object with a real + * constructor, and the constructor is the only thing that ever writes + * m_magic. If the loader does not walk DT_INIT_ARRAY, the object simply + * stays in .bss and every field reads back zero -- the library still loads, + * still links, and still runs, and quietly answers wrong. That is why the + * demo checks for a magic rather than just printing a total: the failure + * this is here to catch does not announce itself. + * + * m_total is the second half: it lives in the library's writable segment, + * which is copied once per running instance, so two tasks sharing this + * library's flash-resident code still count independently. + */ + +#include +#include +#include +#include +#include + +#define SHAPE_MAGIC 0x5ba9e0 + +class Registry +{ +public: + Registry() : m_magic(SHAPE_MAGIC), m_total(0), m_adds(0) + { + m_marker[0] = '\0'; + syslog(LOG_INFO, " [libshape] Registry() ran\n"); + } + + /* Runs last of the two objects, because destruction mirrors construction + * and this library was constructed first. So a marker written here is + * evidence that the whole DT_FINI_ARRAY chain ran, not just the module's + * half -- which is why the marker is written from the library rather than + * from the module that knows the path. + */ + + ~Registry() + { + syslog(LOG_INFO, " [libshape] ~Registry() ran after %d adds\n", m_adds); + + if (m_marker[0] != '\0') + { + char text[16]; + int len; + int fd; + + len = snprintf(text, sizeof(text), "%d", m_total); + + fd = open(m_marker, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd >= 0) + { + /* xipfs wants the size declared up front so it can reserve one + * contiguous extent. + */ + + if (ftruncate(fd, len) >= 0) + { + write(fd, text, len); + } + + close(fd); + } + else + { + syslog(LOG_INFO, " [libshape] marker %s failed\n", m_marker); + } + } + } + + /* Where ~Registry() should record its final total. Optional: the demo + * does not set one and simply logs instead. + */ + + void marker(const char *path) + { + strlcpy(m_marker, path, sizeof(m_marker)); + } + + bool constructed() const + { + return m_magic == SHAPE_MAGIC; + } + + int add(int by) + { + m_adds++; + m_total += by; + return m_total; + } + + int total() const + { + return m_total; + } + +private: + int m_magic; + int m_total; + int m_adds; + char m_marker[64]; +}; + +static Registry g_registry; + +/* C linkage, because the module imports these by name and the loader + * matches dynamic symbols as plain strings. + */ + +extern "C" +{ + int shape_add(int by); + int shape_total(void); + bool shape_constructed(void); + void shape_marker(const char *path); +} + +int shape_add(int by) +{ + return g_registry.add(by); +} + +void shape_marker(const char *path) +{ + g_registry.marker(path); +} + +int shape_total(void) +{ + return g_registry.total(); +} + +bool shape_constructed(void) +{ + return g_registry.constructed(); +} diff --git a/examples/fdpicxip/modules/missingsym.c b/examples/fdpicxip/modules/missingsym.c new file mode 100644 index 000000000..184675dde --- /dev/null +++ b/examples/fdpicxip/modules/missingsym.c @@ -0,0 +1,40 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/missingsym.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* A module that imports a firmware symbol which does not exist. It links + * cleanly -- the symbol simply becomes an undefined import resolved at load + * -- so the loader is the only thing that can catch it, when resolution + * fails rather than at link time. This is why the build runs fdpic-verify + * by default; this module is the one that deliberately does not pass it, so + * it is linked without that check. + */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +extern int __xipfs_test_nonexistent(int value); + +int main(int argc, char *argv[]) +{ + return __xipfs_test_nonexistent(argc); +} diff --git a/examples/fdpicxip/modules/qsorter.c b/examples/fdpicxip/modules/qsorter.c new file mode 100644 index 000000000..3dead3f03 --- /dev/null +++ b/examples/fdpicxip/modules/qsorter.c @@ -0,0 +1,89 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/qsorter.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Demonstrates the two things worth knowing about NuttX FDPIC modules. + * + * 1. qsort() runs in the firmware and calls back into compare(), which + * lives here. That works because the firmware reserves the FDPIC + * register, so this module's data base survives the call in, and + * because libc resolves the callback descriptor on the way back out. + * + * 2. g_data is module data. Every running instance gets a private copy, + * while the code itself is shared and executed in place from flash. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define N 8 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_data[N]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int compare(const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int seed = (argc > 1) ? atoi(argv[1]) : 0; + int i; + + for (i = 0; i < N; i++) + { + g_data[i] = (N - i) * 10 + seed; + } + + /* Overlap with any other instance, so shared data would show up as + * corruption rather than passing by luck. + */ + + usleep(300000); + + qsort(g_data, N, sizeof(int), compare); + + syslog(LOG_INFO, "[inst %d] %d %d %d %d %d %d %d %d\n", seed, + g_data[0], g_data[1], g_data[2], g_data[3], + g_data[4], g_data[5], g_data[6], g_data[7]); + return 0; +} diff --git a/examples/fdpicxip/modules/user.c b/examples/fdpicxip/modules/user.c new file mode 100644 index 000000000..bd82dbf31 --- /dev/null +++ b/examples/fdpicxip/modules/user.c @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/examples/fdpicxip/modules/user.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Uses libcounter.so. Two instances run concurrently: if the library's + * data were shared between them the totals would interleave. + * + * libcounter is also a *leaf* library -- it calls nothing outside itself, + * so it has no PLT and therefore no DT_PLTGOT. The loader has to fall back + * to PT_DYNAMIC vaddr + memsz to find its GOT. If that fallback is wrong + * the library cannot reach its own globals, which is exactly what the total + * below would expose. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +extern int counter_bump(int by); +extern int counter_total(void); + +int main(int argc, char *argv[]) +{ + int seed = (argc > 1) ? atoi(argv[1]) : 0; + int i; + + for (i = 0; i < 3; i++) + { + counter_bump(seed); + usleep(100000); + } + + syslog(LOG_INFO, "[user %d] library total = %d (expected %d) -- %s\n", + seed, counter_total(), seed * 3, + counter_total() == seed * 3 ? "PASS" : "FAIL"); + + /* Reported through the exit status as well as the log, so a test can + * assert on it rather than a human reading the console. + */ + + return counter_total() == seed * 3 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/examples/fdpicxip/qsorter_bin.h b/examples/fdpicxip/qsorter_bin.h new file mode 100644 index 000000000..6e1f39c00 --- /dev/null +++ b/examples/fdpicxip/qsorter_bin.h @@ -0,0 +1,268 @@ +/**************************************************************************** + * apps/examples/fdpicxip/qsorter_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from qsorter.fdpic -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_qsorter_nxf[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xa1, 0x02, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x28, 0x08, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x11, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, + 0x58, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x58, 0x13, 0x00, 0x00, + 0x58, 0x13, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x58, 0x03, 0x00, 0x00, 0x58, 0x13, 0x00, 0x00, 0x58, 0x13, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xa1, 0x02, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, 0x75, + 0x73, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x71, 0x73, 0x6f, 0x72, 0x74, 0x00, + 0x73, 0x79, 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, + 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, + 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, + 0x44, 0x5f, 0x5f, 0x00, 0x14, 0x14, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0xec, 0x13, 0x00, 0x00, 0xa4, 0x07, 0x00, 0x00, 0xf4, 0x13, 0x00, 0x00, + 0xa4, 0x08, 0x00, 0x00, 0xfc, 0x13, 0x00, 0x00, 0xa4, 0x0c, 0x00, 0x00, + 0x04, 0x14, 0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0x0c, 0x14, 0x00, 0x00, + 0xa4, 0x02, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x0c, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x14, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x1c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x24, 0x00, 0x00, 0x00, + 0x70, 0xb5, 0x01, 0x28, 0x4e, 0x46, 0x88, 0xb0, 0x34, 0xdd, 0x48, 0x68, + 0xff, 0xf7, 0xee, 0xff, 0xb1, 0x46, 0x1a, 0x4b, 0x00, 0xf1, 0x50, 0x05, + 0x59, 0xf8, 0x03, 0x30, 0x43, 0xf8, 0x04, 0x5b, 0x0a, 0x3d, 0x85, 0x42, + 0xfa, 0xd1, 0x14, 0x48, 0xff, 0xf7, 0xc2, 0xff, 0xb1, 0x46, 0x13, 0x4b, + 0x04, 0x22, 0x59, 0xf8, 0x03, 0x40, 0x12, 0x4b, 0x20, 0x46, 0x4b, 0x44, + 0x08, 0x21, 0xff, 0xf7, 0xc1, 0xff, 0xe3, 0x69, 0x0f, 0x49, 0x06, 0x93, + 0xa3, 0x69, 0xb1, 0x46, 0x05, 0x93, 0x63, 0x69, 0x2a, 0x46, 0x04, 0x93, + 0x23, 0x69, 0x06, 0x20, 0x03, 0x93, 0xe3, 0x68, 0x79, 0x44, 0x02, 0x93, + 0xa3, 0x68, 0x01, 0x93, 0x63, 0x68, 0x00, 0x93, 0x23, 0x68, 0xff, 0xf7, + 0xb5, 0xff, 0x00, 0x20, 0x08, 0xb0, 0x70, 0xbd, 0x00, 0x20, 0xcc, 0xe7, + 0xe0, 0x93, 0x04, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x0b, 0x68, 0xc0, 0x1a, 0x70, 0x47, + 0x5b, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x25, 0x64, 0x5d, 0x20, 0x25, 0x64, + 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, + 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x00, + 0xe0, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xe0, 0x13, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfb, 0xff, 0xff, 0x6f, 0x01, 0x00, 0x00, 0x00, 0xfa, 0xff, 0xff, 0x6f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x18, 0x14, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x41, 0x72, + 0x6d, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x54, 0x6f, 0x6f, 0x6c, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x52, 0x65, 0x6c, + 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x20, 0x61, 0x72, 0x6d, + 0x2d, 0x31, 0x35, 0x2e, 0x38, 0x36, 0x29, 0x29, 0x20, 0x31, 0x35, 0x2e, + 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x32, 0x35, 0x31, 0x32, 0x30, 0x33, + 0x00, 0x41, 0x2c, 0x00, 0x00, 0x00, 0x61, 0x65, 0x61, 0x62, 0x69, 0x00, + 0x01, 0x22, 0x00, 0x00, 0x00, 0x05, 0x37, 0x2d, 0x4d, 0x00, 0x06, 0x0a, + 0x07, 0x4d, 0x09, 0x02, 0x12, 0x04, 0x14, 0x01, 0x15, 0x01, 0x17, 0x03, + 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1e, 0x04, 0x22, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, + 0x0b, 0x00, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x29, 0x03, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0xa0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x18, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x18, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x18, 0x14, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x2a, 0x00, 0x00, 0x00, + 0x58, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf1, 0xff, + 0x33, 0x00, 0x00, 0x00, 0xe0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x74, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x88, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x8c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x9c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x66, 0x00, 0x00, 0x00, 0xa1, 0x02, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, 0x6b, 0x00, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x71, 0x73, 0x6f, + 0x72, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x00, 0x24, 0x74, 0x00, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x00, 0x24, 0x64, 0x00, 0x2e, 0x4c, 0x41, + 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x30, 0x00, 0x67, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, + 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, + 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, 0x75, 0x73, 0x6c, + 0x65, 0x65, 0x70, 0x00, 0x71, 0x73, 0x6f, 0x72, 0x74, 0x00, 0x5f, 0x5f, + 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, + 0x5f, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, + 0x49, 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, + 0x73, 0x79, 0x73, 0x6c, 0x6f, 0x67, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, + 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, + 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, + 0x61, 0x62, 0x00, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, + 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, + 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x70, + 0x6c, 0x74, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, + 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, + 0x70, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, + 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 0x41, 0x52, 0x4d, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0xa0, 0x02, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x58, 0x13, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xe0, 0x13, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x14, 0x00, 0x00, + 0x18, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x69, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x04, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x04, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0x04, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x07, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00, 0x00, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_qsorter_nxf_len = 2768; diff --git a/examples/fdpicxip/user_bin.h b/examples/fdpicxip/user_bin.h new file mode 100644 index 000000000..fd417418c --- /dev/null +++ b/examples/fdpicxip/user_bin.h @@ -0,0 +1,265 @@ +/**************************************************************************** + * apps/examples/fdpicxip/user_bin.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Generated from user.fdpic -- do not edit. + * + * An FDPIC module, embedded so the demo has something to load without + * needing a filesystem populated from the host first. + */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const unsigned char g_user[] = +{ + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd1, 0x02, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x08, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x34, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x00, + 0x10, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, + 0x94, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, + 0x94, 0x13, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x94, 0x03, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xd1, 0x02, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x75, 0x6d, 0x70, 0x00, + 0x75, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x73, 0x79, 0x73, + 0x6c, 0x6f, 0x67, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x2e, 0x73, 0x6f, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, + 0x58, 0x55, 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x5f, + 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, + 0x5f, 0x5f, 0x00, 0x00, 0x30, 0x14, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x00, + 0x38, 0x14, 0x00, 0x00, 0xa4, 0x07, 0x00, 0x00, 0x40, 0x14, 0x00, 0x00, + 0xa4, 0x08, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, 0xa4, 0x0c, 0x00, 0x00, + 0x50, 0x14, 0x00, 0x00, 0xa4, 0x0d, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x0c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x14, 0x00, 0x00, 0x00, + 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, + 0xdc, 0xf8, 0x00, 0xf0, 0x1c, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, + 0x0c, 0xeb, 0x09, 0x0c, 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, + 0x24, 0x00, 0x00, 0x00, 0xdf, 0xf8, 0x0c, 0xc0, 0x0c, 0xeb, 0x09, 0x0c, + 0xdc, 0xf8, 0x04, 0x90, 0xdc, 0xf8, 0x00, 0xf0, 0x2c, 0x00, 0x00, 0x00, + 0xf0, 0xb5, 0x01, 0x28, 0x4c, 0x46, 0x85, 0xb0, 0x2f, 0xdd, 0x48, 0x68, + 0xff, 0xf7, 0xee, 0xff, 0xa1, 0x46, 0x05, 0x46, 0x03, 0x26, 0x17, 0x4b, + 0x03, 0x93, 0x28, 0x46, 0xff, 0xf7, 0xc8, 0xff, 0x03, 0x98, 0xa1, 0x46, + 0xff, 0xf7, 0xba, 0xff, 0x01, 0x3e, 0xa1, 0x46, 0xf5, 0xd1, 0xff, 0xf7, + 0xc9, 0xff, 0xa1, 0x46, 0x07, 0x46, 0xff, 0xf7, 0xc5, 0xff, 0x05, 0xeb, + 0x45, 0x06, 0x86, 0x42, 0xa1, 0x46, 0x14, 0xd1, 0x0c, 0x4a, 0x7a, 0x44, + 0x0c, 0x49, 0x3b, 0x46, 0xcd, 0xe9, 0x00, 0x62, 0x79, 0x44, 0x2a, 0x46, + 0x06, 0x20, 0xff, 0xf7, 0xbf, 0xff, 0xa1, 0x46, 0xff, 0xf7, 0xb2, 0xff, + 0x30, 0x1a, 0x18, 0xbf, 0x01, 0x20, 0x05, 0xb0, 0xf0, 0xbd, 0x00, 0x25, + 0xd2, 0xe7, 0x04, 0x4a, 0x7a, 0x44, 0xe9, 0xe7, 0xa0, 0x86, 0x01, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x50, 0x41, 0x53, 0x53, 0x00, 0x46, 0x41, 0x49, 0x4c, 0x00, 0x5b, 0x75, + 0x73, 0x65, 0x72, 0x20, 0x25, 0x64, 0x5d, 0x20, 0x6c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x79, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x20, 0x3d, 0x20, + 0x25, 0x64, 0x20, 0x28, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x20, 0x25, 0x64, 0x29, 0x20, 0x2d, 0x2d, 0x20, 0x25, 0x73, 0x0a, 0x00, + 0x24, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0xff, 0xff, 0x6f, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, + 0x20, 0x28, 0x41, 0x72, 0x6d, 0x20, 0x47, 0x4e, 0x55, 0x20, 0x54, 0x6f, + 0x6f, 0x6c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x31, 0x35, 0x2e, 0x32, + 0x2e, 0x52, 0x65, 0x6c, 0x31, 0x20, 0x28, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x20, 0x61, 0x72, 0x6d, 0x2d, 0x31, 0x35, 0x2e, 0x38, 0x36, 0x29, 0x29, + 0x20, 0x31, 0x35, 0x2e, 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x32, 0x35, + 0x31, 0x32, 0x30, 0x33, 0x00, 0x41, 0x2c, 0x00, 0x00, 0x00, 0x61, 0x65, + 0x61, 0x62, 0x69, 0x00, 0x01, 0x22, 0x00, 0x00, 0x00, 0x05, 0x37, 0x2d, + 0x4d, 0x00, 0x06, 0x0a, 0x07, 0x4d, 0x09, 0x02, 0x12, 0x04, 0x14, 0x01, + 0x15, 0x01, 0x17, 0x03, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1e, 0x04, + 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, + 0x08, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, + 0x0e, 0x00, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xf1, 0xff, 0x17, 0x00, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0xa4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x08, 0x00, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, + 0xbc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0xcc, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x5f, 0x00, 0x00, 0x00, + 0xd1, 0x02, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x08, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x63, 0x00, 0x24, 0x74, 0x00, 0x24, + 0x64, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, + 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, + 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, 0x75, 0x73, 0x6c, + 0x65, 0x65, 0x70, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x5f, + 0x62, 0x75, 0x6d, 0x70, 0x00, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, + 0x49, 0x58, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x6d, + 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x5f, 0x52, 0x4f, 0x46, 0x49, 0x58, 0x55, + 0x50, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x73, 0x79, 0x73, + 0x6c, 0x6f, 0x67, 0x00, 0x61, 0x74, 0x6f, 0x69, 0x00, 0x00, 0x2e, 0x73, + 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, + 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, + 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x72, + 0x65, 0x6c, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x70, 0x6c, 0x74, 0x00, + 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, + 0x61, 0x00, 0x2e, 0x72, 0x6f, 0x66, 0x69, 0x78, 0x75, 0x70, 0x00, 0x2e, + 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, + 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 0x41, + 0x52, 0x4d, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0xb4, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, + 0x44, 0x02, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x6c, 0x02, 0x00, 0x00, 0x6c, 0x02, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, + 0x54, 0x03, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x4d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x90, 0x03, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x94, 0x13, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x14, 0x00, 0x00, + 0x24, 0x04, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x04, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x04, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0x04, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2c, 0x07, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x07, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned int g_user_len = 2732; diff --git a/examples/flowc/flowc.h b/examples/flowc/flowc.h index c96fe76af..12caefa93 100644 --- a/examples/flowc/flowc.h +++ b/examples/flowc/flowc.h @@ -29,7 +29,7 @@ #ifdef EXAMPLES_FLOWC_HOST #else -# include +# include #endif /**************************************************************************** diff --git a/examples/foc/foc_fixed16_thr.c b/examples/foc/foc_fixed16_thr.c index 2ba2998e6..3c9a8c77b 100644 --- a/examples/foc/foc_fixed16_thr.c +++ b/examples/foc/foc_fixed16_thr.c @@ -26,6 +26,8 @@ #include +#include + #include #include #include diff --git a/examples/foc/foc_float_thr.c b/examples/foc/foc_float_thr.c index af967f255..4c52b86bf 100644 --- a/examples/foc/foc_float_thr.c +++ b/examples/foc/foc_float_thr.c @@ -26,6 +26,8 @@ #include +#include + #include #include #include diff --git a/examples/foc/foc_main.c b/examples/foc/foc_main.c index 4b280e848..2f1b6b0c4 100644 --- a/examples/foc/foc_main.c +++ b/examples/foc/foc_main.c @@ -142,7 +142,7 @@ struct args_s g_args = } }; -/* Start allowed at defaule */ +/* Start allowed at default */ static bool g_start_allowed = true; static pthread_mutex_t g_start_allowed_lock = PTHREAD_MUTEX_INITIALIZER; @@ -294,16 +294,10 @@ int main(int argc, char *argv[]) goto errout_no_nxscope; } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - -# ifdef CONFIG_BOARDCTL_FINALINIT +#ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -# endif #endif PRINTF("\nStart foc_main application!\n\n"); diff --git a/examples/ft80x/ft80x.h b/examples/ft80x/ft80x.h index 0349f6eb4..70492f24e 100644 --- a/examples/ft80x/ft80x.h +++ b/examples/ft80x/ft80x.h @@ -31,7 +31,7 @@ #include #include -#include +#include #ifdef CONFIG_EXAMPLES_FT80X diff --git a/examples/ftpd/ftpd_main.c b/examples/ftpd/ftpd_main.c index 5c7888b24..e2673572a 100644 --- a/examples/ftpd/ftpd_main.c +++ b/examples/ftpd/ftpd_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/gps/gps_main.c b/examples/gps/gps_main.c index 72673944d..a308f5213 100644 --- a/examples/gps/gps_main.c +++ b/examples/gps/gps_main.c @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -52,9 +53,11 @@ int main(int argc, FAR char *argv[]) { int fd; int cnt; + ssize_t nread; char ch; char line[MINMEA_MAX_LENGTH]; char *port = "/dev/ttyS1"; + bool truncated; /* Get the GPS serial port argument. If none specified, default to ttyS1 */ @@ -79,18 +82,38 @@ int main(int argc, FAR char *argv[]) /* Read until we complete a line */ cnt = 0; - do + truncated = false; + + for (; ; ) { - read(fd, &ch, 1); - if (ch != '\r' && ch != '\n') + nread = read(fd, &ch, 1); + if (nread <= 0) + { + continue; + } + + if (ch == '\r' || ch == '\n') + { + break; + } + + if (cnt < MINMEA_MAX_LENGTH - 1) { line[cnt++] = ch; } + else + { + truncated = true; + } } - while (ch != '\r' && ch != '\n'); line[cnt] = '\0'; + if (truncated) + { + continue; + } + switch (minmea_sentence_id(line, false)) { case MINMEA_SENTENCE_RMC: @@ -99,21 +122,24 @@ int main(int argc, FAR char *argv[]) if (minmea_parse_rmc(&frame, line)) { - printf("Fixed-point Latitude...........: %" PRIdLEAST32 - "\n", + printf("Fixed-point Latitude................: %" + PRIdLEAST32 " (NMEA * 1000)\n", minmea_rescale(&frame.latitude, 1000)); - printf("Fixed-point Longitude..........: %" PRIdLEAST32 - "\n", + printf("Fixed-point Longitude...............: %" + PRIdLEAST32 " (NMEA * 1000)\n", minmea_rescale(&frame.longitude, 1000)); - printf("Fixed-point Speed..............: %" PRIdLEAST32 - "\n", + printf("Fixed-point Speed...................: %" + PRIdLEAST32 " (knots * 1000)\n", minmea_rescale(&frame.speed, 1000)); - printf("Floating point degree latitude.: %2.6f\n", + printf("Floating point Latitude.............: %2.6f " + "degrees\n", minmea_tocoord(&frame.latitude)); - printf("Floating point degree longitude: %2.6f\n", + printf("Floating point Longitude............: %2.6f " + "degrees\n", minmea_tocoord(&frame.longitude)); - printf("Floating point speed...........: %2.6f\n", - minmea_tocoord(&frame.speed)); + printf("Floating point Speed................: %2.6f " + "knots\n", + minmea_tofloat(&frame.speed)); } else { @@ -130,9 +156,9 @@ int main(int argc, FAR char *argv[]) { printf("Fix quality....................: %d\n", frame.fix_quality); - printf("Altitude.......................: %" PRIdLEAST32 - "\n", - frame.altitude.value); + printf("Altitude.......................: %2.6f %c\n", + minmea_tofloat(&frame.altitude), + frame.altitude_units ? frame.altitude_units : '?'); printf("Tracked satellites.............: %d\n", frame.satellites_tracked); } diff --git a/examples/hall/hall_main.c b/examples/hall/hall_main.c index ed3c3b570..e0fdc2e78 100644 --- a/examples/hall/hall_main.c +++ b/examples/hall/hall_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include diff --git a/examples/hello_nim/Kconfig b/examples/hello_nim/Kconfig index a34c8fcdb..e955ef88f 100644 --- a/examples/hello_nim/Kconfig +++ b/examples/hello_nim/Kconfig @@ -4,10 +4,10 @@ # config EXAMPLES_HELLO_NIM - tristate "\"Hello, World!\" example (Nim)" + tristate "\"Hello, Nim!\" example" default n ---help--- - Enable the \"Hello, World!\" example + Enable the \"Hello, Nim!\" example in Nim language if EXAMPLES_HELLO_NIM diff --git a/examples/helloxx/helloxx_main.cxx b/examples/helloxx/helloxx_main.cxx index c3425ff6b..8df920916 100644 --- a/examples/helloxx/helloxx_main.cxx +++ b/examples/helloxx/helloxx_main.cxx @@ -27,7 +27,7 @@ #include #include -#include +#include //*************************************************************************** // Definitions diff --git a/examples/hidkbd/Kconfig b/examples/hidkbd/Kconfig index c80d40943..8b938de37 100644 --- a/examples/hidkbd/Kconfig +++ b/examples/hidkbd/Kconfig @@ -36,7 +36,7 @@ config EXAMPLES_HIDKBD_DEVNAME config EXAMPLES_HIDKBD_ENCODED bool "Encode Special Keys" default y - depends on HIDKBD_ENCODED && LIBC_KBDCODEC + depends on INPUT_KEYBOARD_BYTESTREAM ---help--- Decode special key press events in the user buffer. In this case, the example coded will use the interfaces defined in diff --git a/examples/hidkbd/hidkbd_main.c b/examples/hidkbd/hidkbd_main.c index 1515d6526..d26082136 100644 --- a/examples/hidkbd/hidkbd_main.c +++ b/examples/hidkbd/hidkbd_main.c @@ -35,6 +35,8 @@ #include #ifdef CONFIG_EXAMPLES_HIDKBD_ENCODED +# include + # include # include #endif @@ -55,7 +57,8 @@ # define CONFIG_EXAMPLES_HIDKBD_DEVNAME "/dev/kbda" #endif -#if !defined(CONFIG_HIDKBD_ENCODED) || !defined(CONFIG_LIBC_KBDCODEC) +#if !defined(CONFIG_INPUT_KEYBOARD_BYTESTREAM) || \ + !defined(CONFIG_LIBC_KBDCODEC) # undef CONFIG_EXAMPLES_HIDKBD_ENCODED #endif diff --git a/examples/hts221_reader/hts221_reader_main.c b/examples/hts221_reader/hts221_reader_main.c index ac00ede5e..5c1c39664 100644 --- a/examples/hts221_reader/hts221_reader_main.c +++ b/examples/hts221_reader/hts221_reader_main.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include diff --git a/examples/i2schar/i2schar_main.c b/examples/i2schar/i2schar_main.c index dec7b70c1..176def927 100644 --- a/examples/i2schar/i2schar_main.c +++ b/examples/i2schar/i2schar_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/examples/i2schar/i2schar_receiver.c b/examples/i2schar/i2schar_receiver.c index fdcc0c8ba..e088bee9f 100644 --- a/examples/i2schar/i2schar_receiver.c +++ b/examples/i2schar/i2schar_receiver.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/i2schar/i2schar_transmitter.c b/examples/i2schar/i2schar_transmitter.c index df1560ac6..14e4ec347 100644 --- a/examples/i2schar/i2schar_transmitter.c +++ b/examples/i2schar/i2schar_transmitter.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/i2sloop/Kconfig b/examples/i2sloop/Kconfig index cea8333cd..982c0b020 100644 --- a/examples/i2sloop/Kconfig +++ b/examples/i2sloop/Kconfig @@ -6,6 +6,6 @@ config EXAMPLES_I2SLOOP tristate "I2S loopback test" default n - depends on I2S && AUDIO && DRIVERS_AUDIO && AUDIO_I2SCHAR + depends on I2S && AUDIO && DRIVERS_AUDIO && AUDIO_I2SCHAR && ENABLE_ALL_SIGNALS ---help--- Enable the I2S loopback test diff --git a/examples/igmp/igmp.c b/examples/igmp/igmp.c index da904fa34..7da32c680 100644 --- a/examples/igmp/igmp.c +++ b/examples/igmp/igmp.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/igmp/igmp.h b/examples/igmp/igmp.h index 11ffb57f9..3cf99b1b0 100644 --- a/examples/igmp/igmp.h +++ b/examples/igmp/igmp.h @@ -27,7 +27,7 @@ * Included Files ****************************************************************************/ -#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/examples/ipcfg/ipcfg_main.c b/examples/ipcfg/ipcfg_main.c index f64a73ea8..51c411081 100644 --- a/examples/ipcfg/ipcfg_main.c +++ b/examples/ipcfg/ipcfg_main.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/ipforward/ipforward.c b/examples/ipforward/ipforward.c index 57b146e7e..ae9ee5831 100644 --- a/examples/ipforward/ipforward.c +++ b/examples/ipforward/ipforward.c @@ -542,8 +542,16 @@ static FAR void *ipfwd_receiver(FAR void *arg) int errcode; int i; +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + for (i = 0; i < IPFWD_NPACKETS; i++) { +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif nread = read(fwd->ia_fd, fwd->ia_buffer, IPFWD_BUFSIZE); if (nread < 0) { @@ -840,7 +848,12 @@ errout_with_receiver: /* Wait for receiver thread to terminate */ +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_cancel(fwd.if_receiver); +#else pthread_kill(fwd.if_receiver, 9); +#endif + ret = pthread_join(fwd.if_receiver, &value); if (ret != OK) { diff --git a/examples/keyboard/kbd_main.c b/examples/keyboard/kbd_main.c index ff832c862..d793e6344 100644 --- a/examples/keyboard/kbd_main.c +++ b/examples/keyboard/kbd_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include diff --git a/examples/lely_master/CMakeLists.txt b/examples/lely_master/CMakeLists.txt new file mode 100644 index 000000000..003c3e9fb --- /dev/null +++ b/examples/lely_master/CMakeLists.txt @@ -0,0 +1,53 @@ +# ############################################################################## +# apps/examples/lely_master/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_LELYMASTER) + + set(LELY_INCDIR ${NUTTX_APPS_DIR}/canutils/lely-canopen/lely-core/include) + + set(SRCS master_main.c sdev_master.c) + + if(CONFIG_EXAMPLES_LELYMASTER_CHARDEV) + list(APPEND SRCS candev_char.c) + endif() + + if(CONFIG_EXAMPLES_LELYMASTER_SOCKET) + list(APPEND SRCS candev_sock.c) + endif() + + nuttx_add_application( + NAME + comaster + PRIORITY + ${CONFIG_EXAMPLES_LELYMASTER_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_LELYMASTER_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_LELYMASTER} + COMPILE_FLAGS + -Wno-undef + INCLUDE_DIRECTORIES + ${LELY_INCDIR} + SRCS + ${SRCS}) + +endif() diff --git a/examples/lely_master/Kconfig b/examples/lely_master/Kconfig new file mode 100644 index 000000000..b370ea1f9 --- /dev/null +++ b/examples/lely_master/Kconfig @@ -0,0 +1,50 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_LELYMASTER + tristate "Lely CANopen master example" + default n + depends on CANUTILS_LELYCANOPEN && CANUTILS_LELYCANOPEN_SDEV + depends on CANUTILS_LELYCANOPEN_TIME && CANUTILS_LELYCANOPEN_OBJUPLOAD + depends on CANUTILS_LELYCANOPEN_MASTER + ---help--- + Enable the Lely CANopen master (comaster) example application. + +if EXAMPLES_LELYMASTER + +choice + prompt "CAN backend" + default EXAMPLES_LELYMASTER_CHARDEV + +config EXAMPLES_LELYMASTER_CHARDEV + bool "CAN character driver" + depends on CAN + +config EXAMPLES_LELYMASTER_SOCKET + bool "SocketCAN" + depends on NET_CAN + select NETUTILS_NETLIB + +endchoice + +config EXAMPLES_LELYMASTER_DEVPATH + string "CAN character device path" + default "/dev/can0" + depends on EXAMPLES_LELYMASTER_CHARDEV + +config EXAMPLES_LELYMASTER_INTF + string "SocketCAN interface" + default "can0" + depends on EXAMPLES_LELYMASTER_SOCKET + +config EXAMPLES_LELYMASTER_PRIORITY + int "Task priority" + default 100 + +config EXAMPLES_LELYMASTER_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/lely_master/Make.defs b/examples/lely_master/Make.defs new file mode 100644 index 000000000..d47806ad6 --- /dev/null +++ b/examples/lely_master/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/lely_master/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_LELYMASTER),) +CONFIGURED_APPS += $(APPDIR)/examples/lely_master +endif diff --git a/examples/lely_master/Makefile b/examples/lely_master/Makefile new file mode 100644 index 000000000..68db84998 --- /dev/null +++ b/examples/lely_master/Makefile @@ -0,0 +1,47 @@ +############################################################################ +# apps/examples/lely_master/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = comaster +PRIORITY = $(CONFIG_EXAMPLES_LELYMASTER_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_LELYMASTER_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_LELYMASTER) + +CFLAGS += -DHAVE_CONFIG_H=1 + +# Lely headers use bare "#if __cplusplus"; silence -Wundef like the library + +CFLAGS += -Wno-undef + +CSRCS = sdev_master.c + +ifeq ($(CONFIG_EXAMPLES_LELYMASTER_CHARDEV),y) +CSRCS += candev_char.c +endif +ifeq ($(CONFIG_EXAMPLES_LELYMASTER_SOCKET),y) +CSRCS += candev_sock.c +endif + +MAINSRC = master_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/lely_master/candev.h b/examples/lely_master/candev.h new file mode 100644 index 000000000..47a6fb8a9 --- /dev/null +++ b/examples/lely_master/candev.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * apps/examples/lely_master/candev.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_LELY_MASTER_CANDEV_H +#define __APPS_EXAMPLES_LELY_MASTER_CANDEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int comaster_candev_init(void); +int comaster_candev_send(FAR const struct can_msg *msg); +int comaster_candev_recv(FAR struct can_msg *msg); + +#endif /* __APPS_EXAMPLES_LELY_MASTER_CANDEV_H */ diff --git a/examples/lely_master/candev_char.c b/examples/lely_master/candev_char.c new file mode 100644 index 000000000..3506b75d9 --- /dev/null +++ b/examples/lely_master/candev_char.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * apps/examples/lely_master/candev_char.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "candev.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_candev_fd; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: comaster_candev_init + ****************************************************************************/ + +int comaster_candev_init(void) +{ + struct canioc_bittiming_s bt; + int ret; + + /* Non-blocking so the main loop keeps servicing the CANopen timers */ + + g_candev_fd = open(CONFIG_EXAMPLES_LELYMASTER_DEVPATH, + O_RDWR | O_NONBLOCK); + if (g_candev_fd < 0) + { + ret = -errno; + printf("ERROR: open %s failed %d\n", + CONFIG_EXAMPLES_LELYMASTER_DEVPATH, errno); + return ret; + } + + ret = ioctl(g_candev_fd, CANIOC_GET_BITTIMING, &bt); + if (ret < 0) + { + printf("Bit timing not available: %d\n", errno); + } + else + { + printf("Bit timing:\n"); + printf(" Baud: %lu\n", (unsigned long)bt.bt_baud); + printf(" TSEG1: %u\n", bt.bt_tseg1); + printf(" TSEG2: %u\n", bt.bt_tseg2); + printf(" SJW: %u\n", bt.bt_sjw); + } + + return OK; +} + +/**************************************************************************** + * Name: comaster_candev_send + ****************************************************************************/ + +int comaster_candev_send(FAR const struct can_msg *msg) +{ + struct can_msg_s txmsg; + ssize_t nwritten; + int i; + + txmsg.cm_hdr.ch_id = msg->id; + txmsg.cm_hdr.ch_rtr = (msg->flags & CAN_FLAG_RTR) != 0; + txmsg.cm_hdr.ch_dlc = msg->len; +#ifdef CONFIG_CAN_EXTID + txmsg.cm_hdr.ch_extid = (msg->flags & CAN_FLAG_IDE) != 0; +#endif +#ifdef CONFIG_CAN_ERRORS + txmsg.cm_hdr.ch_error = 0; +#endif + txmsg.cm_hdr.ch_tcf = 0; + + for (i = 0; i < msg->len; i++) + { + txmsg.cm_data[i] = msg->data[i]; + } + + nwritten = write(g_candev_fd, &txmsg, CAN_MSGLEN(txmsg.cm_hdr.ch_dlc)); + if (nwritten != CAN_MSGLEN(txmsg.cm_hdr.ch_dlc)) + { + return -1; + } + + return 0; +} + +/**************************************************************************** + * Name: comaster_candev_recv + ****************************************************************************/ + +int comaster_candev_recv(FAR struct can_msg *msg) +{ + struct can_msg_s rxmsg; + ssize_t nread; + int i; + + nread = read(g_candev_fd, &rxmsg, sizeof(struct can_msg_s)); + if (nread < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 0; + } + + return -1; + } + + if (nread < CAN_MSGLEN(0)) + { + return 0; + } + + msg->id = rxmsg.cm_hdr.ch_id; + msg->flags = 0; + +#ifdef CONFIG_CAN_EXTID + if (rxmsg.cm_hdr.ch_extid) + { + msg->flags |= CAN_FLAG_IDE; + } +#endif + + if (rxmsg.cm_hdr.ch_rtr) + { + msg->flags |= CAN_FLAG_RTR; + } + + msg->len = rxmsg.cm_hdr.ch_dlc; + + for (i = 0; i < rxmsg.cm_hdr.ch_dlc; i++) + { + msg->data[i] = rxmsg.cm_data[i]; + } + + return 1; +} diff --git a/examples/lely_master/candev_sock.c b/examples/lely_master/candev_sock.c new file mode 100644 index 000000000..46c30ddc6 --- /dev/null +++ b/examples/lely_master/candev_sock.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * apps/examples/lely_master/candev_sock.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "netutils/netlib.h" +#include "candev.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_candev_sock; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: comaster_candev_init + ****************************************************************************/ + +int comaster_candev_init(void) +{ + struct sockaddr_can addr; + struct ifreq ifr; + int on = 1; + int ret; + + g_candev_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (g_candev_sock < 0) + { + ret = -errno; + printf("ERROR: socket failed %d\n", errno); + return ret; + } + + strlcpy(ifr.ifr_name, CONFIG_EXAMPLES_LELYMASTER_INTF, IFNAMSIZ); + + /* SocketCAN silently drops traffic while the interface is down, so bring + * it up here. This lets the example run without a separate "ifup" step. + */ + + if (netlib_ifup(ifr.ifr_name) < 0) + { + ret = -errno; + printf("ERROR: netlib_ifup %s failed %d\n", ifr.ifr_name, errno); + goto errout; + } + + ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name); + if (ifr.ifr_ifindex == 0) + { + ret = -errno; + printf("ERROR: if_nametoindex %s failed %d\n", ifr.ifr_name, errno); + goto errout; + } + + setsockopt(g_candev_sock, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, + &on, sizeof(on)); + + memset(&addr, 0, sizeof(addr)); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(g_candev_sock, (FAR struct sockaddr *)&addr, sizeof(addr)) < 0) + { + ret = -errno; + printf("ERROR: bind failed %d\n", errno); + goto errout; + } + + ret = fcntl(g_candev_sock, F_SETFL, O_NONBLOCK); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + return OK; + +errout: + close(g_candev_sock); + g_candev_sock = -1; + return ret; +} + +/**************************************************************************** + * Name: comaster_candev_send + ****************************************************************************/ + +int comaster_candev_send(FAR const struct can_msg *msg) +{ + struct can_frame frame; + ssize_t nwritten; + + memset(&frame, 0, sizeof(frame)); + + frame.can_id = msg->id; + if (msg->flags & CAN_FLAG_IDE) + { + frame.can_id |= CAN_EFF_FLAG; + } + + if (msg->flags & CAN_FLAG_RTR) + { + frame.can_id |= CAN_RTR_FLAG; + } + + frame.can_dlc = msg->len; + memcpy(frame.data, msg->data, msg->len); + + nwritten = write(g_candev_sock, &frame, sizeof(frame)); + if (nwritten != sizeof(frame)) + { + return -1; + } + + return 0; +} + +/**************************************************************************** + * Name: comaster_candev_recv + ****************************************************************************/ + +int comaster_candev_recv(FAR struct can_msg *msg) +{ + struct can_frame frame; + ssize_t nread; + + nread = read(g_candev_sock, &frame, sizeof(frame)); + if (nread < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 0; + } + + return -1; + } + + if (nread < sizeof(frame)) + { + return 0; + } + + msg->id = frame.can_id & CAN_EFF_MASK; + msg->flags = 0; + if (frame.can_id & CAN_EFF_FLAG) + { + msg->flags |= CAN_FLAG_IDE; + } + + if (frame.can_id & CAN_RTR_FLAG) + { + msg->flags |= CAN_FLAG_RTR; + } + + msg->len = frame.can_dlc; + memcpy(msg->data, frame.data, frame.can_dlc); + + return 1; +} diff --git a/examples/lely_master/master_main.c b/examples/lely_master/master_main.c new file mode 100644 index 000000000..4148d0d1c --- /dev/null +++ b/examples/lely_master/master_main.c @@ -0,0 +1,358 @@ +/**************************************************************************** + * apps/examples/lely_master/master_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "sdev.h" +#include "candev.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SLAVE_ID 0x02 +#define OBJ_COUNTER 0x2100 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum master_phase_e +{ + PHASE_WAIT, /* wait for the slave, then read its info via SDO */ + PHASE_SDO, /* SDO request in flight */ + PHASE_RUN /* network operational, streaming PDO data */ +}; + +struct co_net_s +{ + can_net_t *net; + co_dev_t *dev; + co_nmt_t *nmt; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct co_net_s g_net; +static bool g_sdo_done; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: elapsed_ms + ****************************************************************************/ + +static long elapsed_ms(const struct timespec *a, const struct timespec *b) +{ + return (a->tv_sec - b->tv_sec) * 1000 + + (a->tv_nsec - b->tv_nsec) / 1000000; +} + +/**************************************************************************** + * Name: on_can_send + ****************************************************************************/ + +static int on_can_send(const struct can_msg *msg, void *data) +{ + UNUSED(data); + + return comaster_candev_send(msg); +} + +/**************************************************************************** + * Name: nmt_start_remote + ****************************************************************************/ + +static void nmt_start_remote(co_unsigned8_t id) +{ + struct can_msg msg; + + /* NMT command frame: COB-ID 0x000, {command specifier, node-ID} */ + + memset(&msg, 0, sizeof(msg)); + msg.len = 2; + msg.data[0] = CO_NMT_CS_START; + msg.data[1] = id; + + comaster_candev_send(&msg); +} + +/**************************************************************************** + * Name: on_sdo_up + ****************************************************************************/ + +static void on_sdo_up(co_csdo_t *sdo, co_unsigned16_t idx, + co_unsigned8_t subidx, co_unsigned32_t ac, + const void *ptr, size_t n, void *data) +{ + co_unsigned32_t val; + + UNUSED(sdo); + UNUSED(idx); + UNUSED(subidx); + UNUSED(data); + + if (ac == 0 && n >= sizeof(co_unsigned32_t)) + { + memcpy(&val, ptr, sizeof(val)); + printf("SDO: slave device type (0x1000) = 0x%08x\n", (unsigned)val); + } + else + { + printf("SDO: read failed, abort code 0x%08x\n", (unsigned)ac); + } + + g_sdo_done = true; +} + +/**************************************************************************** + * Name: on_time + ****************************************************************************/ + +static void on_time(co_time_t *time, const struct timespec *tp, void *data) +{ + UNUSED(time); + UNUSED(data); + + /* Update the wall clock */ + + clock_settime(CLOCK_REALTIME, tp); +} + +/**************************************************************************** + * Name: on_nmt_cs + ****************************************************************************/ + +static void on_nmt_cs(co_nmt_t *nmt, co_unsigned8_t cs, void *data) +{ + UNUSED(data); + + switch (cs) + { + case CO_NMT_CS_START: + case CO_NMT_CS_ENTER_PREOP: + { + co_time_set_ind(co_nmt_get_time(nmt), &on_time, NULL); + break; + } + + case CO_NMT_CS_RESET_NODE: + { + exit(0); + break; + } + + case CO_NMT_CS_STOP: + case CO_NMT_CS_RESET_COMM: + default: + { + break; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct timespec now; + struct can_msg msg; + struct timespec t0; + co_unsigned32_t last = 0xffffffff; + int phase = PHASE_WAIT; + int ret; + co_csdo_t *csdo; + co_unsigned32_t v; + + memset(&g_net, 0, sizeof(g_net)); + g_sdo_done = false; + setvbuf(stdout, NULL, _IONBF, 0); + + ret = comaster_candev_init(); + if (ret < 0) + { + printf("ERROR: comaster_candev_init failed %d\n", ret); + goto errout; + } + + g_net.net = can_net_create(); + if (!g_net.net) + { + printf("ERROR: can_net_create failed\n"); + goto errout; + } + + can_net_set_send_func(g_net.net, &on_can_send, NULL); + + clock_gettime(CLOCK_MONOTONIC, &now); + can_net_set_time(g_net.net, &now); + + /* Create dev from struct */ + + g_net.dev = co_dev_create_from_sdev(&g_sdev_master); + if (!g_net.dev) + { + printf("ERROR: co_dev_create_from_sdev failed\n"); + goto errout; + } + + g_net.nmt = co_nmt_create(g_net.net, g_net.dev); + if (!g_net.nmt) + { + printf("ERROR: co_nmt_create failed\n"); + goto errout; + } + + /* Start the NMT service by resetting the node */ + + co_nmt_cs_ind(g_net.nmt, CO_NMT_CS_RESET_NODE); + + /* Set the NMT indication function after the initial reset */ + + co_nmt_set_cs_ind(g_net.nmt, &on_nmt_cs, NULL); + + printf("lely CANopen master running (node 0x%02x)\n", g_sdev_master.id); + + clock_gettime(CLOCK_MONOTONIC, &t0); + + while (1) + { + clock_gettime(CLOCK_MONOTONIC, &now); + can_net_set_time(g_net.net, &now); + + while (comaster_candev_recv(&msg) > 0) + { + can_net_recv(g_net.net, &msg); + } + + switch (phase) + { + /* Give the slave time to boot, then read its device type with + * a Client-SDO transfer (works while both nodes are pre-op). + */ + + case PHASE_WAIT: + { + if (elapsed_ms(&now, &t0) >= 500) + { + csdo = co_nmt_get_csdo(g_net.nmt, 1); + if (csdo != NULL && co_csdo_up_req( + csdo, 0x1000, 0x00, &on_sdo_up, NULL) == 0) + { + phase = PHASE_SDO; + } + else + { + t0 = now; + } + } + + break; + } + + /* Once the SDO completes, bring the network to OPERATIONAL: + * the master itself and the slave (NMT start). PDO/SYNC then + * flow. + */ + + case PHASE_SDO: + { + if (g_sdo_done) + { + co_nmt_cs_ind(g_net.nmt, CO_NMT_CS_START); + nmt_start_remote(SLAVE_ID); + printf("NMT: master + slave 0x%02x OPERATIONAL\n", + SLAVE_ID); + phase = PHASE_RUN; + } + + break; + } + + /* The slave's synchronous TPDO lands in object 0x2100 via the + * RPDO mapping; print it whenever it changes. + */ + + case PHASE_RUN: + { + v = co_dev_get_val_u32(g_net.dev, OBJ_COUNTER, 0x00); + if (v != last) + { + printf("PDO rx: counter = %u\n", (unsigned)v); + last = v; + } + + break; + } + } + + usleep(1000); + } + +errout: + if (g_net.nmt) + { + co_nmt_destroy(g_net.nmt); + } + + if (g_net.dev) + { + co_dev_destroy(g_net.dev); + } + + if (g_net.net) + { + can_net_destroy(g_net.net); + } + + return 0; +} diff --git a/examples/lely_master/sdev.h b/examples/lely_master/sdev.h new file mode 100644 index 000000000..559b8468d --- /dev/null +++ b/examples/lely_master/sdev.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/examples/lely_master/sdev.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_LELY_SDEV_H +#define __APPS_EXAMPLES_LELY_SDEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public data + ****************************************************************************/ + +extern const struct co_sdev g_sdev_master; + +#endif /* __APPS_EXAMPLES_LELY_SDEV_H */ diff --git a/examples/lely_master/sdev_master.c b/examples/lely_master/sdev_master.c new file mode 100644 index 000000000..34d5c6620 --- /dev/null +++ b/examples/lely_master/sdev_master.c @@ -0,0 +1,714 @@ +/**************************************************************************** + * apps/examples/lely_master/sdev_master.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "sdev.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CO_SDEV_STRING(s) s + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Object 0x1000 sub-objects */ + +static const struct co_ssub g_1000_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Device type"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1001 sub-objects */ + +static const struct co_ssub g_1001_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Error register"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = CO_UNSIGNED8_MIN, +#endif + .val.u8 = CO_UNSIGNED8_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1005 sub-objects */ + +static const struct co_ssub g_1005_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID SYNC message"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x40000080lu, +#endif + .val.u32 = 0x40000080lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1006 sub-objects */ + +static const struct co_ssub g_1006_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Communication cycle period"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x000186a0lu, +#endif + .val.u32 = 0x000186a0lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1012 sub-objects */ + +static const struct co_ssub g_1012_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID time stamp object"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x80000100lu, +#endif + .val.u32 = 0x80000100lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1017 sub-objects */ + +static const struct co_ssub g_1017_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Producer heartbeat time"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED16, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u16 = CO_UNSIGNED16_MIN, + .max.u16 = CO_UNSIGNED16_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u16 = 0x0032, +#endif + .val.u16 = 0x0032, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1018 sub-objects */ + +static const struct co_ssub g_1018_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Highest sub-index supported"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x04, +#endif + .val.u8 = 0x04, + .access = CO_ACCESS_CONST, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Vendor-ID"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000360lu, +#endif + .val.u32 = 0x00000360lu, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Product code"), +#endif + .subidx = 0x02, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Revision number"), +#endif + .subidx = 0x03, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Serial number"), +#endif + .subidx = 0x04, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1280 sub-objects */ + +static const struct co_ssub g_1280_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Highest sub-index supported"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x03, +#endif + .val.u8 = 0x03, + .access = CO_ACCESS_CONST, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID client to server"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000602lu, +#endif + .val.u32 = 0x00000602lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID server to client"), +#endif + .subidx = 0x02, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000582lu, +#endif + .val.u32 = 0x00000582lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Node-ID of the SDO server"), +#endif + .subidx = 0x03, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x02, +#endif + .val.u8 = 0x02, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1400 sub-objects */ + +static const struct co_ssub g_1400_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Highest sub-index supported"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x02, +#endif + .val.u8 = 0x02, + .access = CO_ACCESS_CONST, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000182lu, +#endif + .val.u32 = 0x00000182lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Transmission type"), +#endif + .subidx = 0x02, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x01, +#endif + .val.u8 = 0x01, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1600 sub-objects */ + +static const struct co_ssub g_1600_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Number of mapped objects"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x01, +#endif + .val.u8 = 0x01, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Mapping 1"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x21000020lu, +#endif + .val.u32 = 0x21000020lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1f80 sub-objects */ + +static const struct co_ssub g_1f80_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("NMT startup"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = 0x00000000lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + | CO_OBJ_FLAGS_PARAMETER_VALUE + } +}; + +/* Object 0x2000 sub-objects */ + +static const struct co_ssub g_2000_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO download callback"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x2001 sub-objects */ + +static const struct co_ssub g_2001_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO upload callback"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x2100 sub-objects */ + +static const struct co_ssub g_2100_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Counter"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RW, + .pdo_mapping = 1, + .flags = 0 + } +}; + +/* Object dictionary */ + +static const struct co_sobj g_g_sdev_master_objs[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Device type"), +#endif + .idx = 0x1000, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1000_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Error register"), +#endif + .idx = 0x1001, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1001_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID SYNC message"), +#endif + .idx = 0x1005, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1005_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Communication cycle period"), +#endif + .idx = 0x1006, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1006_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID time stamp object"), +#endif + .idx = 0x1012, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1012_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Producer heartbeat time"), +#endif + .idx = 0x1017, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1017_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Identity object"), +#endif + .idx = 0x1018, + .code = CO_OBJECT_RECORD, + .nsub = 5, + .subs = g_1018_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("SDO client parameter"), +#endif + .idx = 0x1280, + .code = CO_OBJECT_RECORD, + .nsub = 4, + .subs = g_1280_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("RPDO communication parameter"), +#endif + .idx = 0x1400, + .code = CO_OBJECT_RECORD, + .nsub = 3, + .subs = g_1400_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("RPDO mapping parameter"), +#endif + .idx = 0x1600, + .code = CO_OBJECT_RECORD, + .nsub = 2, + .subs = g_1600_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("NMT startup"), +#endif + .idx = 0x1f80, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1f80_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO download callback"), +#endif + .idx = 0x2000, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2000_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO upload callback"), +#endif + .idx = 0x2001, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2001_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Counter"), +#endif + .idx = 0x2100, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2100_sub + } +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct co_sdev g_sdev_master = +{ + .id = 0x01, + .name = NULL, + .vendor_name = CO_SDEV_STRING("Apache NuttX RTOS"), + .vendor_id = 0x00000000, + .product_name = NULL, + .product_code = 0x00000000, + .revision = 0x00000000, + .order_code = NULL, + .baud = CO_BAUD_125, + .rate = 0, + .lss = 0, + .dummy = 0x000000fe, + .nobj = 14, + .objs = g_g_sdev_master_objs +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/examples/lely_slave/CMakeLists.txt b/examples/lely_slave/CMakeLists.txt new file mode 100644 index 000000000..4ffdb62ea --- /dev/null +++ b/examples/lely_slave/CMakeLists.txt @@ -0,0 +1,55 @@ +# ############################################################################## +# apps/examples/lely_slave/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_LELYSLAVE) + + set(LELY_INCDIR ${NUTTX_APPS_DIR}/canutils/lely-canopen/lely-core/include) + + # The main source must be first so it receives the main=coslave_main alias + + set(SRCS slave_main.c sdev_slave.c) + + if(CONFIG_EXAMPLES_LELYSLAVE_CHARDEV) + list(APPEND SRCS candev_char.c) + endif() + + if(CONFIG_EXAMPLES_LELYSLAVE_SOCKET) + list(APPEND SRCS candev_sock.c) + endif() + + nuttx_add_application( + NAME + coslave + PRIORITY + ${CONFIG_EXAMPLES_LELYSLAVE_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_LELYSLAVE_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_LELYSLAVE} + COMPILE_FLAGS + -Wno-undef + INCLUDE_DIRECTORIES + ${LELY_INCDIR} + SRCS + ${SRCS}) + +endif() diff --git a/examples/lely_slave/Kconfig b/examples/lely_slave/Kconfig new file mode 100644 index 000000000..de8f9d30a --- /dev/null +++ b/examples/lely_slave/Kconfig @@ -0,0 +1,49 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_LELYSLAVE + tristate "Lely CANopen slave example" + default n + depends on CANUTILS_LELYCANOPEN && CANUTILS_LELYCANOPEN_SDEV + depends on CANUTILS_LELYCANOPEN_TIME && CANUTILS_LELYCANOPEN_OBJUPLOAD + ---help--- + Enable the Lely CANopen slave (coslave) example application. + +if EXAMPLES_LELYSLAVE + +choice + prompt "CAN backend" + default EXAMPLES_LELYSLAVE_CHARDEV + +config EXAMPLES_LELYSLAVE_CHARDEV + bool "CAN character driver" + depends on CAN + +config EXAMPLES_LELYSLAVE_SOCKET + bool "SocketCAN" + depends on NET_CAN + select NETUTILS_NETLIB + +endchoice + +config EXAMPLES_LELYSLAVE_DEVPATH + string "CAN character device path" + default "/dev/can0" + depends on EXAMPLES_LELYSLAVE_CHARDEV + +config EXAMPLES_LELYSLAVE_INTF + string "SocketCAN interface" + default "can0" + depends on EXAMPLES_LELYSLAVE_SOCKET + +config EXAMPLES_LELYSLAVE_PRIORITY + int "Task priority" + default 100 + +config EXAMPLES_LELYSLAVE_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/lely_slave/Make.defs b/examples/lely_slave/Make.defs new file mode 100644 index 000000000..20382554f --- /dev/null +++ b/examples/lely_slave/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/lely_slave/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_LELYSLAVE),) +CONFIGURED_APPS += $(APPDIR)/examples/lely_slave +endif diff --git a/examples/lely_slave/Makefile b/examples/lely_slave/Makefile new file mode 100644 index 000000000..dcc135012 --- /dev/null +++ b/examples/lely_slave/Makefile @@ -0,0 +1,47 @@ +############################################################################ +# apps/examples/lely_slave/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = coslave +PRIORITY = $(CONFIG_EXAMPLES_LELYSLAVE_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_LELYSLAVE_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_LELYSLAVE) + +CFLAGS += -DHAVE_CONFIG_H=1 + +# Lely headers use bare "#if __cplusplus"; silence -Wundef like the library + +CFLAGS += -Wno-undef + +CSRCS = sdev_slave.c + +ifeq ($(CONFIG_EXAMPLES_LELYSLAVE_CHARDEV),y) +CSRCS += candev_char.c +endif +ifeq ($(CONFIG_EXAMPLES_LELYSLAVE_SOCKET),y) +CSRCS += candev_sock.c +endif + +MAINSRC = slave_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/lely_slave/candev.h b/examples/lely_slave/candev.h new file mode 100644 index 000000000..e044f8e89 --- /dev/null +++ b/examples/lely_slave/candev.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * apps/examples/lely_slave/candev.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_LELY_SLAVE_CANDEV_H +#define __APPS_EXAMPLES_LELY_SLAVE_CANDEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int coslave_candev_init(void); +int coslave_candev_send(FAR const struct can_msg *msg); +int coslave_candev_recv(FAR struct can_msg *msg); + +#endif /* __APPS_EXAMPLES_LELY_SLAVE_CANDEV_H */ diff --git a/examples/lely_slave/candev_char.c b/examples/lely_slave/candev_char.c new file mode 100644 index 000000000..71c2aed11 --- /dev/null +++ b/examples/lely_slave/candev_char.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * apps/examples/lely_slave/candev_char.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "candev.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_candev_fd; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: coslave_candev_init + ****************************************************************************/ + +int coslave_candev_init(void) +{ + struct canioc_bittiming_s bt; + int ret; + + /* Non-blocking so the main loop keeps servicing the CANopen timers */ + + g_candev_fd = open(CONFIG_EXAMPLES_LELYSLAVE_DEVPATH, + O_RDWR | O_NONBLOCK); + if (g_candev_fd < 0) + { + ret = -errno; + printf("ERROR: open %s failed %d\n", + CONFIG_EXAMPLES_LELYSLAVE_DEVPATH, errno); + return ret; + } + + ret = ioctl(g_candev_fd, CANIOC_GET_BITTIMING, &bt); + if (ret < 0) + { + printf("Bit timing not available: %d\n", errno); + } + else + { + printf("Bit timing:\n"); + printf(" Baud: %lu\n", (unsigned long)bt.bt_baud); + printf(" TSEG1: %u\n", bt.bt_tseg1); + printf(" TSEG2: %u\n", bt.bt_tseg2); + printf(" SJW: %u\n", bt.bt_sjw); + } + + return OK; +} + +/**************************************************************************** + * Name: coslave_candev_send + ****************************************************************************/ + +int coslave_candev_send(FAR const struct can_msg *msg) +{ + struct can_msg_s txmsg; + ssize_t nwritten; + int i; + + txmsg.cm_hdr.ch_id = msg->id; + txmsg.cm_hdr.ch_rtr = (msg->flags & CAN_FLAG_RTR) != 0; + txmsg.cm_hdr.ch_dlc = msg->len; +#ifdef CONFIG_CAN_EXTID + txmsg.cm_hdr.ch_extid = (msg->flags & CAN_FLAG_IDE) != 0; +#endif +#ifdef CONFIG_CAN_ERRORS + txmsg.cm_hdr.ch_error = 0; +#endif + txmsg.cm_hdr.ch_tcf = 0; + + for (i = 0; i < msg->len; i++) + { + txmsg.cm_data[i] = msg->data[i]; + } + + nwritten = write(g_candev_fd, &txmsg, CAN_MSGLEN(txmsg.cm_hdr.ch_dlc)); + if (nwritten != CAN_MSGLEN(txmsg.cm_hdr.ch_dlc)) + { + return -1; + } + + return 0; +} + +/**************************************************************************** + * Name: coslave_candev_recv + ****************************************************************************/ + +int coslave_candev_recv(FAR struct can_msg *msg) +{ + struct can_msg_s rxmsg; + ssize_t nread; + int i; + + nread = read(g_candev_fd, &rxmsg, sizeof(struct can_msg_s)); + if (nread < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 0; + } + + return -1; + } + + if (nread < CAN_MSGLEN(0)) + { + return 0; + } + + msg->id = rxmsg.cm_hdr.ch_id; + msg->flags = 0; + +#ifdef CONFIG_CAN_EXTID + if (rxmsg.cm_hdr.ch_extid) + { + msg->flags |= CAN_FLAG_IDE; + } +#endif + + if (rxmsg.cm_hdr.ch_rtr) + { + msg->flags |= CAN_FLAG_RTR; + } + + msg->len = rxmsg.cm_hdr.ch_dlc; + + for (i = 0; i < rxmsg.cm_hdr.ch_dlc; i++) + { + msg->data[i] = rxmsg.cm_data[i]; + } + + return 1; +} diff --git a/examples/lely_slave/candev_sock.c b/examples/lely_slave/candev_sock.c new file mode 100644 index 000000000..d52db0b97 --- /dev/null +++ b/examples/lely_slave/candev_sock.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * apps/examples/lely_slave/candev_sock.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "netutils/netlib.h" +#include "candev.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_candev_sock; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: coslave_candev_init + ****************************************************************************/ + +int coslave_candev_init(void) +{ + struct sockaddr_can addr; + struct ifreq ifr; + int on = 1; + int ret; + + g_candev_sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (g_candev_sock < 0) + { + ret = -errno; + printf("ERROR: socket failed %d\n", errno); + return ret; + } + + strlcpy(ifr.ifr_name, CONFIG_EXAMPLES_LELYSLAVE_INTF, IFNAMSIZ); + + /* SocketCAN silently drops traffic while the interface is down, so bring + * it up here. This lets the example run without a separate "ifup" step. + */ + + if (netlib_ifup(ifr.ifr_name) < 0) + { + ret = -errno; + printf("ERROR: netlib_ifup %s failed %d\n", ifr.ifr_name, errno); + goto errout; + } + + ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name); + if (ifr.ifr_ifindex == 0) + { + ret = -errno; + printf("ERROR: if_nametoindex %s failed %d\n", ifr.ifr_name, errno); + goto errout; + } + + setsockopt(g_candev_sock, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, + &on, sizeof(on)); + + memset(&addr, 0, sizeof(addr)); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(g_candev_sock, (FAR struct sockaddr *)&addr, sizeof(addr)) < 0) + { + ret = -errno; + printf("ERROR: bind failed %d\n", errno); + goto errout; + } + + ret = fcntl(g_candev_sock, F_SETFL, O_NONBLOCK); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + return OK; + +errout: + close(g_candev_sock); + g_candev_sock = -1; + return ret; +} + +/**************************************************************************** + * Name: coslave_candev_send + ****************************************************************************/ + +int coslave_candev_send(FAR const struct can_msg *msg) +{ + struct can_frame frame; + ssize_t nwritten; + + memset(&frame, 0, sizeof(frame)); + + frame.can_id = msg->id; + if (msg->flags & CAN_FLAG_IDE) + { + frame.can_id |= CAN_EFF_FLAG; + } + + if (msg->flags & CAN_FLAG_RTR) + { + frame.can_id |= CAN_RTR_FLAG; + } + + frame.can_dlc = msg->len; + memcpy(frame.data, msg->data, msg->len); + + nwritten = write(g_candev_sock, &frame, sizeof(frame)); + if (nwritten != sizeof(frame)) + { + return -1; + } + + return 0; +} + +/**************************************************************************** + * Name: coslave_candev_recv + ****************************************************************************/ + +int coslave_candev_recv(FAR struct can_msg *msg) +{ + struct can_frame frame; + ssize_t nread; + + nread = read(g_candev_sock, &frame, sizeof(frame)); + if (nread < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return 0; + } + + return -1; + } + + if (nread < sizeof(frame)) + { + return 0; + } + + msg->id = frame.can_id & CAN_EFF_MASK; + msg->flags = 0; + if (frame.can_id & CAN_EFF_FLAG) + { + msg->flags |= CAN_FLAG_IDE; + } + + if (frame.can_id & CAN_RTR_FLAG) + { + msg->flags |= CAN_FLAG_RTR; + } + + msg->len = frame.can_dlc; + memcpy(msg->data, frame.data, frame.can_dlc); + + return 1; +} diff --git a/examples/lely_slave/sdev.h b/examples/lely_slave/sdev.h new file mode 100644 index 000000000..ca13a532d --- /dev/null +++ b/examples/lely_slave/sdev.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/examples/lely_slave/sdev.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_LELY_SDEV_H +#define __APPS_EXAMPLES_LELY_SDEV_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public data + ****************************************************************************/ + +extern const struct co_sdev g_sdev_slave; + +#endif /* __APPS_EXAMPLES_LELY_SDEV_H */ diff --git a/examples/lely_slave/sdev_slave.c b/examples/lely_slave/sdev_slave.c new file mode 100644 index 000000000..8f71558f5 --- /dev/null +++ b/examples/lely_slave/sdev_slave.c @@ -0,0 +1,594 @@ +/**************************************************************************** + * apps/examples/lely_slave/sdev_slave.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "sdev.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CO_SDEV_STRING(s) s + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Object 0x1000 sub-objects */ + +static const struct co_ssub g_1000_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Device type"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1001 sub-objects */ + +static const struct co_ssub g_1001_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Error register"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = CO_UNSIGNED8_MIN, +#endif + .val.u8 = CO_UNSIGNED8_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1005 sub-objects */ + +static const struct co_ssub g_1005_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID SYNC message"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000080lu, +#endif + .val.u32 = 0x00000080lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1012 sub-objects */ + +static const struct co_ssub g_1012_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID time stamp object"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x80000100lu, +#endif + .val.u32 = 0x80000100lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1017 sub-objects */ + +static const struct co_ssub g_1017_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Producer heartbeat time"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED16, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u16 = CO_UNSIGNED16_MIN, + .max.u16 = CO_UNSIGNED16_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u16 = 0x0032, +#endif + .val.u16 = 0x0032, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1018 sub-objects */ + +static const struct co_ssub g_1018_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Highest sub-index supported"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x04, +#endif + .val.u8 = 0x04, + .access = CO_ACCESS_CONST, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Vendor-ID"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000360lu, +#endif + .val.u32 = 0x00000360lu, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Product code"), +#endif + .subidx = 0x02, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Revision number"), +#endif + .subidx = 0x03, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Serial number"), +#endif + .subidx = 0x04, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1800 sub-objects */ + +static const struct co_ssub g_1800_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Highest sub-index supported"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x02, +#endif + .val.u8 = 0x02, + .access = CO_ACCESS_CONST, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x00000182lu, +#endif + .val.u32 = 0x00000182lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Transmission type"), +#endif + .subidx = 0x02, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x01, +#endif + .val.u8 = 0x01, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1a00 sub-objects */ + +static const struct co_ssub g_1a00_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Number of mapped objects"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED8, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u8 = CO_UNSIGNED8_MIN, + .max.u8 = CO_UNSIGNED8_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u8 = 0x01, +#endif + .val.u8 = 0x01, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Mapping 1"), +#endif + .subidx = 0x01, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = 0x21000020lu, +#endif + .val.u32 = 0x21000020lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x1f80 sub-objects */ + +static const struct co_ssub g_1f80_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("NMT startup"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = 0x00000004lu, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + | CO_OBJ_FLAGS_PARAMETER_VALUE + } +}; + +/* Object 0x2000 sub-objects */ + +static const struct co_ssub g_2000_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO download callback"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RW, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x2001 sub-objects */ + +static const struct co_ssub g_2001_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO upload callback"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RO, + .pdo_mapping = 0, + .flags = 0 + } +}; + +/* Object 0x2100 sub-objects */ + +static const struct co_ssub g_2100_sub[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Counter"), +#endif + .subidx = 0x00, + .type = CO_DEFTYPE_UNSIGNED32, +#if !LELY_NO_CO_OBJ_LIMITS + .min.u32 = CO_UNSIGNED32_MIN, + .max.u32 = CO_UNSIGNED32_MAX, +#endif +#if !LELY_NO_CO_OBJ_DEFAULT + .def.u32 = CO_UNSIGNED32_MIN, +#endif + .val.u32 = CO_UNSIGNED32_MIN, + .access = CO_ACCESS_RW, + .pdo_mapping = 1, + .flags = 0 + } +}; + +/* Object dictionary */ + +static const struct co_sobj g_g_sdev_slave_objs[] = +{ + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Device type"), +#endif + .idx = 0x1000, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1000_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Error register"), +#endif + .idx = 0x1001, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1001_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID SYNC message"), +#endif + .idx = 0x1005, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1005_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("COB-ID time stamp object"), +#endif + .idx = 0x1012, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1012_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Producer heartbeat time"), +#endif + .idx = 0x1017, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1017_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Identity object"), +#endif + .idx = 0x1018, + .code = CO_OBJECT_RECORD, + .nsub = 5, + .subs = g_1018_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("TPDO communication parameter"), +#endif + .idx = 0x1800, + .code = CO_OBJECT_RECORD, + .nsub = 3, + .subs = g_1800_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("TPDO mapping parameter"), +#endif + .idx = 0x1a00, + .code = CO_OBJECT_RECORD, + .nsub = 2, + .subs = g_1a00_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("NMT startup"), +#endif + .idx = 0x1f80, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_1f80_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO download callback"), +#endif + .idx = 0x2000, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2000_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Object with custom SDO upload callback"), +#endif + .idx = 0x2001, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2001_sub + }, + { +#if !LELY_NO_CO_OBJ_NAME + .name = CO_SDEV_STRING("Counter"), +#endif + .idx = 0x2100, + .code = CO_OBJECT_VAR, + .nsub = 1, + .subs = g_2100_sub + } +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct co_sdev g_sdev_slave = +{ + .id = 0x02, + .name = NULL, + .vendor_name = CO_SDEV_STRING("Apache NuttX RTOS"), + .vendor_id = 0x00000000, + .product_name = NULL, + .product_code = 0x00000000, + .revision = 0x00000000, + .order_code = NULL, + .baud = CO_BAUD_125, + .rate = 0, + .lss = 0, + .dummy = 0x000000fe, + .nobj = 12, + .objs = g_g_sdev_slave_objs +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/examples/lely_slave/slave_main.c b/examples/lely_slave/slave_main.c new file mode 100644 index 000000000..39cfa8af7 --- /dev/null +++ b/examples/lely_slave/slave_main.c @@ -0,0 +1,242 @@ +/**************************************************************************** + * apps/examples/lely_slave/slave_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "sdev.h" +#include "candev.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct co_net_s +{ + can_net_t *net; + co_dev_t *dev; + co_nmt_t *nmt; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct co_net_s g_net; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: elapsed_ms + ****************************************************************************/ + +static long elapsed_ms(const struct timespec *a, const struct timespec *b) +{ + return (a->tv_sec - b->tv_sec) * 1000 + + (a->tv_nsec - b->tv_nsec) / 1000000; +} + +/**************************************************************************** + * Name: on_can_send + ****************************************************************************/ + +static int on_can_send(const struct can_msg *msg, void *data) +{ + UNUSED(data); + + return coslave_candev_send(msg); +} + +/**************************************************************************** + * Name: on_time + ****************************************************************************/ + +static void on_time(co_time_t *time, const struct timespec *tp, void *data) +{ + UNUSED(time); + UNUSED(data); + + /* Update the wall clock */ + + clock_settime(CLOCK_REALTIME, tp); +} + +/**************************************************************************** + * Name: on_nmt_cs + ****************************************************************************/ + +static void on_nmt_cs(co_nmt_t *nmt, co_unsigned8_t cs, void *data) +{ + UNUSED(data); + + switch (cs) + { + case CO_NMT_CS_START: + case CO_NMT_CS_ENTER_PREOP: + { + co_time_set_ind(co_nmt_get_time(nmt), &on_time, NULL); + break; + } + + case CO_NMT_CS_RESET_NODE: + { + exit(0); + break; + } + + case CO_NMT_CS_STOP: + case CO_NMT_CS_RESET_COMM: + default: + { + break; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct timespec now; + struct timespec t_last; + struct can_msg msg; + co_unsigned32_t counter = 0; + int ret; + + memset(&g_net, 0, sizeof(g_net)); + setvbuf(stdout, NULL, _IONBF, 0); + + ret = coslave_candev_init(); + if (ret < 0) + { + printf("ERROR: coslave_candev_init failed %d\n", ret); + goto errout; + } + + g_net.net = can_net_create(); + if (!g_net.net) + { + printf("ERROR: can_net_create failed\n"); + goto errout; + } + + can_net_set_send_func(g_net.net, &on_can_send, NULL); + + clock_gettime(CLOCK_MONOTONIC, &now); + can_net_set_time(g_net.net, &now); + + /* Create dev from struct */ + + g_net.dev = co_dev_create_from_sdev(&g_sdev_slave); + if (!g_net.dev) + { + printf("ERROR: co_dev_create_from_sdev failed\n"); + goto errout; + } + + g_net.nmt = co_nmt_create(g_net.net, g_net.dev); + if (!g_net.nmt) + { + printf("ERROR: co_nmt_create failed\n"); + goto errout; + } + + /* Start the NMT service by resetting the node */ + + co_nmt_cs_ind(g_net.nmt, CO_NMT_CS_RESET_NODE); + + /* Set the NMT indication function after the initial reset */ + + co_nmt_set_cs_ind(g_net.nmt, &on_nmt_cs, NULL); + + printf("lely CANopen slave running (node 0x%02x)\n", g_sdev_slave.id); + + clock_gettime(CLOCK_MONOTONIC, &t_last); + + while (1) + { + clock_gettime(CLOCK_MONOTONIC, &now); + can_net_set_time(g_net.net, &now); + + while (coslave_candev_recv(&msg) > 0) + { + can_net_recv(g_net.net, &msg); + } + + /* Update the counter mapped into TPDO1 (0x2100) every 100 ms. + * When the master has brought us to OPERATIONAL, the synchronous TPDO + * sends the current value on each SYNC. + */ + + if (elapsed_ms(&now, &t_last) >= 100) + { + t_last = now; + counter++; + co_dev_set_val_u32(g_net.dev, 0x2100, 0x00, counter); + } + + usleep(1000); + } + +errout: + if (g_net.nmt) + { + co_nmt_destroy(g_net.nmt); + } + + if (g_net.dev) + { + co_dev_destroy(g_net.dev); + } + + if (g_net.net) + { + can_net_destroy(g_net.net); + } + + return 0; +} diff --git a/examples/lis3dsh_reader/lis3dsh_reader_main.c b/examples/lis3dsh_reader/lis3dsh_reader_main.c index dad00b312..98194ea66 100644 --- a/examples/lis3dsh_reader/lis3dsh_reader_main.c +++ b/examples/lis3dsh_reader/lis3dsh_reader_main.c @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/lp503x/lp503x_main.c b/examples/lp503x/lp503x_main.c index 5a43da39c..fafb617e4 100644 --- a/examples/lp503x/lp503x_main.c +++ b/examples/lp503x/lp503x_main.c @@ -119,9 +119,9 @@ static struct lp503x_cmd_s g_lp503x_cmds[] = }, { "c", - "hex", + "[hex]", lp503x_cmd_setcolour, - LP503X_HELP_TEXT("Hex RGB colour required") + LP503X_HELP_TEXT("show or set current RGB colour") }, { "l", @@ -350,7 +350,7 @@ static int lp503x_cmd_set_individual_mode(FAR char *parg) static int lp503x_cmd_set_banka_colour(FAR char *parg) { - uint8_t requested_colour; + int requested_colour; requested_colour = atoi(parg); @@ -369,7 +369,7 @@ static int lp503x_cmd_set_banka_colour(FAR char *parg) static int lp503x_cmd_set_bankb_colour(FAR char *parg) { - uint8_t requested_colour; + int requested_colour; requested_colour = atoi(parg); @@ -388,7 +388,7 @@ static int lp503x_cmd_set_bankb_colour(FAR char *parg) static int lp503x_cmd_set_bankc_colour(FAR char *parg) { - uint8_t requested_colour; + int requested_colour; requested_colour = atoi(parg); @@ -417,8 +417,9 @@ static int lp503x_cmd_brightness(FAR char *parg) int lednum; int requested_brightness; - requested_brightness = ((int)strtol(parg, NULL, 0)) & 0xff; - if ((requested_brightness > 255) || (requested_brightness < 0)) + requested_brightness = (int)strtol(parg, NULL, 0); + if ((requested_brightness > MAX_BRIGHTNESS) || + (requested_brightness < 0)) { printf("ERROR: brightness must be in range 0..255\n"); ret = -EINVAL; @@ -516,7 +517,7 @@ static int lp503x_cmd_pattern(FAR char *parg) static int lp503x_cmd_set_bank_brightness(FAR char *parg) { int bank_brightness; - if (parg != NULL || *parg != '\0') + if (parg != NULL && *parg != '\0') { bank_brightness = atoi(parg); @@ -540,13 +541,14 @@ static int lp503x_cmd_set_bank_brightness(FAR char *parg) /**************************************************************************** * Name:lp503x_setcolour * + * shows the current RGB colour when no argument is provided, otherwise * sets the current RGB colour to be used ****************************************************************************/ static int lp503x_cmd_setcolour(FAR char *parg) { int colour; - if (parg == NULL || *parg != '\0') + if (parg == NULL || *parg == '\0') { printf("current colour is: 0x%06x\n", current_colour); } @@ -632,7 +634,7 @@ int main(int argc, FAR char *argv[]) int len; int x; - fd = open(CONFIG_EXAMPLES_LP503X_DEVPATH, O_CREAT); + fd = open(CONFIG_EXAMPLES_LP503X_DEVPATH, O_WRONLY); if (fd < 0) { fprintf(stderr, "ERROR: Failed to open %s: %d\n", @@ -650,9 +652,9 @@ int main(int argc, FAR char *argv[]) len = readline_stream(buffer, sizeof(buffer), stdin, stdout); - buffer[len] = '\0'; if (len > 0) { + buffer[len] = '\0'; if (strncmp(buffer, "!", 1) != 0) { /* a command */ diff --git a/examples/lsm303_reader/lsm303_reader_main.c b/examples/lsm303_reader/lsm303_reader_main.c index b48e81a2d..51611720d 100644 --- a/examples/lsm303_reader/lsm303_reader_main.c +++ b/examples/lsm303_reader/lsm303_reader_main.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include diff --git a/examples/lsm6dsl_reader/lsm6dsl_reader_main.c b/examples/lsm6dsl_reader/lsm6dsl_reader_main.c index c3ec68ff7..6f7cf0355 100644 --- a/examples/lsm6dsl_reader/lsm6dsl_reader_main.c +++ b/examples/lsm6dsl_reader/lsm6dsl_reader_main.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include diff --git a/examples/lvgldemo/lvgldemo.c b/examples/lvgldemo/lvgldemo.c index 55d6620ed..b6ab1e5ab 100644 --- a/examples/lvgldemo/lvgldemo.c +++ b/examples/lvgldemo/lvgldemo.c @@ -38,23 +38,6 @@ * Pre-processor Definitions ****************************************************************************/ -/* Should we perform board-specific driver initialization? There are two - * ways that board initialization can occur: 1) automatically via - * board_late_initialize() during bootupif CONFIG_BOARD_LATE_INITIALIZE - * or 2). - * via a call to boardctl() if the interface is enabled - * (CONFIG_BOARDCTL=y). - * If this task is running as an NSH built-in application, then that - * initialization has probably already been performed otherwise we do it - * here. - */ - -#undef NEED_BOARDINIT - -#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_NSH_ARCHINIT) -# define NEED_BOARDINIT 1 -#endif - /**************************************************************************** * Private Types ****************************************************************************/ @@ -122,13 +105,6 @@ int main(int argc, FAR char *argv[]) return -1; } -#ifdef NEED_BOARDINIT - /* Perform board-specific driver initialization */ - - boardctl(BOARDIOC_INIT, 0); - -#endif - lv_init(); lv_nuttx_dsc_init(&info); diff --git a/examples/lvglterm/CMakeLists.txt b/examples/lvglterm/CMakeLists.txt index fe6d3c48c..c3f84b26e 100644 --- a/examples/lvglterm/CMakeLists.txt +++ b/examples/lvglterm/CMakeLists.txt @@ -21,6 +21,12 @@ # ############################################################################## if(CONFIG_EXAMPLES_LVGLTERM) + if(CONFIG_EXAMPLES_LVGLTERM_INPUT_TOUCH) + set(lvglterm_input lvglterm_touch.c) + else() + set(lvglterm_input lvglterm_kbd.c) + endif() + nuttx_add_application( NAME lvglterm @@ -31,5 +37,6 @@ if(CONFIG_EXAMPLES_LVGLTERM) MODULE ${CONFIG_EXAMPLES_LVGLTERM} SRCS - lvglterm.c) + lvglterm.c + ${lvglterm_input}) endif() diff --git a/examples/lvglterm/Kconfig b/examples/lvglterm/Kconfig index dec9d9b24..7c4a70683 100644 --- a/examples/lvglterm/Kconfig +++ b/examples/lvglterm/Kconfig @@ -12,6 +12,60 @@ menuconfig EXAMPLES_LVGLTERM if EXAMPLES_LVGLTERM +choice + prompt "LVGL Terminal input source" + default EXAMPLES_LVGLTERM_INPUT_TOUCH + ---help--- + Select how the terminal receives keystrokes. Only one input source + is built at a time. + +config EXAMPLES_LVGLTERM_INPUT_TOUCH + bool "On-screen keyboard (touch)" + ---help--- + Input comes from an on-screen LVGL keyboard operated by touch; a + command line is typed and submitted with Enter. This is the default + and matches the original behaviour. + +config EXAMPLES_LVGLTERM_INPUT_KBD + bool "Physical keyboard" + depends on INPUT_KEYBOARD + ---help--- + Any keyboard registered with keyboard_register(): USB HID, a matrix, + the simulator, virtio. The terminal does not need to know which one + it is. Cursor Up and Down scroll the terminal, everything else goes + to the shell. + +endchoice + +config EXAMPLES_LVGLTERM_KBD_DEV + string "Keyboard device path" + default "/dev/kbd0" + depends on EXAMPLES_LVGLTERM_INPUT_KBD + ---help--- + Keyboard device the terminal reads from. Can also be overridden at + run time by passing the path as the first command-line argument + (useful when more than one keyboard is present). + + Note that the USB HID driver names its devices /dev/kbda onwards, + since they come and go as keyboards are plugged in. + +choice + prompt "LVGL Terminal font" + default EXAMPLES_LVGLTERM_FONT_UNSCII_16 + ---help--- + Monospaced font used to render the terminal. Use the smaller UNSCII 8 + on low-resolution displays where UNSCII 16 shows too few columns. + +config EXAMPLES_LVGLTERM_FONT_UNSCII_8 + bool "UNSCII 8 (small)" + select LV_FONT_UNSCII_8 + +config EXAMPLES_LVGLTERM_FONT_UNSCII_16 + bool "UNSCII 16" + select LV_FONT_UNSCII_16 + +endchoice + config EXAMPLES_LVGLTERM_PRIORITY int "lvglterm task priority" default 100 diff --git a/examples/lvglterm/Makefile b/examples/lvglterm/Makefile index 531d8c405..0d4c5d0c2 100644 --- a/examples/lvglterm/Makefile +++ b/examples/lvglterm/Makefile @@ -27,8 +27,15 @@ PRIORITY = $(CONFIG_EXAMPLES_LVGLTERM_PRIORITY) STACKSIZE = $(CONFIG_EXAMPLES_LVGLTERM_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_LVGLTERM) -# LVGL Terminal +# LVGL Terminal. lvglterm.c holds the shared core; the input source is +# selected at build time (on-screen keyboard for touch, /dev/kbdN otherwise). MAINSRC = lvglterm.c +ifneq ($(CONFIG_EXAMPLES_LVGLTERM_INPUT_TOUCH),y) +CSRCS = lvglterm_kbd.c +else +CSRCS = lvglterm_touch.c +endif + include $(APPDIR)/Application.mk diff --git a/examples/lvglterm/lvglterm.c b/examples/lvglterm/lvglterm.c index 90269daa8..f2dcb1f10 100644 --- a/examples/lvglterm/lvglterm.c +++ b/examples/lvglterm/lvglterm.c @@ -23,6 +23,12 @@ /* Reference: * "NuttX RTOS for PinePhone: LVGL Terminal for NSH Shell" * https://lupyuen.github.io/articles/terminal + * + * Code shared by both input variants: it starts the NSH shell with its + * standard streams redirected through pipes, renders the shell output in an + * LVGL text area, and delegates the input source to the selected variant + * (on-screen keyboard in lvglterm_touch.c, physical keyboard in + * lvglterm_kbd.c). */ /**************************************************************************** @@ -35,12 +41,15 @@ #include #include #include +#include #include -#include +#include #include #include #include +#include "lvglterm.h" + /* NSH Task requires posix_spawn() */ #ifndef CONFIG_LIBC_EXECFUNCS @@ -53,41 +62,34 @@ # error FIFO and Named Pipe Drivers should be enabled in the configuration #endif -/* NSH Output requires a Monospaced Font */ +/* NSH Output requires a Monospaced Font. The size is selectable so that + * low-resolution displays can use the smaller UNSCII 8. + */ -#ifndef CONFIG_LV_FONT_UNSCII_16 -# error LVGL Font UNSCII 16 should be enabled in the configuration +#if defined(CONFIG_EXAMPLES_LVGLTERM_FONT_UNSCII_8) +# ifndef CONFIG_LV_FONT_UNSCII_8 +# error LVGL Font UNSCII 8 should be enabled in the configuration +# endif +# define LVGLTERM_FONT lv_font_unscii_8 +#else +# ifndef CONFIG_LV_FONT_UNSCII_16 +# error LVGL Font UNSCII 16 should be enabled in the configuration +# endif +# define LVGLTERM_FONT lv_font_unscii_16 #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* Should we perform board-specific driver initialization? There are two - * ways that board initialization can occur: 1) automatically via - * board_late_initialize() during bootupif CONFIG_BOARD_LATE_INITIALIZE - * or 2). - * via a call to boardctl() if the interface is enabled - * (CONFIG_BOARDCTL=y). - * If this task is running as an NSH built-in application, then that - * initialization has probably already been performed otherwise we do it - * here. - */ - -#undef NEED_BOARDINIT - -#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_NSH_ARCHINIT) -# define NEED_BOARDINIT 1 -#endif - /* How often to poll for output from NSH Shell (milliseconds) */ -#define TIMER_PERIOD_MS 100 +#define TIMER_PERIOD_MS 20 -/* Read and Write Pipes for NSH stdin, stdout and stderr */ +/* Trim the output text area once it grows past this many characters */ -#define READ_PIPE 0 -#define WRITE_PIPE 1 +#define TERM_MAXCHARS 4096 +#define TERM_KEEPCHARS 3072 /* NSH Task to be started */ @@ -98,38 +100,34 @@ ****************************************************************************/ static int create_widgets(void); -static void timer_callback(lv_timer_t * timer); -static void input_callback(lv_event_t * e); -static bool has_input(int fd); -static void remove_escape_codes(char *buf, int len); +static void timer_callback(lv_timer_t *timer); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Pipe to NSH stdin, written by the selected input variant */ + +int g_nsh_stdin[2]; + +/* LVGL Column Container and NSH Output Text Area (shared with the variant) */ + +lv_obj_t *g_col; +lv_obj_t *g_output; + +/* LVGL Font Style for NSH Input and Output */ + +lv_style_t g_terminal_style; /**************************************************************************** * Private Data ****************************************************************************/ -/* Pipes for NSH Shell: stdin, stdout, stderr */ +/* Pipes for NSH stdout and stderr */ -static int g_nsh_stdin[2]; static int g_nsh_stdout[2]; static int g_nsh_stderr[2]; -/* LVGL Column Container for NSH Widgets */ - -static lv_obj_t *g_col; - -/* LVGL Text Area Widgets for NSH Input and Output */ - -static lv_obj_t *g_input; -static lv_obj_t *g_output; - -/* LVGL Keyboard Widget for NSH Terminal */ - -static lv_obj_t *g_kb; - -/* LVGL Font Style for NSH Input and Output */ - -static lv_style_t g_terminal_style; - /* LVGL Timer for polling NSH Output */ static lv_timer_t *g_timer; @@ -142,108 +140,121 @@ static char * const g_nsh_argv[] = }; /**************************************************************************** - * Private Functions + * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: create_terminal + * Name: lvglterm_has_input * * Description: - * Create the LVGL Terminal. Start the NSH Shell and redirect the NSH - * stdin, stdout and stderr to the LVGL Widgets. - * - * Input Parameters: - * None - * - * Returned Value: - * Zero (OK) on success; a negated errno value is returned on any failure. + * Return true if the file descriptor has data to be read. * ****************************************************************************/ -static int create_terminal(void) +bool lvglterm_has_input(int fd) { - int ret; - pid_t pid; + struct pollfd fdp; - /* Create the pipes for NSH Shell: stdin, stdout and stderr */ - - ret = pipe(g_nsh_stdin); - if (ret < 0) - { - _err("stdin pipe failed: %d\n", errno); - return ERROR; - } - - ret = pipe(g_nsh_stdout); - if (ret < 0) - { - _err("stdout pipe failed: %d\n", errno); - return ERROR; - } - - ret = pipe(g_nsh_stderr); - if (ret < 0) - { - _err("stderr pipe failed: %d\n", errno); - return ERROR; - } - - /* Close default stdin, stdout and stderr */ - - close(0); - close(1); - close(2); - - /* Assign the new pipes as stdin, stdout and stderr */ - - dup2(g_nsh_stdin[READ_PIPE], 0); - dup2(g_nsh_stdout[WRITE_PIPE], 1); - dup2(g_nsh_stderr[WRITE_PIPE], 2); - - /* Start the NSH Shell and inherit stdin, stdout and stderr */ - - ret = posix_spawn(&pid, /* Returned Task ID */ - NSH_TASK, /* NSH Path */ - NULL, /* Inherit stdin, stdout and stderr */ - NULL, /* Default spawn attributes */ - g_nsh_argv, /* Arguments */ - NULL); /* No environment */ - if (ret < 0) - { - int errcode = errno; - _err("posix_spawn failed: %d\n", errcode); - return -errcode; - } - - /* Create an LVGL Timer to poll for output from NSH Shell */ - - g_timer = lv_timer_create(timer_callback, /* Callback Function */ - TIMER_PERIOD_MS, /* Timer Period (millisec) */ - NULL); /* Callback Argument */ - DEBUGASSERT(g_timer != NULL); - - /* Create the LVGL Terminal Widgets */ - - ret = create_widgets(); - if (ret < 0) - { - return ret; - } - - return OK; + fdp.fd = fd; + fdp.events = POLLIN; + return poll(&fdp, 1, 0) > 0 && (fdp.revents & POLLIN) != 0; } +/**************************************************************************** + * Name: lvglterm_add_output + * + * Description: + * Append text to the NSH output text area, dropping VT100 escape sequences + * and carriage returns, honouring backspace, and trimming the buffer once + * it grows too large. Must run in the LVGL thread. + * + ****************************************************************************/ + +void lvglterm_add_output(FAR const char *buf, int len) +{ + char clean[64]; + int ci = 0; + int i; + + for (i = 0; i < len; i++) + { + char c = buf[i]; + + if (c == 0x1b) /* ESC: skip the escape sequence */ + { + i++; + if (i < len && buf[i] == '[') + { + for (i++; i < len; i++) + { + if ((buf[i] >= 'A' && buf[i] <= 'Z') || + (buf[i] >= 'a' && buf[i] <= 'z')) + { + break; + } + } + } + + continue; + } + + if (c == '\r') /* Drop carriage return */ + { + continue; + } + + if (c == 0x08 || c == 0x7f) /* Backspace/DEL: erase last character */ + { + if (ci > 0) + { + clean[ci] = '\0'; + lv_textarea_add_text(g_output, clean); + ci = 0; + } + + lv_textarea_delete_char(g_output); + continue; + } + + if (c == '\n' || c == '\t' || (c >= 0x20 && c < 0x7f)) + { + clean[ci++] = c; + if (ci >= (int)sizeof(clean) - 1) + { + clean[ci] = '\0'; + lv_textarea_add_text(g_output, clean); + ci = 0; + } + } + } + + if (ci > 0) + { + clean[ci] = '\0'; + lv_textarea_add_text(g_output, clean); + } + + /* Trim the text area if it has grown too large */ + + if ((int)strlen(lv_textarea_get_text(g_output)) > TERM_MAXCHARS) + { + FAR const char *txt = lv_textarea_get_text(g_output); + int total = strlen(txt); + lv_textarea_set_text(g_output, txt + (total - TERM_KEEPCHARS)); + lv_textarea_set_cursor_pos(g_output, LV_TEXTAREA_CURSOR_LAST); + } +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + /**************************************************************************** * Name: create_widgets * * Description: - * Create the LVGL Widgets for LVGL Terminal. - * - * Input Parameters: - * None - * - * Returned Value: - * Zero (OK) on success; a negated errno value is returned on any failure. + * Create the shared LVGL widgets: a column container and the NSH output + * text area. The input source is added by the selected variant. * ****************************************************************************/ @@ -252,7 +263,7 @@ static int create_widgets(void) /* Set the Font Style for NSH Input and Output to a Monospaced Font */ lv_style_init(&g_terminal_style); - lv_style_set_text_font(&g_terminal_style, &lv_font_unscii_16); + lv_style_set_text_font(&g_terminal_style, &LVGLTERM_FONT); /* Create an LVGL Container with Column Flex Direction */ @@ -260,7 +271,7 @@ static int create_widgets(void) DEBUGASSERT(g_col != NULL); lv_obj_set_size(g_col, LV_PCT(100), LV_PCT(100)); lv_obj_set_flex_flow(g_col, LV_FLEX_FLOW_COLUMN); - lv_obj_set_style_pad_all(g_col, 0, 0); /* No padding */ + lv_obj_set_style_pad_all(g_col, 0, 0); /* Create an LVGL Text Area Widget for NSH Output */ @@ -268,29 +279,69 @@ static int create_widgets(void) DEBUGASSERT(g_output != NULL); lv_obj_add_style(g_output, &g_terminal_style, 0); lv_obj_set_width(g_output, LV_PCT(100)); - lv_obj_set_flex_grow(g_output, 1); /* Fill the column */ + lv_obj_set_flex_grow(g_output, 1); - /* Create an LVGL Text Area Widget for NSH Input */ + return OK; +} - g_input = lv_textarea_create(g_col); - DEBUGASSERT(g_input != NULL); - lv_obj_add_style(g_input, &g_terminal_style, 0); - lv_obj_set_size(g_input, LV_PCT(100), LV_SIZE_CONTENT); +/**************************************************************************** + * Name: create_terminal + * + * Description: + * Start the NSH shell with its streams redirected to pipes, create the + * shared widgets and the output-polling timer, and set up the input + * variant. + * + ****************************************************************************/ - /* Create an LVGL Keyboard Widget */ +static int create_terminal(int argc, FAR char *argv[]) +{ + int ret; + pid_t pid; - g_kb = lv_keyboard_create(g_col); - DEBUGASSERT(g_kb != NULL); - lv_obj_set_style_pad_all(g_kb, 0, 0); /* No padding */ + /* Create the pipes for NSH Shell: stdin, stdout and stderr */ - /* Register the Callback Function for NSH Input */ + if (pipe(g_nsh_stdin) < 0 || pipe(g_nsh_stdout) < 0 || + pipe(g_nsh_stderr) < 0) + { + fprintf(stderr, "pipe failed: %d\n", errno); + return ERROR; + } - lv_obj_add_event_cb(g_input, input_callback, LV_EVENT_ALL, NULL); + /* Close default stdin, stdout and stderr and assign the new pipes */ - /* Set the Keyboard to populate the NSH Input Text Area */ + close(0); + close(1); + close(2); - lv_keyboard_set_textarea(g_kb, g_input); + dup2(g_nsh_stdin[READ_PIPE], 0); + dup2(g_nsh_stdout[WRITE_PIPE], 1); + dup2(g_nsh_stderr[WRITE_PIPE], 2); + /* Start the NSH Shell and inherit stdin, stdout and stderr */ + + ret = posix_spawn(&pid, NSH_TASK, NULL, NULL, g_nsh_argv, NULL); + if (ret < 0) + { + int errcode = errno; + fprintf(stderr, "posix_spawn failed: %d\n", errcode); + return -errcode; + } + + /* Create an LVGL Timer to poll for output from NSH Shell */ + + g_timer = lv_timer_create(timer_callback, TIMER_PERIOD_MS, NULL); + DEBUGASSERT(g_timer != NULL); + + /* Create the shared widgets and the input source for the variant */ + + ret = create_widgets(); + if (ret < 0) + { + return ret; + } + + lvglterm_input_create(argc, argv); return OK; } @@ -298,224 +349,37 @@ static int create_widgets(void) * Name: timer_callback * * Description: - * Callback Function for LVGL Timer. Poll NSH stdout and stderr for output - * and display the output. - * - * Input Parameters: - * timer - LVGL Timer for the callback - * - * Returned Value: - * None + * Poll NSH stdout and stderr for output and render it, then let the input + * variant perform its periodic work. Runs in the LVGL thread. * ****************************************************************************/ static void timer_callback(lv_timer_t *timer) { - int ret; static char buf[64]; - - DEBUGASSERT(g_nsh_stdout[READ_PIPE] != 0); - DEBUGASSERT(g_nsh_stderr[READ_PIPE] != 0); - - /* Poll NSH stdout to check if there's output to be processed */ - - if (has_input(g_nsh_stdout[READ_PIPE])) - { - /* Read the output from NSH stdout */ - - ret = read(g_nsh_stdout[READ_PIPE], buf, sizeof(buf) - 1); - if (ret > 0) - { - /* Add to NSH Output Text Area */ - - buf[ret] = 0; - remove_escape_codes(buf, ret); - - DEBUGASSERT(g_output != NULL); - lv_textarea_add_text(g_output, buf); - } - } - - /* Poll NSH stderr to check if there's output to be processed */ - - if (has_input(g_nsh_stderr[READ_PIPE])) - { - /* Read the output from NSH stderr */ - - ret = read(g_nsh_stderr[READ_PIPE], buf, sizeof(buf) - 1); - if (ret > 0) - { - /* Add to NSH Output Text Area */ - - buf[ret] = 0; - remove_escape_codes(buf, ret); - - DEBUGASSERT(g_output != NULL); - lv_textarea_add_text(g_output, buf); - } - } -} - -/**************************************************************************** - * Name: input_callback - * - * Description: - * Callback Function for NSH Input Text Area. If Enter Key was pressed, - * send the NSH Input Command to NSH stdin. - * - * Input Parameters: - * e - LVGL Event for the callback - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void input_callback(lv_event_t *e) -{ int ret; - /* Decode the LVGL Event */ + /* Drain the input variant first (local echo, scroll) so that what the user + * typed is rendered before the resulting shell output. + */ - const lv_event_code_t code = lv_event_get_code(e); + lvglterm_input_poll(); - /* If NSH Input Text Area has changed, get the Key Pressed */ - - if (code == LV_EVENT_VALUE_CHANGED) + if (lvglterm_has_input(g_nsh_stdout[READ_PIPE])) { - /* Get the Button Index of the Keyboard Button Pressed */ - - const uint16_t id = lv_keyboard_get_selected_button(g_kb); - - /* Get the Text of the Keyboard Button */ - - const char *key = lv_keyboard_get_button_text(g_kb, id); - if (key == NULL) + ret = read(g_nsh_stdout[READ_PIPE], buf, sizeof(buf)); + if (ret > 0) { - return; - } - - /* If Key Pressed is Enter, send the Command to NSH stdin */ - - if (key[0] == 0xef && key[1] == 0xa2 && key[2] == 0xa2) - { - /* Read the NSH Input */ - - const char *cmd; - DEBUGASSERT(g_input != NULL); - cmd = lv_textarea_get_text(g_input); - if (cmd == NULL || cmd[0] == 0) - { - return; - } - - /* Send the Command to NSH stdin */ - - DEBUGASSERT(g_nsh_stdin[WRITE_PIPE] != 0); - ret = write(g_nsh_stdin[WRITE_PIPE], cmd, strlen(cmd)); - DEBUGASSERT(ret == strlen(cmd)); - - /* Erase the NSH Input */ - - lv_textarea_set_text(g_input, ""); + lvglterm_add_output(buf, ret); } } -} -/**************************************************************************** - * Name: has_input - * - * Description: - * Return true if the File Descriptor has data to be read. - * - * Input Parameters: - * fd - File Descriptor to be checked - * - * Returned Value: - * True if File Descriptor has data to be read; False otherwise - * - ****************************************************************************/ - -static bool has_input(int fd) -{ - int ret; - - /* Poll the File Descriptor for input */ - - struct pollfd fdp; - fdp.fd = fd; - fdp.events = POLLIN; - ret = poll(&fdp, /* File Descriptors */ - 1, /* Number of File Descriptors */ - 0); /* Poll Timeout (Milliseconds) */ - - if (ret > 0) + if (lvglterm_has_input(g_nsh_stderr[READ_PIPE])) { - /* If poll is OK and there is input */ - - if ((fdp.revents & POLLIN) != 0) + ret = read(g_nsh_stderr[READ_PIPE], buf, sizeof(buf)); + if (ret > 0) { - /* Report that there is input */ - - return true; - } - - /* Else report no input */ - - return false; - } - else if (ret == 0) - { - /* If timeout, report no input */ - - return false; - } - else if (ret < 0) - { - /* Handle error */ - - _err("poll failed: %d, fd=%d\n", ret, fd); - return false; - } - - /* Never comes here */ - - DEBUGPANIC(); - return false; -} - -/**************************************************************************** - * Name: remove_escape_codes - * - * Description: - * Remove ANSI Escape Codes from the string. Assumes that buf[len] is 0. - * - * Input Parameters: - * buf - String to be processed - * len - Length of string - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void remove_escape_codes(char *buf, int len) -{ - int i; - int j; - - for (i = 0; i < len; i++) - { - /* Escape Code looks like 0x1b 0x5b 0x4b */ - - if (buf[i] == 0x1b) - { - /* Remove 3 bytes */ - - for (j = i; j + 2 < len; j++) - { - buf[j] = buf[j + 3]; - } + lvglterm_add_output(buf, ret); } } } @@ -528,15 +392,10 @@ static void remove_escape_codes(char *buf, int len) * Name: main or lvglterm_main * * Description: - * Start an LVGL Terminal that runs interactive commands with NSH Shell. - * NSH Commands are entered through an LVGL Keyboard. NSH Output is - * rendered in an LVGL Widget. - * - * Input Parameters: - * Standard argc and argv - * - * Returned Value: - * Zero on success; a positive, non-zero value on failure. + * Start an LVGL Terminal that runs interactive commands with NSH. + * NSH output is rendered in an LVGL text area; the input comes from an + * on-screen keyboard (touch) or a physical keyboard, selected at build + * time. * ****************************************************************************/ @@ -550,22 +409,11 @@ int main(int argc, FAR char *argv[]) uv_loop_t ui_loop; #endif -#ifdef NEED_BOARDINIT - /* Perform board-specific driver initialization */ - - boardctl(BOARDIOC_INIT, 0); - #ifdef CONFIG_BOARDCTL_FINALINIT - /* Perform architecture-specific final-initialization (if configured) */ - boardctl(BOARDIOC_FINALINIT, 0); #endif -#endif - - /* LVGL initialization */ lv_init(); - lv_nuttx_dsc_init(&info); #ifdef CONFIG_LV_USE_NUTTX_LCD @@ -573,23 +421,18 @@ int main(int argc, FAR char *argv[]) #endif lv_nuttx_init(&info, &result); - if (result.disp == NULL) { - LV_LOG_ERROR("lv_demos initialization failure!"); + LV_LOG_ERROR("lv_nuttx_init failure!"); return 1; } - /* Create the LVGL Widgets */ - - ret = create_terminal(); + ret = create_terminal(argc, argv); if (ret < 0) { return EXIT_FAILURE; } - /* Handle LVGL tasks */ - #ifdef CONFIG_LV_USE_NUTTX_LIBUV lv_nuttx_uv_loop(&ui_loop, &result); #else @@ -597,13 +440,10 @@ int main(int argc, FAR char *argv[]) { uint32_t idle; idle = lv_timer_handler(); - - /* Minimum sleep of 1ms */ - idle = idle ? idle : 1; usleep(idle * 1000); } - #endif + return EXIT_SUCCESS; } diff --git a/examples/lvglterm/lvglterm.h b/examples/lvglterm/lvglterm.h new file mode 100644 index 000000000..627919cdd --- /dev/null +++ b/examples/lvglterm/lvglterm.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * apps/examples/lvglterm/lvglterm.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_LVGLTERM_LVGLTERM_H +#define __APPS_EXAMPLES_LVGLTERM_LVGLTERM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Read and Write ends of the NSH pipes */ + +#define READ_PIPE 0 +#define WRITE_PIPE 1 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Shared state owned by the core (lvglterm.c). The input variant writes the + * shell input to g_nsh_stdin, adds its widgets under g_col (styled with + * g_terminal_style) and may render into g_output. + */ + +extern int g_nsh_stdin[2]; /* Input variant -> NSH stdin */ +extern lv_obj_t *g_col; /* Column container (widget parent) */ +extern lv_obj_t *g_output; /* NSH output text area */ +extern lv_style_t g_terminal_style; /* Monospaced font style */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Shared helpers provided by the core */ + +bool lvglterm_has_input(int fd); +void lvglterm_add_output(FAR const char *buf, int len); + +/* Provided by the selected input variant (lvglterm_touch.c or + * lvglterm_kbd.c). lvglterm_input_create() sets up the input source under + * g_col and wires it to g_nsh_stdin. lvglterm_input_poll() runs on every + * LVGL timer tick (LVGL thread) for periodic work; it may be a no-op. + */ + +void lvglterm_input_create(int argc, FAR char *argv[]); +void lvglterm_input_poll(void); + +#endif /* __APPS_EXAMPLES_LVGLTERM_LVGLTERM_H */ diff --git a/examples/lvglterm/lvglterm_kbd.c b/examples/lvglterm/lvglterm_kbd.c new file mode 100644 index 000000000..daea4c50b --- /dev/null +++ b/examples/lvglterm/lvglterm_kbd.c @@ -0,0 +1,255 @@ +/**************************************************************************** + * apps/examples/lvglterm/lvglterm_kbd.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Physical-keyboard input variant of the LVGL terminal: the keyboard device + * is polled (non-blocking) from the LVGL thread and key presses are streamed + * to NSH; the shell output fills the whole screen. Works on any board with + * a keyboard driver; the device defaults to CONFIG_EXAMPLES_LVGLTERM_KBD_DEV + * and can be overridden by the first command-line argument. + * + * Any keyboard registered with keyboard_register() works: USB HID, a + * matrix, the simulator, virtio. Which one it is makes no difference here. + * + * The device delivers struct keyboard_event_s events unless the kernel was + * built with INPUT_KEYBOARD_BYTESTREAM, in which case it delivers the byte + * stream that the keyboard codec defines. That is a property of the build + * rather than of the hardware, so it is what selects the reader below. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM +# include +# include +#endif + +#include + +#include "lvglterm.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SCROLL_STEP 24 /* Pixels scrolled per Up/Down keypress */ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_kfd = -1; /* Keyboard device fd (opened O_NONBLOCK) */ +static FAR const char *g_kbddev; /* Keyboard device path (/dev/kbdN) */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: feed_char + * + * Description: + * Forward one character to NSH stdin and, for printable characters and + * newline, echo it on screen. Control keys such as backspace are echoed + * by the shell's own line editor (via its stdout) to avoid handling them + * twice. Carriage return is normalised to newline. Runs in the LVGL + * thread, so it may touch the LVGL widgets directly. + * + ****************************************************************************/ + +static void feed_char(char ch) +{ + if (ch == '\r') + { + ch = '\n'; + } + + write(g_nsh_stdin[WRITE_PIPE], &ch, 1); + + if (ch == '\n' || ((uint8_t)ch >= 0x20 && (uint8_t)ch < 0x7f)) + { + lvglterm_add_output(&ch, 1); + } +} + +/**************************************************************************** + * Name: scroll_terminal + * + * Description: + * Scroll the output text area up (older output) or down by one step. Runs + * in the LVGL thread. + * + ****************************************************************************/ + +static void scroll_terminal(bool up) +{ + int32_t dy = up ? SCROLL_STEP : -SCROLL_STEP; + + lv_obj_scroll_by(g_output, 0, dy, LV_ANIM_OFF); +} + +/**************************************************************************** + * Name: handle_key + * + * Description: + * Act on one key press. Cursor Up and Down scroll the terminal, anything + * else goes to the shell. + * + * Input Parameters: + * code - The character, or a value from enum kbd_keycode_e + * special - True if the code is a keycode rather than a character. The + * two ranges overlap, so this is what tells them apart. + * + ****************************************************************************/ + +static void handle_key(uint32_t code, bool special) +{ + if (special) + { + if (code == KEYCODE_UP) + { + scroll_terminal(true); + } + else if (code == KEYCODE_DOWN) + { + scroll_terminal(false); + } + + /* Any other special key has no meaning to a terminal */ + + return; + } + + feed_char(code); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lvglterm_input_create + * + * Description: + * Resolve the keyboard device (first command-line argument, else the + * configured default) and open it non-blocking so that it can be polled + * from the LVGL thread. + * + ****************************************************************************/ + +void lvglterm_input_create(int argc, FAR char *argv[]) +{ + g_kbddev = (argc > 1) ? argv[1] : CONFIG_EXAMPLES_LVGLTERM_KBD_DEV; + + g_kfd = open(g_kbddev, O_RDONLY | O_NONBLOCK); + if (g_kfd < 0) + { + gerr("ERROR: open %s failed: %d\n", g_kbddev, errno); + } +} + +/**************************************************************************** + * Name: lvglterm_input_poll + * + * Description: + * Drain any pending key presses from the keyboard device and forward them + * to NSH (echoing what is typed on screen). Runs in the LVGL thread from + * the terminal's periodic timer, so the read must not block. + * + ****************************************************************************/ + +void lvglterm_input_poll(void) +{ + char buf[64]; + ssize_t nread; +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + struct lib_meminstream_s stream; + struct kbd_getstate_s state; + uint8_t ch; + int ret; +#else + FAR struct keyboard_event_s *evt; +#endif + + if (g_kfd < 0) + { + return; + } + + nread = read(g_kfd, buf, sizeof(buf)); + if (nread <= 0) + { + return; + } + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + memset(&state, 0, sizeof(state)); + lib_meminstream(&stream, buf, nread); + + for (; ; ) + { + ret = kbd_decode(&stream.common, &state, &ch); + if (ret == KBD_ERROR) + { + break; + } + + if (ret == KBD_PRESS) + { + handle_key(ch, false); + } + else if (ret == KBD_SPECPRESS) + { + handle_key(ch, true); + } + } +#else + evt = (FAR struct keyboard_event_s *)buf; + + while (nread >= (ssize_t)sizeof(struct keyboard_event_s)) + { + if (evt->type == KEYBOARD_PRESS) + { + handle_key(evt->code, false); + } + else if (evt->type == KEYBOARD_SPECPRESS) + { + handle_key(evt->code, true); + } + + nread -= sizeof(struct keyboard_event_s); + evt++; + } +#endif +} diff --git a/examples/lvglterm/lvglterm_touch.c b/examples/lvglterm/lvglterm_touch.c new file mode 100644 index 000000000..4285c97e6 --- /dev/null +++ b/examples/lvglterm/lvglterm_touch.c @@ -0,0 +1,164 @@ +/**************************************************************************** + * apps/examples/lvglterm/lvglterm_touch.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Touch input variant of the LVGL terminal: an on-screen LVGL keyboard feeds + * an input text area; pressing Enter sends the line to NSH stdin. This is + * the original PinePhone behaviour and suits touchscreen boards. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "lvglterm.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void input_callback(lv_event_t *e); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* LVGL Text Area for NSH Input and the on-screen Keyboard Widget */ + +static lv_obj_t *g_input; +static lv_obj_t *g_kb; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: input_callback + * + * Description: + * Callback for the NSH input text area. When the on-screen keyboard Enter + * key is pressed, send the input command to NSH stdin. + * + ****************************************************************************/ + +static void input_callback(lv_event_t *e) +{ + int ret; + const lv_event_code_t code = lv_event_get_code(e); + + if (code == LV_EVENT_VALUE_CHANGED) + { + const uint16_t id = lv_keyboard_get_selected_button(g_kb); + const char *key = lv_keyboard_get_button_text(g_kb, id); + + if (key == NULL) + { + return; + } + + /* If the key pressed is Enter, send the command to NSH stdin */ + + if (key[0] == 0xef && key[1] == 0xa2 && key[2] == 0xa2) + { + const char *cmd; + int len; + + DEBUGASSERT(g_input != NULL); + cmd = lv_textarea_get_text(g_input); + if (cmd == NULL || cmd[0] == 0) + { + return; + } + + /* Echo the command on the output so the prompt line reads + * "nsh> ", then send it to the shell. + */ + + len = strlen(cmd); + lvglterm_add_output(cmd, len); + if (cmd[len - 1] != '\n') + { + lvglterm_add_output("\n", 1); + } + + DEBUGASSERT(g_nsh_stdin[WRITE_PIPE] != 0); + ret = write(g_nsh_stdin[WRITE_PIPE], cmd, len); + DEBUGASSERT(ret == len); + + lv_textarea_set_text(g_input, ""); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lvglterm_input_create + * + * Description: + * Create the on-screen keyboard and input text area under the shared + * column container. + * + ****************************************************************************/ + +void lvglterm_input_create(int argc, FAR char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + /* Create an LVGL Text Area Widget for NSH Input */ + + g_input = lv_textarea_create(g_col); + DEBUGASSERT(g_input != NULL); + lv_obj_add_style(g_input, &g_terminal_style, 0); + lv_obj_set_size(g_input, LV_PCT(100), LV_SIZE_CONTENT); + + /* Create an LVGL Keyboard Widget */ + + g_kb = lv_keyboard_create(g_col); + DEBUGASSERT(g_kb != NULL); + lv_obj_set_style_pad_all(g_kb, 0, 0); + + /* Wire the keyboard to the input text area */ + + lv_obj_add_event_cb(g_input, input_callback, LV_EVENT_ALL, NULL); + lv_keyboard_set_textarea(g_kb, g_input); +} + +/**************************************************************************** + * Name: lvglterm_input_poll + * + * Description: + * No periodic work is needed for the touch variant. + * + ****************************************************************************/ + +void lvglterm_input_poll(void) +{ +} diff --git a/examples/lws_echo/CMakeLists.txt b/examples/lws_echo/CMakeLists.txt new file mode 100644 index 000000000..d959489f6 --- /dev/null +++ b/examples/lws_echo/CMakeLists.txt @@ -0,0 +1,37 @@ +# ############################################################################## +# apps/examples/lws_echo/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_LWS_ECHO) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_LWS_ECHO_PROGNAME} + PRIORITY + ${CONFIG_EXAMPLES_LWS_ECHO_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_LWS_ECHO_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_LWS_ECHO} + DEPENDS + libwebsockets + SRCS + lws_echo_main.c) +endif() diff --git a/examples/lws_echo/Kconfig b/examples/lws_echo/Kconfig new file mode 100644 index 000000000..b58daa24e --- /dev/null +++ b/examples/lws_echo/Kconfig @@ -0,0 +1,31 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_LWS_ECHO + tristate "libwebsockets WebSocket echo server" + default n + depends on NETUTILS_LIBWEBSOCKETS && NETUTILS_LIBWEBSOCKETS_SERVER + ---help--- + Simple WebSocket echo server using libwebsockets without TLS. + +if EXAMPLES_LWS_ECHO + +config EXAMPLES_LWS_ECHO_PROGNAME + string "Program name" + default "lws_echo" + +config EXAMPLES_LWS_ECHO_PRIORITY + int "Task priority" + default 100 + +config EXAMPLES_LWS_ECHO_STACKSIZE + int "Stack size" + default 16384 + +config EXAMPLES_LWS_ECHO_PORT + int "WebSocket server TCP port" + default 9000 + +endif diff --git a/examples/lws_echo/Make.defs b/examples/lws_echo/Make.defs new file mode 100644 index 000000000..b0a46ccd5 --- /dev/null +++ b/examples/lws_echo/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/lws_echo/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_LWS_ECHO),) +CONFIGURED_APPS += $(APPDIR)/examples/lws_echo +endif diff --git a/examples/lws_echo/Makefile b/examples/lws_echo/Makefile new file mode 100644 index 000000000..48da93216 --- /dev/null +++ b/examples/lws_echo/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/lws_echo/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_LWS_ECHO_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_LWS_ECHO_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_LWS_ECHO_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_LWS_ECHO) + +MAINSRC = lws_echo_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/lws_echo/lws_echo_main.c b/examples/lws_echo/lws_echo_main.c new file mode 100644 index 000000000..48bf644f0 --- /dev/null +++ b/examples/lws_echo/lws_echo_main.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * apps/examples/lws_echo/lws_echo_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_EXAMPLES_LWS_ECHO_PORT +# define CONFIG_EXAMPLES_LWS_ECHO_PORT 9000 +#endif + +#define LWS_ECHO_IOBUF_SIZE 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct lws_echo_session_s +{ + unsigned char buf[LWS_PRE + LWS_ECHO_IOBUF_SIZE]; + size_t len; + int pending; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int callback_echo(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, void *in, size_t len); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct lws_protocols g_protocols[] = +{ + { + "echo-protocol", + callback_echo, + sizeof(struct lws_echo_session_s), + LWS_ECHO_IOBUF_SIZE, + 0, NULL, 0 + }, + LWS_PROTOCOL_LIST_TERM +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: callback_echo + * + * Description: + * libwebsockets protocol callback for the echo server. + * + * Input Parameters: + * wsi - Websocket instance. + * reason - Callback reason. + * user - Per-session data. + * in - Received data. + * len - Length of received data. + * + * Returned Value: + * Zero on success; non-zero on failure. + * + ****************************************************************************/ + +static int callback_echo(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct lws_echo_session_s *pss = (struct lws_echo_session_s *)user; + + switch (reason) + { + case LWS_CALLBACK_ESTABLISHED: + printf("lws_echo: client connected\n"); + pss->pending = 0; + break; + + case LWS_CALLBACK_RECEIVE: + if (len > sizeof(pss->buf) - LWS_PRE) + { + len = sizeof(pss->buf) - LWS_PRE; + } + + memcpy(&pss->buf[LWS_PRE], in, len); + pss->len = len; + pss->pending = 1; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + if (!pss->pending) + { + break; + } + + lws_write(wsi, &pss->buf[LWS_PRE], pss->len, LWS_WRITE_TEXT); + pss->pending = 0; + break; + + case LWS_CALLBACK_CLOSED: + printf("lws_echo: client disconnected\n"); + break; + + default: + break; + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + * + * Description: + * Start a libwebsockets WebSocket echo server. + * + * Input Parameters: + * Standard argc and argv. + * + * Returned Value: + * Zero on success; non-zero on failure. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n; + + lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL); + + printf("lws_echo: starting WebSocket echo server on port %d\n", + CONFIG_EXAMPLES_LWS_ECHO_PORT); + printf("lws_echo: connect with ws://:%d " + "(subprotocol echo-protocol)\n", + CONFIG_EXAMPLES_LWS_ECHO_PORT); + + memset(&info, 0, sizeof(info)); + info.port = CONFIG_EXAMPLES_LWS_ECHO_PORT; + info.protocols = g_protocols; + info.vhost_name = "localhost"; + info.options = 0; + + context = lws_create_context(&info); + if (context == NULL) + { + printf("lws_echo: ERROR creating context\n"); + return 1; + } + + printf("lws_echo: server running\n"); + + n = 0; + while (n >= 0) + { + n = lws_service(context, 100); + } + + lws_context_destroy(context); + printf("lws_echo: server stopped\n"); + + return 0; +} diff --git a/examples/mdnsd/Makefile b/examples/mdnsd/Makefile index 316e6d817..5dc84bd13 100644 --- a/examples/mdnsd/Makefile +++ b/examples/mdnsd/Makefile @@ -26,11 +26,11 @@ include $(APPDIR)/Make.defs CSRCS = mdnsd_daemon.c -MAINSRC = mdnsd_start.c mdnsd_stop.c +MAINSRC = mdnsd_start.c mdnsd_stop.c mdnsd_event.c # MDNSD built-in application info -PROGNAME = mdnsd_start mdnsd_stop +PROGNAME = mdnsd_start mdnsd_stop mdnsd_event PRIORITY = SCHED_PRIORITY_DEFAULT STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_MDNSD) diff --git a/examples/mdnsd/mdnsd_daemon.c b/examples/mdnsd/mdnsd_daemon.c index bfa7759e8..bc2ea4563 100644 --- a/examples/mdnsd/mdnsd_daemon.c +++ b/examples/mdnsd/mdnsd_daemon.c @@ -25,7 +25,6 @@ ****************************************************************************/ #include - #include #include diff --git a/examples/mdnsd/mdnsd_event.c b/examples/mdnsd/mdnsd_event.c new file mode 100644 index 000000000..432042372 --- /dev/null +++ b/examples/mdnsd/mdnsd_event.c @@ -0,0 +1,244 @@ +/**************************************************************************** + * apps/examples/mdnsd/mdnsd_event.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netutils/mdnsd.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MDNSD_EVENT_BUFSIZE 512 +#define MDNSD_EVENT_SETTLE_MS 500 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mdnsd_event_has_ipv4addr + * + * Description: + * Check whether any network interface already has a non-zero IPv4 + * address. Uses SIOCGIFCONF to enumerate interfaces. + * + * Returned Value: + * true if at least one interface has a non-zero IPv4 address, + * false otherwise. + * + ****************************************************************************/ + +static bool mdnsd_event_has_ipv4addr(void) +{ + struct ifconf ifc; + char buf[sizeof(struct ifreq) * 4]; + int sd; + int i; + + sd = socket(AF_INET, SOCK_DGRAM, 0); + if (sd < 0) + { + return false; + } + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if (ioctl(sd, SIOCGIFCONF, &ifc) < 0) + { + close(sd); + return false; + } + + close(sd); + + for (i = 0; i < ifc.ifc_len; i += sizeof(struct ifreq)) + { + FAR struct ifreq *ifr = (FAR struct ifreq *)&buf[i]; + FAR struct sockaddr_in *sin; + + if (ifr->ifr_addr.sa_family != AF_INET) + { + continue; + } + + sin = (FAR struct sockaddr_in *)&ifr->ifr_addr; + if (sin->sin_addr.s_addr != INADDR_ANY && + sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + { + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: mdnsd_event_is_newaddr + * + * Description: + * Checks whether the given Netlink message header represents a new + * IPv4 or IPv6 address event (RTM_NEWADDR). + * + * Input Parameters: + * hdr - Pointer to the Netlink message header to inspect. + * + * Returned Value: + * true if the message is a new IPv4 or IPv6 address notification, + * false otherwise. + * + ****************************************************************************/ + +static bool mdnsd_event_is_newaddr(FAR struct nlmsghdr *hdr) +{ + FAR struct ifaddrmsg *addr; + + if (hdr->nlmsg_type != RTM_NEWADDR) + { + return false; + } + + if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifaddrmsg))) + { + return false; + } + + addr = (FAR struct ifaddrmsg *)NLMSG_DATA(hdr); + return addr->ifa_family == AF_INET || addr->ifa_family == AF_INET6; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + * + * Description: + * Entry point for the mdnsd_event application. Listens for Netlink + * address events and starts the mDNS daemon when an IP address is + * assigned to a network interface. + * + * Input Parameters: + * argc - Number of command-line arguments. + * argv - Array of command-line argument strings. + * + * Returned Value: + * EXIT_SUCCESS on success, EXIT_FAILURE on error. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ +#ifdef CONFIG_NETLINK_ROUTE + struct sockaddr_nl addr; + uint8_t buffer[MDNSD_EVENT_BUFSIZE]; + int sd; + int ret; + + /* If an interface already has an IP (DHCP finished before we started), + * skip the netlink wait and start mDNS right away. + */ + + if (mdnsd_event_has_ipv4addr()) + { + printf("mdnsd_event: address already set, starting mDNS\n"); + usleep(MDNSD_EVENT_SETTLE_MS * 1000); + ret = mdnsd_start(CONFIG_EXAMPLES_MDNS_SERVICE, + CONFIG_EXAMPLES_MDNS_SERVICE_PORT); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + + sd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sd < 0) + { + fprintf(stderr, "mdnsd_event: socket failed: %d\n", errno); + return EXIT_FAILURE; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; + + ret = bind(sd, (FAR struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + { + fprintf(stderr, "mdnsd_event: bind failed: %d\n", errno); + close(sd); + return EXIT_FAILURE; + } + + printf("mdnsd_event: waiting for network address event\n"); + + for (; ; ) + { + FAR struct nlmsghdr *hdr; + ssize_t nread; + int remaining; + + nread = recv(sd, buffer, sizeof(buffer), 0); + if (nread < 0) + { + fprintf(stderr, "mdnsd_event: recv failed: %d\n", errno); + close(sd); + return EXIT_FAILURE; + } + + remaining = nread; + for (hdr = (FAR struct nlmsghdr *)buffer; + NLMSG_OK(hdr, remaining); + hdr = NLMSG_NEXT(hdr, remaining)) + { + if (mdnsd_event_is_newaddr(hdr)) + { + printf("mdnsd_event: address set, starting mDNS\n"); + usleep(MDNSD_EVENT_SETTLE_MS * 1000); + ret = mdnsd_start(CONFIG_EXAMPLES_MDNS_SERVICE, + CONFIG_EXAMPLES_MDNS_SERVICE_PORT); + close(sd); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + } + } +#else + fprintf(stderr, "mdnsd_event: CONFIG_NETLINK_ROUTE is required\n"); + return EXIT_FAILURE; +#endif +} diff --git a/examples/microros_pub/CMakeLists.txt b/examples/microros_pub/CMakeLists.txt new file mode 100644 index 000000000..8bf83218e --- /dev/null +++ b/examples/microros_pub/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/microros_pub/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_MICROROS_PUB) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_MICROROS_PUB_PROGNAME} + SRCS + microros_pub_main.c + STACKSIZE + ${CONFIG_EXAMPLES_MICROROS_PUB_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_MICROROS_PUB_PRIORITY} + DEPENDS + microros_transport) +endif() diff --git a/examples/microros_pub/Kconfig b/examples/microros_pub/Kconfig new file mode 100644 index 000000000..a1d75478d --- /dev/null +++ b/examples/microros_pub/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_MICROROS_PUB + tristate "micro-ROS int32 publisher example" + default n + depends on SYSTEM_MICROROS + ---help--- + Minimal micro-ROS publisher that creates a node and publishes + an std_msgs/Int32 every second on the topic /nuttx_pub. Used + to validate the NuttX-native transport layer end-to-end with + a micro-ROS agent. + +if EXAMPLES_MICROROS_PUB + +config EXAMPLES_MICROROS_PUB_PROGNAME + string "Program name" + default "microros_pub" + +config EXAMPLES_MICROROS_PUB_PRIORITY + int "micro-ROS pub task priority" + default 100 + +config EXAMPLES_MICROROS_PUB_STACKSIZE + int "micro-ROS pub stack size" + default 8192 + +endif diff --git a/examples/microros_pub/Make.defs b/examples/microros_pub/Make.defs new file mode 100644 index 000000000..16128b96e --- /dev/null +++ b/examples/microros_pub/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/microros_pub/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_MICROROS_PUB),) +CONFIGURED_APPS += $(APPDIR)/examples/microros_pub +endif diff --git a/examples/microros_pub/Makefile b/examples/microros_pub/Makefile new file mode 100644 index 000000000..1377e68b5 --- /dev/null +++ b/examples/microros_pub/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/microros_pub/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_MICROROS_PUB_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_MICROROS_PUB_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_MICROROS_PUB_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_MICROROS_PUB) + +MAINSRC = microros_pub_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/microros_pub/microros_pub_main.c b/examples/microros_pub/microros_pub_main.c new file mode 100644 index 000000000..d9211c36b --- /dev/null +++ b/examples/microros_pub/microros_pub_main.c @@ -0,0 +1,109 @@ +/**************************************************************************** + * apps/examples/microros_pub/microros_pub_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + rcl_publisher_t publisher; + std_msgs__msg__Int32 msg; + rcl_allocator_t allocator; + rclc_support_t support; + rcl_node_t node; + int i; + + printf("microros_pub: starting\n"); + + if (microros_transport_init() != 0) + { + printf("microros_pub: transport init failed\n"); + return 1; + } + + allocator = rcl_get_default_allocator(); + + if (rclc_support_init(&support, 0, NULL, &allocator) != RCL_RET_OK) + { + printf("microros_pub: rclc_support_init failed\n"); + return 1; + } + + if (rclc_node_init_default(&node, "nuttx_node", "", &support) + != RCL_RET_OK) + { + printf("microros_pub: node init failed\n"); + return 1; + } + + if (rclc_publisher_init_default( + &publisher, + &node, + ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), + "nuttx_pub") != RCL_RET_OK) + { + printf("microros_pub: publisher init failed\n"); + return 1; + } + + msg.data = 0; + + for (i = 0; i < 30; i++) + { + if (rcl_publish(&publisher, &msg, NULL) == RCL_RET_OK) + { + printf("microros_pub: sent %d\n", (int)msg.data); + } + else + { + printf("microros_pub: publish failed\n"); + } + + msg.data++; + sleep(1); + } + + rcl_publisher_fini(&publisher, &node); + rcl_node_fini(&node); + rclc_support_fini(&support); + + printf("microros_pub: done\n"); + return 0; +} diff --git a/examples/microros_sub/CMakeLists.txt b/examples/microros_sub/CMakeLists.txt new file mode 100644 index 000000000..1c094949a --- /dev/null +++ b/examples/microros_sub/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/microros_sub/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_MICROROS_SUB) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_MICROROS_SUB_PROGNAME} + SRCS + microros_sub_main.c + STACKSIZE + ${CONFIG_EXAMPLES_MICROROS_SUB_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_MICROROS_SUB_PRIORITY} + DEPENDS + microros_transport) +endif() diff --git a/examples/microros_sub/Kconfig b/examples/microros_sub/Kconfig new file mode 100644 index 000000000..3bbbf5efb --- /dev/null +++ b/examples/microros_sub/Kconfig @@ -0,0 +1,31 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_MICROROS_SUB + tristate "micro-ROS int32 subscriber example" + default n + depends on SYSTEM_MICROROS + ---help--- + Minimal micro-ROS subscriber that creates a node, subscribes to + the topic /nuttx_sub with std_msgs/Int32, and prints each + received value. Drives an rclc_executor spin loop and is used + together with microros_pub (or any external publisher) to + validate the NuttX-native transport in the receive direction. + +if EXAMPLES_MICROROS_SUB + +config EXAMPLES_MICROROS_SUB_PROGNAME + string "Program name" + default "microros_sub" + +config EXAMPLES_MICROROS_SUB_PRIORITY + int "micro-ROS sub task priority" + default 100 + +config EXAMPLES_MICROROS_SUB_STACKSIZE + int "micro-ROS sub stack size" + default 8192 + +endif diff --git a/examples/microros_sub/Make.defs b/examples/microros_sub/Make.defs new file mode 100644 index 000000000..e9fe5aa1d --- /dev/null +++ b/examples/microros_sub/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/microros_sub/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_MICROROS_SUB),) +CONFIGURED_APPS += $(APPDIR)/examples/microros_sub +endif diff --git a/examples/microros_sub/Makefile b/examples/microros_sub/Makefile new file mode 100644 index 000000000..a1144a70c --- /dev/null +++ b/examples/microros_sub/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/microros_sub/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_MICROROS_SUB_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_MICROROS_SUB_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_MICROROS_SUB_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_MICROROS_SUB) + +MAINSRC = microros_sub_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/microros_sub/microros_sub_main.c b/examples/microros_sub/microros_sub_main.c new file mode 100644 index 000000000..1e1a6c550 --- /dev/null +++ b/examples/microros_sub/microros_sub_main.c @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/examples/microros_sub/microros_sub_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static std_msgs__msg__Int32 g_rx_msg; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void sub_callback(const void *msgin) +{ + const std_msgs__msg__Int32 *msg = (const std_msgs__msg__Int32 *)msgin; + + printf("microros_sub: got %d\n", (int)msg->data); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + rcl_subscription_t subscriber; + rcl_allocator_t allocator; + rclc_support_t support; + rcl_node_t node; + rclc_executor_t executor; + + printf("microros_sub: starting\n"); + + if (microros_transport_init() != 0) + { + printf("microros_sub: transport init failed\n"); + return 1; + } + + allocator = rcl_get_default_allocator(); + + if (rclc_support_init(&support, 0, NULL, &allocator) != RCL_RET_OK) + { + printf("microros_sub: rclc_support_init failed\n"); + return 1; + } + + if (rclc_node_init_default(&node, "nuttx_sub_node", "", &support) + != RCL_RET_OK) + { + printf("microros_sub: node init failed\n"); + return 1; + } + + if (rclc_subscription_init_default( + &subscriber, + &node, + ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), + "nuttx_sub") != RCL_RET_OK) + { + printf("microros_sub: subscription init failed\n"); + return 1; + } + + executor = rclc_executor_get_zero_initialized_executor(); + if (rclc_executor_init(&executor, &support.context, 1, &allocator) + != RCL_RET_OK) + { + printf("microros_sub: executor init failed\n"); + return 1; + } + + if (rclc_executor_add_subscription(&executor, + &subscriber, + &g_rx_msg, + &sub_callback, + ON_NEW_DATA) != RCL_RET_OK) + { + printf("microros_sub: add_subscription failed\n"); + return 1; + } + + printf("microros_sub: spinning on /nuttx_sub\n"); + + rclc_executor_spin(&executor); + + rclc_executor_fini(&executor); + rcl_subscription_fini(&subscriber, &node); + rcl_node_fini(&node); + rclc_support_fini(&support); + + printf("microros_sub: done\n"); + return 0; +} diff --git a/examples/microwindows/Kconfig b/examples/microwindows/Kconfig new file mode 100644 index 000000000..6319ac587 --- /dev/null +++ b/examples/microwindows/Kconfig @@ -0,0 +1,26 @@ +config EXAMPLES_MICROWINDOWS + tristate "Microwindows example" + default n + depends on GRAPHICS_MICROWINDOWS + depends on MICROWINDOWS_MWIN + ---help--- + Enable the Microwindows example application + +if EXAMPLES_MICROWINDOWS + +config EXAMPLES_MICROWINDOWS_PROGNAME + string "Program name" + default "mwexample" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_MICROWINDOWS_PRIORITY + int "Microwindows task priority" + default 100 + +config EXAMPLES_MICROWINDOWS_STACKSIZE + int "Microwindows stack size" + default 32768 + +endif diff --git a/examples/microwindows/Make.defs b/examples/microwindows/Make.defs new file mode 100644 index 000000000..6c3595cc3 --- /dev/null +++ b/examples/microwindows/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/microwindows/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_MICROWINDOWS),) +CONFIGURED_APPS += $(APPDIR)/examples/microwindows +endif diff --git a/examples/microwindows/Makefile b/examples/microwindows/Makefile new file mode 100644 index 000000000..c8c588abc --- /dev/null +++ b/examples/microwindows/Makefile @@ -0,0 +1,55 @@ +############################################################################ +# apps/examples/microwindows/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_EXAMPLES_MICROWINDOWS_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_MICROWINDOWS_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_MICROWINDOWS_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_MICROWINDOWS) + +MAINSRC = mwdemo_main.c +CSRCS += mwdemo.c +MW_IMAGES = penguin microwin +CSRCS += $(MW_IMAGES:%=$(MW_IMGDIR)/%.c) + +# Generate image .c files from .bmp +MW_SRC = $(APPDIR)/graphics/microwindows/microwindows/src +MW_IMGDIR = images +MW_CONVBMP = convbmp$(HOSTEXEEXT) + +$(MW_CONVBMP): $(MW_SRC)/images/tools/convbmp.c + $(HOSTCC) $(HOSTCFLAGS) -I$(MW_SRC)/include $< -o $@ + +$(MW_IMGDIR)/%.c: $(MW_SRC)/images/demos/mwin/%.bmp $(MW_CONVBMP) + mkdir -p $(MW_IMGDIR) + $(CURDIR)/$(MW_CONVBMP) -o $@ $< + +clean:: + $(call DELFILE, $(MW_IMAGES:%=$(MW_IMGDIR)/%.c)) + $(Q) rmdir $(MW_IMGDIR) 2>/dev/null || true + $(call DELFILE, $(MW_CONVBMP)) + +CFLAGS += -Wno-undef +CFLAGS += -DNOGDI + +include $(APPDIR)/Application.mk diff --git a/examples/microwindows/mwdemo.c b/examples/microwindows/mwdemo.c new file mode 100644 index 000000000..c672fd459 --- /dev/null +++ b/examples/microwindows/mwdemo.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * apps/examples/microwindows/mwdemo.c + * + * Copyright (c) 1999, 2000, 2001, 2010 Greg Haerr + * Portions Copyright (c) 2002 by Koninklijke Philips Electronics N.V. + * + * Demo program for Microwindows + * + * GB: 10-14-2004: Modified to store degrees data on each window, + * for new Timers features. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#define MWINCLUDECOLORS +#include +#include +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int startdegrees; + int enddegrees; +} demoWndData; + +typedef demoWndData *pdemoWndData; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +extern MWIMAGEHDR image_penguin; +PMWIMAGEHDR g_image = &image_penguin; + +#define APPCLASS "test" +#define APPCHILD "test2" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp); + +LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT uMsg, WPARAM wp, LPARAM lp); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +int MwUserInit(int ac, char **av) +{ + /* test user init procedure - do nothing */ + + return 0; +} + +int RegisterAppClass(HINSTANCE hInstance) +{ + WNDCLASS wc; + + /* register builtin controls and dialog classes */ + + MwInitializeDialogs(hInstance); + + wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(LONG_PTR); + wc.hInstance = 0; + wc.hIcon = 0; /* LoadIcon(GetHInstance(), MAKEINTRESOURCE( + * 1)); */ + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = APPCLASS; + RegisterClass(&wc); + + wc.lpfnWndProc = (WNDPROC) ChildWndProc; + wc.lpszClassName = APPCHILD; + return RegisterClass(&wc); +} + +HWND CreateAppWindow(void) +{ + HWND hwnd; + static int nextid = 1; + int width; + int height; + RECT r; + GetWindowRect(GetDesktopWindow(), &r); + width = height = r.right / 2; + + hwnd = CreateWindowEx(0L, APPCLASS, + "Microwindows Application", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, + width, height, + NULL, (HMENU)(LONG_PTR)nextid++, NULL, NULL); + + if (hwnd && (nextid & 03) != 2) + { + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + 4, 4, width / 3 - 6, height / 3, + hwnd, (HMENU)2, NULL, NULL); + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width / 3, height / 3, + width / 3 - 6, height / 3, + hwnd, (HMENU)3, NULL, NULL); + CreateWindowEx(0L, APPCHILD, + "", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 3 / 5, height * 3 / 5, + width * 2 / 3, height * 2 / 3, + hwnd, (HMENU)4, NULL, NULL); + CreateWindowEx(0L, "EDIT", + "OK", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 5 / 8, 10, + 100, 18, + hwnd, (HMENU)5, NULL, NULL); + CreateWindowEx(0L, "PROGBAR", + "OK", + WS_BORDER | WS_CHILD | WS_VISIBLE, + width * 5 / 8, 32, + 100, 18, + hwnd, (HMENU)6, NULL, NULL); + + HWND hlist = CreateWindowEx(0L, "LISTBOX", + "OK", + WS_HSCROLL | WS_VSCROLL | + WS_BORDER | WS_CHILD | + WS_VISIBLE, + width * 5 / 8, 54, + 100, 48, + hwnd, (HMENU)7, NULL, NULL); + SendMessage(hlist, LB_ADDSTRING, 0, + (LPARAM)(LPSTR)"Cherry"); + SendMessage(hlist, LB_ADDSTRING, 0, + (LPARAM)(LPSTR)"Apple"); + SendMessage(hlist, LB_ADDSTRING, 0, + (LPARAM)(LPSTR)"Orange"); + + CreateWindowEx(0L, "BUTTON", + "Cancel", + BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + width * 5 / 8 + 50, 106, + 50, 14, + hwnd, (HMENU)8, NULL, NULL); + } + + return hwnd; +} + +LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + RECT rc; + PAINTSTRUCT ps; + + switch (msg) + { + case WM_PAINT: + BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &rc); + + HDC hdcMem; + HBITMAP hbmp, hbmpOrg; + + /* redirect painting to offscreen dc, then use blit function */ + + hdcMem = CreateCompatibleDC(ps.hdc); + + /* Note: rc.right, rc.bottom happens to be smaller than image + * width/height. We use the image size, so we can stretchblit + * from the whole image. + */ + + hbmp = CreateCompatibleBitmap(hdcMem, g_image->width, + g_image->height); + hbmpOrg = SelectObject(hdcMem, hbmp); + + /* draw onto offscreen dc */ + + DrawDIB(hdcMem, 0, 0, g_image); + + /* blit offscreen with physical screen */ + + StretchBlt(ps.hdc, 0, 0, rc.right * 4 / 5, + rc.bottom * 4 / 5, hdcMem, + 0, 0, g_image->width, g_image->height, MWROP_COPY); + DeleteObject(SelectObject(hdcMem, hbmpOrg)); + DeleteDC(hdcMem); + EndPaint(hwnd, &ps); + break; + default: + return DefWindowProc(hwnd, msg, wp, lp); + } + + return 0; +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + PAINTSTRUCT ps; + HDC hdc; + RECT rc; + static int countup = 1; + int id; + static vec1 gx; + static vec1 gy; + static POINT mousept; + pdemoWndData pData; + + switch (msg) + { + case WM_CREATE: + pData = (pdemoWndData) malloc(sizeof(demoWndData)); + SetWindowLongPtr(hwnd, 0, (LONG_PTR) pData); + mousept.x = 60; + mousept.y = 20; + pData->startdegrees = 0; + pData->enddegrees = 30; + SetTimer(hwnd, 1, (50 + random() % 250), NULL); + break; + + case WM_TIMER: + pData = (pdemoWndData)(LONG_PTR)GetWindowLongPtr(hwnd, 0); + GetClientRect(hwnd, &rc); + if (countup) + { + mousept.y += 20; + if (mousept.y >= rc.bottom) + { + mousept.y -= 20; + countup = 0; + } + } + else + { + mousept.y -= 20; + if (mousept.y < 20) + { + mousept.y += 20; + countup = 1; + } + } + + SendMessage(hwnd, WM_MOUSEMOVE, 0, + MAKELONG(mousept.x, mousept.y)); + break; + case WM_DESTROY: + KillTimer(hwnd, 1); + pData = (pdemoWndData)(LONG_PTR)GetWindowLongPtr(hwnd, 0); + free(pData); + SetWindowLongPtr(hwnd, 0, (LONG_PTR)0); + break; + case WM_SIZE: + break; + case WM_MOVE: + break; + + case WM_ERASEBKGND: + if ((GetWindowLong(hwnd, GWL_ID) & 03) == 1) + { + return 1; + } + + return DefWindowProc(hwnd, msg, wp, lp); + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + id = (int)GetWindowLong(hwnd, GWL_ID) & 03; + init3(hdc, id == 1 ? hwnd : NULL); + switch (id) + { + case 0: + rose(1.0, 7, 13); + break; + case 1: + look3(-2 * gx, -2 * gy, 1.2); + drawgrid(-8.0, 8.0, 10, -8.0, 8.0, 10); + break; + case 2: + setcolor3(BLACK); + circle3(1.0); + break; + case 3: + setcolor3(BLUE); + daisy(1.0, 20); + break; + } + + paint3(hdc); + EndPaint(hwnd, &ps); + break; + + case WM_MOUSEMOVE: + pData = (pdemoWndData)(LONG_PTR)GetWindowLongPtr(hwnd, 0); + if ((GetWindowLong(hwnd, GWL_ID) & 03) == 1) + { + POINT pt; + + POINTSTOPOINT(pt, lp); + GetClientRect(hwnd, &rc); + gx = (vec1) pt.x / rc.right; + gy = (vec1) pt.y / rc.bottom; + InvalidateRect(hwnd, NULL, FALSE); + mousept.x = pt.x; + mousept.y = pt.y; + } + break; + case WM_LBUTTONDOWN: + break; + case WM_LBUTTONUP: + break; + case WM_RBUTTONDOWN: + break; + default: + return DefWindowProc(hwnd, msg, wp, lp); + } + + return 0; +} diff --git a/examples/microwindows/mwdemo_main.c b/examples/microwindows/mwdemo_main.c new file mode 100644 index 000000000..f8860074b --- /dev/null +++ b/examples/microwindows/mwdemo_main.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * apps/examples/microwindows/mwdemo_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +extern MWIMAGEHDR image_microwin; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +extern int RegisterAppClass(HINSTANCE hInstance); +extern HWND CreateAppWindow(void); + +#define APPCLASS "test" +#define APPCHILD "test2" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + MSG msg; + HWND hwnd; + RECT rc; + + invoke_WinMain_Start(argc, argv); + + srandom(time(NULL)); + RegisterAppClass(0); + GetWindowRect(GetDesktopWindow(), &rc); + + /* create penguin window */ + + CreateWindowEx(0L, APPCHILD, "", + WS_BORDER | WS_VISIBLE, + rc.right - 130 - 1, rc.bottom - 153 - 1, + 130, 153, + GetDesktopWindow(), (HMENU)1000, NULL, NULL); + + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + CreateAppWindow(); + + /* set background wallpaper */ + + MwSetDesktopWallpaper(&image_microwin); + hwnd = CreateAppWindow(); + GetWindowRect(hwnd, &rc); + OffsetRect(&rc, 50, 50); + MoveWindow(hwnd, rc.left, rc.top, + rc.bottom - rc.top, + rc.right - rc.left, TRUE); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + invoke_WinMain_End(); + + return 0; +} diff --git a/examples/mld/mld.h b/examples/mld/mld.h index 69d8f13a4..936a37062 100644 --- a/examples/mld/mld.h +++ b/examples/mld/mld.h @@ -27,7 +27,7 @@ * Included Files ****************************************************************************/ -#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/examples/mld/mld_main.c b/examples/mld/mld_main.c index 363a7e41d..594115a22 100644 --- a/examples/mld/mld_main.c +++ b/examples/mld/mld_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/module/chardev/chardev.c b/examples/module/chardev/chardev.c index f0faa3bfb..b2fd7b61d 100644 --- a/examples/module/chardev/chardev.c +++ b/examples/module/chardev/chardev.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/module/main/Makefile b/examples/module/main/Makefile index f5294b257..466af6dbf 100644 --- a/examples/module/main/Makefile +++ b/examples/module/main/Makefile @@ -34,14 +34,13 @@ MAINSRC = module_main.c SYMTABSRC = mod_symtab.c SYMTABOBJ = $(SYMTABSRC:.c=$(OBJEXT)) - +MODLUE_NAME = chardev ifneq ($(CONFIG_BUILD_FLAT),y) PASS1_SYMTAB = $(TOPDIR)/pass1/mod_symtab.c - MODLUE_NAME = chardev endif $(SYMTABSRC): - $(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh $(BINDIR) g_mod >$@.tmp + $(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh $(BINDIR)$(DELIM)$(MODLUE_NAME) g_mod >$@.tmp $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) diff --git a/examples/module/main/module_main.c b/examples/module/main/module_main.c index 37da14fee..9eb9b205e 100644 --- a/examples/module/main/module_main.c +++ b/examples/module/main/module_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/mount/mount_main.c b/examples/mount/mount_main.c index c2b4e45b4..e42f0d88b 100644 --- a/examples/mount/mount_main.c +++ b/examples/mount/mount_main.c @@ -217,6 +217,13 @@ static void show_directories(const char *path, int indent) snprintf(g_namebuffer, sizeof(g_namebuffer), "%s/%s", path, direntry->d_name); subdir = strdup(g_namebuffer); + if (subdir == NULL) + { + printf("show_directories: ERROR out of memory\n"); + g_nerrors++; + continue; + } + show_directories(subdir, indent + 1); free(subdir); } diff --git a/examples/mount/ramdisk.c b/examples/mount/ramdisk.c index 7eeeafaab..11b29130a 100644 --- a/examples/mount/ramdisk.c +++ b/examples/mount/ramdisk.c @@ -97,8 +97,8 @@ int create_ramdisk(void) ret = mkfatfs(g_source, &g_fmt); if (ret < 0) { - printf("create_ramdisk: Failed to create FAT filesystem on ramdisk at %s\n", - g_source); + printf("create_ramdisk: Failed to create FAT filesystem on " + "ramdisk at %s\n", g_source); return ret; } diff --git a/examples/mqttc/CMakeLists.txt b/examples/mqttc/CMakeLists.txt index 567712558..6e4ee010a 100644 --- a/examples/mqttc/CMakeLists.txt +++ b/examples/mqttc/CMakeLists.txt @@ -23,11 +23,23 @@ if(CONFIG_EXAMPLES_MQTTC) nuttx_add_application( NAME - ${CONFIG_EXAMPLES_MQTTC_PROGNAME} + ${CONFIG_EXAMPLES_MQTTC_PUB_PROGNAME} STACKSIZE ${CONFIG_EXAMPLES_MQTTC_STACKSIZE} MODULE ${CONFIG_EXAMPLES_MQTTC} SRCS mqttc_pub.c) + + if(CONFIG_EXAMPLES_MQTTC_SUB) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_MQTTC_SUB_PROGNAME} + STACKSIZE + ${CONFIG_EXAMPLES_MQTTC_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_MQTTC} + SRCS + mqttc_sub.c) + endif() endif() diff --git a/examples/mqttc/Kconfig b/examples/mqttc/Kconfig index adf91b726..61a2e6ba1 100644 --- a/examples/mqttc/Kconfig +++ b/examples/mqttc/Kconfig @@ -16,6 +16,17 @@ config EXAMPLES_MQTTC_PROGNAME string "Program name" default "mqttc_pub" +config EXAMPLES_MQTTC_SUB + bool "Enable MQTT-C Subscriber Client" + default n + ---help--- + Enable a simple MQTT-C subscriber example alongside publisher + +config EXAMPLES_MQTTC_SUB_PROGNAME + string "Subscriber program name" + default "mqttc_sub" + depends on EXAMPLES_MQTTC_SUB + config EXAMPLES_MQTTC_STACKSIZE int "Task's stack size" default 8192 @@ -28,6 +39,19 @@ config EXAMPLES_MQTTC_RXSIZE int "RX Buffer size" default 256 +config MQTT_USE_MBEDTLS + bool + default n + +config EXAMPLES_MQTTC_MBEDTLS + bool "Enable MQTT over TLS (mbedTLS)" + default n + depends on NETUTILS_MQTTC_WITH_MBEDTLS + select MQTT_USE_MBEDTLS + ---help--- + Enable TLS/SSL support for MQTT connections using mbedTLS. + Requires NETUTILS_MQTTC_WITH_MBEDTLS to be enabled. + config EXAMPLES_MQTTC_ALLOW_UNVERIFIED_TLS bool "Allow unverified TLS" default n diff --git a/examples/mqttc/Makefile b/examples/mqttc/Makefile index 9aacf67f6..45a2d9f04 100644 --- a/examples/mqttc/Makefile +++ b/examples/mqttc/Makefile @@ -25,10 +25,20 @@ include $(APPDIR)/Make.defs PROGNAME = $(CONFIG_EXAMPLES_MQTTC_PROGNAME) PRIORITY = SCHED_PRIORITY_DEFAULT STACKSIZE = $(CONFIG_EXAMPLES_MQTTC_STACKSIZE) + +ifeq ($(CONFIG_EXAMPLES_MQTTC_SUB),y) + PROGNAME += $(CONFIG_EXAMPLES_MQTTC_SUB_PROGNAME) + PRIORITY += SCHED_PRIORITY_DEFAULT + STACKSIZE += $(CONFIG_EXAMPLES_MQTTC_STACKSIZE) +endif + MODULE = $(CONFIG_EXAMPLES_MQTTC) # MQTT-C example source code MAINSRC = mqttc_pub.c +ifeq ($(CONFIG_EXAMPLES_MQTTC_SUB), y) + MAINSRC += mqttc_sub.c +endif include $(APPDIR)/Application.mk diff --git a/examples/mqttc/cert.inc b/examples/mqttc/cert.inc new file mode 100644 index 000000000..542602db5 --- /dev/null +++ b/examples/mqttc/cert.inc @@ -0,0 +1,30 @@ +/* Broker CA certificate (PEM), embedded in the firmware so the subscriber + * can authenticate the broker without copying a certificate file onto the + * board. + * Replace the body below if the broker's CA changes; init_conn() parses + * it whenever no CA file is supplied on the command line via -c. + */ + +# define TEST_CA_CRT_RSA_SHA256_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIIDYTCCAkmgAwIBAgIUAk1VwJ7il2775wUnEgBZSvW77dUwDQYJKoZIhvcNAQEL\r\n" \ + "BQAwQDELMAkGA1UEBhMCUk8xEjAQBgNVBAgMCUJ1Y2hhcmVzdDEOMAwGA1UECgwF\r\n" \ + "TXlPcmcxDTALBgNVBAMMBE15Q0EwHhcNMjYwNjA5MDA0MjExWhcNMzYwNjA2MDA0\r\n" \ + "MjExWjBAMQswCQYDVQQGEwJSTzESMBAGA1UECAwJQnVjaGFyZXN0MQ4wDAYDVQQK\r\n" \ + "DAVNeU9yZzENMAsGA1UEAwwETXlDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\r\n" \ + "AQoCggEBAMGfLamBuio0mYqL8TDLqcxqCDhELmrmYxccz8/BAJ3xG0bbqAg0o2CM\r\n" \ + "2nUcDWeDTTOFTEAOVKhqqbu7jzQA+/XRQaptYAzt13N0pMUFlKZQ0QVefAH11mph\r\n" \ + "M2u0exWZObRQxUyfzzJG1UZQ4xGqXUekg+2LMSS09g/QAsCreu8O+xzAK9XUA6np\r\n" \ + "8lebaIqxFHabrjlbHgan5qKP0+EncMRN/5Lb11MDzfjyIusoFWPZF8rF2x9D+UFv\r\n" \ + "hZuL9Z40jz3cRlVUxuVtUq1tna2CCwWhwl5gwAQ/b0rkhe+5/b3cgSs3VbIdWHic\r\n" \ + "nwM4Yq4K/nAetYiOxtjKY5zLIvRopd0CAwEAAaNTMFEwHQYDVR0OBBYEFFR43w+x\r\n" \ + "ri1ssaAyJzQGDH/dP2e0MB8GA1UdIwQYMBaAFFR43w+xri1ssaAyJzQGDH/dP2e0\r\n" \ + "MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADU/lBjTGb7RlgGq\r\n" \ + "HYCktoWZBNkJFPVTPrC1mwzJX/vSaz9QUpRA5dpprSl84QKvWNxO5fPEMmwK2Wrw\r\n" \ + "x6tdE2aWb5x+3W0302JnaUPNhDF26Z5+LK4CoRHjbT2J3FrDg2u6/3Si+RpNSHYh\r\n" \ + "0OCdApzCyzqWdmvb7VD+u+isSh9CEUcaBOCq/72GNqdIjnSsgMGNHp9Ld6+ctwJO\r\n" \ + "sQkj0M3MaciNSPZ3y0w/GqeWNQaK3Rk5pSaqrc9RQS42KtJuGOqMiBMG1Ib6b7D4\r\n" \ + "bJSEDBzhO1M0o9vwr9xYaCUfbQzuuYJ3al4weRl0E4B63o2OFUjMOILIO3LeuuRD\r\n" \ + "+HXdOE0=\r\n" \ + "-----END CERTIFICATE-----\r\n" +/* END FILE */ diff --git a/examples/mqttc/mqttc_pub.c b/examples/mqttc/mqttc_pub.c index 6948afb42..d979fb24a 100644 --- a/examples/mqttc/mqttc_pub.c +++ b/examples/mqttc/mqttc_pub.c @@ -24,6 +24,7 @@ * Included Files ****************************************************************************/ +#include #include #include #include @@ -42,39 +43,12 @@ # include # include +#include "cert.inc" + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/* This is taken from mbedtls/tests/data_files/test-ca-sha256.crt. */ - -/* BEGIN FILE string macro TEST_CA_CRT_RSA_SHA256_PEM - * mbedtls/tests/data_files/test-ca-sha256.crt - */ - -# define TEST_CA_CRT_RSA_SHA256_PEM \ - "-----BEGIN CERTIFICATE-----\r\n" \ - "MIIDQTCCAimgAwIBAgIBAzANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER\r\n" \ - "MA8GA1UECgwIUG9sYXJTU0wxGTAXBgNVBAMMEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" \ - "MTkwMjEwMTQ0NDAwWhcNMjkwMjEwMTQ0NDAwWjA7MQswCQYDVQQGEwJOTDERMA8G\r\n" \ - "A1UECgwIUG9sYXJTU0wxGTAXBgNVBAMMEFBvbGFyU1NMIFRlc3QgQ0EwggEiMA0G\r\n" \ - "CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA3zf8F7vglp0/ht6WMn1EpRagzSHx\r\n" \ - "mdTs6st8GFgIlKXsm8WL3xoemTiZhx57wI053zhdcHgH057Zk+i5clHFzqMwUqny\r\n" \ - "50BwFMtEonILwuVA+T7lpg6z+exKY8C4KQB0nFc7qKUEkHHxvYPZP9al4jwqj+8n\r\n" \ - "YMPGn8u67GB9t+aEMr5P+1gmIgNb1LTV+/Xjli5wwOQuvfwu7uJBVcA0Ln0kcmnL\r\n" \ - "R7EUQIN9Z/SG9jGr8XmksrUuEvmEF/Bibyc+E1ixVA0hmnM3oTDPb5Lc9un8rNsu\r\n" \ - "KNF+AksjoBXyOGVkCeoMbo4bF6BxyLObyavpw/LPh5aPgAIynplYb6LVAgMBAAGj\r\n" \ - "UDBOMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFLRa5KWz3tJS9rnVppUP6z68x/3/\r\n" \ - "MB8GA1UdIwQYMBaAFLRa5KWz3tJS9rnVppUP6z68x/3/MA0GCSqGSIb3DQEBCwUA\r\n" \ - "A4IBAQA4qFSCth2q22uJIdE4KGHJsJjVEfw2/xn+MkTvCMfxVrvmRvqCtjE4tKDl\r\n" \ - "oK4MxFOek07oDZwvtAT9ijn1hHftTNS7RH9zd/fxNpfcHnMZXVC4w4DNA1fSANtW\r\n" \ - "5sY1JB5Je9jScrsLSS+mAjyv0Ow3Hb2Bix8wu7xNNrV5fIf7Ubm+wt6SqEBxu3Kb\r\n" \ - "+EfObAT4huf3czznhH3C17ed6NSbXwoXfby7stWUDeRJv08RaFOykf/Aae7bY5PL\r\n" \ - "yTVrkAnikMntJ9YI+hNNYt3inqq11A5cN0+rVTst8UKCxzQ4GpvroSwPKTFkbMw4\r\n" \ - "/anT1dVxr/BtwJfiESoK3/4CeXR1\r\n" \ - "-----END CERTIFICATE-----\r\n" -/* END FILE */ - # define EXTRA_OPT "c:" #else # define EXTRA_OPT "" @@ -192,13 +166,13 @@ static void parsearg(int argc, FAR char *argv[], case 'q': switch (strtol(optarg, NULL, 10)) { - case '0': + case 0L: cfg->qos = MQTT_PUBLISH_QOS_0; break; - case '1': + case 1L: cfg->qos = MQTT_PUBLISH_QOS_1; break; - case '2': + case 2L: cfg->qos = MQTT_PUBLISH_QOS_2; break; } @@ -528,7 +502,7 @@ int main(int argc, FAR char *argv[]) NULL); if (mqtterr != MQTT_OK) { - printf("ERRPR! mqtt_init() failed.\n"); + printf("ERROR! mqtt_init() failed.\n"); goto err_with_conn; } diff --git a/examples/mqttc/mqttc_sub.c b/examples/mqttc/mqttc_sub.c new file mode 100644 index 000000000..d7ad1858a --- /dev/null +++ b/examples/mqttc/mqttc_sub.c @@ -0,0 +1,820 @@ +/**************************************************************************** + * apps/examples/mqttc/mqttc_sub.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MQTT_USE_MBEDTLS +# include + +# include +# include +# include +# include +# include + +# include "cert.inc" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +# define EXTRA_OPT "c:" +#else +# define EXTRA_OPT "" +#endif + +/* Seconds to wait between failed connection attempts in the reconnect + * callback. + */ + +#define MQTTC_RECONNECT_DELAY_S 2 + +/* Network interface to wait on before the first connection attempt, and the + * polling parameters for that wait. Change MQTTC_NETIF to match the board's + * network device (e.g. "eth0" for wired Ethernet). + */ + +#define MQTTC_NETIF "wlan0" +#define MQTTC_NET_POLL_MS 200 +#define MQTTC_NET_WAIT_MS 30000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mqttc_cfg_s +{ + FAR const char *host; + FAR const char *port; + FAR const char *topic; + FAR const char *msg; + FAR const char *id; + FAR const char *user; + FAR const char *pass; + #ifdef MQTT_USE_MBEDTLS + FAR const char *ca_file; + #endif + uint32_t tmo; + uint8_t flags; + uint8_t qos; +}; + +struct mqtt_conn_context_s +{ + struct mqtt_client client; + #ifdef MQTT_USE_MBEDTLS + mbedtls_net_context net_ctx; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + mbedtls_x509_crt ca_crt; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + #endif + uint8_t sendbuf[CONFIG_EXAMPLES_MQTTC_TXSIZE]; + uint8_t recvbuf[CONFIG_EXAMPLES_MQTTC_RXSIZE]; +}; + +/* State handed to the reconnect callback so it can rebuild the transport + * connection and reconfigure the MQTT session from scratch. + */ + +struct mqttc_reconnect_s +{ + FAR const struct mqttc_cfg_s *cfg; + FAR struct mqtt_conn_context_s *conn; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static FAR void *client_refresher(FAR void *data); +static void parsearg(int argc, FAR char *argv[], + FAR struct mqttc_cfg_s *cfg); +static int init_conn(FAR const struct mqttc_cfg_s *cfg, + FAR struct mqtt_conn_context_s *ctx, + FAR mqtt_pal_socket_handle *handle); +static void close_conn(FAR struct mqtt_conn_context_s *ctx); +static void reconnect_client(FAR struct mqtt_client *client, + FAR void **state); +static void wait_for_network(void); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef MQTT_USE_MBEDTLS + +/**************************************************************************** + * Name: mqttc_net_recv + * + * Description: + * mbedTLS BIO receive callback for non-blocking sockets. The stock + * mbedtls_net_recv() relies on fcntl(F_GETFL) reporting O_NONBLOCK to + * classify EAGAIN as "would block". On NuttX, F_SETFL applies the + * non-blocking mode via FIONBIO but does not retain O_NONBLOCK in the + * file status flags, so F_GETFL never reports it. The stock callback + * therefore turns a benign EAGAIN into MBEDTLS_ERR_NET_RECV_FAILED. This + * wrapper inspects errno directly and returns MBEDTLS_ERR_SSL_WANT_READ + * so callers (e.g. MQTT-C's mqtt_sync) keep retrying instead of aborting. + * + ****************************************************************************/ + +static int mqttc_net_recv(FAR void *ctx, FAR unsigned char *buf, size_t len) +{ + int fd = ((FAR mbedtls_net_context *)ctx)->fd; + int ret; + + ret = recv(fd, buf, len, 0); + if (ret < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + return MBEDTLS_ERR_SSL_WANT_READ; + } + + if (errno == EPIPE || errno == ECONNRESET) + { + return MBEDTLS_ERR_NET_CONN_RESET; + } + + return MBEDTLS_ERR_NET_RECV_FAILED; + } + + return ret; +} + +/**************************************************************************** + * Name: mqttc_net_send + * + * Description: + * mbedTLS BIO send callback, mirroring mqttc_net_recv() for the same + * non-blocking/EAGAIN reason on the transmit side. + * + ****************************************************************************/ + +static int mqttc_net_send(FAR void *ctx, FAR const unsigned char *buf, + size_t len) +{ + int fd = ((FAR mbedtls_net_context *)ctx)->fd; + int ret; + + ret = send(fd, buf, len, 0); + if (ret < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + if (errno == EPIPE || errno == ECONNRESET) + { + return MBEDTLS_ERR_NET_CONN_RESET; + } + + return MBEDTLS_ERR_NET_SEND_FAILED; + } + + return ret; +} + +#endif /* MQTT_USE_MBEDTLS */ + +/**************************************************************************** + * Name: client_refresher + * + * Description: + * The client's refresher. This function triggers back-end routines to + * handle ingress/egress traffic to the broker. + * + ****************************************************************************/ + +static FAR void *client_refresher(FAR void *data) +{ + while (1) + { + mqtt_sync((FAR struct mqtt_client *)data); + usleep(100000U); + } + + return NULL; +} + +/**************************************************************************** + * Name: publish_callback + * + * Description: + * Print received publish message. + * + ****************************************************************************/ + +void publish_callback(void** unused, struct mqtt_response_publish *published) +{ + /* this is a byte stream, we have to convert it to C string */ + + char message[CONFIG_EXAMPLES_MQTTC_RXSIZE]; + + /* convert to C string (null terminated) */ + + size_t message_size = published->application_message_size; + if (message_size >= CONFIG_EXAMPLES_MQTTC_RXSIZE) + { + message_size = CONFIG_EXAMPLES_MQTTC_RXSIZE - 1; + } + + memcpy(message, published->application_message, message_size); + message[message_size] = '\0'; + + printf("Received: %s\n", message); +} + +/**************************************************************************** + * Name: parsearg + * + * Description: + * Parse command line arguments. + * + ****************************************************************************/ + +static void parsearg(int argc, FAR char *argv[], + FAR struct mqttc_cfg_s *cfg) +{ + int opt; + + while ((opt = getopt(argc, argv, "h:p:t:q:" EXTRA_OPT)) != ERROR) + { + switch (opt) + { + case 'h': + cfg->host = optarg; + break; + + case 'p': + cfg->port = optarg; + break; + + case 't': + cfg->topic = optarg; + break; + + case 'q': + switch (strtol(optarg, NULL, 10)) + { + case 0L: + cfg->qos = 0; + break; + case 1L: + cfg->qos = 1; + break; + case 2L: + cfg->qos = 2; + break; + } + break; +#ifdef MQTT_USE_MBEDTLS + case 'c': + cfg->ca_file = optarg; + break; +#endif + + default: + fprintf(stderr, "ERROR: Unrecognized option\n"); + break; + } + } +} + +/**************************************************************************** + * Name: init_conn + * + * Description: + * Resolve server's name and try to establish a connection. + * + ****************************************************************************/ + +static int init_conn(FAR const struct mqttc_cfg_s *cfg, + FAR struct mqtt_conn_context_s *conn, + FAR mqtt_pal_socket_handle *socketfd) +{ +#ifdef MQTT_USE_MBEDTLS + FAR mbedtls_net_context *net_ctx = &conn->net_ctx; + FAR mbedtls_ssl_context *ssl_ctx = &conn->ssl_ctx; + FAR mbedtls_ssl_config *ssl_conf = &conn->ssl_conf; + FAR mbedtls_x509_crt *ca_crt = &conn->ca_crt; + FAR mbedtls_entropy_context *entropy = &conn->entropy; + FAR mbedtls_ctr_drbg_context *ctr_drbg = &conn->ctr_drbg; + uint32_t result; + + FAR const unsigned char *additional = (FAR const unsigned char *)"MQTT-C"; + size_t additional_len = 6; +#else + struct addrinfo hints; + FAR struct addrinfo *servinfo; + FAR struct addrinfo *itr; + int fd; +#endif + int ret; + + printf("Connecting to %s:%s...\n", cfg->host, cfg->port); + +#ifdef MQTT_USE_MBEDTLS + mbedtls_entropy_init(entropy); + mbedtls_ctr_drbg_init(ctr_drbg); + ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, + additional, additional_len); + if (ret != 0) + { + printf("ERROR! mbedtls_ctr_drbg_seed() failed: %d\n", ret); + goto err_with_ctr_drbg; + } + + mbedtls_x509_crt_init(ca_crt); + if (cfg->ca_file != NULL) + { + ret = mbedtls_x509_crt_parse_file(ca_crt, cfg->ca_file); + } + else + { + ret = mbedtls_x509_crt_parse(ca_crt, + (FAR const unsigned char *)TEST_CA_CRT_RSA_SHA256_PEM, + sizeof(TEST_CA_CRT_RSA_SHA256_PEM)); + } + + if (ret != 0) + { + printf("ERROR! mbedtls_x509_crt_parse_file() failed: %d\n", ret); + goto err_with_x509_crt; + } + + mbedtls_ssl_config_init(ssl_conf); + ret = mbedtls_ssl_config_defaults(ssl_conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) + { + printf("ERROR! mbedtls_ssl_config_defaults() failed: %d\n", ret); + goto err_with_ssl_conf; + } + + mbedtls_ssl_conf_ca_chain(ssl_conf, ca_crt, NULL); + mbedtls_ssl_conf_authmode(ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL); + mbedtls_ssl_conf_rng(ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg); + + mbedtls_net_init(net_ctx); + ret = mbedtls_net_connect(net_ctx, cfg->host, cfg->port, + MBEDTLS_NET_PROTO_TCP); + if (ret != 0) + { + printf("ERROR! mbedtls_net_connect() failed: %d\n", ret); + goto err_with_net; + } + + ret = mbedtls_net_set_nonblock(net_ctx); + if (ret != 0) + { + printf("ERROR! mbedtls_net_set_nonblock() failed: %d\n", ret); + goto err_with_net; + } + + mbedtls_ssl_init(ssl_ctx); + ret = mbedtls_ssl_setup(ssl_ctx, ssl_conf); + if (ret != 0) + { + printf("ERROR! mbedtls_ssl_setup() failed: %d\n", ret); + goto err_with_ssl; + } + + ret = mbedtls_ssl_set_hostname(ssl_ctx, cfg->host); + if (ret != 0) + { + printf("ERROR! mbedtls_ssl_set_hostname() failed: %d\n", ret); + goto err_with_ssl; + } + + mbedtls_ssl_set_bio(ssl_ctx, net_ctx, + mqttc_net_send, mqttc_net_recv, NULL); + + for (; ; ) + { + uint32_t want = 0; + ret = mbedtls_ssl_handshake(ssl_ctx); + if (ret == MBEDTLS_ERR_SSL_WANT_READ) + { + want |= MBEDTLS_NET_POLL_READ; + } + else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) + { + want |= MBEDTLS_NET_POLL_WRITE; + } + else + { + break; + } + + ret = mbedtls_net_poll(net_ctx, want, -1); + if (ret < 0) + { + printf("ERROR! mbedtls_net_poll() failed: %d\n", ret); + goto err_with_ssl; + } + } + + if (ret != 0) + { + printf("ERROR! mbedtls_ssl_handshake() failed: %d\n", ret); + goto err_with_ssl; + } + + result = mbedtls_ssl_get_verify_result(ssl_ctx); + if (result != 0) + { + if (result == 0xffffffff) + { + printf("ERROR! mbedtls_ssl_get_verify_result() failed\n"); + goto err_with_ssl; + } + else + { +#if defined(MBEDTLS_X509_REMOVE_INFO) + const char *buf = ""; +#else + char buf[512]; + mbedtls_x509_crt_verify_info(buf, sizeof(buf), "\t", result); +#endif + printf("Certificate verification failed (%0" PRIx32 ")\n%s\n", + result, buf); + +#ifndef CONFIG_EXAMPLES_MQTTC_ALLOW_UNVERIFIED_TLS + goto err_with_ssl; +#endif + } + } + + *socketfd = ssl_ctx; + + return 0; + +err_with_ssl: + mbedtls_ssl_free(ssl_ctx); +err_with_net: + mbedtls_net_free(net_ctx); +err_with_ssl_conf: + mbedtls_ssl_config_free(ssl_conf); +err_with_x509_crt: + mbedtls_x509_crt_free(ca_crt); +err_with_ctr_drbg: + mbedtls_ctr_drbg_free(ctr_drbg); + mbedtls_entropy_free(entropy); + + return -1; +#else + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + ret = getaddrinfo(cfg->host, cfg->port, &hints, &servinfo); + if (ret != 0) + { + printf("ERROR! getaddrinfo() failed: %s\n", gai_strerror(ret)); + return -1; + } + + itr = servinfo; + do + { + fd = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol); + if (fd < 0) + { + continue; + } + + ret = connect(fd, itr->ai_addr, itr->ai_addrlen); + if (ret == 0) + { + break; + } + + close(fd); + fd = -1; + } + while ((itr = itr->ai_next) != NULL); + + freeaddrinfo(servinfo); + + if (fd < 0) + { + printf("ERROR! Couldn't create socket\n"); + return -1; + } + + ret = fcntl(fd, F_GETFL, 0); + if (ret < 0) + { + close(fd); + printf("ERROR! fcntl() F_GETFL failed, errno: %d\n", errno); + return -1; + } + + ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); + if (ret < 0) + { + close(fd); + printf("ERROR! fcntl() F_SETFL failed, errno: %d\n", errno); + return -1; + } + + *socketfd = fd; + + return 0; +#endif +} + +/**************************************************************************** + * Name: close_conn + * + * Description: + * Shut down connection to server established by init_conn. + * + ****************************************************************************/ + +static void close_conn(FAR struct mqtt_conn_context_s *conn) +{ +#ifndef MQTT_USE_MBEDTLS + close(conn->client.socketfd); +#else + mbedtls_net_free(&conn->net_ctx); + mbedtls_ssl_free(&conn->ssl_ctx); + mbedtls_ssl_config_free(&conn->ssl_conf); + mbedtls_x509_crt_free(&conn->ca_crt); + mbedtls_ctr_drbg_free(&conn->ctr_drbg); + mbedtls_entropy_free(&conn->entropy); +#endif +} + +/**************************************************************************** + * Name: wait_for_network + * + * Description: + * Block until the network interface has a usable IPv4 address (i.e. the + * link is up and DHCP has completed) or a timeout elapses. This avoids + * the spurious first-attempt connect() failure that happens when the + * example starts before Wi-Fi association/DHCP have finished. + * + ****************************************************************************/ + +static void wait_for_network(void) +{ + struct in_addr addr; + int waited = 0; + + for (; ; ) + { + if (netlib_get_ipv4addr(MQTTC_NETIF, &addr) == 0 && + addr.s_addr != INADDR_ANY && + addr.s_addr != htonl(INADDR_LOOPBACK)) + { + return; + } + + if (waited >= MQTTC_NET_WAIT_MS) + { + return; + } + + usleep(MQTTC_NET_POLL_MS * 1000); + waited += MQTTC_NET_POLL_MS; + } +} + +/**************************************************************************** + * Name: reconnect_client + * + * Description: + * Reconnect callback registered with mqtt_init_reconnect(). MQTT-C calls + * this whenever the client enters an error state, and once at start-up + * (with client->error == MQTT_ERROR_INITIAL_RECONNECT) to set up the + * initial session. It tears down the previous connection, re-establishes + * the transport (TCP + optional TLS), reinitializes the client and then + * reconfigures the session (CONNECT + SUBSCRIBE). + * + ****************************************************************************/ + +static void reconnect_client(FAR struct mqtt_client *client, + FAR void **state) +{ + FAR struct mqttc_reconnect_s *rc = + *((FAR struct mqttc_reconnect_s **)state); + FAR const struct mqttc_cfg_s *cfg = rc->cfg; + FAR struct mqtt_conn_context_s *conn = rc->conn; + mqtt_pal_socket_handle socketfd; + enum MQTTErrors mqtterr; + + /* Tear down the previous connection, unless this is the initial set-up + * call (in which case there is nothing to clean up yet). + */ + + if (client->error != MQTT_ERROR_INITIAL_RECONNECT) + { + printf("reconnect_client: reconnecting after error \"%s\"\n", + mqtt_error_str(client->error)); + close_conn(conn); + } + + /* (Re)establish the transport connection. + * + * mqtt_sync() unconditionally proceeds to recv/send on client->socketfd + * after this callback returns. Until mqtt_reinit() runs, socketfd is the + * sentinel value -1 set by mqtt_init_reconnect(); in the mbedTLS build + * that is a pointer (0xffffffff) that mbedtls_ssl_read() would dereference + * and crash on. We must therefore not return until a connection has + * actually been established, so retry with a short back-off on failure. + */ + + while (init_conn(cfg, conn, &socketfd) < 0) + { + printf("ERROR! init_conn() failed; retrying in %d s\n", + MQTTC_RECONNECT_DELAY_S); + sleep(MQTTC_RECONNECT_DELAY_S); + } + + printf("Success: Connected to broker!\n"); + + /* Reinitialize the client with the fresh socket/SSL handle. */ + + mqtt_reinit(client, socketfd, + conn->sendbuf, sizeof(conn->sendbuf), + conn->recvbuf, sizeof(conn->recvbuf)); + + /* Send the connection request to the broker. */ + + mqtterr = mqtt_connect(client, cfg->id, NULL, NULL, 0, NULL, NULL, + cfg->flags, cfg->tmo); + if (mqtterr != MQTT_OK) + { + printf("ERROR! mqtt_connect() failed: %s\n", mqtt_error_str(mqtterr)); + return; + } + + /* (Re)subscribe to the configured topic. */ + + mqtterr = mqtt_subscribe(client, cfg->topic, cfg->qos); + if (mqtterr != MQTT_OK) + { + printf("ERROR! mqtt_subscribe() failed: %s\n", + mqtt_error_str(mqtterr)); + } +} + +int main(int argc, FAR char * argv[]) +{ + struct mqtt_conn_context_s mqtt_conn = + { + 0 + }; + + struct mqttc_reconnect_s reconnect_state; + int timeout = 100; + enum MQTTErrors mqtterr; + pthread_attr_t attr; + pthread_t thrdid; + struct mqttc_cfg_s mqtt_cfg = + { + .host = "broker.hivemq.com", + #ifndef MQTT_USE_MBEDTLS + .port = "1883", + #else + .port = "8883", + #endif + .topic = "test", + .msg = "NULL", + .flags = MQTT_CONNECT_CLEAN_SESSION, + .tmo = 400, + .id = NULL, + .user = NULL, + .pass = NULL, + .qos = MQTT_PUBLISH_QOS_0 + }; + + parsearg(argc, argv, &mqtt_cfg); + + /* Wait for the network to come up so the first connection attempt does not + * fail with ENETUNREACH + */ + + wait_for_network(); + + /* Bundle the configuration and connection context for the reconnect + * callback. mqtt_init_reconnect() leaves the client in the + * MQTT_ERROR_INITIAL_RECONNECT state; the first mqtt_sync() (driven by the + * refresher thread) invokes reconnect_client(), which establishes the + * transport, calls mqtt_reinit() and then mqtt_connect()/mqtt_subscribe(). + */ + + reconnect_state.cfg = &mqtt_cfg; + reconnect_state.conn = &mqtt_conn; + + mqtt_init_reconnect(&mqtt_conn.client, + reconnect_client, &reconnect_state, + publish_callback); + + /* Start a thread to refresh the client (handle egress and ingress client + * traffic) + */ + + if (pthread_attr_init(&attr) != 0) + { + printf("ERROR! pthread_attr_init() failed.\n"); + goto err_with_conn; + } + + pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_MQTTC_STACKSIZE); + + if (pthread_create(&thrdid, &attr, client_refresher, &mqtt_conn.client)) + { + printf("ERROR! pthread_create() failed.\n"); + goto err_with_conn; + } + + /* Wait for the initial MQTT connection to be acknowledged or time-out. + * On time-out we keep going anyway: the reconnect callback will continue + * retrying in the background. + */ + + while (!mqtt_conn.client.event_connect && --timeout > 0) + { + usleep(10000); + } + + if (timeout == 0) + { + printf("WARNING! Initial connection timed out; " + "reconnect will keep retrying.\n"); + } + else + { + printf("Success: Connected to broker!\n"); + } + + printf("Listening for %s.\nType q to exit.\n\n", mqtt_cfg.topic); + + /* Wait for messages */ + + while (fgetc(stdin) != 'q'); + + /* Disconnect */ + + printf("\nDisconnecting from %s\n\n", mqtt_cfg.host); + mqtterr = mqtt_disconnect(&mqtt_conn.client); + if (mqtterr != MQTT_OK) + { + printf("ERROR! mqtt_disconnect() failed\n"); + } + + /* Force sending the DISCONNECT, the thread will be canceled before getting + * the chance to sync this last packet. + * Note however that close() would cleanly close the connection but only + * through TCP (i.e. no MQTT DISCONNECT packet). + */ + + mqtt_sync(&mqtt_conn.client); + + pthread_cancel(thrdid); + +err_with_conn: + close_conn(&mqtt_conn); + + return 0; +} diff --git a/examples/mtdpart/mtdpart_main.c b/examples/mtdpart/mtdpart_main.c index 35ba65ce7..428eddc57 100644 --- a/examples/mtdpart/mtdpart_main.c +++ b/examples/mtdpart/mtdpart_main.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/mtdrwb/mtdrwb_main.c b/examples/mtdrwb/mtdrwb_main.c index 1ebbe44f5..93b9d3290 100644 --- a/examples/mtdrwb/mtdrwb_main.c +++ b/examples/mtdrwb/mtdrwb_main.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/netlink_route/netlink_route_main.c b/examples/netlink_route/netlink_route_main.c index c69f4dd12..896a9796e 100644 --- a/examples/netlink_route/netlink_route_main.c +++ b/examples/netlink_route/netlink_route_main.c @@ -168,11 +168,7 @@ static void dump_neighbor(void) printf(" "); } -#ifdef CONFIG_SYSTEM_TIME64 printf("Time 0x%" PRIx64 "\n", nb->ne_time); -#else - printf("Time 0x%" PRIx32 "\n", nb->ne_time); -#endif } free(nbtab); diff --git a/examples/netloop/lo_listener.c b/examples/netloop/lo_listener.c index 8b51027bb..57362cec8 100644 --- a/examples/netloop/lo_listener.c +++ b/examples/netloop/lo_listener.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -64,7 +64,7 @@ struct net_listener_s struct sockaddr_in addr; fd_set master; fd_set working; - char buffer[IOBUFFER_SIZE]; + char buffer[IOBUFFER_SIZE + 1]; int listensd; int mxsd; }; diff --git a/examples/netloop/lo_main.c b/examples/netloop/lo_main.c index cfdfab429..3c6a0c72f 100644 --- a/examples/netloop/lo_main.c +++ b/examples/netloop/lo_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -63,7 +63,7 @@ static int lo_client(void) { struct sockaddr_in myaddr; char outbuf[IOBUFFER_SIZE]; - char inbuf[IOBUFFER_SIZE]; + char inbuf[IOBUFFER_SIZE + 1]; int sockfd; int len; int nbytessent; diff --git a/examples/netloop/netloop.h b/examples/netloop/netloop.h index c4d6415d6..2696e89ae 100644 --- a/examples/netloop/netloop.h +++ b/examples/netloop/netloop.h @@ -45,7 +45,6 @@ * buffering s not yet support for UDP) */ - #ifndef CONFIG_NET # error Network socket support not enabled #endif diff --git a/examples/netpkt/netpkt_ethercat.c b/examples/netpkt/netpkt_ethercat.c index 8b8ab298a..ea24791a7 100644 --- a/examples/netpkt/netpkt_ethercat.c +++ b/examples/netpkt/netpkt_ethercat.c @@ -26,10 +26,13 @@ #include +#include #include #include #include +#include +#include #include #include #include @@ -66,7 +69,7 @@ int main(int argc, FAR const char *argv[]) int num_packets = 0; int len; int ifindex; - int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); + int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sockfd == -1) { @@ -96,6 +99,7 @@ int main(int argc, FAR const char *argv[]) addr.sll_family = AF_PACKET; addr.sll_ifindex = ifindex; + addr.sll_protocol = htons(ETH_P_ALL); if (bind(sockfd, (FAR const struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("ERROR: binding socket failed"); @@ -115,9 +119,9 @@ int main(int argc, FAR const char *argv[]) len = recvfrom(sockfd, recvbuff, 100, 0, NULL, NULL); clock_gettime(CLOCK_REALTIME, &recv_time); - printf("Data recv: %d bytes, spent time %ld ns\n", len, - (recv_time.tv_sec - send_time.tv_sec) * NSEC_PER_SEC + - recv_time.tv_nsec - send_time.tv_nsec); + printf("Data recv: %d bytes, spent time %jd ns\n", len, + (intmax_t)(recv_time.tv_sec - send_time.tv_sec) * + NSEC_PER_SEC + recv_time.tv_nsec - send_time.tv_nsec); usleep(1000); } diff --git a/examples/netpkt/netpkt_main.c b/examples/netpkt/netpkt_main.c index 5f9aec81f..ab0595be4 100644 --- a/examples/netpkt/netpkt_main.c +++ b/examples/netpkt/netpkt_main.c @@ -29,7 +29,10 @@ #include #include #include +#include +#include #include +#include /**************************************************************************** * Private Functions @@ -39,11 +42,17 @@ * Name: psock_create ****************************************************************************/ -static int psock_create(void) +static int psock_create(const char *ifname) { int sd; struct sockaddr_ll addr; int addrlen = sizeof(addr); + unsigned int ifindex = 0; + + if (ifname && ifname[0]) + { + ifindex = if_nametoindex(ifname); + } sd = socket(AF_PACKET, SOCK_RAW, 0); if (sd < 0) @@ -55,7 +64,8 @@ static int psock_create(void) /* Prepare sockaddr struct */ addr.sll_family = AF_PACKET; - addr.sll_ifindex = 0; + addr.sll_ifindex = ifindex; + addr.sll_protocol = htons(ETH_P_ALL); if (bind(sd, (const struct sockaddr *)&addr, addrlen) < 0) { perror("ERROR: binding socket failed"); @@ -105,6 +115,7 @@ static void netpkt_usage(void) printf(" -r receive\n"); printf(" -t transmit\n"); printf(" -v verbose\n"); + printf(" -i specify interface name (e.g. eth0)\n"); printf("\n"); } @@ -116,7 +127,7 @@ static void netpkt_usage(void) * Name: netpkt_main ****************************************************************************/ -int main(int argc, FAR char *argv[]) +int main(int argc, char *argv[]) { int sd; int i; @@ -140,6 +151,9 @@ int main(int argc, FAR char *argv[]) int do_rxtimes = 3; int do_tx = 0; int do_txtimes = 3; + char ifname[IFNAMSIZ]; + + memset(ifname, 0, sizeof(ifname)); if (argc == 1) { @@ -149,7 +163,7 @@ int main(int argc, FAR char *argv[]) /* Parse arguments */ - while ((opt = getopt(argc, argv, "artv")) != -1) + while ((opt = getopt(argc, argv, "artvi:")) != -1) { switch (opt) { @@ -170,13 +184,17 @@ int main(int argc, FAR char *argv[]) verbose = 1; break; + case 'i': + strlcpy(ifname, optarg, sizeof(ifname)); + break; + default: netpkt_usage(); return -1; } } - sd = psock_create(); + sd = psock_create(ifname); if (do_tx) { diff --git a/examples/nettest/nettest.h b/examples/nettest/nettest.h index e4ff7f3cc..fd314befb 100644 --- a/examples/nettest/nettest.h +++ b/examples/nettest/nettest.h @@ -31,7 +31,7 @@ #ifdef NETTEST_HOST #else -# include +# include #endif /**************************************************************************** diff --git a/examples/nettest/nettest_netinit.c b/examples/nettest/nettest_netinit.c index 5c29ca411..e2e786164 100644 --- a/examples/nettest/nettest_netinit.c +++ b/examples/nettest/nettest_netinit.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/examples/nettest/nettest_target1.c b/examples/nettest/nettest_target1.c index 3875be272..47db8916b 100644 --- a/examples/nettest/nettest_target1.c +++ b/examples/nettest/nettest_target1.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "nettest.h" diff --git a/examples/nimble/nimble_main.c b/examples/nimble/nimble_main.c index ba46199f7..68eb2f051 100644 --- a/examples/nimble/nimble_main.c +++ b/examples/nimble/nimble_main.c @@ -255,12 +255,6 @@ int main(int argc, FAR char *argv[]) ble_hci_sock_set_device(atoi(argv[1])); } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization */ - - boardctl(BOARDIOC_INIT, 0); -#endif - #ifndef CONFIG_NSH_NETINIT /* Bring up the network */ diff --git a/examples/nimble_blecent/nimble_blecent_main.c b/examples/nimble_blecent/nimble_blecent_main.c index a20dd282d..5af9d2b03 100644 --- a/examples/nimble_blecent/nimble_blecent_main.c +++ b/examples/nimble_blecent/nimble_blecent_main.c @@ -412,7 +412,7 @@ static int blecent_gap_event(FAR struct ble_gap_event *event, FAR void *arg) return 0; } - /* An advertisment report was received during GAP discovery. */ + /* An advertisement report was received during GAP discovery. */ print_adv_fields(&fields); @@ -632,12 +632,6 @@ int main(int argc, FAR char **argv) ble_hci_sock_set_device(atoi(argv[1])); } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization */ - - boardctl(BOARDIOC_INIT, 0); -#endif - #ifndef CONFIG_NSH_NETINIT /* Bring up the network */ diff --git a/examples/nimble_bleprph/nimble_bleprph_main.c b/examples/nimble_bleprph/nimble_bleprph_main.c index 9dea4d226..9c41f72b2 100644 --- a/examples/nimble_bleprph/nimble_bleprph_main.c +++ b/examples/nimble_bleprph/nimble_bleprph_main.c @@ -140,7 +140,7 @@ static void bleprph_advertise(void) BLE_HS_ADV_F_BREDR_UNSUP; /* Indicate that the TX power level field should be included; have the - * stack fill this value automatically. This is done by assiging the + * stack fill this value automatically. This is done by assigning the * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. */ @@ -372,12 +372,6 @@ int main(int argc, FAR char *argv[]) ble_hci_sock_set_device(atoi(argv[1])); } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization */ - - boardctl(BOARDIOC_INIT, 0); -#endif - #ifndef CONFIG_NSH_NETINIT /* Bring up the network */ diff --git a/examples/nng_test/pubsub.c b/examples/nng_test/pubsub.c index 4f285906e..78e427eb2 100644 --- a/examples/nng_test/pubsub.c +++ b/examples/nng_test/pubsub.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/noteprintf/noteprintf_main.c b/examples/noteprintf/noteprintf_main.c index dbc81ee8f..2717d7a1b 100644 --- a/examples/noteprintf/noteprintf_main.c +++ b/examples/noteprintf/noteprintf_main.c @@ -53,9 +53,7 @@ int main(int argc, FAR char *argv[]) short s = 2; int i = 3; long l = 4; -#ifdef CONFIG_HAVE_LONG_LONG long long ll = 5; -#endif intmax_t im = 6; size_t sz = 7; ptrdiff_t ptr = 8; @@ -78,9 +76,7 @@ int main(int argc, FAR char *argv[]) sched_note_printf(NOTE_TAG_ALWAYS, "%hd", s); sched_note_printf(NOTE_TAG_ALWAYS, "%d", i); sched_note_printf(NOTE_TAG_ALWAYS, "%ld", l); -#ifdef CONFIG_HAVE_LONG_LONG sched_note_printf(NOTE_TAG_ALWAYS, "%lld", ll); -#endif sched_note_printf(NOTE_TAG_ALWAYS, "%jd", im); sched_note_printf(NOTE_TAG_ALWAYS, "%zd", sz); sched_note_printf(NOTE_TAG_ALWAYS, "%td", ptr); diff --git a/examples/nrf24l01_btle/nrf24l01_btle.c b/examples/nrf24l01_btle/nrf24l01_btle.c index ee373798f..d7f24ab3c 100644 --- a/examples/nrf24l01_btle/nrf24l01_btle.c +++ b/examples/nrf24l01_btle/nrf24l01_btle.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include @@ -484,9 +484,9 @@ int nrf24_read(int wl_fd) { int ret; uint32_t pipeno; - uint8_t rbuf[32]; + uint8_t rbuf[NRF24L01_MAX_PAYLOAD_LEN + 1]; - ret = read(wl_fd, rbuf, sizeof(rbuf)); + ret = read(wl_fd, rbuf, NRF24L01_MAX_PAYLOAD_LEN); if (ret < 0) { perror("Error reading packet\n"); @@ -512,7 +512,7 @@ int nrf24_read(int wl_fd) #ifdef CONFIG_DEBUG_WIRELESS syslog(LOG_INFO, "Message received : (on pipe %d)\n", pipeno); - nrf24_dumpbuffer("Hex Dump", &rbuf[0], 32); + nrf24_dumpbuffer("Hex Dump", &rbuf[0], ret); #endif return 0; } diff --git a/examples/nrf24l01_btle/nrf24l01_btle.h b/examples/nrf24l01_btle/nrf24l01_btle.h index 73d166356..9394ee568 100644 --- a/examples/nrf24l01_btle/nrf24l01_btle.h +++ b/examples/nrf24l01_btle/nrf24l01_btle.h @@ -31,7 +31,7 @@ #include #include -#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/examples/nrf24l01_term/nrf24l01_term.c b/examples/nrf24l01_term/nrf24l01_term.c index b8af3dca0..e8c41e8c3 100644 --- a/examples/nrf24l01_term/nrf24l01_term.c +++ b/examples/nrf24l01_term/nrf24l01_term.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -221,7 +221,7 @@ int read_pkt(int wl_fd) int ret; uint32_t pipeno; - ret = read(wl_fd, buff, sizeof(buff)); + ret = read(wl_fd, buff, sizeof(buff) - 1); if (ret < 0) { perror("Error reading packet\n"); diff --git a/examples/nunchuck/nunchuck_main.c b/examples/nunchuck/nunchuck_main.c index fa72e1ce6..63200288c 100644 --- a/examples/nunchuck/nunchuck_main.c +++ b/examples/nunchuck/nunchuck_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nx/nx_events.c b/examples/nx/nx_events.c index 360856584..065c4e3cf 100644 --- a/examples/nx/nx_events.c +++ b/examples/nx/nx_events.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -52,25 +52,30 @@ static void nxeg_redraw(NXEGWINDOW hwnd, FAR const struct nxgl_rect_s *rect, bool morem, FAR void *arg); -static void nxeg_position(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxeg_position(NXEGWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nxeg_mousein(NXEGWINDOW hwnd, FAR const struct nxgl_point_s *pos, +static void nxeg_mousein(NXEGWINDOW hwnd, + FAR const struct nxgl_point_s *pos, uint8_t buttons, FAR void *arg); #endif #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS -static void nxeg_tbredraw(NXEGWINDOW hwnd, FAR const struct nxgl_rect_s *rect, +static void nxeg_tbredraw(NXEGWINDOW hwnd, + FAR const struct nxgl_rect_s *rect, bool morem, FAR void *arg); -static void nxeg_tbposition(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxeg_tbposition(NXEGWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nxeg_tbmousein(NXEGWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg); +static void nxeg_tbmousein(NXEGWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg); #endif #endif @@ -137,6 +142,7 @@ static inline void nxeg_fillwindow(NXEGWINDOW hwnd, printf("nxeg_fillwindow: nxtk_fillwindow failed: %d\n", errno); } #endif + #ifdef CONFIG_NX_KBD nxeg_filltext(hwnd, rect, st); #endif @@ -148,8 +154,8 @@ static inline void nxeg_fillwindow(NXEGWINDOW hwnd, #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS static inline void nxeg_filltoolbar(NXTKWINDOW htb, - FAR const struct nxgl_rect_s *rect, - nxgl_mxpixel_t color[CONFIG_NX_NPLANES]) + FAR const struct nxgl_rect_s *rect, + nxgl_mxpixel_t color[CONFIG_NX_NPLANES]) { int ret; @@ -181,7 +187,8 @@ static void nxeg_redraw(NXEGWINDOW hwnd, FAR const struct nxgl_rect_s *rect, * Name: nxeg_position ****************************************************************************/ -static void nxeg_position(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxeg_position(NXEGWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg) @@ -190,7 +197,8 @@ static void nxeg_position(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, /* Report the position */ - printf("nxeg_position%d: hwnd=%p size=(%d,%d) pos=(%d,%d) bounds={(%d,%d),(%d,%d)}\n", + printf("nxeg_position%d: hwnd=%p size=(%d,%d) pos=(%d,%d) " + "bounds={(%d,%d),(%d,%d)}\n", st->wnum, hwnd, size->w, size->h, pos->x, pos->y, bounds->pt1.x, bounds->pt1.y, bounds->pt2.x, bounds->pt2.y); @@ -198,7 +206,9 @@ static void nxeg_position(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, if (!b_haveresolution) { - /* Save the window limits (these should be the same for all places and all windows */ + /* Save the window limits (these should be the same for all places + * and all windows. + */ g_xres = bounds->pt2.x; g_yres = bounds->pt2.y; @@ -228,7 +238,8 @@ static void nxeg_mousein(NXEGWINDOW hwnd, FAR const struct nxgl_point_s *pos, ****************************************************************************/ #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS -static void nxeg_tbredraw(NXEGWINDOW hwnd, FAR const struct nxgl_rect_s *rect, +static void nxeg_tbredraw(NXEGWINDOW hwnd, + FAR const struct nxgl_rect_s *rect, bool more, FAR void *arg) { FAR struct nxeg_state_s *st = (FAR struct nxeg_state_s *)arg; @@ -245,16 +256,18 @@ static void nxeg_tbredraw(NXEGWINDOW hwnd, FAR const struct nxgl_rect_s *rect, ****************************************************************************/ #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS -static void nxeg_tbposition(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, - FAR const struct nxgl_point_s *pos, - FAR const struct nxgl_rect_s *bounds, - FAR void *arg) +static void nxeg_tbposition(NXEGWINDOW hwnd, + FAR const struct nxgl_size_s *size, + FAR const struct nxgl_point_s *pos, + FAR const struct nxgl_rect_s *bounds, + FAR void *arg) { FAR struct nxeg_state_s *st = (FAR struct nxeg_state_s *)arg; /* Report the position */ - printf("nxeg_ptbosition%d: hwnd=%p size=(%d,%d) pos=(%d,%d) bounds={(%d,%d),(%d,%d)}\n", + printf("nxeg_ptbosition%d: hwnd=%p size=(%d,%d) pos=(%d,%d) " + "bounds={(%d,%d),(%d,%d)}\n", st->wnum, hwnd, size->w, size->h, pos->x, pos->y, bounds->pt1.x, bounds->pt1.y, bounds->pt2.x, bounds->pt2.y); } @@ -266,8 +279,9 @@ static void nxeg_tbposition(NXEGWINDOW hwnd, FAR const struct nxgl_size_s *size, #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS #ifdef CONFIG_NX_XYINPUT -static void nxeg_tbmousein(NXEGWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg) +static void nxeg_tbmousein(NXEGWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg) { FAR struct nxeg_state_s *st = (FAR struct nxeg_state_s *)arg; @@ -291,7 +305,7 @@ FAR void *nx_listenerthread(FAR void *arg) /* Process events forever */ - for (;;) + for (; ; ) { /* Handle the next event. If we were configured blocking, then * we will stay right here until the next event is received. Since diff --git a/examples/nx/nx_kbdin.c b/examples/nx/nx_kbdin.c index 856323a24..51f0e7efd 100644 --- a/examples/nx/nx_kbdin.c +++ b/examples/nx/nx_kbdin.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -109,7 +109,8 @@ static void nxeg_fillchar(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, if (!nxgl_nullrect(&intersection)) { #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS - ret = nxtk_bitmapwindow((NXTKWINDOW)hwnd, &intersection, (FAR const void **)&src, + ret = nxtk_bitmapwindow((NXTKWINDOW)hwnd, &intersection, + (FAR const void **)&src, &bm->bounds.pt1, (unsigned int)bm->glyph->stride); if (ret < 0) @@ -165,7 +166,7 @@ nxeg_renderglyph(FAR struct nxeg_state_s *st, /* Allocate memory to hold the glyph with its offsets */ glyph->stride = (glyph->width * CONFIG_EXAMPLES_NX_BPP + 7) / 8; - bmsize = glyph->stride * glyph->height; + bmsize = glyph->stride * glyph->height; glyph->bitmap = (FAR uint8_t *)malloc(bmsize); if (glyph->bitmap) @@ -178,13 +179,13 @@ nxeg_renderglyph(FAR struct nxeg_state_s *st, /* Pack 1-bit pixels into a 2-bits */ pixel &= 0x01; - pixel = (pixel) << 1 |pixel; + pixel = (pixel) << 1 | pixel; # endif # if CONFIG_EXAMPLES_NX_BPP < 4 /* Pack 2-bit pixels into a nibble */ pixel &= 0x03; - pixel = (pixel) << 2 |pixel; + pixel = (pixel) << 2 | pixel; # endif /* Pack 4-bit nibbles into a byte */ @@ -222,7 +223,7 @@ nxeg_renderglyph(FAR struct nxeg_state_s *st, /* Then render the glyph into the allocated memory */ - ret = RENDERER((FAR nxgl_mxpixel_t*)glyph->bitmap, + ret = RENDERER((FAR nxgl_mxpixel_t *)glyph->bitmap, glyph->height, glyph->width, glyph->stride, bm, CONFIG_EXAMPLES_NX_FONTCOLOR); if (ret < 0) @@ -236,9 +237,9 @@ nxeg_renderglyph(FAR struct nxeg_state_s *st, } else { - /* Make it permanent */ + /* Make it permanent */ - st->nglyphs++; + st->nglyphs++; } } } @@ -269,6 +270,7 @@ nxeg_addspace(FAR struct nxeg_state_s *st, uint8_t ch) st->nglyphs++; } + return glyph; } @@ -283,13 +285,14 @@ nxeg_findglyph(FAR struct nxeg_state_s *st, uint8_t ch) /* First, try to find the glyph in the cache of pre-rendered glyphs */ - for (i = 0; i < st->nglyphs; i++) + for (i = 0; i < st->nglyphs; i++) { if (st->glyph[i].code == ch) { return &st->glyph[i]; } } + return NULL; } @@ -313,21 +316,22 @@ nxeg_getglyph(FAR struct nxeg_state_s *st, uint8_t ch) bm = nxf_getbitmap(g_fonthandle, ch); if (!bm) { - /* No, there is no glyph for this code. Use space */ + /* No, there is no glyph for this code. Use space. */ glyph = nxeg_findglyph(st, ' '); if (!glyph) { - /* There isn't fake glyph for ' ' yet... create one */ + /* There isn't a fake glyph for ' ' yet... create one */ glyph = nxeg_addspace(st, ' '); } } else { - glyph = nxeg_renderglyph(st, bm, ch); + glyph = nxeg_renderglyph(st, bm, ch); } } + return glyph; } @@ -346,41 +350,42 @@ nxeg_addchar(FAR struct nxeg_state_s *st, uint8_t ch) if (st->nchars < NXTK_MAXKBDCHARS) { - /* Yes, setup the bitmap */ + /* Yes, setup the bitmap */ - bm = &st->bm[st->nchars]; + bm = &st->bm[st->nchars]; - /* Find the matching glyph */ + /* Find the matching glyph */ - bm->glyph = nxeg_getglyph(st, ch); - if (!bm->glyph) - { - return NULL; - } + bm->glyph = nxeg_getglyph(st, ch); + if (!bm->glyph) + { + return NULL; + } - /* Set up the bounds for the bitmap */ + /* Set up the bounds for the bitmap */ - if (st->nchars <= 0) - { - /* The first character is one space from the left */ + if (st->nchars <= 0) + { + /* The first character is one space from the left */ - leftx = st->spwidth; - } - else - { - /* Otherwise, it is to the left of the preceding char */ + leftx = st->spwidth; + } + else + { + /* Otherwise, it is to the left of the preceding char */ - bmleft = &st->bm[st->nchars-1]; - leftx = bmleft->bounds.pt2.x + 1; - } + bmleft = &st->bm[st->nchars - 1]; + leftx = bmleft->bounds.pt2.x + 1; + } - bm->bounds.pt1.x = leftx; - bm->bounds.pt1.y = 2; - bm->bounds.pt2.x = leftx + bm->glyph->width - 1; - bm->bounds.pt2.y = 2 + bm->glyph->height - 1; + bm->bounds.pt1.x = leftx; + bm->bounds.pt1.y = 2; + bm->bounds.pt2.x = leftx + bm->glyph->width - 1; + bm->bounds.pt2.y = 2 + bm->glyph->height - 1; - st->nchars++; + st->nchars++; } + return bm; } @@ -424,10 +429,12 @@ void nxeg_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, ****************************************************************************/ #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS -void nxeg_tbkbdin(NXWINDOW hwnd, uint8_t nch, const uint8_t *ch, FAR void *arg) +void nxeg_tbkbdin(NXWINDOW hwnd, uint8_t nch, const uint8_t *ch, + FAR void *arg) { FAR struct nxeg_state_s *st = (FAR struct nxeg_state_s *)arg; - printf("nxeg_tbkbdin: ERROR -- toolbar should not received keyboard input\n"); + printf("nxeg_tbkbdin: ERROR -- toolbar should not received keyboard " + "input\n"); printf("nxeg_tbkbdin%d: hwnd=%p nch=%d\n", st->wnum, hwnd, nch); } #endif diff --git a/examples/nx/nx_main.c b/examples/nx/nx_main.c index a7b9134c0..9bdeded9c 100644 --- a/examples/nx/nx_main.c +++ b/examples/nx/nx_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -54,6 +54,7 @@ ****************************************************************************/ /* Configuration ************************************************************/ + /* If not specified, assume that the hardware supports one video plane */ #ifndef CONFIG_EXAMPLES_NX_VPLANE @@ -98,7 +99,10 @@ nxgl_coord_t g_yres; bool b_haveresolution = false; bool g_connected = false; -sem_t g_semevent = {0}; +sem_t g_semevent = +{ + 0 +}; /* Colors used to fill window 1 & 2 */ @@ -181,14 +185,16 @@ static void nxeg_freestate(FAR struct nxeg_state_s *st) { for (i = 0; i < st->nglyphs; i++) { - if (st->glyph[i].bitmap) - { - free(st->glyph[i].bitmap); - } - st->glyph[i].bitmap = NULL; + if (st->glyph[i].bitmap) + { + free(st->glyph[i].bitmap); + } + + st->glyph[i].bitmap = NULL; } + st->nchars = 0; - } + } #endif } #endif @@ -199,7 +205,7 @@ static void nxeg_freestate(FAR struct nxeg_state_s *st) #ifdef CONFIG_EXAMPLES_NX_RAWWINDOWS static inline NXEGWINDOW nxeg_openwindow(FAR const struct nx_callback_s *cb, - FAR struct nxeg_state_s *state) + FAR struct nxeg_state_s *state) { NXEGWINDOW hwnd; @@ -209,8 +215,10 @@ static inline NXEGWINDOW nxeg_openwindow(FAR const struct nx_callback_s *cb, printf("nxeg_openwindow: nx_openwindow failed: %d\n", errno); g_exitcode = NXEXIT_NXOPENWINDOW; } + return hwnd; } + #else static inline NXEGWINDOW nxeg_openwindow(FAR const struct nx_callback_s *cb, FAR struct nxeg_state_s *state) @@ -223,8 +231,10 @@ static inline NXEGWINDOW nxeg_openwindow(FAR const struct nx_callback_s *cb, printf("nxeg_openwindow: nxtk_openwindow failed: %d\n", errno); g_exitcode = NXEXIT_NXOPENWINDOW; } + return hwnd; } + #endif /**************************************************************************** @@ -232,7 +242,8 @@ static inline NXEGWINDOW nxeg_openwindow(FAR const struct nx_callback_s *cb, ****************************************************************************/ #ifdef CONFIG_EXAMPLES_NX_RAWWINDOWS -static inline int nxeg_closewindow(NXEGWINDOW hwnd, FAR struct nxeg_state_s *state) +static inline int nxeg_closewindow(NXEGWINDOW hwnd, + FAR struct nxeg_state_s *state) { int ret = nx_closewindow(hwnd); if (ret < 0) @@ -240,10 +251,13 @@ static inline int nxeg_closewindow(NXEGWINDOW hwnd, FAR struct nxeg_state_s *sta printf("nxeg_closewindow: nx_closewindow failed: %d\n", errno); g_exitcode = NXEXIT_NXCLOSEWINDOW; } + return ret; } + #else -static inline int nxeg_closewindow(NXEGWINDOW hwnd, FAR struct nxeg_state_s *state) +static inline int nxeg_closewindow(NXEGWINDOW hwnd, + FAR struct nxeg_state_s *state) { int ret = nxtk_closewindow(hwnd); if (ret < 0) @@ -251,9 +265,11 @@ static inline int nxeg_closewindow(NXEGWINDOW hwnd, FAR struct nxeg_state_s *sta printf("nxeg_closewindow: nxtk_closewindow failed: %d\n", errno); g_exitcode = NXEXIT_NXCLOSEWINDOW; } + nxeg_freestate(state); return ret; } + #endif /**************************************************************************** @@ -261,7 +277,8 @@ static inline int nxeg_closewindow(NXEGWINDOW hwnd, FAR struct nxeg_state_s *sta ****************************************************************************/ #ifdef CONFIG_EXAMPLES_NX_RAWWINDOWS -static inline int nxeg_setsize(NXEGWINDOW hwnd, FAR struct nxgl_size_s *size) +static inline int nxeg_setsize(NXEGWINDOW hwnd, + FAR struct nxgl_size_s *size) { int ret = nx_setsize(hwnd, size); if (ret < 0) @@ -269,10 +286,13 @@ static inline int nxeg_setsize(NXEGWINDOW hwnd, FAR struct nxgl_size_s *size) printf("nxeg_setsize: nx_setsize failed: %d\n", errno); g_exitcode = NXEXIT_NXSETSIZE; } + return ret; } + #else -static inline int nxeg_setsize(NXEGWINDOW hwnd, FAR struct nxgl_size_s *size) +static inline int nxeg_setsize(NXEGWINDOW hwnd, + FAR struct nxgl_size_s *size) { int ret = nxtk_setsize(hwnd, size); if (ret < 0) @@ -280,8 +300,10 @@ static inline int nxeg_setsize(NXEGWINDOW hwnd, FAR struct nxgl_size_s *size) printf("nxeg_setsize: nxtk_setsize failed: %d\n", errno); g_exitcode = NXEXIT_NXSETSIZE; } + return ret; } + #endif /**************************************************************************** @@ -289,7 +311,8 @@ static inline int nxeg_setsize(NXEGWINDOW hwnd, FAR struct nxgl_size_s *size) ****************************************************************************/ #ifdef CONFIG_EXAMPLES_NX_RAWWINDOWS -static inline int nxeg_setposition(NXEGWINDOW hwnd, FAR struct nxgl_point_s *pos) +static inline int nxeg_setposition(NXEGWINDOW hwnd, + FAR struct nxgl_point_s *pos) { int ret = nx_setposition(hwnd, pos); if (ret < 0) @@ -297,10 +320,13 @@ static inline int nxeg_setposition(NXEGWINDOW hwnd, FAR struct nxgl_point_s *pos printf("nxeg_setposition: nx_setposition failed: %d\n", errno); g_exitcode = NXEXIT_NXSETPOSITION; } + return ret; } + #else -static inline int nxeg_setposition(NXEGWINDOW hwnd, FAR struct nxgl_point_s *pos) +static inline int nxeg_setposition(NXEGWINDOW hwnd, + FAR struct nxgl_point_s *pos) { int ret = nxtk_setposition(hwnd, pos); if (ret < 0) @@ -308,8 +334,10 @@ static inline int nxeg_setposition(NXEGWINDOW hwnd, FAR struct nxgl_point_s *pos printf("nxeg_setposition: nxtk_setposition failed: %d\n", errno); g_exitcode = NXEXIT_NXSETPOSITION; } + return ret; } + #endif /**************************************************************************** @@ -328,8 +356,10 @@ static inline int nxeq_opentoolbar(NXEGWINDOW hwnd, nxgl_coord_t height, printf("nxeq_opentoolbar: nxtk_opentoolbar failed: %d\n", errno); g_exitcode = NXEXIT_NXOPENTOOLBAR; } + return ret; } + #endif /**************************************************************************** @@ -345,8 +375,10 @@ static inline int nxeg_lower(NXEGWINDOW hwnd) printf("nxeg_lower: nx_lower failed: %d\n", errno); g_exitcode = NXEXIT_NXLOWER; } + return ret; } + #else static inline int nxeg_lower(NXEGWINDOW hwnd) { @@ -356,8 +388,10 @@ static inline int nxeg_lower(NXEGWINDOW hwnd) printf("nxeg_lower: nxtk_lower failed: %d\n", errno); g_exitcode = NXEXIT_NXLOWER; } + return ret; } + #endif /**************************************************************************** @@ -373,8 +407,10 @@ static inline int nxeg_raise(NXEGWINDOW hwnd) printf("nxeg_raise: nx_raise failed: %d\n", errno); g_exitcode = NXEXIT_NXRAISE; } + return ret; } + #else static inline int nxeg_raise(NXEGWINDOW hwnd) { @@ -384,8 +420,10 @@ static inline int nxeg_raise(NXEGWINDOW hwnd) printf("nxeg_raise: nxtk_raise failed: %d\n", errno); g_exitcode = NXEXIT_NXRAISE; } + return ret; } + #endif /**************************************************************************** @@ -416,7 +454,7 @@ static int nxeg_initialize(void) ret = sched_setparam(0, ¶m); if (ret < 0) { - printf("nxeg_initialize: sched_setparam failed: %d\n" , ret); + printf("nxeg_initialize: sched_setparam failed: %d\n", ret); g_exitcode = NXEXIT_SCHEDSETPARAM; return ERROR; } @@ -426,7 +464,8 @@ static int nxeg_initialize(void) ret = boardctl(BOARDIOC_NX_START, 0); if (ret < 0) { - printf("nxeg_initialize: Failed to start the NX server: %d\n", errno); + printf("nxeg_initialize: Failed to start the NX server: %d\n", + errno); g_exitcode = NXEXIT_TASKCREATE; return ERROR; } @@ -436,54 +475,54 @@ static int nxeg_initialize(void) g_hnx = nx_connect(); if (g_hnx) { - pthread_attr_t attr; + pthread_attr_t attr; #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, g_hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, g_hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); - nx_disconnect(g_hnx); - g_exitcode = NXEXIT_FBINITIALIZE; - return ERROR; - } + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); + nx_disconnect(g_hnx); + g_exitcode = NXEXIT_FBINITIALIZE; + return ERROR; + } #endif - /* Start a separate thread to listen for server events. This is probably - * the least efficient way to do this, but it makes this example flow more - * smoothly. - */ + /* Start a separate thread to listen for server events. This is + * probably the least efficient way to do this, but it makes this + * example flow more smoothly. + */ - pthread_attr_init(&attr); - param.sched_priority = CONFIG_EXAMPLES_NX_LISTENERPRIO; - pthread_attr_setschedparam(&attr, ¶m); - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_NX_STACKSIZE); + pthread_attr_init(&attr); + param.sched_priority = CONFIG_EXAMPLES_NX_LISTENERPRIO; + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_NX_STACKSIZE); - ret = pthread_create(&thread, &attr, nx_listenerthread, NULL); - if (ret != 0) - { - printf("nxeg_initialize: pthread_create failed: %d\n", ret); - g_exitcode = NXEXIT_PTHREADCREATE; - return ERROR; - } + ret = pthread_create(&thread, &attr, nx_listenerthread, NULL); + if (ret != 0) + { + printf("nxeg_initialize: pthread_create failed: %d\n", ret); + g_exitcode = NXEXIT_PTHREADCREATE; + return ERROR; + } - /* Don't return until we are connected to the server */ + /* Don't return until we are connected to the server */ - while (!g_connected) - { - /* Wait for the listener thread to wake us up when we really - * are connected. - */ + while (!g_connected) + { + /* Wait for the listener thread to wake us up when we really + * are connected. + */ - sem_wait(&g_semevent); - } + sem_wait(&g_semevent); + } } else { @@ -562,6 +601,7 @@ int main(int argc, FAR char *argv[]) { sem_wait(&g_semevent); } + printf("nx_main: Screen resolution (%d,%d)\n", g_xres, g_yres); /* Set the size of the window 1 */ @@ -576,9 +616,8 @@ int main(int argc, FAR char *argv[]) goto errout_with_hwnd1; } - /* Sleep a bit -- both so that we can see the result of the above operations - * but also, in the multi-user case, so that the server can get a chance to - * actually do them! + /* Sleep a bit. This lets us see the result of the above operations and, + * in the multi-user case, gives the server a chance to actually do them. */ printf("nx_main: Sleeping\n\n"); @@ -605,7 +644,8 @@ int main(int argc, FAR char *argv[]) #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS printf("nx_main: Add toolbar to window #1\n"); - ret = nxeq_opentoolbar(hwnd1, CONFIG_EXAMPLES_NX_TOOLBAR_HEIGHT, &g_tbcb, &g_wstate[0]); + ret = nxeq_opentoolbar(hwnd1, CONFIG_EXAMPLES_NX_TOOLBAR_HEIGHT, + &g_tbcb, &g_wstate[0]); if (ret < 0) { goto errout_with_hwnd1; @@ -633,7 +673,7 @@ int main(int argc, FAR char *argv[]) printf("nx_main: Sleeping\n\n"); sleep(1); - /* Set the size of the window 2 == size of window 1*/ + /* Set the size of window 2 equal to the size of window 1 */ printf("nx_main: Set hwnd2 size to (%d,%d)\n", size.w, size.h); ret = nxeg_setsize(hwnd2, &size); @@ -666,7 +706,8 @@ int main(int argc, FAR char *argv[]) #ifndef CONFIG_EXAMPLES_NX_RAWWINDOWS printf("nx_main: Add toolbar to window #2\n"); - ret = nxeq_opentoolbar(hwnd2, CONFIG_EXAMPLES_NX_TOOLBAR_HEIGHT, &g_tbcb, &g_wstate[1]); + ret = nxeq_opentoolbar(hwnd2, CONFIG_EXAMPLES_NX_TOOLBAR_HEIGHT, + &g_tbcb, &g_wstate[1]); if (ret < 0) { goto errout_with_hwnd2; @@ -770,6 +811,7 @@ errout_with_hwnd1: nxeg_closewindow(hwnd1, &g_wstate[0]); errout_with_nx: + /* Disconnect from the server */ printf("nx_main: Disconnect from the server\n"); diff --git a/examples/nxdemo/nxdemo_bkgd.c b/examples/nxdemo/nxdemo_bkgd.c index fd03f08c0..ecd82b238 100644 --- a/examples/nxdemo/nxdemo_bkgd.c +++ b/examples/nxdemo/nxdemo_bkgd.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nxdemo/nxdemo_main.c b/examples/nxdemo/nxdemo_main.c index ca5d5b878..e6875b9cd 100644 --- a/examples/nxdemo/nxdemo_main.c +++ b/examples/nxdemo/nxdemo_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nxflat/nxflat_main.c b/examples/nxflat/nxflat_main.c index 0c8425e2e..c7ea6cf19 100644 --- a/examples/nxflat/nxflat_main.c +++ b/examples/nxflat/nxflat_main.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nxflat/tests/signal/signal.c b/examples/nxflat/tests/signal/signal.c index 7deb7768e..63ee50767 100644 --- a/examples/nxflat/tests/signal/signal.c +++ b/examples/nxflat/tests/signal/signal.c @@ -66,8 +66,8 @@ static int sigusr2_rcvd = 0; * Name: siguser_action ****************************************************************************/ -/* NOTE: it is necessary for functions that are referred to by function pointers - * pointer to be declared with global scope (at least for ARM). Otherwise, +/* NOTE: it is necessary for functions that are referred to by function + * pointers to be declared with global scope (at least for ARM). Otherwise, * a relocation type that is not supported by ELF is generated by GCC. */ @@ -225,7 +225,7 @@ int main(int argc, char **argv) /* Verify that the handler that was removed was siguser_action */ - if ((void*)act.sa_handler != (void*)siguser_action) + if ((void *)act.sa_handler != (void *)siguser_action) { fprintf(stderr, "Old SIGUSR2 signal handler (%p) is not siguser_action (%p)\n", diff --git a/examples/nxflat/tests/struct/struct_main.c b/examples/nxflat/tests/struct/struct_main.c index 18aa49178..b3721f87a 100644 --- a/examples/nxflat/tests/struct/struct_main.c +++ b/examples/nxflat/tests/struct/struct_main.c @@ -35,7 +35,7 @@ const struct struct_dummy_s dummy_struct = { - DUMMY_SCALAR_VALUE3 + DUMMY_SCALAR_VALUE3 }; int dummy_scalar = DUMMY_SCALAR_VALUE2; diff --git a/examples/nxflatxip/.gitignore b/examples/nxflatxip/.gitignore new file mode 100644 index 000000000..c04a5d03c --- /dev/null +++ b/examples/nxflatxip/.gitignore @@ -0,0 +1,2 @@ +/symtab.c +/xipmod_bin.h diff --git a/examples/nxflatxip/CMakeLists.txt b/examples/nxflatxip/CMakeLists.txt new file mode 100644 index 000000000..dc5dd064e --- /dev/null +++ b/examples/nxflatxip/CMakeLists.txt @@ -0,0 +1,29 @@ +# ############################################################################## +# apps/examples/nxflatxip/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +# The module and the symbol table it needs are built by module/Makefile, which +# the CMake build does not drive. As with apps/examples/nxflat, use the make +# build for this example. + +if(CONFIG_EXAMPLES_NXFLATXIP) + nuttx_add_application(NAME nxflatxip SRCS nxflatxip_main.c symtab.c) +endif() diff --git a/examples/nxflatxip/Kconfig b/examples/nxflatxip/Kconfig new file mode 100644 index 000000000..8a798d503 --- /dev/null +++ b/examples/nxflatxip/Kconfig @@ -0,0 +1,38 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_NXFLATXIP + tristate "NXFLAT execute-in-place from xipfs demo" + depends on FS_XIPFS && NXFLAT && LIBC_EXECFUNCS + default n + ---help--- + Write an NXFLAT module into xipfs at run time, the way a download + would, then run two instances of it concurrently. The module's + text is executed directly out of flash and shared between the + instances, while each instance gets its own data. + + Subcommands: + + bench XIP read throughput before and after a flash write + defrag fragment the volume, compact it, with a block map + + Building the module needs the mknxflat and ldnxflat host tools + from the NuttX toolchain, and the board's Make.defs must name + them, exactly as apps/examples/nxflat requires. + +if EXAMPLES_NXFLATXIP + +config EXAMPLES_NXFLATXIP_MOUNTPT + string "xipfs mountpoint" + default "/mnt/xipfs" + +config EXAMPLES_NXFLATXIP_MTD + string "MTD device backing the volume" + default "/dev/rpflash" + ---help--- + Used only by the defrag subcommand, which remounts the volume to + show that what it did survives. + +endif # EXAMPLES_NXFLATXIP diff --git a/examples/nxflatxip/Make.defs b/examples/nxflatxip/Make.defs new file mode 100644 index 000000000..2290cf00c --- /dev/null +++ b/examples/nxflatxip/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/nxflatxip/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_NXFLATXIP),) +CONFIGURED_APPS += $(APPDIR)/examples/nxflatxip +endif diff --git a/examples/nxflatxip/Makefile b/examples/nxflatxip/Makefile new file mode 100644 index 000000000..e74981c17 --- /dev/null +++ b/examples/nxflatxip/Makefile @@ -0,0 +1,54 @@ +############################################################################ +# apps/examples/nxflatxip/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# NXFLAT execute-in-place from xipfs + +CSRCS = symtab.c +MAINSRC = nxflatxip_main.c + +# The generated symbol table declares every imported symbol as void *, which +# the compiler objects to for the ones it knows as builtins. + +symtab.c_CFLAGS = -fno-builtin + +PROGNAME = nxflatxip +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_NXFLATXIP) + +# Both are generated by the module build below + +symtab.c: build +xipmod_bin.h: build + +$(MAINSRC): xipmod_bin.h + +.PHONY: build +build: + +$(Q) $(MAKE) -C module TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV) + +clean:: + +$(Q) $(MAKE) -C module TOPDIR="$(TOPDIR)" APPDIR="$(APPDIR)" CROSSDEV=$(CROSSDEV) clean + +include $(APPDIR)/Application.mk diff --git a/examples/nxflatxip/module/.gitignore b/examples/nxflatxip/module/.gitignore new file mode 100644 index 000000000..111804b12 --- /dev/null +++ b/examples/nxflatxip/module/.gitignore @@ -0,0 +1,4 @@ +/xipmod +/xipmod-thunk.S +/*.r1 +/*.r2 diff --git a/examples/nxflatxip/module/Makefile b/examples/nxflatxip/module/Makefile new file mode 100644 index 000000000..89ad64866 --- /dev/null +++ b/examples/nxflatxip/module/Makefile @@ -0,0 +1,94 @@ +############################################################################ +# apps/examples/nxflatxip/module/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# The NXFLAT module the demo downloads into xipfs. Built exactly the way +# apps/examples/nxflat builds its test programs, so it needs the same two +# host tools from the NuttX toolchain: mknxflat and ldnxflat. + +BIN = xipmod + +R1SRCS = $(BIN).c +R1OBJS = $(R1SRCS:.c=.o) + +R2SRC = $(BIN)-thunk.S +R2OBJ = $(R2SRC:.S=.o) + +EXAMPLDIR = $(APPDIR)$(DELIM)examples$(DELIM)nxflatxip +BINHDR = $(EXAMPLDIR)$(DELIM)$(BIN)_bin.h +SYMTAB = $(EXAMPLDIR)$(DELIM)symtab.c + +all: $(BINHDR) $(SYMTAB) +.PHONY: all clean + +$(R1OBJS): %.o: %.c + @echo "CC: $<" + $(Q) $(CC) -c $(CPICFLAGS) $< -o $@ + +$(R2OBJ): %.o: %.S + @echo "AS: $<" + $(Q) $(CC) -c $(CPICFLAGS) $< -o $@ + +$(BIN).r1: $(R1OBJS) + @echo "LD: $<" + $(Q) $(LD) $(NXFLATLDFLAGS1) -o $@ $^ + +$(R2SRC): $(BIN).r1 + @echo "MK: $<" + $(Q) $(MKNXFLAT) -o $@ $^ + +$(BIN).r2: $(R2OBJ) + @echo "LD: $<" + $(Q) $(LD) $(NXFLATLDFLAGS2) -o $@ $(R1OBJS) $(R2OBJ) + +$(BIN): $(BIN).r2 + @echo "LD: $<" + $(Q) $(LDNXFLAT) $(LDNXFLATFLAGS) -o $@ $^ + +# Embed the module in the demo. It is not the demo's own text, it is a +# payload the demo writes into the filesystem, so a plain byte array is what +# is wanted. + +$(BINHDR): $(BIN) + @echo "GEN: $@" + $(Q) xxd -i -n g_xipmod $(BIN) | \ + sed -e "s/^unsigned char/static const unsigned char/" \ + -e "s/^unsigned int/static const unsigned int/" >$@.tmp + $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) + +# The symbols the module imports from the firmware. mknxflat left the list +# in the thunk file it generated, so that is what is scanned. + +$(SYMTAB): $(R2SRC) + @echo "GEN: $@" + $(Q) $(APPDIR)$(DELIM)tools$(DELIM)mksymtab.sh . g_nxflat >$@.tmp + $(Q) $(call TESTANDREPLACEFILE, $@.tmp, $@) + +clean: + $(call DELFILE, $(BIN)) + $(call DELFILE, $(R2SRC)) + $(call DELFILE, *.r1) + $(call DELFILE, *.r2) + $(call DELFILE, $(BINHDR)) + $(call DELFILE, $(SYMTAB)) + $(call CLEAN) diff --git a/examples/nxflatxip/module/xipmod.c b/examples/nxflatxip/module/xipmod.c new file mode 100644 index 000000000..1c721acbd --- /dev/null +++ b/examples/nxflatxip/module/xipmod.c @@ -0,0 +1,116 @@ +/**************************************************************************** + * apps/examples/nxflatxip/module/xipmod.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * The module the demo downloads into xipfs and runs. + * + * Given a seed on the command line it fills an array derived from it, + * sleeps long enough for a second instance to do the same with a different + * seed, checks its own values are untouched, and hands the result to the + * firmware. It reports the address of its own text, which is the point of + * the exercise: that address is inside the memory mapped flash window, it + * is the same in both instances, and the module is executing from it rather + * than from a copy in RAM. + * + * Two things this module deliberately does not have, both because of what + * the ldnxflat in circulation does with them: + * + * - No global or static variables. Those are reached through the GOT, + * and the GOT entries come out pointing into the import table rather + * than into .bss, so the module overwrites the addresses of the + * functions it imports and jumps into nothing on its next call out. + * - No string constants. With this linker script .rodata lives in the + * text segment and is reached PC-relatively, and the addend already + * in the instruction is dropped when that relocation is resolved, so + * the pointer lands somewhere in the middle of the code. + * + * Hence the reporting callback: the module passes numbers to the firmware + * and the firmware, which has a working printf and a format string, does + * the printing. It also exercises the other direction of the interface -- + * firmware code entered with the module's data base still in r10. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NVALUES 64 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Exported by the demo in the firmware and imported by this module */ + +void xipmod_report(int seed, const void *text, const void *stack, + long sum, int errors); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int values[NVALUES]; + int errors = 0; + int seed = 1; + long sum = 0; + int i; + + if (argc > 1) + { + seed = atoi(argv[1]); + } + + for (i = 0; i < NVALUES; i++) + { + values[i] = seed * (i + 1); + } + + /* Long enough that the other instance has certainly filled its own copy + * by the time this one checks. + */ + + usleep(200000); + + for (i = 0; i < NVALUES; i++) + { + if (values[i] != seed * (i + 1)) + { + errors++; + } + + sum += values[i]; + } + + xipmod_report(seed, (const void *)main, (const void *)values, sum, errors); + + return errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/examples/nxflatxip/nxflatxip_main.c b/examples/nxflatxip/nxflatxip_main.c new file mode 100644 index 000000000..946e2b649 --- /dev/null +++ b/examples/nxflatxip/nxflatxip_main.c @@ -0,0 +1,895 @@ +/**************************************************************************** + * apps/examples/nxflatxip/nxflatxip_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * End to end demonstration of what a writable execute-in-place file system + * is for: + * + * 1. write an NXFLAT module into xipfs at run time, the way a download + * would, + * 2. confirm the file system can hand out a direct flash pointer for it, + * 3. run two instances of it concurrently, + * 4. confirm both ran correctly, that their data did not interfere, and + * that the shared text was pinned in place while they ran. + * + * The same module in a ROMFS image would give the same execute-in-place + * behaviour; what it could not do is arrive after the firmware was built. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "xipmod_bin.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUNTPT CONFIG_EXAMPLES_NXFLATXIP_MOUNTPT +#define MTDDEV CONFIG_EXAMPLES_NXFLATXIP_MTD +#define MODPATH MOUNTPT "/xipmod" +#define BENCH_PATH MOUNTPT "/big" +#define BENCH_SIZE (256 * 1024) +#define DF_FILES 55 +#define DF_MAPCOLS 50 + +/**************************************************************************** + * External Symbols + ****************************************************************************/ + +/* Generated by module/Makefile from the import list mknxflat left in the + * module's thunk file. The NXFLAT loader has no other way to resolve the + * functions the module calls. + */ + +extern const struct symtab_s g_nxflat_exports[]; +extern const int g_nxflat_nexports; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char g_map[512]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xipmod_report + * + * Description: + * Called by the module, running in place from flash, to report what it + * found. It passes numbers rather than formatting its own output; see + * module/xipmod.c for why. + * + * This is also the direction of the interface that needs r10 to survive: + * the module reaches its own data through that register, and this + * function runs with the module's data base still in it. That is what + * the --fixed-r10 the ARM toolchain adds under CONFIG_PIC is for. + * + ****************************************************************************/ + +void xipmod_report(int seed, FAR const void *text, FAR const void *stack, + long sum, int errors) +{ + syslog(LOG_INFO, " instance seed %d: text %p, stack %p, sum %ld -- %s\n", + seed, text, stack, sum, + errors == 0 ? "data private and intact" : "DATA CORRUPTED"); +} + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stage_module + * + * Description: + * Write the module into the file system exactly as a download would: + * declare the size up front so xipfs reserves an exactly sized + * contiguous extent, then write it once. + * + ****************************************************************************/ + +static int stage_module(void) +{ + ssize_t nwritten; + int fd; + + unlink(MODPATH); + + fd = open(MODPATH, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0755); + if (fd < 0) + { + syslog(LOG_INFO, "ERROR: open %s failed: %d\n", MODPATH, errno); + return -errno; + } + + if (ftruncate(fd, g_xipmod_len) < 0) + { + syslog(LOG_INFO, "ERROR: ftruncate failed: %d\n", errno); + close(fd); + return -errno; + } + + nwritten = write(fd, g_xipmod, g_xipmod_len); + close(fd); + + if (nwritten != (ssize_t)g_xipmod_len) + { + syslog(LOG_INFO, "ERROR: short write: %zd of %u\n", nwritten, + g_xipmod_len); + return -EIO; + } + + syslog(LOG_INFO, "staged %u bytes to %s\n", g_xipmod_len, MODPATH); + return 0; +} + +/**************************************************************************** + * Name: extent_info_path + * + * Description: + * Where a file physically lies: its extent, its flash address, and how + * many live mappings are pinning it in place. + * + ****************************************************************************/ + +static int extent_info_path(FAR const char *path, + FAR struct xipfs_extent_info_s *info) +{ + int fd; + int ret; + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + return -errno; + } + + ret = ioctl(fd, XIPFSIOC_EXTENTINFO, (unsigned long)(uintptr_t)info); + close(fd); + + return ret < 0 ? -errno : 0; +} + +/**************************************************************************** + * Name: bench_read + * + * Description: + * Time a bulk read straight out of the memory mapped flash, big enough to + * miss the XIP cache. Run once on a freshly booted system, where the + * bootrom's fast read mode is still in effect, and again after a flash + * write has forced the driver to restore XIP itself. The difference is + * the cost of whichever restore path the driver used. + * + ****************************************************************************/ + +static long bench_read(void) +{ + struct timespec t0; + struct timespec t1; + FAR uint8_t *buf; + ssize_t n; + long total = 0; + int fd; + + buf = malloc(4096); + if (buf == NULL) + { + return -1; + } + + fd = open(BENCH_PATH, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + free(buf); + return -1; + } + + clock_gettime(CLOCK_MONOTONIC, &t0); + + while ((n = read(fd, buf, 4096)) > 0) + { + total += n; + } + + clock_gettime(CLOCK_MONOTONIC, &t1); + + close(fd); + free(buf); + + if (total <= 0) + { + return -1; + } + + return (t1.tv_sec - t0.tv_sec) * 1000 + + (t1.tv_nsec - t0.tv_nsec) / 1000000; +} + +static int bench_main(void) +{ + long before; + long after; + int fd; + + syslog(LOG_INFO, "\nXIP read benchmark (%d KB)\n", BENCH_SIZE / 1024); + + before = bench_read(); + if (before < 0) + { + syslog(LOG_INFO, " create %s first: " + "dd if=/dev/zero of=%s bs=512 count=512\n", + BENCH_PATH, BENCH_PATH); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, " before any flash write : %ld ms\n", before); + + /* Any write forces the driver to tear XIP down and put it back */ + + fd = open(MOUNTPT "/benchw", + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd >= 0) + { + write(fd, "x", 1); + close(fd); + } + + after = bench_read(); + syslog(LOG_INFO, " after a flash write : %ld ms\n", after); + + if (before > 0) + { + syslog(LOG_INFO, " ratio : %ld.%02ldx\n", + after / before, ((after * 100) / before) % 100); + } + + unlink(MOUNTPT "/benchw"); + return EXIT_SUCCESS; +} + +static int remount(void) +{ + int ret; + + ret = umount(MOUNTPT); + if (ret < 0 && errno != ENOENT && errno != EINVAL) + { + return -errno; + } + + ret = mount(MTDDEV, MOUNTPT, "xipfs", 0, NULL); + return ret < 0 ? -errno : 0; +} + +static void fill_pattern(FAR uint8_t *buf, size_t len, uint8_t seed) +{ + size_t i; + + for (i = 0; i < len; i++) + { + buf[i] = (uint8_t)(seed + (i * 7)); + } +} + +static int create_file(FAR const char *path, size_t len, uint8_t seed) +{ + FAR uint8_t *buf; + ssize_t nw; + int fd; + int ret = 0; + + buf = malloc(len); + if (buf == NULL) + { + return -ENOMEM; + } + + fill_pattern(buf, len, seed); + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd < 0) + { + free(buf); + return -errno; + } + + if (ftruncate(fd, len) < 0) + { + ret = -errno; + } + else + { + nw = write(fd, buf, len); + if (nw != (ssize_t)len) + { + ret = nw < 0 ? -errno : -EIO; + } + } + + close(fd); + free(buf); + return ret; +} + +static bool verify_file(FAR const char *path, size_t len, uint8_t seed) +{ + FAR uint8_t *got; + FAR uint8_t *want; + bool ok = false; + ssize_t n; + int fd; + + got = malloc(len); + want = malloc(len); + if (got == NULL || want == NULL) + { + free(got); + free(want); + return false; + } + + fill_pattern(want, len, seed); + + fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd >= 0) + { + n = read(fd, got, len); + ok = (n == (ssize_t)len) && (memcmp(got, want, len) == 0); + close(fd); + } + + free(got); + free(want); + return ok; +} + +/**************************************************************************** + * Name: run_defrag + * + * Description: + * Compact the volume. The command acts on the volume, so it goes through + * a descriptor for the mountpoint directory: a descriptor for a file + * inside it would hold that file open, and an open extent cannot be + * relocated. + * + ****************************************************************************/ + +static int run_defrag(uint32_t max_ms, + FAR struct xipfs_defrag_result_s *result) +{ + struct xipfs_defrag_arg_s arg; + int fd; + int ret; + + memset(&arg, 0, sizeof(arg)); + arg.max_ms = max_ms; + + fd = open(MOUNTPT, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (fd < 0) + { + return -errno; + } + + ret = ioctl(fd, XIPFSIOC_DEFRAG, (unsigned long)(uintptr_t)&arg); + close(fd); + + if (ret < 0) + { + return -errno; + } + + *result = arg.result; + return 0; +} + +/**************************************************************************** + * Defragmentation showcase + * + * Fragments the volume until an allocation genuinely cannot be satisfied -- + * plenty of free space, but none of it contiguous -- then compacts and + * retries the same allocation. On a real board everything below runs + * against real NOR: each move is a page-at-a-time copy, a metadata commit, + * and an erase of the vacated blocks. + ****************************************************************************/ + +static int df_mapfill(FAR uint32_t *dstart, FAR uint32_t *dblocks) +{ + struct xipfs_extent_info_s info; + FAR struct dirent *de; + FAR DIR *dirp; + char path[80]; + int files = 0; + uint32_t i; + + memset(g_map, '.', sizeof(g_map)); + *dstart = 0; + *dblocks = 0; + + dirp = opendir(MOUNTPT); + if (dirp == NULL) + { + return -1; + } + + while ((de = readdir(dirp)) != NULL) + { + snprintf(path, sizeof(path), "%s/%s", MOUNTPT, de->d_name); + if (extent_info_path(path, &info) < 0) + { + continue; + } + + *dstart = info.data_start; + *dblocks = info.data_nblocks; + files++; + + for (i = 0; i < info.nblocks; i++) + { + uint32_t idx = info.start_block - info.data_start + i; + if (idx < sizeof(g_map)) + { + g_map[idx] = '#'; + } + } + } + + closedir(dirp); + return files; +} + +static void df_showmap(FAR const char *title, uint32_t dblocks, + FAR uint32_t *freeout, FAR uint32_t *largestout) +{ + uint32_t largest = 0; + uint32_t run = 0; + uint32_t free = 0; + uint32_t i; + char row[DF_MAPCOLS + 1]; + + syslog(LOG_INFO, "\n %s\n", title); + + for (i = 0; i < dblocks; i++) + { + if (g_map[i] == '.') + { + free++; + if (++run > largest) + { + largest = run; + } + } + else + { + run = 0; + } + } + + for (i = 0; i < dblocks; i += DF_MAPCOLS) + { + uint32_t n = dblocks - i; + if (n > DF_MAPCOLS) + { + n = DF_MAPCOLS; + } + + memcpy(row, &g_map[i], n); + row[n] = '\0'; + syslog(LOG_INFO, " %3" PRIu32 " |%s|\n", i, row); + } + + syslog(LOG_INFO, " free %" PRIu32 + " blocks, largest contiguous run %" PRIu32 "\n", free, largest); + + if (freeout != NULL) + { + *freeout = free; + } + + if (largestout != NULL) + { + *largestout = largest; + } +} + +static int df_try_alloc(uint32_t nblocks, uint32_t erasesize) +{ + int fd; + int ret; + + unlink(MOUNTPT "/bigalloc"); + + fd = open(MOUNTPT "/bigalloc", + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (fd < 0) + { + return -errno; + } + + ret = ftruncate(fd, (off_t)nblocks * erasesize); + close(fd); + + if (ret < 0) + { + ret = -errno; + unlink(MOUNTPT "/bigalloc"); + } + + return ret; +} + +/**************************************************************************** + * Name: empty_dir + * + * Description: + * Delete everything below 'dir', descending into subdirectories. + * + * One entry is taken per pass rather than a listing being collected up + * front, because this recurses and the task stack is the default 2 KB: an + * array of names big enough to be useful costs more than a kilobyte a + * frame, and two levels of that overflows and returns through a smashed + * frame. A pass costs an opendir instead, which no one is timing. + * + * The directory is closed before anything is removed, since unlinking + * while iterating skips entries. + * + ****************************************************************************/ + +static void empty_dir(FAR const char *dir) +{ + char path[80]; + bool more = true; + + while (more) + { + FAR struct dirent *de; + FAR DIR *dirp; + bool isdir; + + dirp = opendir(dir); + if (dirp == NULL) + { + return; + } + + de = readdir(dirp); + if (de == NULL) + { + closedir(dirp); + return; + } + + isdir = (de->d_type == DTYPE_DIRECTORY); + snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); + closedir(dirp); + + if (isdir) + { + empty_dir(path); + more = rmdir(path) == 0; + } + else + { + more = unlink(path) == 0; + } + } +} + +static int defrag_main(void) +{ + struct xipfs_defrag_result_s res; + struct xipfs_extent_info_s info; + struct timespec t0; + struct timespec t1; + uint32_t dstart; + uint32_t dblocks; + uint32_t erasesize = 4096; + uint32_t want; + uint32_t freeblk = 0; + uint32_t largest = 0; + char path[80]; + long ms; + int i; + int ret; + + memset(&res, 0, sizeof(res)); + + syslog(LOG_INFO, "\n=== xipfs defragmentation ===\n"); + + /* Clean slate, so the map below accounts for every used block */ + + empty_dir(MOUNTPT); + + syslog(LOG_INFO, "\n[1] writing %d files...\n", DF_FILES); + + for (i = 0; i < DF_FILES; i++) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (create_file(path, 2000, (uint8_t)(i + 1)) < 0) + { + syslog(LOG_INFO, " stopped at %d (volume full)\n", i); + break; + } + } + + if (extent_info_path(MOUNTPT "/m00", &info) == 0) + { + erasesize = info.erasesize; + } + + df_mapfill(&dstart, &dblocks); + df_showmap("map, all files present:", dblocks, NULL, NULL); + + syslog(LOG_INFO, "\n[2] deleting every other file to fragment it...\n"); + + for (i = 0; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + unlink(path); + } + + df_mapfill(&dstart, &dblocks); + df_showmap("map, fragmented (# used, . free):", dblocks, + &freeblk, &largest); + + /* Bigger than any single hole, but comfortably inside the total free + * space -- so it can only be satisfied once the holes are coalesced. + */ + + want = largest + (freeblk - largest) / 2; + + syslog(LOG_INFO, "\n[3] requesting a %" PRIu32 + "-block (%" PRIu32 " KB) contiguous file\n", want, + want * erasesize / 1024); + + ret = df_try_alloc(want, erasesize); + syslog(LOG_INFO, " result: %s\n", + ret < 0 ? "FAILED -ENOSPC (free space is not contiguous)" : "ok"); + + syslog(LOG_INFO, "\n[4] compacting...\n"); + + clock_gettime(CLOCK_MONOTONIC, &t0); + ret = run_defrag(0, &res); + clock_gettime(CLOCK_MONOTONIC, &t1); + + ms = (t1.tv_sec - t0.tv_sec) * 1000 + (t1.tv_nsec - t0.tv_nsec) / 1000000; + + if (ret < 0) + { + syslog(LOG_INFO, " defrag failed: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, " elapsed : %ld ms\n", ms); + syslog(LOG_INFO, " extents relocated: %" PRIu32 "\n", + res.extents_moved); + syslog(LOG_INFO, " blocks reclaimed : %" PRIu32 "\n", + res.blocks_reclaimed); + syslog(LOG_INFO, " largest free run : %zu bytes (%zu blocks)\n", + res.largest_free_run, res.largest_free_run / erasesize); + syslog(LOG_INFO, " stop reason : %d (%s)\n", res.reason, + res.reason == XIPFS_DEFRAG_DONE ? "done" : + res.reason == XIPFS_DEFRAG_BLOCKED_OPEN ? "blocked by an open " + "file" : + res.reason == XIPFS_DEFRAG_BLOCKED_PINS ? "blocked by a live " + "XIP mapping" : + "other"); + + df_mapfill(&dstart, &dblocks); + df_showmap("map after defrag:", dblocks, NULL, NULL); + + want = res.largest_free_run / erasesize; + + syslog(LOG_INFO, "\n[5] retrying, sized to the reported largest run" + " (%" PRIu32 " blocks)\n", want); + ret = df_try_alloc(want, erasesize); + syslog(LOG_INFO, " result: %s\n", + ret < 0 ? "still failed" : "OK -- allocation now fits"); + unlink(MOUNTPT "/bigalloc"); + + syslog(LOG_INFO, "\n[6] verifying every relocated file byte-for-byte\n"); + + ret = 0; + for (i = 1; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (!verify_file(path, 2000, (uint8_t)(i + 1))) + { + syslog(LOG_INFO, " MISMATCH in %s\n", path); + ret = -1; + } + } + + syslog(LOG_INFO, " %s\n", ret == 0 ? "all survivors intact" : "FAILED"); + + syslog(LOG_INFO, "\n[7] remounting to prove it is durable\n"); + if (remount() < 0) + { + syslog(LOG_INFO, " remount FAILED\n"); + return EXIT_FAILURE; + } + + ret = 0; + for (i = 1; i < DF_FILES; i += 2) + { + snprintf(path, sizeof(path), MOUNTPT "/m%02d", i); + if (!verify_file(path, 2000, (uint8_t)(i + 1))) + { + ret = -1; + } + } + + syslog(LOG_INFO, " %s\n", + ret == 0 ? "all files intact after remount" : "FAILED"); + + syslog(LOG_INFO, "\n=== defrag complete ===\n"); + return EXIT_SUCCESS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct xipfs_extent_info_s info; + FAR char *args[3]; + char seed[2][8]; + pid_t pid[2]; + int status; + int ret; + int i; + + if (argc > 1 && strcmp(argv[1], "bench") == 0) + { + return bench_main(); + } + + if (argc > 1 && strcmp(argv[1], "defrag") == 0) + { + return defrag_main(); + } + + syslog(LOG_INFO, + "\n=== NXFLAT module executed in place from xipfs ===\n\n"); + + ret = stage_module(); + if (ret < 0) + { + return EXIT_FAILURE; + } + + ret = extent_info_path(MODPATH, &info); + if (ret < 0) + { + syslog(LOG_INFO, "ERROR: cannot query extent: %d\n", ret); + return EXIT_FAILURE; + } + + syslog(LOG_INFO, "extent: block %" PRIu32 " x%" PRIu32 ", size %" PRIu32 + ", flash addr 0x%08" PRIxPTR "\n", + info.start_block, info.nblocks, info.size, info.xipaddr); + + if (info.xipaddr == 0) + { + syslog(LOG_INFO, + "ERROR: the file system cannot expose a flash pointer, so the " + "module would have to be copied to RAM. Does the MTD driver " + "answer BIOC_XIPBASE?\n"); + return EXIT_FAILURE; + } + + /* Launch two instances. They overlap deliberately: each fills its array, + * sleeps, then checks it. Shared data would show up as a mismatch. + */ + + syslog(LOG_INFO, "\nrunning two concurrent instances...\n\n"); + + for (i = 0; i < 2; i++) + { + snprintf(seed[i], sizeof(seed[i]), "%d", i + 1); + + args[0] = (FAR char *)"xipmod"; + args[1] = seed[i]; + args[2] = NULL; + + /* exec() rather than posix_spawn() because the symbol table the + * module links against is the demo's, not one the OS holds. + */ + + pid[i] = exec(MODPATH, args, NULL, g_nxflat_exports, + g_nxflat_nexports); + if (pid[i] < 0) + { + syslog(LOG_INFO, "ERROR: exec of instance %d failed: %d\n", i + 1, + pid[i]); + return EXIT_FAILURE; + } + } + + /* While both are alive the shared text must be pinned once per instance, + * which is what stops the defragmenter relocating code that is executing. + */ + + usleep(100000); + + if (extent_info_path(MODPATH, &info) == 0) + { + syslog(LOG_INFO, + "\n[while running] pin count on the shared text = %" PRIu32 + "\n\n", info.pincount); + } + + for (i = 0; i < 2; i++) + { + if (waitpid(pid[i], &status, 0) >= 0 && + (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)) + { + syslog(LOG_INFO, "instance %d did not exit cleanly\n", i + 1); + ret = -1; + } + } + + /* The pins outlive the instances. binfmt maps the text in the context of + * whoever called exec(), so the mappings belong to this task's address + * space, not to the tasks that ran, and they are released when this task + * exits. Run "xipfs -n" after this demo returns to see the count back at + * zero and the extent movable again. + */ + + if (extent_info_path(MODPATH, &info) == 0) + { + syslog(LOG_INFO, + "[after exit] pin count on the shared text = %" PRIu32 + " (released when this task exits)\n", info.pincount); + } + + syslog(LOG_INFO, "\n=== %s ===\n", ret == 0 ? "done" : "FAILED"); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/examples/nxhello/nxhello_bkgd.c b/examples/nxhello/nxhello_bkgd.c index ee73f2614..5e0a5b80e 100644 --- a/examples/nxhello/nxhello_bkgd.c +++ b/examples/nxhello/nxhello_bkgd.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -76,19 +76,21 @@ ****************************************************************************/ static void nxhello_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, - bool morem, FAR void *arg); -static void nxhello_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, - FAR const struct nxgl_point_s *pos, - FAR const struct nxgl_rect_s *bounds, - FAR void *arg); + bool morem, FAR void *arg); +static void nxhello_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, + FAR const struct nxgl_point_s *pos, + FAR const struct nxgl_rect_s *bounds, + FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nxhello_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg); +static void nxhello_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg); #endif #ifdef CONFIG_NX_KBD -static void nxhello_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, - FAR void *arg); +static void nxhello_kbdin(NXWINDOW hwnd, uint8_t nch, + FAR const uint8_t *ch, FAR void *arg); #endif /**************************************************************************** @@ -125,7 +127,7 @@ const struct nx_callback_s g_nxhellocb = ****************************************************************************/ static void nxhello_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, - bool more, FAR void *arg) + bool more, FAR void *arg) { ginfo("hwnd=%p rect={(%d,%d),(%d,%d)} more=%s\n", hwnd, rect->pt1.x, rect->pt1.y, rect->pt2.x, rect->pt2.y, @@ -136,10 +138,11 @@ static void nxhello_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, * Name: nxhello_position ****************************************************************************/ -static void nxhello_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, - FAR const struct nxgl_point_s *pos, - FAR const struct nxgl_rect_s *bounds, - FAR void *arg) +static void nxhello_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, + FAR const struct nxgl_point_s *pos, + FAR const struct nxgl_rect_s *bounds, + FAR void *arg) { /* Report the position */ @@ -171,8 +174,9 @@ static void nxhello_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, ****************************************************************************/ #ifdef CONFIG_NX_XYINPUT -static void nxhello_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg) +static void nxhello_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg) { printf("nxhello_mousein: hwnd=%p pos=(%d,%d) button=%02x\n", hwnd, pos->x, pos->y, buttons); @@ -184,16 +188,16 @@ static void nxhello_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, ****************************************************************************/ #ifdef CONFIG_NX_KBD -static void nxhello_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, - FAR void *arg) +static void nxhello_kbdin(NXWINDOW hwnd, uint8_t nch, + FAR const uint8_t *ch, FAR void *arg) { ginfo("hwnd=%p nch=%d\n", hwnd, nch); - /* In this example, there is no keyboard so a keyboard event is not - * expected. - */ + /* In this example, there is no keyboard so a keyboard event is not + * expected. + */ - printf("nxhello_kbdin: Unexpected keyboard callback\n"); + printf("nxhello_kbdin: Unexpected keyboard callback\n"); } #endif @@ -212,7 +216,7 @@ static void nxhello_center(FAR struct nxgl_point_s *pos, * hello world message. */ - for (ptr = (uint8_t*)g_hello, width = 0; *ptr; ptr++) + for (ptr = (uint8_t *)g_hello, width = 0; *ptr; ptr++) { /* Get the font bitmap for this character */ @@ -225,7 +229,7 @@ static void nxhello_center(FAR struct nxgl_point_s *pos, } else { - /* Use the width of a space */ + /* Use the width of a space */ width += fontset->spwidth; } @@ -266,7 +270,7 @@ static void nxhello_initglyph(FAR uint8_t *glyph, uint8_t height, /* Pack 1-bit pixels into a 2-bits */ pixel &= 0x01; - pixel = (pixel) << 1 |pixel; + pixel = (pixel) << 1 | pixel; # endif # if CONFIG_EXAMPLES_NXHELLO_BPP < 4 @@ -274,7 +278,7 @@ static void nxhello_initglyph(FAR uint8_t *glyph, uint8_t height, /* Pack 2-bit pixels into a nibble */ pixel &= 0x03; - pixel = (pixel) << 2 |pixel; + pixel = (pixel) << 2 | pixel; # endif @@ -348,10 +352,10 @@ void nxhello_hello(NXWINDOW hwnd) mxstride = (fontset->mxwidth * CONFIG_EXAMPLES_NXHELLO_BPP + 7) >> 3; glyphsize = (unsigned int)fontset->mxheight * mxstride; - glyph = (FAR uint8_t*)malloc(glyphsize); + glyph = (FAR uint8_t *)malloc(glyphsize); - /* NOTE: no check for failure to allocate the memory. In a real application - * you would need to handle that event. + /* NOTE: no check for failure to allocate the memory. In a real + * application you would need to handle that event. */ /* Get a position so the "Hello, World!" string will be centered on the @@ -389,7 +393,7 @@ void nxhello_hello(NXWINDOW hwnd) #if CONFIG_NX_NPLANES > 1 # warning "More logic is needed for the case where CONFIG_NX_PLANES > 1" #endif - RENDERER((FAR nxgl_mxpixel_t*)glyph, fheight, fwidth, + RENDERER((FAR nxgl_mxpixel_t *)glyph, fheight, fwidth, fstride, fbm, CONFIG_EXAMPLES_NXHELLO_FONTCOLOR); /* Describe the destination of the font with a rectangle */ @@ -411,15 +415,15 @@ void nxhello_hello(NXWINDOW hwnd) printf("nxhello_write: nx_bitmapwindow failed: %d\n", errno); } - /* Skip to the right the width of the font */ + /* Skip to the right the width of the font */ pos.x += fwidth; } else { - /* No bitmap (probably because the font is a space). Skip to the - * right the width of a space. - */ + /* No bitmap (probably because the font is a space). Skip to the + * right the width of a space. + */ pos.x += fontset->spwidth; } diff --git a/examples/nxhello/nxhello_main.c b/examples/nxhello/nxhello_main.c index 13e2b8a62..d524f6f2c 100644 --- a/examples/nxhello/nxhello_main.c +++ b/examples/nxhello/nxhello_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -95,7 +95,8 @@ static inline int nxhello_initialize(void) ret = boardctl(BOARDIOC_NX_START, 0); if (ret < 0) { - printf("nxhello_initialize: Failed to start the NX server: %d\n", errno); + printf("nxhello_initialize: Failed to start the NX server: %d\n", + errno); return ERROR; } @@ -104,52 +105,53 @@ static inline int nxhello_initialize(void) g_nxhello.hnx = nx_connect(); if (g_nxhello.hnx) { - pthread_attr_t attr; + pthread_attr_t attr; #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, g_nxhello.hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, g_nxhello.hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); - nx_disconnect(g_nxhello.hnx); - return ERROR; - } + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); + nx_disconnect(g_nxhello.hnx); + return ERROR; + } #endif - /* Start a separate thread to listen for server events. This is probably - * the least efficient way to do this, but it makes this example flow more - * smoothly. - */ + /* Start a separate thread to listen for server events. This is + * probably the least efficient way to do this, but it makes this + * example flow more smoothly. + */ - pthread_attr_init(&attr); - param.sched_priority = CONFIG_EXAMPLES_NXHELLO_LISTENERPRIO; - pthread_attr_setschedparam(&attr, ¶m); - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_NXHELLO_LISTENER_STACKSIZE); + pthread_attr_init(&attr); + param.sched_priority = CONFIG_EXAMPLES_NXHELLO_LISTENERPRIO; + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setstacksize(&attr, + CONFIG_EXAMPLES_NXHELLO_LISTENER_STACKSIZE); - ret = pthread_create(&thread, &attr, nxhello_listener, NULL); - if (ret != 0) - { - printf("nxhello_initialize: pthread_create failed: %d\n", ret); - return ERROR; - } + ret = pthread_create(&thread, &attr, nxhello_listener, NULL); + if (ret != 0) + { + printf("nxhello_initialize: pthread_create failed: %d\n", ret); + return ERROR; + } - /* Don't return until we are connected to the server */ + /* Don't return until we are connected to the server */ - while (!g_nxhello.connected) - { - /* Wait for the listener thread to wake us up when we really - * are connected. - */ + while (!g_nxhello.connected) + { + /* Wait for the listener thread to wake us up when we really + * are connected. + */ - sem_wait(&g_nxhello.eventsem); - } + sem_wait(&g_nxhello.eventsem); + } } else { @@ -225,7 +227,8 @@ int main(int argc, FAR char *argv[]) sem_wait(&g_nxhello.eventsem); } - printf("nxhello_main: Screen resolution (%d,%d)\n", g_nxhello.xres, g_nxhello.yres); + printf("nxhello_main: Screen resolution (%d,%d)\n", + g_nxhello.xres, g_nxhello.yres); /* Now, say hello and exit, sleeping a little before each. */ diff --git a/examples/nximage/nximage_bkgd.c b/examples/nximage/nximage_bkgd.c index 36ac04eda..8e3a2f651 100644 --- a/examples/nximage/nximage_bkgd.c +++ b/examples/nximage/nximage_bkgd.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -109,19 +109,21 @@ struct nximage_run_s ****************************************************************************/ static void nximage_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, - bool more, FAR void *arg); -static void nximage_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, - FAR const struct nxgl_point_s *pos, - FAR const struct nxgl_rect_s *bounds, - FAR void *arg); + bool more, FAR void *arg); +static void nximage_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, + FAR const struct nxgl_point_s *pos, + FAR const struct nxgl_rect_s *bounds, + FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nximage_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg); +static void nximage_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg); #endif #ifdef CONFIG_NX_KBD -static void nximage_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, - FAR void *arg); +static void nximage_kbdin(NXWINDOW hwnd, uint8_t nch, + FAR const uint8_t *ch, FAR void *arg); #endif /**************************************************************************** @@ -164,7 +166,7 @@ const struct nx_callback_s g_nximagecb = ****************************************************************************/ static void nximage_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, - bool more, FAR void *arg) + bool more, FAR void *arg) { ginfo("hwnd=%p rect={(%d,%d),(%d,%d)} more=%s\n", hwnd, rect->pt1.x, rect->pt1.y, rect->pt2.x, rect->pt2.y, @@ -179,10 +181,11 @@ static void nximage_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, * ****************************************************************************/ -static void nximage_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, - FAR const struct nxgl_point_s *pos, - FAR const struct nxgl_rect_s *bounds, - FAR void *arg) +static void nximage_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, + FAR const struct nxgl_point_s *pos, + FAR const struct nxgl_rect_s *bounds, + FAR void *arg) { /* Report the position */ @@ -218,8 +221,9 @@ static void nximage_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, ****************************************************************************/ #ifdef CONFIG_NX_XYINPUT -static void nximage_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, - uint8_t buttons, FAR void *arg) +static void nximage_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, + uint8_t buttons, FAR void *arg) { printf("nximage_mousein: hwnd=%p pos=(%d,%d) button=%02x\n", hwnd, pos->x, pos->y, buttons); @@ -235,16 +239,16 @@ static void nximage_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, ****************************************************************************/ #ifdef CONFIG_NX_KBD -static void nximage_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, - FAR void *arg) +static void nximage_kbdin(NXWINDOW hwnd, uint8_t nch, + FAR const uint8_t *ch, FAR void *arg) { ginfo("hwnd=%p nch=%d\n", hwnd, nch); - /* In this example, there is no keyboard so a keyboard event is not - * expected. - */ + /* In this example, there is no keyboard so a keyboard event is not + * expected. + */ - printf("nximage_kbdin: Unexpected keyboard callback\n"); + printf("nximage_kbdin: Unexpected keyboard callback\n"); } #endif @@ -288,9 +292,9 @@ void nximage_image(NXWINDOW hwnd) { /* Read input row(s) */ - nximage_blitrow(g_runs[0].run, &state); + nximage_blitrow(g_runs[0].run, &state); #if NINPUT_ROWS > 1 - nximage_blitrow(g_runs[1].run, &state); + nximage_blitrow(g_runs[1].run, &state); #endif /* Output rows before averaging */ @@ -306,7 +310,8 @@ void nximage_image(NXWINDOW hwnd) #if CONFIG_NX_NPLANES > 1 # warning "More logic is needed for the case where CONFIG_NX_PLANES > 1" #endif - ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, SCALED_WIDTH*sizeof(nxgl_mxpixel_t)); + ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, + SCALED_WIDTH * sizeof(nxgl_mxpixel_t)); if (ret < 0) { printf("nximage_image: nx_bitmapwindow failed: %d\n", errno); @@ -348,7 +353,8 @@ void nximage_image(NXWINDOW hwnd) #if CONFIG_NX_NPLANES > 1 # warning "More logic is needed for the case where CONFIG_NX_PLANES > 1" #endif - ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, SCALED_WIDTH*sizeof(nxgl_mxpixel_t)); + ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, + SCALED_WIDTH * sizeof(nxgl_mxpixel_t)); if (ret < 0) { printf("nximage_image: nx_bitmapwindow failed: %d\n", errno); @@ -369,7 +375,8 @@ void nximage_image(NXWINDOW hwnd) #if CONFIG_NX_NPLANES > 1 # warning "More logic is needed for the case where CONFIG_NX_PLANES > 1" #endif - ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, SCALED_WIDTH*sizeof(nxgl_mxpixel_t)); + ret = nx_bitmap((NXWINDOW)hwnd, &dest, src, &pos, + SCALED_WIDTH * sizeof(nxgl_mxpixel_t)); if (ret < 0) { printf("nximage_image: nx_bitmapwindow failed: %d\n", errno); diff --git a/examples/nximage/nximage_main.c b/examples/nximage/nximage_main.c index 4598ae4f6..4d9648f0f 100644 --- a/examples/nximage/nximage_main.c +++ b/examples/nximage/nximage_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -90,7 +90,7 @@ static inline int nximage_initialize(void) ret = sched_setparam(0, ¶m); if (ret < 0) { - printf("nximage_initialize: sched_setparam failed: %d\n" , ret); + printf("nximage_initialize: sched_setparam failed: %d\n", ret); return ERROR; } @@ -99,7 +99,8 @@ static inline int nximage_initialize(void) ret = boardctl(BOARDIOC_NX_START, 0); if (ret < 0) { - printf("nximage_initialize: Failed to start the NX server: %d\n", errno); + printf("nximage_initialize: Failed to start the NX server: %d\n", + errno); return ERROR; } @@ -108,52 +109,53 @@ static inline int nximage_initialize(void) g_nximage.hnx = nx_connect(); if (g_nximage.hnx) { - pthread_attr_t attr; + pthread_attr_t attr; #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, g_nximage.hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, g_nximage.hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); - nx_disconnect(g_nximage.hnx); - return ERROR; - } + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); + nx_disconnect(g_nximage.hnx); + return ERROR; + } #endif - /* Start a separate thread to listen for server events. This is probably - * the least efficient way to do this, but it makes this example flow more - * smoothly. - */ + /* Start a separate thread to listen for server events. This is + * probably the least efficient way to do this, but it makes this + * example flow more smoothly. + */ - pthread_attr_init(&attr); - param.sched_priority = CONFIG_EXAMPLES_NXIMAGE_LISTENERPRIO; - pthread_attr_setschedparam(&attr, ¶m); - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_NXIMAGE_LISTENER_STACKSIZE); + pthread_attr_init(&attr); + param.sched_priority = CONFIG_EXAMPLES_NXIMAGE_LISTENERPRIO; + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setstacksize(&attr, + CONFIG_EXAMPLES_NXIMAGE_LISTENER_STACKSIZE); - ret = pthread_create(&thread, &attr, nximage_listener, NULL); - if (ret != 0) - { - printf("nximage_initialize: pthread_create failed: %d\n", ret); - return ERROR; - } + ret = pthread_create(&thread, &attr, nximage_listener, NULL); + if (ret != 0) + { + printf("nximage_initialize: pthread_create failed: %d\n", ret); + return ERROR; + } - /* Don't return until we are connected to the server */ + /* Don't return until we are connected to the server */ - while (!g_nximage.connected) - { - /* Wait for the listener thread to wake us up when we really - * are connected. - */ + while (!g_nximage.connected) + { + /* Wait for the listener thread to wake us up when we really + * are connected. + */ - sem_wait(&g_nximage.eventsem); - } + sem_wait(&g_nximage.eventsem); + } } else { @@ -194,7 +196,7 @@ int main(int argc, FAR char *argv[]) /* Set the background to the configured background color */ - color = nximage_bgcolor(); + color = nximage_bgcolor(); printf("nximage_main: Set background color=%u\n", color); ret = nx_setbgcolor(g_nximage.hnx, &color); @@ -222,7 +224,8 @@ int main(int argc, FAR char *argv[]) sem_wait(&g_nximage.eventsem); } - printf("nximage_main: Screen resolution (%d,%d)\n", g_nximage.xres, g_nximage.yres); + printf("nximage_main: Screen resolution (%d,%d)\n", + g_nximage.xres, g_nximage.yres); /* Now, put up the NuttX logo and wait a bit so that it visible. */ diff --git a/examples/nxlines/nxlines_bkgd.c b/examples/nxlines/nxlines_bkgd.c index 22b068c64..f3e3940fd 100644 --- a/examples/nxlines/nxlines_bkgd.c +++ b/examples/nxlines/nxlines_bkgd.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nxlines/nxlines_main.c b/examples/nxlines/nxlines_main.c index 7b3d4ba0d..f13294fd0 100644 --- a/examples/nxlines/nxlines_main.c +++ b/examples/nxlines/nxlines_main.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -51,6 +51,7 @@ ****************************************************************************/ /* Configuration ************************************************************/ + /* If not specified, assume that the hardware supports one video plane */ #ifndef CONFIG_EXAMPLES_NXLINES_VPLANE @@ -111,7 +112,7 @@ static inline int nxlines_initialize(void) ret = sched_setparam(0, ¶m); if (ret < 0) { - printf("nxlines_initialize: sched_setparam failed: %d\n" , ret); + printf("nxlines_initialize: sched_setparam failed: %d\n", ret); return ERROR; } @@ -120,7 +121,8 @@ static inline int nxlines_initialize(void) ret = boardctl(BOARDIOC_NX_START, 0); if (ret < 0) { - printf("nxlines_initialize: Failed to start the NX server: %d\n", errno); + printf("nxlines_initialize: Failed to start the NX server: %d\n", + errno); return ERROR; } @@ -129,52 +131,53 @@ static inline int nxlines_initialize(void) g_nxlines.hnx = nx_connect(); if (g_nxlines.hnx) { - pthread_attr_t attr; + pthread_attr_t attr; #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, g_nxlines.hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, g_nxlines.hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); - nx_disconnect(g_nxlines.hnx); - return ERROR; - } + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); + nx_disconnect(g_nxlines.hnx); + return ERROR; + } #endif - /* Start a separate thread to listen for server events. This is probably - * the least efficient way to do this, but it makes this example flow more - * smoothly. - */ + /* Start a separate thread to listen for server events. This is + * probably the least efficient way to do this, but it makes this + * example flow more smoothly. + */ - pthread_attr_init(&attr); - param.sched_priority = CONFIG_EXAMPLES_NXLINES_LISTENERPRIO; - pthread_attr_setschedparam(&attr, ¶m); - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_NXLINES_LISTENER_STACKSIZE); + pthread_attr_init(&attr); + param.sched_priority = CONFIG_EXAMPLES_NXLINES_LISTENERPRIO; + pthread_attr_setschedparam(&attr, ¶m); + pthread_attr_setstacksize(&attr, + CONFIG_EXAMPLES_NXLINES_LISTENER_STACKSIZE); - ret = pthread_create(&thread, &attr, nxlines_listener, NULL); - if (ret != 0) - { - printf("nxlines_initialize: pthread_create failed: %d\n", ret); - return ERROR; - } + ret = pthread_create(&thread, &attr, nxlines_listener, NULL); + if (ret != 0) + { + printf("nxlines_initialize: pthread_create failed: %d\n", ret); + return ERROR; + } - /* Don't return until we are connected to the server */ + /* Don't return until we are connected to the server */ - while (!g_nxlines.connected) - { - /* Wait for the listener thread to wake us up when we really - * are connected. - */ + while (!g_nxlines.connected) + { + /* Wait for the listener thread to wake us up when we really + * are connected. + */ - sem_wait(&g_nxlines.eventsem); - } + sem_wait(&g_nxlines.eventsem); + } } else { @@ -240,7 +243,8 @@ int main(int argc, FAR char *argv[]) sem_wait(&g_nxlines.eventsem); } - printf("nxlines_main: Screen resolution (%d,%d)\n", g_nxlines.xres, g_nxlines.yres); + printf("nxlines_main: Screen resolution (%d,%d)\n", + g_nxlines.xres, g_nxlines.yres); /* Now, say perform the lines (these test does not return so the remaining * logic is cosmetic). diff --git a/examples/nxmbserver/CMakeLists.txt b/examples/nxmbserver/CMakeLists.txt new file mode 100644 index 000000000..d6880be38 --- /dev/null +++ b/examples/nxmbserver/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/nxmbserver/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_NXMBSERVER) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_NXMBSERVER_PROGNAME} + PRIORITY + ${CONFIG_EXAMPLES_NXMBSERVER_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_NXMBSERVER_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_NXMBSERVER} + SRCS + nxmbserver_main.c) +endif() diff --git a/examples/nxmbserver/Kconfig b/examples/nxmbserver/Kconfig new file mode 100644 index 000000000..428f49c5e --- /dev/null +++ b/examples/nxmbserver/Kconfig @@ -0,0 +1,33 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_NXMBSERVER + tristate "NxModbus Server Example" + default n + depends on NXMODBUS_SERVER + ---help--- + Enable the NxModbus server example. This example demonstrates + how to create a Modbus server (slave) that responds to client + requests. Supports RTU, ASCII, and TCP transports with simulated + register data. + +if EXAMPLES_NXMBSERVER + +config EXAMPLES_NXMBSERVER_PROGNAME + string "Program name" + default "nxmbserver" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_NXMBSERVER_PRIORITY + int "NxModbus server task priority" + default 100 + +config EXAMPLES_NXMBSERVER_STACKSIZE + int "NxModbus server stack size" + default DEFAULT_TASK_STACKSIZE + +endif # EXAMPLES_NXMBSERVER diff --git a/examples/nxmbserver/Make.defs b/examples/nxmbserver/Make.defs new file mode 100644 index 000000000..57fd777e7 --- /dev/null +++ b/examples/nxmbserver/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/nxmbserver/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_NXMBSERVER),) +CONFIGURED_APPS += $(APPDIR)/examples/nxmbserver +endif diff --git a/examples/nxmbserver/Makefile b/examples/nxmbserver/Makefile new file mode 100644 index 000000000..d3699d90b --- /dev/null +++ b/examples/nxmbserver/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/nxmbserver/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MAINSRC = nxmbserver_main.c + +PROGNAME = $(CONFIG_EXAMPLES_NXMBSERVER_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_NXMBSERVER_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_NXMBSERVER_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_NXMBSERVER) + +include $(APPDIR)/Application.mk diff --git a/examples/nxmbserver/nxmbserver_main.c b/examples/nxmbserver/nxmbserver_main.c new file mode 100644 index 000000000..8e8572dda --- /dev/null +++ b/examples/nxmbserver/nxmbserver_main.c @@ -0,0 +1,543 @@ +/**************************************************************************** + * apps/examples/nxmbserver/nxmbserver_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEFAULT_UNIT_ID 1 +#define DEFAULT_TCP_PORT 502 +#define DEFAULT_BAUDRATE 19200 + +#define NUM_COILS 100 +#define NUM_DISCRETE 100 +#define NUM_INPUT_REGS 100 +#define NUM_HOLDING_REGS 100 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Modbus type supported by example */ + +enum transport_type_e +{ + TRANSPORT_RTU = 0, + TRANSPORT_ASCII, + TRANSPORT_TCP +}; + +/* Modbus registers */ + +struct server_data_s +{ + uint8_t coils[NUM_COILS / 8 + 1]; + uint8_t discrete[NUM_DISCRETE / 8 + 1]; + uint16_t input_regs[NUM_INPUT_REGS]; + uint16_t holding_regs[NUM_HOLDING_REGS]; +}; + +/* Modbus server configuration */ + +struct server_config_s +{ + enum transport_type_e transport; + enum nxmb_parity_e parity; + FAR const char *device; + FAR const char *bindaddr; + uint32_t baudrate; + uint16_t port; + uint8_t unit_id; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct server_data_s g_data; +static volatile bool g_running = true; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: signal_handler + ****************************************************************************/ + +static void signal_handler(int signo) +{ + g_running = false; +} + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(FAR const char *progname) +{ + printf("Usage: %s [OPTIONS]\n\n", progname); + printf("Transport Options:\n"); + printf(" -t TYPE Transport type: rtu, ascii, tcp (required)\n"); + printf(" -d DEVICE Serial device path (for RTU/ASCII)\n"); + printf(" -b BAUD Baud rate (default: %u)\n", DEFAULT_BAUDRATE); + printf(" -p PARITY Parity: none, even, odd (default: even)\n"); + printf(" -a ADDR Bind address (for TCP, default: 0.0.0.0)\n"); + printf(" -P PORT TCP port (default: %u)\n", DEFAULT_TCP_PORT); + printf("\nModbus Options:\n"); + printf(" -u UNIT Unit ID (default: %u)\n", DEFAULT_UNIT_ID); + printf("\nExample:\n"); + printf(" %s -t rtu -d /dev/ttyS1 -b 19200\n", progname); + printf(" %s -t tcp -P 502\n", progname); +} + +/**************************************************************************** + * Name: parse_parity + ****************************************************************************/ + +static int parse_parity(FAR const char *str, FAR enum nxmb_parity_e *parity) +{ + if (strcmp(str, "none") == 0) + { + *parity = NXMB_PAR_NONE; + } + else if (strcmp(str, "even") == 0) + { + *parity = NXMB_PAR_EVEN; + } + else if (strcmp(str, "odd") == 0) + { + *parity = NXMB_PAR_ODD; + } + else + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: parse_transport + ****************************************************************************/ + +static int parse_transport(FAR const char *str, + FAR enum transport_type_e *transport) +{ + if (strcmp(str, "rtu") == 0) + { + *transport = TRANSPORT_RTU; + } + else if (strcmp(str, "ascii") == 0) + { + *transport = TRANSPORT_ASCII; + } + else if (strcmp(str, "tcp") == 0) + { + *transport = TRANSPORT_TCP; + } + else + { + return -EINVAL; + } + + return OK; +} + +/**************************************************************************** + * Name: coils_callback + ****************************************************************************/ + +static int coils_callback(FAR uint8_t *buf, uint16_t addr, uint16_t count, + enum nxmb_regmode_e mode, FAR void *priv) +{ + uint16_t byte_idx; + uint16_t bit_idx; + uint16_t i; + + if (addr + count > NUM_COILS) + { + return -ENOENT; + } + + if (mode == NXMB_REG_READ) + { + for (i = 0; i < count; i++) + { + byte_idx = (addr + i) / 8; + bit_idx = (addr + i) % 8; + + if (g_data.coils[byte_idx] & (1 << bit_idx)) + { + buf[i / 8] |= (1 << (i % 8)); + } + else + { + buf[i / 8] &= ~(1 << (i % 8)); + } + } + } + else + { + for (i = 0; i < count; i++) + { + byte_idx = (addr + i) / 8; + bit_idx = (addr + i) % 8; + + if (buf[i / 8] & (1 << (i % 8))) + { + g_data.coils[byte_idx] |= (1 << bit_idx); + } + else + { + g_data.coils[byte_idx] &= ~(1 << bit_idx); + } + } + } + + return OK; +} + +/**************************************************************************** + * Name: discrete_callback + ****************************************************************************/ + +static int discrete_callback(FAR uint8_t *buf, uint16_t addr, + uint16_t count, FAR void *priv) +{ + uint16_t byte_idx; + uint16_t bit_idx; + uint16_t i; + + if (addr + count > NUM_DISCRETE) + { + return -ENOENT; + } + + for (i = 0; i < count; i++) + { + byte_idx = (addr + i) / 8; + bit_idx = (addr + i) % 8; + + if (g_data.discrete[byte_idx] & (1 << bit_idx)) + { + buf[i / 8] |= (1 << (i % 8)); + } + else + { + buf[i / 8] &= ~(1 << (i % 8)); + } + } + + return OK; +} + +/**************************************************************************** + * Name: input_callback + ****************************************************************************/ + +static int input_callback(FAR uint8_t *buf, uint16_t addr, uint16_t count, + FAR void *priv) +{ + uint16_t i; + + if (addr + count > NUM_INPUT_REGS) + { + return -ENOENT; + } + + for (i = 0; i < count; i++) + { + buf[i * 2] = (uint8_t)(g_data.input_regs[addr + i] >> 8); + buf[i * 2 + 1] = (uint8_t)(g_data.input_regs[addr + i] & 0xff); + } + + return OK; +} + +/**************************************************************************** + * Name: holding_callback + ****************************************************************************/ + +static int holding_callback(FAR uint8_t *buf, uint16_t addr, uint16_t count, + enum nxmb_regmode_e mode, FAR void *priv) +{ + uint16_t i; + + if (addr + count > NUM_HOLDING_REGS) + { + return -ENOENT; + } + + if (mode == NXMB_REG_READ) + { + for (i = 0; i < count; i++) + { + buf[i * 2] = (uint8_t)(g_data.holding_regs[addr + i] >> 8); + buf[i * 2 + 1] = (uint8_t)(g_data.holding_regs[addr + i] & 0xff); + } + } + else + { + for (i = 0; i < count; i++) + { + g_data.holding_regs[addr + i] = + (uint16_t)(buf[i * 2] << 8) | (uint16_t)buf[i * 2 + 1]; + } + } + + return OK; +} + +/**************************************************************************** + * Name: init_data + ****************************************************************************/ + +static void init_data(void) +{ + uint16_t i; + + memset(&g_data, 0, sizeof(g_data)); + + for (i = 0; i < NUM_INPUT_REGS; i++) + { + g_data.input_regs[i] = i * 10; + } + + for (i = 0; i < NUM_HOLDING_REGS; i++) + { + g_data.holding_regs[i] = i * 100; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmbserver_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct server_config_s config; + struct nxmb_config_s mb_config; + struct nxmb_callbacks_s callbacks; + nxmb_handle_t handle; + bool transport_set = false; + int option; + int ret; + + /* Default config */ + + memset(&config, 0, sizeof(config)); + config.baudrate = DEFAULT_BAUDRATE; + config.parity = NXMB_PAR_EVEN; + config.port = DEFAULT_TCP_PORT; + config.unit_id = DEFAULT_UNIT_ID; + + /* Handle CLI */ + + while ((option = getopt(argc, argv, "t:d:b:p:a:P:u:h")) != -1) + { + switch (option) + { + case 't': + if (parse_transport(optarg, &config.transport) < 0) + { + fprintf(stderr, "Error: invalid transport '%s'\n", optarg); + return EXIT_FAILURE; + } + + transport_set = true; + break; + + case 'd': + config.device = optarg; + break; + + case 'b': + config.baudrate = (uint32_t)strtoul(optarg, NULL, 0); + break; + + case 'p': + if (parse_parity(optarg, &config.parity) < 0) + { + fprintf(stderr, "Error: invalid parity '%s'\n", optarg); + return EXIT_FAILURE; + } + + break; + + case 'a': + config.bindaddr = optarg; + break; + + case 'P': + config.port = (uint16_t)strtoul(optarg, NULL, 0); + break; + + case 'u': + config.unit_id = (uint8_t)strtoul(optarg, NULL, 0); + break; + + case 'h': + default: + show_usage(argv[0]); + return (option == 'h') ? EXIT_SUCCESS : EXIT_FAILURE; + } + } + + /* Validate input */ + + if (!transport_set) + { + fprintf(stderr, "Error: transport type (-t) is required\n\n"); + show_usage(argv[0]); + return EXIT_FAILURE; + } + + if ((config.transport == TRANSPORT_RTU || + config.transport == TRANSPORT_ASCII) && + config.device == NULL) + { + fprintf(stderr, "Error: device path (-d) required for RTU/ASCII\n"); + return EXIT_FAILURE; + } + + /* Connect signals */ + + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + /* Init modbus data */ + + init_data(); + + /* Init modbus stack */ + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.coil_cb = coils_callback; + callbacks.discrete_cb = discrete_callback; + callbacks.input_cb = input_callback; + callbacks.holding_cb = holding_callback; + + memset(&mb_config, 0, sizeof(mb_config)); + mb_config.unit_id = config.unit_id; + mb_config.is_client = false; + + switch (config.transport) + { + case TRANSPORT_RTU: + mb_config.mode = NXMB_MODE_RTU; + mb_config.transport.serial.devpath = config.device; + mb_config.transport.serial.baudrate = config.baudrate; + mb_config.transport.serial.parity = config.parity; + printf("Starting Modbus RTU server on %s (baud=%" PRId32 + ", unit=%u)\n", config.device, config.baudrate, + config.unit_id); + break; + + case TRANSPORT_ASCII: + mb_config.mode = NXMB_MODE_ASCII; + mb_config.transport.serial.devpath = config.device; + mb_config.transport.serial.baudrate = config.baudrate; + mb_config.transport.serial.parity = config.parity; + printf("Starting Modbus ASCII server on %s (baud=%" PRId32 + ", unit=%u)\n", config.device, config.baudrate, + config.unit_id); + break; + + case TRANSPORT_TCP: + mb_config.mode = NXMB_MODE_TCP; + mb_config.transport.tcp.port = config.port; + mb_config.transport.tcp.bindaddr = config.bindaddr; + printf("Starting Modbus TCP server on port %u (unit=%u)\n", + config.port, config.unit_id); + break; + } + + ret = nxmb_create(&handle, &mb_config); + if (ret < 0) + { + fprintf(stderr, "Error: failed to create Modbus context: %d\n", ret); + return EXIT_FAILURE; + } + + ret = nxmb_set_callbacks(handle, &callbacks); + if (ret < 0) + { + fprintf(stderr, "Error: failed to set callbacks: %d\n", ret); + nxmb_destroy(handle); + return EXIT_FAILURE; + } + + ret = nxmb_enable(handle); + if (ret < 0) + { + fprintf(stderr, "Error: failed to enable context: %d\n", ret); + nxmb_destroy(handle); + return EXIT_FAILURE; + } + + printf("Server running. Press Ctrl+C to stop.\n"); + printf("Register map:\n"); + printf(" Coils: 1-%d (read/write)\n", NUM_COILS); + printf(" Discrete: 1-%d (read-only)\n", NUM_DISCRETE); + printf(" Input regs: 1-%d (read-only, value=addr*10)\n", + NUM_INPUT_REGS); + printf(" Holding regs: 1-%d (read/write, initial=addr*100)\n", + NUM_HOLDING_REGS); + + /* Poll loop */ + + while (g_running) + { + ret = nxmb_poll(handle); + if (ret < 0 && ret != -EAGAIN) + { + fprintf(stderr, "Error: poll failed: %d\n", ret); + break; + } + } + + printf("\nShutting down...\n"); + nxmb_disable(handle); + nxmb_destroy(handle); + + return EXIT_SUCCESS; +} diff --git a/examples/nxscope/Kconfig b/examples/nxscope/Kconfig index b29f97f26..49d36f270 100644 --- a/examples/nxscope/Kconfig +++ b/examples/nxscope/Kconfig @@ -43,6 +43,15 @@ config EXAMPLES_NXSCOPE_CDCACM endif # LOGGING_NXSCOPE_INTF_SERIAL +if LOGGING_NXSCOPE_INTF_UDP + +config EXAMPLES_NXSCOPE_UDP_PORT + int "nxscope UDP local port" + default 50000 + range 1 65535 + +endif # LOGGING_NXSCOPE_INTF_UDP + config EXAMPLES_NXSCOPE_FORCE_ENABLE bool "nxscope force enable" default n diff --git a/examples/nxscope/nxscope_main.c b/examples/nxscope/nxscope_main.c index b2311a74a..ba0a3c226 100644 --- a/examples/nxscope/nxscope_main.c +++ b/examples/nxscope/nxscope_main.c @@ -29,15 +29,17 @@ #include #include +#include +#include #include #include +#include #include #include #ifdef CONFIG_EXAMPLES_NXSCOPE_TIMER # include # include -# include # include # include #endif @@ -52,7 +54,8 @@ # error "math library must be selected for this example" #endif -#define SIN_DT (0.01f) +#define SIN_DT (0.01f) +#define STREAM_THREAD_USLEP 100 /**************************************************************************** * Private Types @@ -61,6 +64,7 @@ struct nxscope_thr_env_s { FAR struct nxscope_s *nxs; + int interval; }; /**************************************************************************** @@ -93,12 +97,76 @@ static int nxscope_cb_start(FAR void *priv, bool start) return OK; } +/**************************************************************************** + * Name: nxscope_show_usage + ****************************************************************************/ + +static void nxscope_show_usage(FAR const char *progname) +{ + printf("Usage: %s [-i ]\n", progname); + printf(" [-m ]\n"); +} + +/**************************************************************************** + * Name: nxscope_parse_args + ****************************************************************************/ + +static int nxscope_parse_args(int argc, FAR char *argv[], + FAR int *stream_interval, + FAR int *main_interval) +{ + unsigned long value = 0; + int opt = 0; + + DEBUGASSERT(argv); + DEBUGASSERT(stream_interval); + DEBUGASSERT(main_interval); + + while ((opt = getopt(argc, argv, "i:m:")) != -1) + { + switch (opt) + { + case 'i': + { + value = strtoul(optarg, NULL, 10); + if (value == 0) + { + printf("ERROR: invalid interval: %s\n", optarg); + return -EINVAL; + } + + *stream_interval = (int)value; + break; + } + + case 'm': + { + value = strtoul(optarg, NULL, 10); + if (value == 0) + { + printf("ERROR: invalid interval: %s\n", optarg); + return -EINVAL; + } + + *main_interval = (int)value; + break; + } + + default: + printf("ERROR: unsupported argument\n"); + return -EINVAL; + } + } + + return OK; +} + #ifdef CONFIG_EXAMPLES_NXSCOPE_TIMER /**************************************************************************** * Name: nxscope_timer_init ****************************************************************************/ -static int nxscope_timer_init(void) +static int nxscope_timer_init(int interval_us) { int fd = 0; int ret = 0; @@ -116,8 +184,7 @@ static int nxscope_timer_init(void) /* Set the timer interval */ - ret = ioctl(fd, TCIOC_SETTIMEOUT, - CONFIG_EXAMPLES_NXSCOPE_TIMER_INTERVAL); + ret = ioctl(fd, TCIOC_SETTIMEOUT, interval_us); if (ret < 0) { printf("ERROR: Failed to set the timer interval: %d\n", errno); @@ -197,7 +264,7 @@ static FAR void *nxscope_samples_thr(FAR void *arg) #ifdef CONFIG_EXAMPLES_NXSCOPE_TIMER /* Initialize timer for periodic signal. */ - ret = nxscope_timer_init(); + ret = nxscope_timer_init(envp->interval); if (ret < 0) { printf("ERROR: nxscope_timer_init() failed: %d\n", errno); @@ -274,7 +341,6 @@ static FAR void *nxscope_samples_thr(FAR void *arg) nxscope_put_b16(envp->nxs, 13, ftob16(-1.0f)); -#ifdef CONFIG_HAVE_LONG_LONG /* Channel 14 */ nxscope_put_ub32(envp->nxs, 14, dtob32(1.0)); @@ -282,7 +348,6 @@ static FAR void *nxscope_samples_thr(FAR void *arg) /* Channel 15 */ nxscope_put_b32(envp->nxs, 15, dtob32(-1.0)); -#endif /* Channel 16 */ @@ -307,7 +372,7 @@ static FAR void *nxscope_samples_thr(FAR void *arg) goto errout; } #else - usleep(100); + usleep(envp->interval); #endif } @@ -421,6 +486,7 @@ int main(int argc, FAR char *argv[]) { struct nxscope_s nxs; int ret = OK; + int interval; pthread_t thread; struct nxscope_thr_env_s env; struct nxscope_cfg_s nxs_cfg; @@ -431,20 +497,38 @@ int main(int argc, FAR char *argv[]) #ifdef CONFIG_LOGGING_NXSCOPE_INTF_SERIAL struct nxscope_ser_cfg_s nxs_ser_cfg; #endif +#ifdef CONFIG_LOGGING_NXSCOPE_INTF_UDP + struct nxscope_udp_cfg_s nxs_udp_cfg; +#endif #ifdef CONFIG_LOGGING_NXSCOPE_INTF_DUMMY struct nxscope_dummy_cfg_s nxs_dummy_cfg; #endif -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ + /* Default settings */ - boardctl(BOARDIOC_INIT, 0); + interval = CONFIG_EXAMPLES_NXSCOPE_MAIN_INTERVAL; +#ifdef CONFIG_EXAMPLES_NXSCOPE_TIMER + env.interval = CONFIG_EXAMPLES_NXSCOPE_TIMER_INTERVAL; +#else + env.interval = STREAM_THREAD_USLEP; +#endif -# ifdef CONFIG_BOARDCTL_FINALINIT + /* Parse args */ + + ret = nxscope_parse_args(argc, argv, &env.interval, &interval); + if (ret < 0) + { + nxscope_show_usage(argv[0]); + return EXIT_FAILURE; + } + + printf("stream interval = %d\n", env.interval); + printf("main interval = %d\n", interval); + +#ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -# endif #endif #ifdef CONFIG_EXAMPLES_NXSCOPE_CDCACM @@ -484,6 +568,22 @@ int main(int argc, FAR char *argv[]) } #endif +#ifdef CONFIG_LOGGING_NXSCOPE_INTF_UDP + /* Configuration */ + + nxs_udp_cfg.port = CONFIG_EXAMPLES_NXSCOPE_UDP_PORT; + nxs_udp_cfg.nonblock = true; + + /* Initialize UDP interface */ + + ret = nxscope_udp_init(&intf, &nxs_udp_cfg); + if (ret < 0) + { + printf("ERROR: nxscope_udp_init failed %d\n", ret); + goto errout_nointf; + } +#endif + #ifdef CONFIG_LOGGING_NXSCOPE_INTF_DUMMY /* Configuration */ @@ -603,7 +703,6 @@ int main(int argc, FAR char *argv[]) u.s.cri = 0; nxscope_chan_init(&nxs, 13, "chan13", u.u8, 1, 0); -#ifdef CONFIG_HAVE_LONG_LONG u.s.dtype = NXSCOPE_TYPE_UB32; u.s._res = 0; u.s.cri = 0; @@ -613,7 +712,6 @@ int main(int argc, FAR char *argv[]) u.s._res = 0; u.s.cri = 0; nxscope_chan_init(&nxs, 15, "chan15", u.u8, 1, 0); -#endif /* Vector data channel */ @@ -717,7 +815,7 @@ int main(int argc, FAR char *argv[]) printf("ERROR: nxscope_recv failed %d\n", ret); } - usleep(CONFIG_EXAMPLES_NXSCOPE_MAIN_INTERVAL); + usleep(interval); } errout: @@ -733,6 +831,9 @@ errout_nonxscope: #if defined(CONFIG_LOGGING_NXSCOPE_INTF_SERIAL) nxscope_ser_deinit(&intf); #endif +#if defined(CONFIG_LOGGING_NXSCOPE_INTF_UDP) + nxscope_udp_deinit(&intf); +#endif #if defined(CONFIG_LOGGING_NXSCOPE_INTF_DUMMY) nxscope_dummy_deinit(&intf); #endif diff --git a/examples/nxterm/nxterm_main.c b/examples/nxterm/nxterm_main.c index c1a912a08..6fbf0be6d 100644 --- a/examples/nxterm/nxterm_main.c +++ b/examples/nxterm/nxterm_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #ifdef CONFIG_NX_LCDDRIVER # include diff --git a/examples/nxterm/nxterm_toolbar.c b/examples/nxterm/nxterm_toolbar.c index 24bcee463..327a54057 100644 --- a/examples/nxterm/nxterm_toolbar.c +++ b/examples/nxterm/nxterm_toolbar.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -54,12 +54,14 @@ static void nxtool_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, bool morem, FAR void *arg); -static void nxtool_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxtool_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nxtool_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, +static void nxtool_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, uint8_t buttons, FAR void *arg); #endif @@ -121,7 +123,8 @@ static void nxtool_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, * Name: nxtool_position ****************************************************************************/ -static void nxtool_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxtool_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg) diff --git a/examples/nxterm/nxterm_wndo.c b/examples/nxterm/nxterm_wndo.c index 2bba6016a..ca88204ae 100644 --- a/examples/nxterm/nxterm_wndo.c +++ b/examples/nxterm/nxterm_wndo.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -49,12 +49,14 @@ static void nxwndo_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, bool morem, FAR void *arg); -static void nxwndo_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxwndo_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg); #ifdef CONFIG_NX_XYINPUT -static void nxwndo_mousein(NXWINDOW hwnd, FAR const struct nxgl_point_s *pos, +static void nxwndo_mousein(NXWINDOW hwnd, + FAR const struct nxgl_point_s *pos, uint8_t buttons, FAR void *arg); #endif @@ -119,7 +121,9 @@ static void nxwndo_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, } else { - /* If the driver has not been opened, then just redraw the window color */ + /* If the driver has not been opened, then just redraw the window + * color. + */ wcolor[0] = CONFIG_EXAMPLES_NXTERM_WCOLOR; nxtk_fillwindow(hwnd, rect, wcolor); @@ -130,7 +134,8 @@ static void nxwndo_redraw(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, * Name: nxwndo_position ****************************************************************************/ -static void nxwndo_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, +static void nxwndo_position(NXWINDOW hwnd, + FAR const struct nxgl_size_s *size, FAR const struct nxgl_point_s *pos, FAR const struct nxgl_rect_s *bounds, FAR void *arg) @@ -163,7 +168,8 @@ static void nxwndo_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, g_nxterm_vars.haveres = true; sem_post(&g_nxterm_vars.eventsem); - ginfo("Have xres=%d yres=%d\n", g_nxterm_vars.xres, g_nxterm_vars.yres); + ginfo("Have xres=%d yres=%d\n", + g_nxterm_vars.xres, g_nxterm_vars.yres); } } diff --git a/examples/nxtext/nxtext_bkgd.c b/examples/nxtext/nxtext_bkgd.c index 1e0d4a18d..b0957bb7a 100644 --- a/examples/nxtext/nxtext_bkgd.c +++ b/examples/nxtext/nxtext_bkgd.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -107,7 +107,8 @@ NXHANDLE g_bgwnd; * Name: nxbg_redrawrect ****************************************************************************/ -static void nxbg_redrawrect(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect) +static void nxbg_redrawrect(NXWINDOW hwnd, + FAR const struct nxgl_rect_s *rect) { int ret; int i; @@ -172,7 +173,9 @@ static void nxbg_position(NXWINDOW hwnd, FAR const struct nxgl_size_s *size, st->wsize.w = size->w; st->wsize.h = size->h; - /* Save the window limits (these should be the same for all places and all windows */ + /* Save the window limits (these should be the same for all places + * and all windows. + */ g_xres = bounds->pt2.x + 1; g_yres = bounds->pt2.y + 1; @@ -219,7 +222,8 @@ static void nxbg_kbdin(NXWINDOW hwnd, uint8_t nch, FAR const uint8_t *ch, * only. ****************************************************************************/ -static inline void nxbg_movedisplay(NXWINDOW hwnd, int bottom, int lineheight) +static inline void nxbg_movedisplay(NXWINDOW hwnd, int bottom, + int lineheight) { FAR struct nxtext_bitmap_s *bm; struct nxgl_rect_s rect; @@ -227,10 +231,10 @@ static inline void nxbg_movedisplay(NXWINDOW hwnd, int bottom, int lineheight) int ret; int i; - /* Move each row, one at a time. They could all be moved at once (by calling - * nxbg_redrawrect), but the since the region is cleared, then re-written, the - * effect would not be good. Below the region is also cleared and re-written, - * however, in much smaller chunks. + /* Move each row, one at a time. They could all be moved at once (by + * calling nxbg_redrawrect), but since the region is cleared, then + * rewritten, the effect would not be good. Below the region is also + * cleared and rewritten, however, in much smaller chunks. */ rect.pt1.x = 0; @@ -256,7 +260,8 @@ static inline void nxbg_movedisplay(NXWINDOW hwnd, int bottom, int lineheight) for (i = 0; i < g_bgstate.nchars; i++) { bm = &g_bgstate.bm[i]; - if (bm->pos.y <= rect.pt2.y && bm->pos.y + g_bgstate.fheight >= rect.pt1.y) + if (bm->pos.y <= rect.pt2.y && + bm->pos.y + g_bgstate.fheight >= rect.pt1.y) { nxtext_fillchar(hwnd, &rect, &g_bgstate, g_bghfont, bm); } @@ -266,7 +271,7 @@ static inline void nxbg_movedisplay(NXWINDOW hwnd, int bottom, int lineheight) /* Finally, clear the bottom part of the display */ rect.pt1.y = bottom; - rect.pt2.y = g_bgstate.wsize.h- 1; + rect.pt2.y = g_bgstate.wsize.h - 1; ret = nx_fill(hwnd, &rect, g_bgstate.wcolor); if (ret < 0) @@ -296,13 +301,15 @@ static inline void nxbg_scroll(NXWINDOW hwnd, int lineheight) { /* Yes... Delete the character by moving all of the data */ - for (j = i; j < g_bgstate.nchars-1; j++) + for (j = i; j < g_bgstate.nchars - 1; j++) { - memcpy(&g_bgstate.bm[j], &g_bgstate.bm[j+1], sizeof(struct nxtext_bitmap_s)); + memcpy(&g_bgstate.bm[j], &g_bgstate.bm[j + 1], + sizeof(struct nxtext_bitmap_s)); } - /* Decrement the number of cached characters ('i' is not incremented - * in this case because it already points to the next character) + /* Decrement the number of cached characters. 'i' is not + * incremented in this case because it already points to the next + * character. */ g_bgstate.nchars--; diff --git a/examples/nxtext/nxtext_main.c b/examples/nxtext/nxtext_main.c index 1ad8bb9b3..c749cd53a 100644 --- a/examples/nxtext/nxtext_main.c +++ b/examples/nxtext/nxtext_main.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -60,6 +60,7 @@ ****************************************************************************/ /* Configuration ************************************************************/ + /* If not specified, assume that the hardware supports one video plane */ #ifndef CONFIG_EXAMPLES_NXTEXT_VPLANE @@ -138,7 +139,10 @@ nxgl_coord_t g_yres; bool b_haveresolution = false; bool g_connected = false; -sem_t g_semevent = {0}; +sem_t g_semevent = +{ + 0 +}; int g_exitcode = NXEXIT_SUCCESS; @@ -162,7 +166,7 @@ static int nxtext_initialize(void) ret = sched_setparam(0, ¶m); if (ret < 0) { - printf("nxtext_initialize: sched_setparam failed: %d\n" , ret); + printf("nxtext_initialize: sched_setparam failed: %d\n", ret); g_exitcode = NXEXIT_SCHEDSETPARAM; return ERROR; } @@ -173,7 +177,8 @@ static int nxtext_initialize(void) ret = boardctl(BOARDIOC_NX_START, 0); if (ret < 0) { - printf("nxtext_initialize: Failed to start the NX server: %d\n", errno); + printf("nxtext_initialize: Failed to start the NX server: %d\n", + errno); g_exitcode = NXEXIT_SERVERSTART; return ERROR; } @@ -188,24 +193,24 @@ static int nxtext_initialize(void) #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, g_hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, g_hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); - nx_disconnect(g_hnx); - g_exitcode = NXEXIT_FBINITIALIZE; - return ERROR; - } + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("boardctl(BOARDIOC_VNC_START) failed: %d\n", ret); + nx_disconnect(g_hnx); + g_exitcode = NXEXIT_FBINITIALIZE; + return ERROR; + } #endif - /* Start a separate thread to listen for server events. This is probably - * the least efficient way to do this, but it makes this example flow more - * smoothly. + /* Start a separate thread to listen for server events. This is + * probably the least efficient way to do this, but it makes this + * example flow more smoothly. */ pthread_attr_init(&attr); @@ -216,10 +221,10 @@ static int nxtext_initialize(void) ret = pthread_create(&thread, &attr, nxtext_listener, NULL); if (ret != 0) { - printf("nxtext_initialize: pthread_create failed: %d\n", ret); + printf("nxtext_initialize: pthread_create failed: %d\n", ret); - g_exitcode = NXEXIT_PTHREADCREATE; - return ERROR; + g_exitcode = NXEXIT_PTHREADCREATE; + return ERROR; } /* Don't return until we are connected to the server */ @@ -277,7 +282,8 @@ int main(int argc, FAR char *argv[]) g_bghfont = nxf_getfonthandle(CONFIG_EXAMPLES_NXTEXT_BGFONTID); if (!g_bghfont) { - printf("nxtext_main: Failed to get background font handle: %d\n", errno); + printf("nxtext_main: Failed to get background font handle: %d\n", + errno); g_exitcode = NXEXIT_FONTOPEN; goto errout; } @@ -285,14 +291,16 @@ int main(int argc, FAR char *argv[]) g_puhfont = nxf_getfonthandle(CONFIG_EXAMPLES_NXTEXT_PUFONTID); if (!g_puhfont) { - printf("nxtext_main: Failed to get pop-up font handle: %d\n", errno); + printf("nxtext_main: Failed to get pop-up font handle: %d\n", + errno); g_exitcode = NXEXIT_FONTOPEN; goto errout; } /* Set the background to the configured background color */ - printf("nxtext_main: Set background color=%d\n", CONFIG_EXAMPLES_NXTEXT_BGCOLOR); + printf("nxtext_main: Set background color=%d\n", + CONFIG_EXAMPLES_NXTEXT_BGCOLOR); color = CONFIG_EXAMPLES_NXTEXT_BGCOLOR; ret = nx_setbgcolor(g_hnx, &color); if (ret < 0) @@ -328,7 +336,7 @@ int main(int argc, FAR char *argv[]) popcnt = 0; bkgndx = 0; - for (;;) + for (; ; ) { /* Sleep for one second */ @@ -346,15 +354,18 @@ int main(int argc, FAR char *argv[]) hwnd = nxpu_open(); #ifdef CONFIG_NX_KBD - /* Give keyboard input to the top window (which should be the pop-up) */ + /* Give keyboard input to the top window (which should be the + * pop-up). + */ printf("nxtext_main: Send keyboard input: %s\n", g_pumsg); - ret = nx_kbdin(g_hnx, strlen((FAR const char *)g_pumsg), g_pumsg); + ret = nx_kbdin(g_hnx, strlen((FAR const char *)g_pumsg), + g_pumsg); if (ret < 0) - { - printf("nxtext_main: nx_kbdin failed: %d\n", errno); - goto errout_with_hwnd; - } + { + printf("nxtext_main: nx_kbdin failed: %d\n", errno); + goto errout_with_hwnd; + } #endif } else if (popcnt == 5) @@ -367,11 +378,12 @@ int main(int argc, FAR char *argv[]) } /* Give another line of text to the background window. Force this - * text to go the background by calling the background window interfaces - * directly. + * text to the background by calling the background window + * interfaces directly. */ - nxbg_write(g_bgwnd, (FAR const uint8_t *)g_bgmsg[bkgndx], strlen(g_bgmsg[bkgndx])); + nxbg_write(g_bgwnd, (FAR const uint8_t *)g_bgmsg[bkgndx], + strlen(g_bgmsg[bkgndx])); if (++bkgndx >= BGMSG_LINES) { bkgndx = 0; @@ -386,13 +398,15 @@ errout_with_hwnd: if (popcnt >= 3) { printf("nxtext_main: Close pop-up\n"); - nxpu_close(hwnd); + nxpu_close(hwnd); } -//errout_with_bkgd: + /* errout_with_bkgd: */ + nx_releasebkgd(g_bgwnd); errout_with_nx: + /* Disconnect from the server */ printf("nxtext_main: Disconnect from the server\n"); diff --git a/examples/nxtext/nxtext_popup.c b/examples/nxtext/nxtext_popup.c index 4ca10e1b7..19dbd3cc5 100644 --- a/examples/nxtext/nxtext_popup.c +++ b/examples/nxtext/nxtext_popup.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/nxtext/nxtext_putc.c b/examples/nxtext/nxtext_putc.c index 47fdd9ba5..325132caf 100644 --- a/examples/nxtext/nxtext_putc.c +++ b/examples/nxtext/nxtext_putc.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -95,6 +95,7 @@ static void nxtext_freeglyph(FAR struct nxtext_glyph_s *glyph) { free(glyph->bitmap); } + memset(glyph, 0, sizeof(struct nxtext_glyph_s)); } @@ -111,11 +112,11 @@ nxtext_allocglyph(FAR struct nxtext_state_s *st) int i; /* Search through the glyph cache looking for an unused glyph. Also, keep - * track of the least used glyph as well. We need that if we have to replace - * a glyph in the cache. + * track of the least used glyph as well. We need that if we have to + * replace a glyph in the cache. */ - for (i = 0; i < st->maxglyphs; i++) + for (i = 0; i < st->maxglyphs; i++) { /* Is this glyph in use? */ @@ -149,9 +150,9 @@ nxtext_allocglyph(FAR struct nxtext_state_s *st) if (luusecnt > 1) { - uint8_t decr = luusecnt - 1; + uint8_t decr = luusecnt - 1; - for (i = 0; i < st->maxglyphs; i++) + for (i = 0; i < st->maxglyphs; i++) { /* Is this glyph in use? */ @@ -180,7 +181,7 @@ nxtext_findglyph(FAR struct nxtext_state_s *st, uint8_t ch) /* First, try to find the glyph in the cache of pre-rendered glyphs */ - for (i = 0; i < st->maxglyphs; i++) + for (i = 0; i < st->maxglyphs; i++) { FAR struct nxtext_glyph_s *glyph = &st->glyph[i]; if (glyph->usecnt > 0 && glyph->code == ch) @@ -189,7 +190,7 @@ nxtext_findglyph(FAR struct nxtext_state_s *st, uint8_t ch) if (glyph->usecnt < MAX_USECNT) { - glyph->usecnt++; + glyph->usecnt++; } /* And return the glyph that we found */ @@ -197,6 +198,7 @@ nxtext_findglyph(FAR struct nxtext_state_s *st, uint8_t ch) return glyph; } } + return NULL; } @@ -248,13 +250,13 @@ nxtext_renderglyph(FAR struct nxtext_state_s *st, /* Pack 1-bit pixels into a 2-bits */ pixel &= 0x01; - pixel = (pixel) << 1 |pixel; + pixel = (pixel) << 1 | pixel; # endif # if CONFIG_EXAMPLES_NXTEXT_BPP < 4 /* Pack 2-bit pixels into a nibble */ pixel &= 0x03; - pixel = (pixel) << 2 |pixel; + pixel = (pixel) << 2 | pixel; # endif /* Pack 4-bit nibbles into a byte */ @@ -292,7 +294,7 @@ nxtext_renderglyph(FAR struct nxtext_state_s *st, /* Then render the glyph into the allocated memory */ - ret = RENDERER((FAR nxgl_mxpixel_t*)glyph->bitmap, + ret = RENDERER((FAR nxgl_mxpixel_t *)glyph->bitmap, glyph->height, glyph->width, glyph->stride, fbm, st->fcolor[0]); if (ret < 0) @@ -312,7 +314,8 @@ nxtext_renderglyph(FAR struct nxtext_state_s *st, * Name: nxtext_fontsize ****************************************************************************/ -static int nxtext_fontsize(NXHANDLE hfont, uint8_t ch, FAR struct nxgl_size_s *size) +static int nxtext_fontsize(NXHANDLE hfont, uint8_t ch, + FAR struct nxgl_size_s *size) { FAR const struct nx_fontbitmap_s *fbm; @@ -365,7 +368,7 @@ nxtext_getglyph(NXHANDLE hfont, FAR struct nxtext_state_s *st, uint8_t ch) * * Description: * This is part of the nxtext_putc logic. It creates and positions a - * the character and renders (or re-uses) a glyph for font. + * the character and renders (or reuses) a glyph for font. * ****************************************************************************/ @@ -379,37 +382,39 @@ nxtext_addchar(NXHANDLE hfont, FAR struct nxtext_state_s *st, uint8_t ch) if (st->nchars < st->maxchars) { - /* Yes, setup the bitmap information */ + /* Yes, setup the bitmap information */ - bm = &st->bm[st->nchars]; - bm->code = ch; - bm->flags = 0; - bm->pos.x = st->fpos.x; - bm->pos.y = st->fpos.y; + bm = &st->bm[st->nchars]; + bm->code = ch; + bm->flags = 0; + bm->pos.x = st->fpos.x; + bm->pos.y = st->fpos.y; - /* Find (or create) the matching glyph */ + /* Find (or create) the matching glyph */ - glyph = nxtext_getglyph(hfont, st, ch); - if (!glyph) - { - /* No, there is no font for this code. Just mark this as a space. */ + glyph = nxtext_getglyph(hfont, st, ch); + if (!glyph) + { + /* No, there is no font for this code. Just mark this as a + * space. + */ - bm->flags |= BMFLAGS_NOGLYPH; + bm->flags |= BMFLAGS_NOGLYPH; - /* Set up the next character position */ + /* Set up the next character position */ - st->fpos.x += st->spwidth; - } - else - { - /* Set up the next character position */ + st->fpos.x += st->spwidth; + } + else + { + /* Set up the next character position */ - st->fpos.x += glyph->width; - } + st->fpos.x += glyph->width; + } - /* Success.. increment nchars to retain this character */ + /* Success.. increment nchars to retain this character */ - st->nchars++; + st->nchars++; } return bm; @@ -465,7 +470,8 @@ void nxtext_newline(FAR struct nxtext_state_s *st) * ****************************************************************************/ -void nxtext_putc(NXWINDOW hwnd, FAR struct nxtext_state_s *st, NXHANDLE hfont, uint8_t ch) +void nxtext_putc(NXWINDOW hwnd, FAR struct nxtext_state_s *st, + NXHANDLE hfont, uint8_t ch) { FAR const struct nxtext_bitmap_s *bm; @@ -562,13 +568,13 @@ void nxtext_fillchar(NXWINDOW hwnd, FAR const struct nxgl_rect_s *rect, /* Find (or create) the glyph that goes with this font */ - glyph = nxtext_getglyph(hfont, st, bm->code); - if (!glyph) - { - /* Shouldn't happen */ + glyph = nxtext_getglyph(hfont, st, bm->code); + if (!glyph) + { + /* Shouldn't happen */ - return; - } + return; + } /* Blit the font bitmap into the window */ diff --git a/examples/oneshot/Kconfig b/examples/oneshot/Kconfig index 43cc117e1..3b9505985 100644 --- a/examples/oneshot/Kconfig +++ b/examples/oneshot/Kconfig @@ -6,7 +6,7 @@ config EXAMPLES_ONESHOT tristate "Oneshot timer example" default n - depends on ONESHOT + depends on ONESHOT && ENABLE_ALL_SIGNALS ---help--- Enable the oneshot timer driver example diff --git a/examples/oneshot/oneshot_main.c b/examples/oneshot/oneshot_main.c index 50760e360..00fac3f2d 100644 --- a/examples/oneshot/oneshot_main.c +++ b/examples/oneshot/oneshot_main.c @@ -167,8 +167,8 @@ int main(int argc, FAR char *argv[]) return EXIT_FAILURE; } - maxus = (uint64_t)ts.tv_sec * USEC_PER_SEC + - (uint64_t)ts.tv_nsec / NSEC_PER_USEC; + maxus = ts.tv_sec * USEC_PER_SEC + + ts.tv_nsec / NSEC_PER_USEC; printf("Maximum delay is %" PRIu64 "\n", maxus); diff --git a/examples/pf_ieee802154/pfieee802154.h b/examples/pf_ieee802154/pfieee802154.h index ee7a88b5c..adf9e3bd6 100644 --- a/examples/pf_ieee802154/pfieee802154.h +++ b/examples/pf_ieee802154/pfieee802154.h @@ -27,7 +27,7 @@ * Included Files ****************************************************************************/ -#include +#include #include /**************************************************************************** diff --git a/examples/pipe/interlock_test.c b/examples/pipe/interlock_test.c index 6be9b6ec4..cb2985800 100644 --- a/examples/pipe/interlock_test.c +++ b/examples/pipe/interlock_test.c @@ -150,8 +150,7 @@ static void *null_writer(pthread_addr_t pvarg) int interlock_test(void) { - pthread_t readerid; - pthread_t writerid; + pthread_t tid; void *value; char data[16]; ssize_t nbytes; @@ -171,7 +170,7 @@ int interlock_test(void) /* Start the null_writer thread */ printf("interlock_test: Starting null_writer thread\n"); - ret = pthread_create(&writerid, NULL, null_writer, NULL); + ret = pthread_create(&tid, NULL, null_writer, NULL); if (ret != 0) { fprintf(stderr, \ @@ -194,7 +193,7 @@ int interlock_test(void) "errno=%d\n", FIFO_PATH2, errno); ret = 3; - goto errout_with_null_writer_thread; + goto errout_with_null_thread; } /* Attempt to read one byte from the FIFO. This should return end-of-file @@ -208,15 +207,15 @@ int interlock_test(void) fprintf(stderr, \ "interlock_test: read failed, errno=%d\n", errno); ret = 4; - goto errout_with_null_writer_thread; + goto errout_with_file; } - else if (ret != 0) + else if (nbytes != 0) { fprintf(stderr, \ "interlock_test: Read %ld bytes of data -- aborting: %d\n", (long)nbytes, errno); ret = 5; - goto errout_with_null_writer_thread; + goto errout_with_file; } printf("interlock_test: read returned\n"); @@ -234,7 +233,7 @@ int interlock_test(void) /* Wait for null_writer thread to complete */ printf("interlock_test: Waiting for null_writer thread\n"); - ret = pthread_join(writerid, &value); + ret = pthread_join(tid, &value); if (ret != 0) { fprintf(stderr, \ @@ -255,7 +254,7 @@ int interlock_test(void) /* Start the null_reader thread */ printf("interlock_test: Starting null_reader thread\n"); - ret = pthread_create(&readerid, NULL, null_reader, NULL); + ret = pthread_create(&tid, NULL, null_reader, NULL); if (ret != 0) { fprintf(stderr, \ @@ -278,29 +277,26 @@ int interlock_test(void) "errno=%d\n", FIFO_PATH2, errno); ret = 9; - goto errout_with_null_reader_thread; + goto errout_with_null_thread; } - /* Attempt to write one byte from the FIFO. This should return 0 bytes - * written because the null_reader closes the FIFO. + /* Attempt to write one byte from the FIFO. This should return n bytes + * written since the null_reader has opened the FIFO. */ printf("interlock_test: Writing to %s\n", FIFO_PATH2); nbytes = write(fd, data, 16); - if (nbytes < 0) + if (nbytes <= 0) { fprintf(stderr, \ "interlock_test: write failed, errno=%d\n", errno); ret = 10; - goto errout_with_null_reader_thread; + goto errout_with_file; } - else if (ret != 0) + else { fprintf(stderr, \ - "interlock_test: Wrote %ld bytes of data -- aborting: %d\n", - (long)nbytes, errno); - ret = 11; - goto errout_with_null_reader_thread; + "interlock_test: Wrote %ld bytes of data\n", (long)nbytes); } printf("interlock_test: write returned\n"); @@ -318,7 +314,7 @@ int interlock_test(void) /* Wait for null_reader thread to complete */ printf("interlock_test: Waiting for null_reader thread\n"); - ret = pthread_join(readerid, &value); + ret = pthread_join(tid, &value); if (ret != 0) { fprintf(stderr, \ @@ -339,21 +335,20 @@ int interlock_test(void) ret = 0; goto errout_with_fifo; -errout_with_null_reader_thread: - pthread_detach(readerid); - pthread_cancel(readerid); - -errout_with_null_writer_thread: - pthread_detach(writerid); - pthread_cancel(writerid); - -errout_with_fifo: - - if (fd != -1 && close(fd) != 0) +errout_with_file: + if (close(fd) != 0) { fprintf(stderr, "interlock_test: close failed: %d\n", errno); } +errout_with_null_thread: + pthread_detach(tid); + pthread_cancel(tid); + +errout_with_fifo: + + /* Close the file */ + ret = remove(FIFO_PATH2); if (ret != 0) { diff --git a/examples/poll/host.c b/examples/poll/host.c index 740bccb22..8d42dd997 100644 --- a/examples/poll/host.c +++ b/examples/poll/host.c @@ -64,7 +64,7 @@ int main(int argc, char **argv, char **envp) { struct sockaddr_in myaddr; char outbuf[IOBUFFER_SIZE]; - char inbuf[IOBUFFER_SIZE]; + char inbuf[IOBUFFER_SIZE + 1]; int sockfd; int len; int nbytessent; diff --git a/examples/poll/net_listener.c b/examples/poll/net_listener.c index 9db855477..3c72e16e3 100644 --- a/examples/poll/net_listener.c +++ b/examples/poll/net_listener.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -64,7 +64,7 @@ struct net_listener_s struct sockaddr_in addr; fd_set master; fd_set working; - char buffer[IOBUFFER_SIZE]; + char buffer[IOBUFFER_SIZE + 1]; int listensd; int mxsd; }; diff --git a/examples/poll/net_reader.c b/examples/poll/net_reader.c index 5e22822e3..191f39875 100644 --- a/examples/poll/net_reader.c +++ b/examples/poll/net_reader.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -121,7 +121,7 @@ static void net_configure(void) static void net_receive(int sd) { struct timeval timeout; - char buffer[IOBUFFER_SIZE]; + char buffer[IOBUFFER_SIZE + 1]; char *ptr; fd_set readset; int nbytes; diff --git a/examples/poll/poll_listener.c b/examples/poll/poll_listener.c index 5864c4222..ce912357d 100644 --- a/examples/poll/poll_listener.c +++ b/examples/poll/poll_listener.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include diff --git a/examples/poll/poll_main.c b/examples/poll/poll_main.c index 817916839..5dfb2d938 100644 --- a/examples/poll/poll_main.c +++ b/examples/poll/poll_main.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include "poll_internal.h" diff --git a/examples/poll/select_listener.c b/examples/poll/select_listener.c index 69cfe92d6..e99eda06d 100644 --- a/examples/poll/select_listener.c +++ b/examples/poll/select_listener.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include "poll_internal.h" diff --git a/examples/popen/Kconfig b/examples/popen/Kconfig index 3b2eba29e..2f571d121 100644 --- a/examples/popen/Kconfig +++ b/examples/popen/Kconfig @@ -7,6 +7,7 @@ config EXAMPLES_POPEN tristate "popen() example" default n depends on SYSTEM_POPEN + depends on !DISABLE_POSIX_TIMERS ---help--- Enable the popen() example diff --git a/examples/posix_spawn/spawn_main.c b/examples/posix_spawn/spawn_main.c index 100030b6c..34428d472 100644 --- a/examples/posix_spawn/spawn_main.c +++ b/examples/posix_spawn/spawn_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/powerled/powerled_main.c b/examples/powerled/powerled_main.c index c0b3cd29d..6743b5e23 100644 --- a/examples/powerled/powerled_main.c +++ b/examples/powerled/powerled_main.c @@ -35,9 +35,13 @@ #include #include #include -#include +#include #include +#ifndef CONFIG_NSH_ARCHINIT +#include +#endif + #include #include @@ -435,16 +439,10 @@ int main(int argc, char *argv[]) goto errout; } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - #ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -#endif #endif /* Set LED current limit */ diff --git a/examples/powermonitor/powermonitor_main.c b/examples/powermonitor/powermonitor_main.c index 3a335dd66..0e13e3b4b 100644 --- a/examples/powermonitor/powermonitor_main.c +++ b/examples/powermonitor/powermonitor_main.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include diff --git a/examples/rmtchar/CMakeLists.txt b/examples/pulsecount/CMakeLists.txt similarity index 76% rename from examples/rmtchar/CMakeLists.txt rename to examples/pulsecount/CMakeLists.txt index 0d03174f5..356b1b982 100644 --- a/examples/rmtchar/CMakeLists.txt +++ b/examples/pulsecount/CMakeLists.txt @@ -1,5 +1,5 @@ # ############################################################################## -# apps/examples/rmtchar/CMakeLists.txt +# apps/examples/pulsecount/CMakeLists.txt # # SPDX-License-Identifier: Apache-2.0 # @@ -20,21 +20,14 @@ # # ############################################################################## -if(CONFIG_EXAMPLES_RMTCHAR) +if(CONFIG_EXAMPLES_PULSECOUNT) nuttx_add_application( NAME - rmtchar + pulsecount STACKSIZE ${CONFIG_DEFAULT_TASK_STACKSIZE} MODULE - ${CONFIG_EXAMPLES_RMTCHAR} + ${CONFIG_EXAMPLES_PULSECOUNT} SRCS - rmtchar_common.c - rmtchar_main.c) - if(CONFIG_EXAMPLES_RMTCHAR_TX) - target_sources(apps PRIVATE rmtchar_transmitter.c) - endif() - if(CONFIG_EXAMPLES_RMTCHAR_RX) - target_sources(apps PRIVATE rmtchar_receiver.c) - endif() + pulsecount_main.c) endif() diff --git a/examples/pulsecount/Kconfig b/examples/pulsecount/Kconfig new file mode 100644 index 000000000..4f4526868 --- /dev/null +++ b/examples/pulsecount/Kconfig @@ -0,0 +1,42 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_PULSECOUNT + tristate "pulsecount example" + default n + depends on PULSECOUNT + ---help--- + Enable the finite pulsecount example + +if EXAMPLES_PULSECOUNT + +config EXAMPLES_PULSECOUNT_DEVPATH + string "pulsecount device path" + default "/dev/pulsecount0" + ---help--- + The path to the pulsecount device. Default: /dev/pulsecount0 + +config EXAMPLES_PULSECOUNT_HIGH_NS + int "Default pulsecount high time" + default 5000000 + ---help--- + The default pulsecount high time in nanoseconds. + Default: 5000000 ns + +config EXAMPLES_PULSECOUNT_LOW_NS + int "Default pulsecount low time" + default 5000000 + ---help--- + The default pulsecount low time in nanoseconds. + Default: 5000000 ns + +config EXAMPLES_PULSECOUNT_COUNT + int "Default pulsecount count" + default 100 + range 1 2147483647 + ---help--- + The default number of pulses to generate. + +endif diff --git a/examples/pulsecount/Make.defs b/examples/pulsecount/Make.defs new file mode 100644 index 000000000..db46b5478 --- /dev/null +++ b/examples/pulsecount/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/pulsecount/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_PULSECOUNT),) +CONFIGURED_APPS += $(APPDIR)/examples/pulsecount +endif diff --git a/examples/pulsecount/Makefile b/examples/pulsecount/Makefile new file mode 100644 index 000000000..b03e07503 --- /dev/null +++ b/examples/pulsecount/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/pulsecount/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MAINSRC = pulsecount_main.c + +PROGNAME = pulsecount +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_PULSECOUNT) + +include $(APPDIR)/Application.mk diff --git a/examples/pulsecount/pulsecount_main.c b/examples/pulsecount/pulsecount_main.c new file mode 100644 index 000000000..82678be08 --- /dev/null +++ b/examples/pulsecount/pulsecount_main.c @@ -0,0 +1,247 @@ +/**************************************************************************** + * apps/examples/pulsecount/pulsecount_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pulsecount_state_s +{ + FAR char *devpath; + uint32_t high_ns; + uint32_t low_ns; + uint32_t count; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void pulsecount_devpath(FAR struct pulsecount_state_s *state, + FAR const char *devpath) +{ + if (state->devpath != NULL) + { + free(state->devpath); + } + + state->devpath = strdup(devpath); +} + +static int arg_string(FAR char **arg, FAR char **value) +{ + FAR char *ptr = *arg; + + if (ptr[2] == '\0') + { + *value = arg[1]; + return 2; + } + + *value = &ptr[2]; + return 1; +} + +static int arg_decimal(FAR char **arg, FAR long *value) +{ + FAR char *string; + int ret; + + ret = arg_string(arg, &string); + *value = strtol(string, NULL, 10); + return ret; +} + +static void pulsecount_help(FAR struct pulsecount_state_s *state) +{ + printf("Usage: pulsecount [OPTIONS]\n"); + printf(" [-p devpath] Default: %s Current: %s\n", + CONFIG_EXAMPLES_PULSECOUNT_DEVPATH, + state->devpath != NULL ? state->devpath : "NONE"); + printf(" [-H high-ns] Default: %d ns Current: %" PRIu32 " ns\n", + CONFIG_EXAMPLES_PULSECOUNT_HIGH_NS, state->high_ns); + printf(" [-L low-ns] Default: %d ns Current: %" PRIu32 " ns\n", + CONFIG_EXAMPLES_PULSECOUNT_LOW_NS, state->low_ns); + printf(" [-n count] Default: %d Current: %" PRIu32 "\n", + CONFIG_EXAMPLES_PULSECOUNT_COUNT, state->count); + printf(" [-h] shows this message and exits\n"); +} + +static void parse_args(FAR struct pulsecount_state_s *state, int argc, + FAR char **argv) +{ + FAR char *str; + long value; + int index; + int nargs; + + for (index = 1; index < argc; ) + { + if (argv[index][0] != '-') + { + printf("Invalid options format: %s\n", argv[index]); + exit(1); + } + + switch (argv[index][1]) + { + case 'p': + nargs = arg_string(&argv[index], &str); + pulsecount_devpath(state, str); + index += nargs; + break; + + case 'H': + nargs = arg_decimal(&argv[index], &value); + if (value < 1) + { + printf("High time out of range: %ld\n", value); + exit(1); + } + + state->high_ns = (uint32_t)value; + index += nargs; + break; + + case 'L': + nargs = arg_decimal(&argv[index], &value); + if (value < 1) + { + printf("Low time out of range: %ld\n", value); + exit(1); + } + + state->low_ns = (uint32_t)value; + index += nargs; + break; + + case 'n': + nargs = arg_decimal(&argv[index], &value); + if (value < 1) + { + printf("Count out of range: %ld\n", value); + exit(1); + } + + state->count = (uint32_t)value; + index += nargs; + break; + + case 'h': + pulsecount_help(state); + exit(0); + + default: + printf("Unsupported option: %s\n", argv[index]); + pulsecount_help(state); + exit(1); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct pulsecount_state_s state; + struct pulsecount_info_s info; + int fd; + int ret; + + /* Defaults */ + + memset(&state, 0, sizeof(state)); + state.high_ns = CONFIG_EXAMPLES_PULSECOUNT_HIGH_NS; + state.low_ns = CONFIG_EXAMPLES_PULSECOUNT_LOW_NS; + state.count = CONFIG_EXAMPLES_PULSECOUNT_COUNT; + + /* Parse args */ + + parse_args(&state, argc, argv); + + if (state.devpath == NULL) + { + pulsecount_devpath(&state, CONFIG_EXAMPLES_PULSECOUNT_DEVPATH); + } + + fd = open(state.devpath, O_RDONLY); + if (fd < 0) + { + printf("pulsecount: open %s failed: %d\n", state.devpath, errno); + goto errout; + } + + memset(&info, 0, sizeof(info)); + info.high_ns = state.high_ns; + info.low_ns = state.low_ns; + info.count = state.count; + + printf("pulsecount: high: %" PRIu32 " ns low: %" PRIu32 + " ns count: %" PRIu32 "\n", + info.high_ns, info.low_ns, info.count); + + ret = ioctl(fd, PULSECOUNTIOC_SETCHARACTERISTICS, + (unsigned long)((uintptr_t)&info)); + if (ret < 0) + { + printf("pulsecount: ioctl(PULSECOUNTIOC_SETCHARACTERISTICS) " + "failed: %d\n", errno); + goto errout_with_dev; + } + + ret = ioctl(fd, PULSECOUNTIOC_START, 0); + if (ret < 0) + { + printf("pulsecount: ioctl(PULSECOUNTIOC_START) failed: %d\n", errno); + goto errout_with_dev; + } + + close(fd); + fflush(stdout); + return OK; + +errout_with_dev: + close(fd); +errout: + fflush(stdout); + return ERROR; +} diff --git a/examples/pwfb/pwfb_events.c b/examples/pwfb/pwfb_events.c index 142985ab7..bec7a845a 100644 --- a/examples/pwfb/pwfb_events.c +++ b/examples/pwfb/pwfb_events.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/pwfb/pwfb_main.c b/examples/pwfb/pwfb_main.c index 6a7bfc438..cd98a48e2 100644 --- a/examples/pwfb/pwfb_main.c +++ b/examples/pwfb/pwfb_main.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -57,6 +57,8 @@ #include "pwfb_internal.h" +#define ARROW1_CURSOR_IMAGE g_arrow1##Cursor + /**************************************************************************** * Private Data ****************************************************************************/ @@ -115,22 +117,22 @@ static bool pwfb_server_initialize(FAR struct pwfb_state_s *st) #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, st->hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, st->hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("pwfb_server_initialize: ERROR: " - "boardctl(BOARDIOC_VNC_START) failed: %d\n", - ret); + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("pwfb_server_initialize: ERROR: " + "boardctl(BOARDIOC_VNC_START) failed: %d\n", + ret); - nx_disconnect(st->hnx); - g_exitcode = NXEXIT_FBINITIALIZE; - return ERROR; - } + nx_disconnect(st->hnx); + g_exitcode = NXEXIT_FBINITIALIZE; + return ERROR; + } #endif } else @@ -168,10 +170,10 @@ static bool pwfb_listener_initialize(FAR struct pwfb_state_s *st) ret = pthread_create(&thread, &attr, pwfb_listener, st); if (ret != 0) { - printf("pwfb_listener_initialize: ERROR: " - "pthread_create failed: %d\n", - ret); - return false; + printf("pwfb_listener_initialize: ERROR: " + "pthread_create failed: %d\n", + ret); + return false; } /* Don't return until we are connected to the server */ @@ -340,8 +342,8 @@ static bool pwfb_putc(FAR struct pwfb_state_s *st, /* Render the glyph into the window */ src = glyph->bitmap; - ret = nxtk_bitmapwindow(wndo->hwnd, &bounds, &src, - fpos, (unsigned int)glyph->stride); + ret = nxtk_bitmapwindow(wndo->hwnd, &bounds, &src, + fpos, (unsigned int)glyph->stride); if (ret < 0) { printf("pwfb_putc: ERROR: " @@ -510,8 +512,8 @@ static bool pwfb_configure_cursor(FAR struct pwfb_state_s *st, st->cursor.state = PFWB_CURSOR_MOVING; st->cursor.countdown = CURSOR_MOVING_DELAY; st->cursor.blinktime = 0; - st->cursor.xmax = itob16(st->xres - g_arrow1Cursor.size.w - 1); - st->cursor.ymax = itob16(st->yres - g_arrow1Cursor.size.h - 1); + st->cursor.xmax = itob16(st->xres - ARROW1_CURSOR_IMAGE.size.w - 1); + st->cursor.ymax = itob16(st->yres - ARROW1_CURSOR_IMAGE.size.h - 1); st->cursor.xpos = itob16(pos->x); st->cursor.ypos = itob16(pos->y); st->cursor.deltax = dtob16(deltax); @@ -519,7 +521,7 @@ static bool pwfb_configure_cursor(FAR struct pwfb_state_s *st, /* Set the cursor image */ - ret = nxcursor_setimage(st->hnx, &g_arrow1Cursor); + ret = nxcursor_setimage(st->hnx, &ARROW1_CURSOR_IMAGE); if (ret < 0) { printf("pwfb_configure_cursor: ERROR: " @@ -651,7 +653,8 @@ int main(int argc, FAR char *argv[]) pos.x = wstate.xres / 8; pos.y = wstate.yres / 8; - if (!pwfb_configure_window(&wstate, 0, &size, &pos, g_wndomsg1, 4.200, 4.285)) + if (!pwfb_configure_window(&wstate, 0, &size, &pos, g_wndomsg1, + 4.200, 4.285)) { printf("pwfb_main: ERROR: " "pwfb_configure_window failed for window 1\n"); @@ -681,7 +684,8 @@ int main(int argc, FAR char *argv[]) pos.x = wstate.xres / 4; pos.y = wstate.yres / 4; - if (!pwfb_configure_window(&wstate, 1, &size, &pos, g_wndomsg2, -3.317, 5.0)) + if (!pwfb_configure_window(&wstate, 1, &size, &pos, g_wndomsg2, + -3.317, 5.0)) { printf("pwfb_main: ERROR: " "pwfb_configure_window failed for window 2\n"); @@ -711,7 +715,8 @@ int main(int argc, FAR char *argv[]) pos.x = (3 * wstate.xres) / 8; pos.y = (3 * wstate.yres) / 8; - if (!pwfb_configure_window(&wstate, 2, &size, &pos, g_wndomsg3, 4.600, -3.852)) + if (!pwfb_configure_window(&wstate, 2, &size, &pos, g_wndomsg3, + 4.600, -3.852)) { printf("pwfb_main: ERROR: " "pwfb_configure_window failed for window 2\n"); @@ -797,6 +802,7 @@ errout_with_hwnd1: #endif errout_with_fontcache: + /* Release the font cache */ nxf_cache_disconnect(wstate.wndo[0].fcache); @@ -804,6 +810,7 @@ errout_with_fontcache: nxf_cache_disconnect(wstate.wndo[2].fcache); errout_with_nx: + /* Disconnect from the server */ printf("pwfb_main: Disconnect from the server\n"); diff --git a/examples/pwlines/pwlines_events.c b/examples/pwlines/pwlines_events.c index 73a2b80a6..3dcd0504c 100644 --- a/examples/pwlines/pwlines_events.c +++ b/examples/pwlines/pwlines_events.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/pwlines/pwlines_main.c b/examples/pwlines/pwlines_main.c index dc776d744..53a2b3a53 100644 --- a/examples/pwlines/pwlines_main.c +++ b/examples/pwlines/pwlines_main.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include @@ -94,21 +94,21 @@ static bool pwlines_server_initialize(FAR struct pwlines_state_s *st) #ifdef CONFIG_VNCSERVER /* Setup the VNC server to support keyboard/mouse inputs */ - struct boardioc_vncstart_s vnc = - { - 0, st->hnx - }; + struct boardioc_vncstart_s vnc = + { + 0, st->hnx + }; - ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); - if (ret < 0) - { - printf("pwlines_server_initialize: ERROR: " - "boardctl(BOARDIOC_VNC_START) failed: %d\n", - ret); + ret = boardctl(BOARDIOC_VNC_START, (uintptr_t)&vnc); + if (ret < 0) + { + printf("pwlines_server_initialize: ERROR: " + "boardctl(BOARDIOC_VNC_START) failed: %d\n", + ret); - nx_disconnect(st->hnx); - return false; - } + nx_disconnect(st->hnx); + return false; + } #endif } else @@ -141,15 +141,16 @@ static bool pwlines_listener_initialize(FAR struct pwlines_state_s *st) pthread_attr_init(&attr); param.sched_priority = CONFIG_EXAMPLES_PWLINES_LISTENER_PRIO; pthread_attr_setschedparam(&attr, ¶m); - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_PWLINES_LISTENER_STACKSIZE); + pthread_attr_setstacksize(&attr, + CONFIG_EXAMPLES_PWLINES_LISTENER_STACKSIZE); ret = pthread_create(&thread, &attr, pwlines_listener, st); if (ret != 0) { - printf("pwlines_listener_initialize: ERROR: " - "pthread_create failed: %d\n", - ret); - return false; + printf("pwlines_listener_initialize: ERROR: " + "pthread_create failed: %d\n", + ret); + return false; } /* Don't return until we are connected to the server */ @@ -193,7 +194,8 @@ static bool pwlines_state_initialize(FAR struct pwlines_state_s *st) * Name: pwlines_configure_window ****************************************************************************/ -static bool pwlines_configure_window(FAR struct pwlines_state_s *st, int wndx, +static bool pwlines_configure_window(FAR struct pwlines_state_s *st, + int wndx, FAR struct nxgl_size_s *size, FAR struct nxgl_point_s *pos, double deltax, double deltay) @@ -353,8 +355,9 @@ int main(int argc, FAR char *argv[]) printf("pwlines_main: Open window 1\n"); - wstate.wndo[0].hwnd = nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, - &g_pwlines_wncb, (FAR void *)&wstate); + wstate.wndo[0].hwnd = + nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, + &g_pwlines_wncb, (FAR void *)&wstate); if (wstate.wndo[0].hwnd == NULL) { printf("pwlines_main: ERROR: " @@ -386,7 +389,8 @@ int main(int argc, FAR char *argv[]) pos.x = wstate.xres / 8; pos.y = wstate.yres / 8; - if (!pwlines_configure_window(&wstate, 0, &size, &pos, 4.200, 4.285)) + if (!pwlines_configure_window(&wstate, 0, &size, &pos, + 4.200, 4.285)) { printf("pwlines_main: ERROR: " "pwlines_configure_window failed for window 1\n"); @@ -397,8 +401,9 @@ int main(int argc, FAR char *argv[]) printf("pwlines_main: Open window 2\n"); - wstate.wndo[1].hwnd = nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, - &g_pwlines_wncb, (FAR void *)&wstate); + wstate.wndo[1].hwnd = + nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, + &g_pwlines_wncb, (FAR void *)&wstate); if (wstate.wndo[1].hwnd == NULL) { printf("pwlines_main: ERROR: " @@ -414,7 +419,8 @@ int main(int argc, FAR char *argv[]) pos.x = wstate.xres / 4; pos.y = wstate.yres / 4; - if (!pwlines_configure_window(&wstate, 1, &size, &pos, -3.317, 5.0)) + if (!pwlines_configure_window(&wstate, 1, &size, &pos, + -3.317, 5.0)) { printf("pwlines_main: ERROR: " "pwlines_configure_window failed for window 2\n"); @@ -425,8 +431,9 @@ int main(int argc, FAR char *argv[]) printf("pwlines_main: Open window 3\n"); - wstate.wndo[2].hwnd = nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, - &g_pwlines_wncb, (FAR void *)&wstate); + wstate.wndo[2].hwnd = + nxtk_openwindow(wstate.hnx, NXBE_WINDOW_RAMBACKED, + &g_pwlines_wncb, (FAR void *)&wstate); if (wstate.wndo[2].hwnd == NULL) { printf("pwlines_main: ERROR: " @@ -442,7 +449,8 @@ int main(int argc, FAR char *argv[]) pos.x = (3 * wstate.xres) / 8; pos.y = (3 * wstate.yres) / 8; - if (!pwlines_configure_window(&wstate, 2, &size, &pos, 4.600, -3.852)) + if (!pwlines_configure_window(&wstate, 2, &size, &pos, + 4.600, -3.852)) { printf("pwlines_main: ERROR: " "pwlines_configure_window failed for window 2\n"); @@ -512,6 +520,7 @@ errout_with_hwnd1: } errout_with_nx: + /* Disconnect from the server */ printf("pwlines_main: Disconnect from the server\n"); diff --git a/examples/pwlines/pwlines_update.c b/examples/pwlines/pwlines_update.c index 2dbe4e39e..37d41456e 100644 --- a/examples/pwlines/pwlines_update.c +++ b/examples/pwlines/pwlines_update.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/pwm/Kconfig b/examples/pwm/Kconfig index 9378003f2..facd1b6dd 100644 --- a/examples/pwm/Kconfig +++ b/examples/pwm/Kconfig @@ -28,11 +28,7 @@ config EXAMPLES_PWM_DURATION int "Default PWM duration" default 5 ---help--- - The default PWM pulse train duration in seconds. Used only if the current - pulse count is zero (pulse count is only supported if PWM_PULSECOUNT - is not defined). Default: 5 seconds - -if PWM_MULTICHAN + The default PWM pulse train duration in seconds. Default: 5 seconds config EXAMPLES_PWM_DUTYPCT1 int "First PWM duty percentage" @@ -139,25 +135,3 @@ config EXAMPLES_PWM_CHANNEL6 endif endif - -if !PWM_MULTICHAN - -config EXAMPLES_PWM_DUTYPCT - int "Default PWM duty percentage" - default 50 - range 1 99 - ---help--- - The default PWM duty as a percentage. Default: 50% - -config EXAMPLES_PWM_PULSECOUNT - int "Default pulse count" - default 0 - depends on PWM_PULSECOUNT - ---help--- - The initial PWM pulse count. This option is only available if - PWM_PULSECOUNT is defined. Default: 0 (i.e., use the duration, not - the count). - -endif - -endif diff --git a/examples/pwm/pwm.h b/examples/pwm/pwm.h index e0447b9b3..389241626 100644 --- a/examples/pwm/pwm.h +++ b/examples/pwm/pwm.h @@ -40,15 +40,13 @@ * Default: /dev/pwm0 * CONFIG_EXAMPLES_PWM_FREQUENCY - The initial PWM frequency. * Default: 100 Hz - * CONFIG_EXAMPLES_PWM_DUTYPCT - The initial PWM duty as a percentage. - * Default: 50% + * CONFIG_EXAMPLES_PWM_DUTYPCTn - The initial PWM duty as a percentage for + * channel n. Default: 50% + * CONFIG_EXAMPLES_PWM_CHANNELn - The PWM channel number for channel n. + * Default: n * CONFIG_EXAMPLES_PWM_DURATION - The initial PWM pulse train duration - * in seconds. Used only if the current pulse count is zero - * (pulse count is only supported if CONFIG_PWM_PULSECOUNT is defined). + * in seconds. * Default: 5 seconds - * CONFIG_EXAMPLES_PWM_PULSECOUNT - The initial PWM pulse count. - * This option is only available if CONFIG_PWM_PULSECOUNT is defined. - * Default: 0 (i.e., use the duration, not the count). */ #ifndef CONFIG_PWM @@ -63,18 +61,10 @@ # define CONFIG_EXAMPLES_PWM_FREQUENCY 100 #endif -#ifndef CONFIG_EXAMPLES_PWM_DUTYPCT -# define CONFIG_EXAMPLES_PWM_DUTYPCT 50 -#endif - #ifndef CONFIG_EXAMPLES_PWM_DURATION # define CONFIG_EXAMPLES_PWM_DURATION 5 #endif -#ifndef CONFIG_EXAMPLES_PWM_PULSECOUNT -# define CONFIG_EXAMPLES_PWM_PULSECOUNT 0 -#endif - /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/examples/pwm/pwm_main.c b/examples/pwm/pwm_main.c index 7a1a83c16..e373a5bb0 100644 --- a/examples/pwm/pwm_main.c +++ b/examples/pwm/pwm_main.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -48,46 +48,44 @@ /* Configuration ************************************************************/ -#ifdef CONFIG_PWM_MULTICHAN -# if CONFIG_PWM_NCHANNELS > 1 -# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL2 -# error "Channel numbers must be unique" -# endif +#if CONFIG_PWM_NCHANNELS > 1 +# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL2 +# error "Channel numbers must be unique" # endif -# if CONFIG_PWM_NCHANNELS > 2 -# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL3 || \ +#endif +#if CONFIG_PWM_NCHANNELS > 2 +# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL3 || \ CONFIG_EXAMPLES_PWM_CHANNEL2 == CONFIG_EXAMPLES_PWM_CHANNEL3 -# error "Channel numbers must be unique" -# endif +# error "Channel numbers must be unique" # endif -# if CONFIG_PWM_NCHANNELS > 3 -# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL4 || \ +#endif +#if CONFIG_PWM_NCHANNELS > 3 +# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL4 || \ CONFIG_EXAMPLES_PWM_CHANNEL2 == CONFIG_EXAMPLES_PWM_CHANNEL4 || \ CONFIG_EXAMPLES_PWM_CHANNEL3 == CONFIG_EXAMPLES_PWM_CHANNEL4 -# error "Channel numbers must be unique" -# endif +# error "Channel numbers must be unique" # endif -# if CONFIG_PWM_NCHANNELS > 4 -# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL5 || \ +#endif +#if CONFIG_PWM_NCHANNELS > 4 +# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL5 || \ CONFIG_EXAMPLES_PWM_CHANNEL2 == CONFIG_EXAMPLES_PWM_CHANNEL5 || \ CONFIG_EXAMPLES_PWM_CHANNEL3 == CONFIG_EXAMPLES_PWM_CHANNEL5 || \ CONFIG_EXAMPLES_PWM_CHANNEL4 == CONFIG_EXAMPLES_PWM_CHANNEL5 -# error "Channel numbers must be unique" -# endif +# error "Channel numbers must be unique" # endif -# if CONFIG_PWM_NCHANNELS > 5 -# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL6 || \ +#endif +#if CONFIG_PWM_NCHANNELS > 5 +# if CONFIG_EXAMPLES_PWM_CHANNEL1 == CONFIG_EXAMPLES_PWM_CHANNEL6 || \ CONFIG_EXAMPLES_PWM_CHANNEL2 == CONFIG_EXAMPLES_PWM_CHANNEL6 || \ CONFIG_EXAMPLES_PWM_CHANNEL3 == CONFIG_EXAMPLES_PWM_CHANNEL6 || \ CONFIG_EXAMPLES_PWM_CHANNEL4 == CONFIG_EXAMPLES_PWM_CHANNEL6 || \ CONFIG_EXAMPLES_PWM_CHANNEL5 == CONFIG_EXAMPLES_PWM_CHANNEL6 -# error "Channel numbers must be unique" -# endif -# endif -# if CONFIG_PWM_NCHANNELS > 6 -# error "Too many PWM channels" +# error "Channel numbers must be unique" # endif #endif +#if CONFIG_PWM_NCHANNELS > 6 +# error "Too many PWM channels" +#endif /**************************************************************************** * Private Types @@ -97,16 +95,9 @@ struct pwm_state_s { bool initialized; FAR char *devpath; -#ifdef CONFIG_PWM_MULTICHAN - uint8_t channels[CONFIG_PWM_NCHANNELS]; + int8_t channels[CONFIG_PWM_NCHANNELS]; uint8_t duties[CONFIG_PWM_NCHANNELS]; -#else - uint8_t duty; -#endif uint32_t freq; -#ifdef CONFIG_PWM_PULSECOUNT - uint32_t count; -#endif int duration; }; @@ -152,54 +143,52 @@ static void pwm_devpath(FAR struct pwm_state_s *pwm, FAR const char *devpath) static void pwm_help(FAR struct pwm_state_s *pwm) { -#ifdef CONFIG_PWM_MULTICHAN - uint8_t channels[CONFIG_PWM_NCHANNELS] = - { - CONFIG_EXAMPLES_PWM_CHANNEL1, + int8_t channels[CONFIG_PWM_NCHANNELS] = + { + CONFIG_EXAMPLES_PWM_CHANNEL1, #if CONFIG_PWM_NCHANNELS > 1 - CONFIG_EXAMPLES_PWM_CHANNEL2, + CONFIG_EXAMPLES_PWM_CHANNEL2, #endif #if CONFIG_PWM_NCHANNELS > 2 - CONFIG_EXAMPLES_PWM_CHANNEL3, + CONFIG_EXAMPLES_PWM_CHANNEL3, #endif #if CONFIG_PWM_NCHANNELS > 3 - CONFIG_EXAMPLES_PWM_CHANNEL4, + CONFIG_EXAMPLES_PWM_CHANNEL4, #endif #if CONFIG_PWM_NCHANNELS > 4 - CONFIG_EXAMPLES_PWM_CHANNEL5, + CONFIG_EXAMPLES_PWM_CHANNEL5, #endif #if CONFIG_PWM_NCHANNELS > 5 - CONFIG_EXAMPLES_PWM_CHANNEL6, + CONFIG_EXAMPLES_PWM_CHANNEL6, #endif - }; + }; uint8_t duties[CONFIG_PWM_NCHANNELS] = - { - CONFIG_EXAMPLES_PWM_DUTYPCT1, + { + CONFIG_EXAMPLES_PWM_DUTYPCT1, #if CONFIG_PWM_NCHANNELS > 1 - CONFIG_EXAMPLES_PWM_DUTYPCT2, + CONFIG_EXAMPLES_PWM_DUTYPCT2, #endif #if CONFIG_PWM_NCHANNELS > 2 - CONFIG_EXAMPLES_PWM_DUTYPCT3, + CONFIG_EXAMPLES_PWM_DUTYPCT3, #endif #if CONFIG_PWM_NCHANNELS > 3 - CONFIG_EXAMPLES_PWM_DUTYPCT4, + CONFIG_EXAMPLES_PWM_DUTYPCT4, #endif #if CONFIG_PWM_NCHANNELS > 4 - CONFIG_EXAMPLES_PWM_DUTYPCT5, + CONFIG_EXAMPLES_PWM_DUTYPCT5, #endif #if CONFIG_PWM_NCHANNELS > 5 - CONFIG_EXAMPLES_PWM_DUTYPCT6, + CONFIG_EXAMPLES_PWM_DUTYPCT6, #endif - }; + }; int i; -#endif printf("Usage: pwm [OPTIONS]\n"); printf("\nArguments are \"sticky\". " "For example, once the PWM frequency is\n"); - printf("specified, that frequency will be re-used until it is changed.\n"); + printf("specified, that frequency will be reused until it is changed.\n"); printf("\n\"sticky\" OPTIONS include:\n"); printf(" [-p devpath] selects the PWM device. " "Default: %s Current: %s\n", @@ -207,7 +196,6 @@ static void pwm_help(FAR struct pwm_state_s *pwm) printf(" [-f frequency] selects the pulse frequency. " "Default: %d Hz Current: %" PRIu32 " Hz\n", CONFIG_EXAMPLES_PWM_FREQUENCY, pwm->freq); -#ifdef CONFIG_PWM_MULTICHAN printf(" [[-c channel1] [[-c channel2] ...]] " "selects the channel number for each channel. "); printf("Default:"); @@ -239,16 +227,6 @@ static void pwm_help(FAR struct pwm_state_s *pwm) } printf("\n"); -#else - printf(" [-d duty] selects the pulse duty as a percentage. " - "Default: %d %% Current: %d %%\n", - CONFIG_EXAMPLES_PWM_DUTYPCT, pwm->duty); -#endif -#ifdef CONFIG_PWM_PULSECOUNT - printf(" [-n count] selects the pulse count. " - "Default: %d Current: %" PRIx32 "\n", - CONFIG_EXAMPLES_PWM_PULSECOUNT, pwm->count); -#endif printf(" [-t duration] is the duration of the pulse train in seconds. " "Default: %d Current: %d\n", CONFIG_EXAMPLES_PWM_DURATION, pwm->duration); @@ -301,10 +279,10 @@ static void parse_args(FAR struct pwm_state_s *pwm, int argc, long value; int index; int nargs; -#ifdef CONFIG_PWM_MULTICHAN +#if CONFIG_PWM_NCHANNELS > 1 int nchannels = 0; - int nduties = 0; #endif + int nduties = 0; for (index = 1; index < argc; ) { @@ -329,7 +307,7 @@ static void parse_args(FAR struct pwm_state_s *pwm, int argc, index += nargs; break; -#ifdef CONFIG_PWM_MULTICHAN +#if CONFIG_PWM_NCHANNELS > 1 case 'c': nargs = arg_decimal(&argv[index], &value); if (value < -1 || value > CONFIG_PWM_NCHANNELS) @@ -361,7 +339,6 @@ static void parse_args(FAR struct pwm_state_s *pwm, int argc, exit(1); } -#ifdef CONFIG_PWM_MULTICHAN if (nduties < CONFIG_PWM_NCHANNELS) { nduties++; @@ -373,26 +350,9 @@ static void parse_args(FAR struct pwm_state_s *pwm, int argc, } pwm->duties[nduties - 1] = (uint8_t)value; -#else - pwm->duty = (uint8_t)value; -#endif index += nargs; break; -#ifdef CONFIG_PWM_PULSECOUNT - case 'n': - nargs = arg_decimal(&argv[index], &value); - if (value < 0) - { - printf("Count must be non-negative: %ld\n", value); - exit(1); - } - - pwm->count = (uint32_t)value; - index += nargs; - break; -#endif - case 'p': nargs = arg_string(&argv[index], &str); pwm_devpath(pwm, str); @@ -436,16 +396,13 @@ int main(int argc, FAR char *argv[]) struct pwm_info_s info; int fd; int ret; -#ifdef CONFIG_PWM_MULTICHAN int i; int j; -#endif /* Initialize the state data */ if (!g_pwmstate.initialized) { -#ifdef CONFIG_PWM_MULTICHAN g_pwmstate.channels[0] = CONFIG_EXAMPLES_PWM_CHANNEL1; g_pwmstate.duties[0] = CONFIG_EXAMPLES_PWM_DUTYPCT1; #if CONFIG_PWM_NCHANNELS > 1 @@ -467,15 +424,9 @@ int main(int argc, FAR char *argv[]) #if CONFIG_PWM_NCHANNELS > 5 g_pwmstate.channels[5] = CONFIG_EXAMPLES_PWM_CHANNEL6; g_pwmstate.duties[5] = CONFIG_EXAMPLES_PWM_DUTYPCT6; -#endif -#else - g_pwmstate.duty = CONFIG_EXAMPLES_PWM_DUTYPCT; #endif g_pwmstate.freq = CONFIG_EXAMPLES_PWM_FREQUENCY; g_pwmstate.duration = CONFIG_EXAMPLES_PWM_DURATION; -#ifdef CONFIG_PWM_PULSECOUNT - g_pwmstate.count = CONFIG_EXAMPLES_PWM_PULSECOUNT; -#endif g_pwmstate.initialized = true; } @@ -483,19 +434,23 @@ int main(int argc, FAR char *argv[]) parse_args(&g_pwmstate, argc, argv); -#ifdef CONFIG_PWM_MULTICHAN - for (i = 0; i < CONFIG_PWM_MULTICHAN; i++) + for (i = 0; i < CONFIG_PWM_NCHANNELS; i++) { - for (j = i + 1; j < CONFIG_PWM_MULTICHAN; j++) + if (g_pwmstate.channels[i] < 1) { - if (g_pwmstate.channels[j] == g_pwmstate.channels[i]) + continue; + } + + for (j = i + 1; j < CONFIG_PWM_NCHANNELS; j++) + { + if (g_pwmstate.channels[j] > 0 && + g_pwmstate.channels[j] == g_pwmstate.channels[i]) { printf("pwm_main: channel numbers must be unique\n"); goto errout; } } } -#endif /* Has a device been assigned? */ @@ -517,8 +472,8 @@ int main(int argc, FAR char *argv[]) /* Configure the characteristics of the pulse train */ + memset(&info, 0, sizeof(info)); info.frequency = g_pwmstate.freq; -#ifdef CONFIG_PWM_MULTICHAN printf("pwm_main: starting output with frequency: %" PRIu32, info.frequency); @@ -533,25 +488,6 @@ int main(int argc, FAR char *argv[]) printf("\n"); -#else - info.duty = g_pwmstate.duty ? \ - b16divi(uitoub16(g_pwmstate.duty) - 1, 100) : 0; -# ifdef CONFIG_PWM_PULSECOUNT - info.count = g_pwmstate.count; - - printf("pwm_main: starting output " - "with frequency: %" PRIu32 " duty: %08" PRIx32 - " count: %" PRIx32 "\n", - info.frequency, (uint32_t)info.duty, info.count); - -# else - printf("pwm_main: starting output " - "with frequency: %" PRIu32 " duty: %08" PRIx32 "\n", - info.frequency, (uint32_t)info.duty); - -# endif -#endif - ret = ioctl(fd, PWMIOC_SETCHARACTERISTICS, (unsigned long)((uintptr_t)&info)); if (ret < 0) @@ -561,9 +497,7 @@ int main(int argc, FAR char *argv[]) goto errout_with_dev; } - /* Then start the pulse train. Since the driver was opened in blocking - * mode, this call will block if the count value is greater than zero. - */ + /* Then start the pulse train. */ ret = ioctl(fd, PWMIOC_START, 0); if (ret < 0) @@ -572,28 +506,19 @@ int main(int argc, FAR char *argv[]) goto errout_with_dev; } - /* It a non-zero count was not specified, then wait for the selected - * duration, then stop the PWM output. - */ + /* Wait for the specified duration, then stop the PWM output. */ -#ifdef CONFIG_PWM_PULSECOUNT - if (info.count == 0) -#endif + sleep(g_pwmstate.duration); + + /* Then stop the pulse train */ + + printf("pwm_main: stopping output\n"); + + ret = ioctl(fd, PWMIOC_STOP, 0); + if (ret < 0) { - /* Wait for the specified duration */ - - sleep(g_pwmstate.duration); - - /* Then stop the pulse train */ - - printf("pwm_main: stopping output\n"); - - ret = ioctl(fd, PWMIOC_STOP, 0); - if (ret < 0) - { - printf("pwm_main: ioctl(PWMIOC_STOP) failed: %d\n", errno); - goto errout_with_dev; - } + printf("pwm_main: ioctl(PWMIOC_STOP) failed: %d\n", errno); + goto errout_with_dev; } close(fd); diff --git a/examples/qencoder/qe_main.c b/examples/qencoder/qe_main.c index 2614fe7fb..23e529802 100644 --- a/examples/qencoder/qe_main.c +++ b/examples/qencoder/qe_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include @@ -307,27 +307,34 @@ int main(int argc, FAR char *argv[]) if (g_qeexample.use_qeindex) { - ret = ioctl(fd, QEIOC_GETINDEX, (unsigned long)((uintptr_t)&index)); + ret = ioctl(fd, QEIOC_GETINDEX, + (unsigned long)((uintptr_t)&index)); if (ret < 0) { - printf("qe_main: ioctl(QEIOC_GETINDEX) failed: %d\n", errno); - printf("Your MCU probably does not support this ioctl call.\n"); + printf("qe_main: ioctl(QEIOC_GETINDEX) failed: %d\n", + errno); + printf("Your MCU probably does not support this ioctl " + "call.\n"); printf("Consider using this example without the -i option.\n"); exitval = EXIT_FAILURE; goto errout_with_dev; } - /* GETINDEX succesful */ + /* GETINDEX successful */ else { - printf("qe_main: %3d. pos: %" PRIi32 ", last index: %" PRIi32 ", hit indexes: %" \ - PRIi16 "\n", nloops + 1, index.qenc_pos, index.indx_pos, index.indx_cnt); + printf("qe_main: %3d. pos: %" PRIi32 ", " + "last index: %" PRIi32 ", " + "hit indexes: %" PRIi16 "\n", + nloops + 1, index.qenc_pos, index.indx_pos, + index.indx_cnt); } } else { - ret = ioctl(fd, QEIOC_POSITION, (unsigned long)((uintptr_t)&position)); + ret = ioctl(fd, QEIOC_POSITION, + (unsigned long)((uintptr_t)&position)); if (ret < 0) { printf("qe_main: ioctl(QEIOC_POSITION) failed: %d\n", errno); diff --git a/examples/random/random_main.c b/examples/random/random_main.c index 2b1be2827..3a35b4e89 100644 --- a/examples/random/random_main.c +++ b/examples/random/random_main.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include /**************************************************************************** diff --git a/examples/relays/relays_main.c b/examples/relays/relays_main.c index f5ab6fb7b..83e108419 100644 --- a/examples/relays/relays_main.c +++ b/examples/relays/relays_main.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #ifdef CONFIG_ARCH_RELAYS diff --git a/examples/rmtchar/Kconfig b/examples/rmtchar/Kconfig deleted file mode 100644 index e88da638a..000000000 --- a/examples/rmtchar/Kconfig +++ /dev/null @@ -1,68 +0,0 @@ -# -# For a description of the syntax of this configuration file, -# see the file kconfig-language.txt in the NuttX tools repository. -# - -config EXAMPLES_RMTCHAR - tristate "RMT character driver test" - default n - depends on RMT - ---help--- - Enable the RMT character driver test - -if EXAMPLES_RMTCHAR - -config EXAMPLES_RMTCHAR_TX - bool "Use RMT transmitter" - default n - ---help--- - This should be set if the RMT device supports a transmitter. - -if EXAMPLES_RMTCHAR_TX - -config EXAMPLES_RMTCHAR_TX_DEVPATH - string "RMT transmitter character device path" - default "/dev/rmt0" - ---help--- - The default path to the RMT transmitter character device. - Default: /dev/rmt0 - -config EXAMPLES_RMTCHAR_TXSTACKSIZE - int "Transmitter thread stack size" - default DEFAULT_TASK_STACKSIZE - ---help--- - This is the stack size to use when starting the transmitter thread. - -endif # EXAMPLES_RMTCHAR_TX - -config EXAMPLES_RMTCHAR_RX - bool "Use RMT receiver" - default n - ---help--- - This should be set if the RMT device supports a receiver. - -if EXAMPLES_RMTCHAR_RX - -config EXAMPLES_RMTCHAR_RX_DEVPATH - string "RMT receiver character device path" - default "/dev/rmt1" - ---help--- - The default path to the RMT receiver character device. - Default: /dev/rmt1 - -config EXAMPLES_RMTCHAR_RXSTACKSIZE - int "Receiver thread stack size" - default DEFAULT_TASK_STACKSIZE - ---help--- - This is the stack size to use when starting the receiver thread. - -endif # EXAMPLES_RMTCHAR_RX - -config EXAMPLES_RMTCHAR_ITEMS - int "Number of words to transmit or receive" - default 64 - ---help--- - This is the total amount of words to be transmitted or - received by the RMT RX device. - -endif diff --git a/examples/rmtchar/rmtchar_main.c b/examples/rmtchar/rmtchar_main.c deleted file mode 100644 index dcf2c54f2..000000000 --- a/examples/rmtchar/rmtchar_main.c +++ /dev/null @@ -1,362 +0,0 @@ -/**************************************************************************** - * apps/examples/rmtchar/rmtchar_main.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "rmtchar.h" - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#if !defined(CONFIG_EXAMPLES_RMTCHAR_RX) && \ - !defined(CONFIG_EXAMPLES_RMTCHAR_TX) -# error "At least one of CONFIG_EXAMPLES_RMTCHAR_RX or \ - CONFIG_EXAMPLES_RMTCHAR_TX must be defined" -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static struct rmtchar_state_s g_rmtchar; - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: rmtchar_devpath - ****************************************************************************/ - -static void rmtchar_devpath(FAR struct rmtchar_state_s *rmtchar, - FAR const char *devpath, - bool is_tx) -{ - /* Get rid of any old device path */ - - if (is_tx) - { - if (rmtchar->txdevpath) - { - free(rmtchar->txdevpath); - } - - /* Then set-up the new device path by copying the string */ - - rmtchar->txdevpath = strdup(devpath); - } - else - { - if (rmtchar->rxdevpath) - { - free(rmtchar->rxdevpath); - } - - /* Then set-up the new device path by copying the string */ - - rmtchar->rxdevpath = strdup(devpath); - } -} - -/**************************************************************************** - * Name: rmtchar_help - ****************************************************************************/ - -static void rmtchar_help(FAR struct rmtchar_state_s *rmtchar) -{ - printf("Usage: rmtchar [OPTIONS]\n"); - printf("\nArguments are \"sticky\".\n"); - printf("For example, once the RMT character device is\n"); - printf("specified, that device will be re-used until it is changed.\n"); - printf("\n\"sticky\" OPTIONS include:\n"); - printf(" [-i items] selects the number of words (items) to be transmitted" - " or received by the RMT character device. " - "Default: %d Current: %d\n", - CONFIG_EXAMPLES_RMTCHAR_ITEMS, - rmtchar->rmtchar_items ? rmtchar->rmtchar_items : - CONFIG_EXAMPLES_RMTCHAR_ITEMS); -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - printf(" [-t devpath] selects the RMT transmitter character device path. " - "Default: %s Current: %s\n", - CONFIG_EXAMPLES_RMTCHAR_TX_DEVPATH, - rmtchar->txdevpath ? rmtchar->txdevpath : "NONE"); -#endif -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - printf(" [-r devpath] selects the RMT receiver character device path. " - "Default: %s Current: %s\n", - CONFIG_EXAMPLES_RMTCHAR_RX_DEVPATH, - rmtchar->rxdevpath ? rmtchar->rxdevpath : "NONE"); -#endif - printf(" [-h] shows this message and exits\n"); -} - -/**************************************************************************** - * Name: arg_string - ****************************************************************************/ - -static int arg_string(FAR char **arg, FAR char **value) -{ - FAR char *ptr = *arg; - - if (ptr[2] == '\0') - { - *value = arg[1]; - return 2; - } - else - { - *value = &ptr[2]; - return 1; - } -} - -/**************************************************************************** - * Name: arg_decimal - ****************************************************************************/ - -static int arg_decimal(FAR char **arg, FAR int *value) -{ - FAR char *string; - int ret; - - ret = arg_string(arg, &string); - *value = strtol(string, NULL, 10); - return ret; -} - -/**************************************************************************** - * Name: parse_args - ****************************************************************************/ - -static void parse_args(FAR struct rmtchar_state_s *rmtchar, - int argc, - FAR char **argv) -{ - FAR char *ptr; - FAR char *str; - int value; - int index; - int nargs; - - for (index = 1; index < argc; ) - { - ptr = argv[index]; - if (ptr[0] != '-') - { - printf("Invalid options format: %s\n", ptr); - exit(0); - } - - switch (ptr[1]) - { - case 'i': - nargs = arg_decimal(&argv[index], &value); - if (value < 0) - { - printf("Count must be non-negative: %d\n", value); - exit(1); - } - - rmtchar->rmtchar_items = value; - index += nargs; - break; -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - case 't': - nargs = arg_string(&argv[index], &str); - rmtchar_devpath(rmtchar, str, true); - index += nargs; - break; -#endif - -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - case 'r': - nargs = arg_string(&argv[index], &str); - rmtchar_devpath(rmtchar, str, false); - index += nargs; - break; -#endif - case 'h': - rmtchar_help(rmtchar); - exit(0); - - default: - printf("Unsupported option: %s\n", ptr); - rmtchar_help(rmtchar); - exit(1); - } - } -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: rmtchar_main - ****************************************************************************/ - -int main(int argc, FAR char *argv[]) -{ - pthread_attr_t attr; - pthread_addr_t result; -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - pthread_t transmitter; -#endif -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - pthread_t receiver; -#endif -#if defined(CONFIG_EXAMPLES_RMTCHAR_RX) && defined(CONFIG_EXAMPLES_RMTCHAR_TX) - struct sched_param param; -#endif - int ret; - - UNUSED(ret); - - /* Check if we have initialized */ - - if (!g_rmtchar.initialized) - { - /* Set the default values */ - - g_rmtchar.rmtchar_items = CONFIG_EXAMPLES_RMTCHAR_ITEMS; - -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - rmtchar_devpath(&g_rmtchar, CONFIG_EXAMPLES_RMTCHAR_TX_DEVPATH, true); -#endif -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - rmtchar_devpath(&g_rmtchar, CONFIG_EXAMPLES_RMTCHAR_RX_DEVPATH, false); -#endif - - g_rmtchar.initialized = true; - } - - /* Parse the command line */ - - parse_args(&g_rmtchar, argc, argv); - -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - /* Start the receiver thread */ - - printf("rmtchar_main: Start receiver thread\n"); - pthread_attr_init(&attr); - -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - /* Bump the receiver priority from the default so that it will be above - * the priority of transmitter. This is important if a loopback test is - * being performed. - */ - - pthread_attr_getschedparam(&attr, ¶m); - param.sched_priority++; - pthread_attr_setschedparam(&attr, ¶m); -#endif - - /* Set the receiver stack size */ - - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_RMTCHAR_RXSTACKSIZE); - - /* Start the receiver */ - - ret = pthread_create(&receiver, &attr, rmtchar_receiver, &g_rmtchar); - if (ret != OK) - { - printf("rmtchar_main: ERROR: failed to Start receiver thread: %d\n", - ret); - return EXIT_FAILURE; - } - - pthread_setname_np(receiver, "receiver"); -#endif - -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - /* Start the transmitter thread */ - - printf("rmtchar_main: Start transmitter thread\n"); - pthread_attr_init(&attr); - - /* Set the transmitter stack size */ - - pthread_attr_setstacksize(&attr, CONFIG_EXAMPLES_RMTCHAR_TXSTACKSIZE); - - /* Start the transmitter */ - - ret = pthread_create(&transmitter, &attr, rmtchar_transmitter, &g_rmtchar); - if (ret != OK) - { - printf("rmtchar_main: ERROR: failed to Start transmitter thread: %d\n", - ret); -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - printf("rmtchar_main: Waiting for the receiver thread\n"); - pthread_join(receiver, &result); -#endif - return EXIT_FAILURE; - } - - pthread_setname_np(transmitter, "transmitter"); -#endif - -#ifdef CONFIG_EXAMPLES_RMTCHAR_TX - printf("rmtchar_main: Waiting for the transmitter thread\n"); - ret = pthread_join(transmitter, &result); - if (ret != OK) - { - printf("rmtchar_main: ERROR: pthread_join failed: %d\n", ret); - } -#endif - -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX - printf("rmtchar_main: Waiting for the receiver thread\n"); - ret = pthread_join(receiver, &result); - if (ret != OK) - { - printf("rmtchar_main: ERROR: pthread_join failed: %d\n", ret); - } -#endif - - return EXIT_SUCCESS; -} diff --git a/examples/rng90/CMakeLists.txt b/examples/rng90/CMakeLists.txt new file mode 100644 index 000000000..65cda8f30 --- /dev/null +++ b/examples/rng90/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/rng90/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_RNG90) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_RNG90_PROGNAME} + PRIORITY + ${CONFIG_EXAMPLES_RNG90_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_RNG90_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_RNG90} + SRCS + rng90_main.c) +endif() diff --git a/examples/rng90/Kconfig b/examples/rng90/Kconfig new file mode 100644 index 000000000..86f1c0660 --- /dev/null +++ b/examples/rng90/Kconfig @@ -0,0 +1,37 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_RNG90 + tristate "Microchip RNG90 TRNG example" + default n + depends on DEV_RNG90 + ---help--- + Enable the RNG90 example. It opens the RNG90 character device, + runs the built-in self-test and reads random bytes from it. + +if EXAMPLES_RNG90 + +config EXAMPLES_RNG90_PROGNAME + string "Program name" + default "rng90" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_RNG90_DEVPATH + string "RNG90 device path" + default "/dev/rng0" + ---help--- + The default path of the RNG90 character device. + +config EXAMPLES_RNG90_PRIORITY + int "RNG90 task priority" + default 100 + +config EXAMPLES_RNG90_STACKSIZE + int "RNG90 stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/rmtchar/Make.defs b/examples/rng90/Make.defs similarity index 89% rename from examples/rmtchar/Make.defs rename to examples/rng90/Make.defs index 0b14b2487..b2b20b4ba 100644 --- a/examples/rmtchar/Make.defs +++ b/examples/rng90/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/examples/rmtchar/Make.defs +# apps/examples/rng90/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -20,6 +20,6 @@ # ############################################################################ -ifneq ($(CONFIG_EXAMPLES_RMTCHAR),) -CONFIGURED_APPS += $(APPDIR)/examples/rmtchar +ifneq ($(CONFIG_EXAMPLES_RNG90),) +CONFIGURED_APPS += $(APPDIR)/examples/rng90 endif diff --git a/examples/rng90/Makefile b/examples/rng90/Makefile new file mode 100644 index 000000000..6905cb569 --- /dev/null +++ b/examples/rng90/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/examples/rng90/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Microchip RNG90 TRNG example built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_RNG90_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_RNG90_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_RNG90_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_RNG90) + +# Microchip RNG90 TRNG example + +MAINSRC = rng90_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/rmtchar/rmtchar_receiver.c b/examples/rng90/rng90_main.c similarity index 54% rename from examples/rmtchar/rmtchar_receiver.c rename to examples/rng90/rng90_main.c index b6d94800e..3ef3ec899 100644 --- a/examples/rmtchar/rmtchar_receiver.c +++ b/examples/rng90/rng90_main.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/examples/rmtchar/rmtchar_receiver.c + * apps/examples/rng90/rng90_main.c * * SPDX-License-Identifier: Apache-2.0 * @@ -26,115 +26,137 @@ #include -#include #include +#include +#include #include -#include -#include -#include #include +#include -#include "rmtchar.h" - -#ifdef CONFIG_EXAMPLES_RMTCHAR_RX +#include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ -/**************************************************************************** - * Private Types - ****************************************************************************/ +#ifndef CONFIG_EXAMPLES_RNG90_DEVPATH +# define CONFIG_EXAMPLES_RNG90_DEVPATH "/dev/rng0" +#endif -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/**************************************************************************** - * Public Data - ****************************************************************************/ +#define RNG90_MAX_BYTES 32 /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: print_hex + * + * Description: + * Dump a buffer as a hex string, 16 bytes per line. + * + ****************************************************************************/ + +static void print_hex(FAR const uint8_t *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + { + printf("%02x", buf[i]); + if ((i & 0x0f) == 0x0f) + { + printf("\n"); + } + else + { + printf(" "); + } + } + + if ((len & 0x0f) != 0) + { + printf("\n"); + } +} + +/**************************************************************************** + * Name: usage + ****************************************************************************/ + +static void usage(FAR const char *progname) +{ + printf("Usage: %s [] []\n", progname); + printf(" RNG90 device (default %s)\n", + CONFIG_EXAMPLES_RNG90_DEVPATH); + printf(" number of random bytes to read, 1..%d (default %d)\n", + RNG90_MAX_BYTES, RNG90_MAX_BYTES); +} + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: rmtchar_receiver() - * - * Description: - * This is the entry point for the receiver thread. - * + * rng90_main ****************************************************************************/ -pthread_addr_t rmtchar_receiver(pthread_addr_t arg) +int main(int argc, FAR char *argv[]) { - FAR struct rmtchar_state_s *rmtchar = (FAR struct rmtchar_state_s *)arg; - struct rmt_item32_s buf[rmtchar->rmtchar_items]; - int nread; + FAR const char *devpath = CONFIG_EXAMPLES_RNG90_DEVPATH; + int count = RNG90_MAX_BYTES; + uint8_t buf[RNG90_MAX_BYTES]; int fd; + int ret; - /* Open the RMT character device */ + /* Parse the optional command line arguments */ - fd = open(rmtchar->rxdevpath, O_RDONLY); + if (argc > 1) + { + if (argv[1][0] == '-') + { + usage(argv[0]); + return EXIT_FAILURE; + } + + devpath = argv[1]; + } + + if (argc > 2) + { + count = atoi(argv[2]); + if (count < 1 || count > RNG90_MAX_BYTES) + { + printf("Invalid count %d, clamping to %d\n", count, + RNG90_MAX_BYTES); + count = RNG90_MAX_BYTES; + } + } + + /* Opening the device wakes the RNG90 up; closing puts it to sleep. */ + + fd = open(devpath, O_RDONLY); if (fd < 0) { - int errcode = errno; - printf("rmtchar_receiver: ERROR: failed to open %s: %d\n", - rmtchar->rxdevpath, errcode); - pthread_exit(NULL); + printf("ERROR: Failed to open %s: %d\n", devpath, errno); + return EXIT_FAILURE; } - /* Flush any output before reading */ + printf("RNG90 example: device %s\n", devpath); - fflush(stdout); + /* Read random bytes from the device */ - /* Read the buffer to the RMT character driver */ - - nread = read(fd, (char *)buf, rmtchar->rmtchar_items * - sizeof(struct rmt_item32_s)); - if (nread < 0) + ret = read(fd, buf, (size_t)count); + if (ret < 0) { - int errcode = errno; - if (errcode != EINTR) - { - printf("rmtchar_receiver: ERROR: read failed: %d\n", - errcode); - close(fd); - pthread_exit(NULL); - } - } - else - { - if (nread != (rmtchar->rmtchar_items * sizeof(struct rmt_item32_s))) - { - printf("rmtchar_receiver: ERROR: partial read: %d\n", - nread); - printf("rmtchar_receiver: Received buffer:\n"); - print_items(buf, nread / sizeof(struct rmt_item32_s)); - close(fd); - pthread_exit(NULL); - } - else - { - printf("rmtchar_receiver: Received buffer:\n"); - print_items(buf, nread / sizeof(struct rmt_item32_s)); - } + printf("ERROR: read failed: %d\n", errno); + close(fd); + return EXIT_FAILURE; } - /* Make sure that the transmitter thread has a chance to run */ - - pthread_yield(); + printf("Read %d random byte(s):\n", ret); + print_hex(buf, (size_t)ret); close(fd); - return NULL; + return EXIT_SUCCESS; } - -#endif /* CONFIG_EXAMPLES_RMTCHAR_RX */ diff --git a/examples/romfs/romfs_main.c b/examples/romfs/romfs_main.c index fa957bbd7..7a7395503 100644 --- a/examples/romfs/romfs_main.c +++ b/examples/romfs/romfs_main.c @@ -394,6 +394,12 @@ static void readdirectories(const char *path, struct node_s *entry) snprintf(g_scratchbuffer, sizeof(g_scratchbuffer), "%s/%s", path, direntry->d_name); fullpath = strdup(g_scratchbuffer); + if (fullpath == NULL) + { + printf(" ERROR: Out of memory\n"); + g_nerrors++; + continue; + } if (DIRENT_ISDIRECTORY(direntry->d_type)) { diff --git a/examples/rpmsgsocket/rpsock_client.c b/examples/rpmsgsocket/rpsock_client.c index 447bf7773..80813404e 100644 --- a/examples/rpmsgsocket/rpsock_client.c +++ b/examples/rpmsgsocket/rpsock_client.c @@ -425,7 +425,140 @@ errout_with_buffers: return -errno; } -static int rpsock_dgram_client(FAR char *argv[]) +static int rpsock_stream_conn_multi_times_client(FAR char *argv[], int argc) +{ + struct sockaddr_rpmsg myaddr; + struct pollfd pfd; + size_t sendsize = BUFHEAD; + ssize_t act; + bool delayflag = false; + bool nonblock = false; + FAR char *outbuf; + FAR char *inbuf; + FAR char *tmp; + int sockfd; + int ret; + + /* Allocate buffers */ + + inbuf = malloc(2 * BUFSIZE); + outbuf = inbuf + BUFSIZE; + if (inbuf == NULL) + { + printf("client: failed to allocate buffers\n"); + ret = -ENOMEM; + goto errout_with_buffers; + } + + /* Create a new rpmsg domain socket */ + + if (strcmp(argv[2], "nonblock") == 0) + { + nonblock = true; + } + + if (argc >= 7 && strcmp(argv[6], "delay") == 0) + { + delayflag = true; + } + + printf("client: create socket SOCK_STREAM nonblock %d\n", nonblock); + + if (nonblock) + { + sockfd = socket(PF_RPMSG, SOCK_STREAM | SOCK_NONBLOCK, 0); + } + else + { + sockfd = socket(PF_RPMSG, SOCK_STREAM, 0); + } + + if (sockfd < 0) + { + printf("client socket failure %d\n", errno); + goto errout_with_buffers; + } + + /* Connect the socket to the server */ + + myaddr.rp_family = AF_RPMSG; + strlcpy(myaddr.rp_name, argv[3], RPMSG_SOCKET_NAME_SIZE); + strlcpy(myaddr.rp_cpu, argv[4], RPMSG_SOCKET_CPU_SIZE); + + printf("client: Connecting to %s,%s...\n", myaddr.rp_cpu, myaddr.rp_name); + ret = connect(sockfd, (FAR struct sockaddr *)&myaddr, sizeof(myaddr)); + if (ret < 0 && errno == EINPROGRESS) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = sockfd; + pfd.events = POLLOUT; + + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("client: poll failure: %d\n", errno); + goto errout_with_socket; + } + } + else if (ret < 0) + { + printf("client: connect failure: %d\n", errno); + goto errout_with_socket; + } + + printf("client: Connected\n"); + + snprintf(outbuf, BUFHEAD, "process%04d, name:%s", getpid(), argv[3]); + + printf("client send data, total len %zu, BUFHEAD %s\n", sendsize, outbuf); + + tmp = outbuf; + + ret = send(sockfd, tmp, sendsize, 0); + if (ret < 0) + { + printf("client: send failure: %d\n", errno); + goto errout_with_socket; + } + + tmp = inbuf; + if (nonblock) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = sockfd; + pfd.events = POLLIN; + + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("client: poll failure: %d\n", errno); + goto errout_with_socket; + } + } + + act = recv(sockfd, tmp, SYNCSIZE, 0); + if (act < 0) + { + printf("client recv data failed %zd\n", act); + goto errout_with_socket; + } + + printf("client: Terminating\n"); + + if (delayflag) + { + sleep(1); + } + +errout_with_socket: + close(sockfd); + +errout_with_buffers: + free(inbuf); + return -errno; +} + +static int rpsock_dgram_client(char *argv[]) { struct sockaddr_rpmsg myaddr; struct rpsock_arg_s args; @@ -534,10 +667,14 @@ errout_with_buffers: int main(int argc, FAR char *argv[]) { + int times = 0; + int ret = 0; + int num = 0; + if (argc < 4) { - printf("Usage: rpsock_client stream/dgram" - " block/nonblock rp_name rp_cpu\n"); + printf("Usage: rpsock_client stream/dgram/conn_multi_times/delay" + " block/nonblock rp_name rp_cpu times\n"); return -EINVAL; } @@ -549,6 +686,26 @@ int main(int argc, FAR char *argv[]) { return rpsock_dgram_client(argv); } + else if (!strcmp(argv[1], "conn_multi_times")) + { + if (argc < 6 || atoi(argv[5]) <= 0) + { + printf("Usage: rpsock_client conn_multi_times" + " block/nonblock rp_name rp_cpu times (delay)\n"); + return -EINVAL; + } + + for (times = atoi(argv[5]); times > 0; times--) + { + printf("client conn_multi_times: %d\n", num++); + ret = rpsock_stream_conn_multi_times_client(argv, argc); + if (ret < 0 && ret != -EINPROGRESS) + { + printf("client socket failure %d\n", ret); + return ret; + } + } + } return -EINVAL; } diff --git a/examples/rpmsgsocket/rpsock_server.c b/examples/rpmsgsocket/rpsock_server.c index 467f4f853..338818630 100644 --- a/examples/rpmsgsocket/rpsock_server.c +++ b/examples/rpmsgsocket/rpsock_server.c @@ -269,6 +269,191 @@ errout_with_listensd: return -errno; } +static int rpsock_recv_send_single(FAR struct rpsock_arg_s *args) +{ + struct pollfd pfd; + ssize_t ret; + char buf[255]; + FAR char *tmp; + int snd; + + if (args->nonblock) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = args->fd; + pfd.events = POLLIN; + + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("server poll failed errno %d\n", errno); + goto end; + } + } + + ret = recv(args->fd, buf, sizeof(buf), 0); + if (ret == 0 || (ret < 0 && errno == ECONNRESET)) + { + printf("server recv data normal exit\n"); + goto end; + } + else if (ret < 0 && errno == EINPROGRESS) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = args->fd; + pfd.events = POLLOUT; + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("server: poll failure: %d\n", errno); + goto end; + } + } + else if(ret < 0) + { + printf("server recv data failed ret %zd, errno %d\n", ret, errno); + goto end; + } + + snd = ret; + tmp = buf; + + if (args->nonblock) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = args->fd; + pfd.events = POLLOUT; + + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("server: poll failure: %d\n", errno); + goto end; + } + } + + ret = send(args->fd, tmp, snd, 0); + if (ret == 0) + { + printf("server send data normal exit\n"); + } + else if (ret < 0) + { + printf("server send data failed errno %d\n", errno); + } + +end: + printf("server Complete ret %zd, errno %d\n", ret, errno); + free(args); + return -errno; +} + +static int rpsock_stream_multi_times_server(FAR char *argv[]) +{ + struct sockaddr_rpmsg myaddr; + FAR struct rpsock_arg_s *args; + struct pollfd pfd; + socklen_t addrlen; + bool nonblock = false; + int fd; + int newfd; + int ret; + + /* Create a new rpmsg domain socket */ + + if (strcmp(argv[2], "nonblock") == 0) + { + nonblock = true; + } + + printf("server: create socket SOCK_STREAM nonblock %d\n", nonblock); + + if (nonblock) + { + fd = socket(PF_RPMSG, SOCK_STREAM | SOCK_NONBLOCK, 0); + } + else + { + fd = socket(PF_RPMSG, SOCK_STREAM, 0); + } + + if (fd < 0) + { + printf("server: socket failure: %d\n", errno); + return -errno; + } + + /* Bind the socket to a local address */ + + myaddr.rp_family = AF_RPMSG; + strlcpy(myaddr.rp_name, argv[3], RPMSG_SOCKET_NAME_SIZE); + strlcpy(myaddr.rp_cpu, argv[4], RPMSG_SOCKET_CPU_SIZE); + + printf("server: bind cpu %s, name %s ...\n", + myaddr.rp_cpu, myaddr.rp_name); + ret = bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)); + if (ret < 0) + { + printf("server: bind failure: %d\n", errno); + goto errout_with_listensd; + } + + /* Listen for connections on the bound socket */ + + printf("server: listen ...\n"); + ret = listen(fd, 16); + if (ret < 0) + { + printf("server: listen failure %d\n", errno); + goto errout_with_listensd; + } + + if (nonblock) + { + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.fd = fd; + pfd.events = POLLIN; + + ret = poll(&pfd, 1, -1); + if (ret < 0) + { + printf("server: poll failure: %d\n", errno); + goto errout_with_listensd; + } + } + + printf("server: try accept ...\n"); + newfd = accept4(fd, (FAR struct sockaddr *)&myaddr, &addrlen, + SOCK_CLOEXEC); + if (newfd < 0) + { + printf("server: accept4 failure: %d\n", errno); + goto errout_with_listensd; + } + + printf("server: Connection accepted -- %d\n", newfd); + + args = malloc(sizeof(struct rpsock_arg_s)); + assert(args); + + args->fd = newfd; + args->nonblock = nonblock; + + ret = rpsock_recv_send_single(args); + if (ret < 0) + { + printf("rpsock_recv_send_single failed errno %d\n", errno); + } + + printf("server: Terminating\n"); + close(fd); + return 0; + +errout_with_listensd: + close(fd); + return -errno; +} + static int rpsock_dgram_server(int argc, FAR char *argv[]) { struct sockaddr_rpmsg myaddr; @@ -366,10 +551,14 @@ static int rpsock_dgram_server(int argc, FAR char *argv[]) int main(int argc, FAR char *argv[]) { + int times = 0; + int ret = 0; + int num = 0; + if (argc < 4) { - printf("Usage: rpsock_server stream/dgram" - " block/nonblock rp_name [rp_cpu]\n"); + printf("Usage: rpsock_server stream/dgram/server_multi_times" + " block/nonblock rp_name [rp_cpu] times\n"); return -EINVAL; } @@ -381,6 +570,26 @@ int main(int argc, FAR char *argv[]) { return rpsock_dgram_server(argc, argv); } + else if (!strcmp(argv[1], "server_multi_times")) + { + if (argc < 6 || atoi(argv[5]) <= 0) + { + printf("Usage: rpsock_server server_multi_times" + " block/nonblock rp_name rp_cpu times\n"); + return -EINVAL; + } + + for (times = atoi(argv[5]); times > 0; times--) + { + printf("server_multi_times: %d\n", num++); + ret = rpsock_stream_multi_times_server(argv); + if (ret < 0 && ret != -EINPROGRESS) + { + printf("server socket failure %d\n", ret); + return ret; + } + } + } return -EINVAL; } diff --git a/examples/rust/hello/CMakeLists.txt b/examples/rust/hello/CMakeLists.txt index c0daea419..b84e0234a 100644 --- a/examples/rust/hello/CMakeLists.txt +++ b/examples/rust/hello/CMakeLists.txt @@ -21,11 +21,16 @@ if(CONFIG_EXAMPLES_HELLO_RUST_CARGO) # Build the Rust crate using nuttx_add_rust - nuttx_add_rust(CRATE_NAME hello CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + if(CONFIG_ARCH_SIM) + nuttx_add_rust(CRATE_NAME hello CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR} + FEATURES sim) + else() + nuttx_add_rust(CRATE_NAME hello CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + endif() nuttx_add_application( NAME ${CONFIG_EXAMPLES_HELLO_RUST_CARGO_PROGNAME} STACKSIZE - ${CONFIG_EXAMPLES_HELLO_STACKSIZE} PRIORITY - ${CONFIG_EXAMPLES_HELLO_PRIORITY}) + ${CONFIG_EXAMPLES_HELLO_RUST_CARGO_STACKSIZE} PRIORITY + ${CONFIG_EXAMPLES_HELLO_RUST_CARGO_PRIORITY}) endif() # CONFIG_EXAMPLES_HELLO_RUST_CARGO diff --git a/examples/rust/hello/Cargo.toml b/examples/rust/hello/Cargo.toml index 0741516b9..d58728934 100644 --- a/examples/rust/hello/Cargo.toml +++ b/examples/rust/hello/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [lib] crate-type = ["staticlib"] +[features] +sim = [] + [profile.dev] panic = "abort" diff --git a/examples/rust/hello/Makefile b/examples/rust/hello/Makefile index 2c638eb3c..0e1b534c4 100644 --- a/examples/rust/hello/Makefile +++ b/examples/rust/hello/Makefile @@ -27,8 +27,19 @@ PRIORITY = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO_PRIORITY) STACKSIZE = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_HELLO_RUST_CARGO) -context:: - $(call RUST_CARGO_BUILD,hello,$(APPDIR)/examples/rust) +RUST_LIB := $(call RUST_GET_BINDIR,hello,$(APPDIR)/examples/rust) +RUST_SRCS := $(call RUST_CARGO_SRCS,hello,$(APPDIR)/examples/rust) + +ifeq ($(CONFIG_ARCH_SIM),y) +HELLO_RUST_FEATURES := sim +endif + +$(RUST_LIB): $(RUST_SRCS) + $(call RUST_CARGO_BUILD,hello,$(APPDIR)/examples/rust,$(HELLO_RUST_FEATURES)) + +context:: $(RUST_LIB) + +all:: $(RUST_LIB) clean:: $(call RUST_CARGO_CLEAN,hello,$(APPDIR)/examples/rust) diff --git a/examples/rust/hello/src/lib.rs b/examples/rust/hello/src/lib.rs index f50cf5bc5..e9b0e0ade 100644 --- a/examples/rust/hello/src/lib.rs +++ b/examples/rust/hello/src/lib.rs @@ -1,6 +1,7 @@ extern crate serde; extern crate serde_json; +use core::ffi::{c_char, c_int, CStr}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -9,10 +10,29 @@ struct Person { age: u8, } -// Function hello_rust_cargo without manglng +fn print_args(argc: c_int, argv: *mut *mut c_char) { + if argv.is_null() { + return; + } + + for i in 1..argc { + let arg = unsafe { *argv.add(i as usize) }; + if arg.is_null() { + continue; + } + + let arg = unsafe { CStr::from_ptr(arg) }.to_string_lossy(); + println!("{}: {}", i, arg); + } +} + +// Function hello_rust_cargo without mangling #[no_mangle] -pub extern "C" fn hello_rust_cargo_main() { - // Print hello world to stdout +pub extern "C" fn hello_rust_cargo_main(argc: c_int, argv: *mut *mut c_char) -> c_int { + print_args(argc, argv); + + #[cfg(feature = "sim")] + println!("On simulator"); let john = Person { name: "John".to_string(), @@ -49,8 +69,5 @@ pub extern "C" fn hello_rust_cargo_main() { .block_on(async { println!("Hello world from tokio!"); }); - - loop { - // Do nothing - } + 0 } diff --git a/examples/rust/slint/Makefile b/examples/rust/slint/Makefile index 34b444e9d..747f8960b 100644 --- a/examples/rust/slint/Makefile +++ b/examples/rust/slint/Makefile @@ -25,9 +25,16 @@ PRIORITY = $(CONFIG_EXAMPLES_RUST_SLINT_PRIORITY) STACKSIZE = $(CONFIG_EXAMPLES_RUST_SLINT_STACKSIZE) MODULE = $(CONFIG_EXAMPLES_RUST_SLINT) -context:: +RUST_LIB := $(call RUST_GET_BINDIR,slint,$(APPDIR)/examples/rust) +RUST_SRCS := $(call RUST_CARGO_SRCS,slint,$(APPDIR)/examples/rust) + +$(RUST_LIB): $(RUST_SRCS) $(call RUST_CARGO_BUILD,slint,$(APPDIR)/examples/rust) +context:: $(RUST_LIB) + +all:: $(RUST_LIB) + clean:: $(call RUST_CARGO_CLEAN,slint,$(APPDIR)/examples/rust) diff --git a/examples/settings/settings_main.c b/examples/settings/settings_main.c index b284a4843..3f718aa7c 100644 --- a/examples/settings/settings_main.c +++ b/examples/settings/settings_main.c @@ -115,7 +115,7 @@ int settings_main(int argc, FAR char *argv[]) if (ret == -ENOENT) { printf("No existing binary storage file found. Creating it.\n"); - fd = open(bin_path, O_CREAT); + fd = open(bin_path, O_CREAT, 0666); if (fd < 0) { printf("Failed to create settings file\n"); @@ -138,7 +138,7 @@ int settings_main(int argc, FAR char *argv[]) if (ret == -ENOENT) { printf("No existing text storage file found. Creating it.\n"); - fd = open(text_path, O_CREAT); + fd = open(text_path, O_CREAT, 0666); if (fd < 0) { printf("Failed to create settings file\n"); diff --git a/examples/shv-nxboot-updater/Kconfig b/examples/shv-nxboot-updater/Kconfig index d25da7cd0..80ace34ba 100644 --- a/examples/shv-nxboot-updater/Kconfig +++ b/examples/shv-nxboot-updater/Kconfig @@ -5,7 +5,7 @@ menuconfig EXAMPLES_SHV_NXBOOT_UPDATER bool "Silicon Heaven Firmware updates for NXBoot" - depends on NETUTILS_LIBSHVC + depends on NETUTILS_LIBSHVC && ENABLE_ALL_SIGNALS default n ---help--- Enable the shv-nxboot-updater application. diff --git a/examples/smf/CMakeLists.txt b/examples/smf/CMakeLists.txt new file mode 100644 index 000000000..bba48ca02 --- /dev/null +++ b/examples/smf/CMakeLists.txt @@ -0,0 +1,18 @@ +# ############################################################################## +# apps/examples/hsm_psicc2/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## + +if(CONFIG_EXAMPLES_SMF) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_SMF_PROGNAME} + SRCS + smf_main.c + hsm_psicc2_thread.c + STACKSIZE + ${CONFIG_EXAMPLES_SMF_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_SMF_PRIORITY}) +endif() diff --git a/examples/smf/Kconfig b/examples/smf/Kconfig new file mode 100644 index 000000000..8d8efc1f7 --- /dev/null +++ b/examples/smf/Kconfig @@ -0,0 +1,37 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_SMF + tristate "State Machine Framework PSICC2 demo (HSM)" + default n + depends on NSH_BUILTIN_APPS + depends on SYSTEM_SMF + depends on SYSTEM_SMF_ANCESTOR_SUPPORT + depends on SYSTEM_SMF_INITIAL_TRANSITION + +if EXAMPLES_SMF + +config EXAMPLES_SMF_PROGNAME + string "Program name" + default "hsm_psicc2" + +config EXAMPLES_SMF_PRIORITY + int "Priority" + default 100 + +config EXAMPLES_SMF_STACKSIZE + int "Stack size" + default 2048 + +config EXAMPLES_SMF_EVENT_QUEUE_SIZE + int "Event queue size" + default 10 + +config EXAMPLES_SMF_MQ_NAME + string "Message queue name" + default "/hsm_psicc2_mq" + +endif + diff --git a/examples/smf/Make.defs b/examples/smf/Make.defs new file mode 100644 index 000000000..00be28079 --- /dev/null +++ b/examples/smf/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/hello/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_SMF),) +CONFIGURED_APPS += $(APPDIR)/examples/smf +endif diff --git a/examples/smf/Makefile b/examples/smf/Makefile new file mode 100644 index 000000000..2fd9309c1 --- /dev/null +++ b/examples/smf/Makefile @@ -0,0 +1,37 @@ +############################################################################ +# apps/examples/hsm_psicc2/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# HSM PSICC2 (SMF demo) built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_SMF_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_SMF_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_SMF_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_SMF) + +# HSM PSICC2 Example + +MAINSRC = smf_main.c +CSRCS += hsm_psicc2_thread.c + +include $(APPDIR)/Application.mk diff --git a/examples/smf/hsm_psicc2_thread.c b/examples/smf/hsm_psicc2_thread.c new file mode 100644 index 000000000..e457969e1 --- /dev/null +++ b/examples/smf/hsm_psicc2_thread.c @@ -0,0 +1,626 @@ +/**************************************************************************** + * apps/examples/smf/hsm_psicc2_thread.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Glenn Andrews + * State Machine example copyright (c) Miro Samek + * + * Implementation of the statechart in Figure 2.11 of + * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek + * https://www.state-machine.com/psicc2 + * Used with permission of the author. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hsm_psicc2_thread.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct s_object +{ + struct smf_ctx ctx; /* MUST be first (matches SMF_CTX(obj)) */ + struct hsm_psicc2_event event; + int foo; +}; + +enum demo_states +{ + STATE_INITIAL, + STATE_S, + STATE_S1, + STATE_S2, + STATE_S11, + STATE_S21, + STATE_S211 +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct s_object s_obj; +static bool g_started; +static const struct smf_state demo_states[]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * STATE_INITIAL + ****************************************************************************/ + +static void initial_entry(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + obj->foo = false; +} + +static enum smf_state_result initial_run(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); + return SMF_EVENT_PROPAGATE; +} + +static void initial_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * STATE_S + ****************************************************************************/ + +static void s_entry(void *o) +{ + (void)o; + + printf("[psicc2] %s", __func__); +} + +static enum smf_state_result s_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_E: + printf("[psicc2] %s received EVENT_E", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]); + break; + + case EVENT_I: + if (obj->foo) + { + printf("[psicc2] %s received EVENT_I and set foo false\n", + __func__); + obj->foo = false; + } + else + { + printf("[psicc2] %s received EVENT_I and did nothing\n", + __func__); + } + + return SMF_EVENT_HANDLED; + + case EVENT_TERMINATE: + printf("[psicc2] %s received EVENT_TERMINATE. Terminating\n", + __func__); + smf_set_terminate(SMF_CTX(obj), -1); + break; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s_exit(void *o) +{ + (void)o; + + printf("%s", __func__); +} + +/**************************************************************************** + * STATE_S1 + ****************************************************************************/ + +static void s1_entry(void *o) +{ + (void)o; + + printf("%s", __func__); +} + +static enum smf_state_result s1_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_A: + printf("[psicc2] %s received EVENT_A\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]); + break; + + case EVENT_B: + printf("[psicc2] %s received EVENT_B\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]); + break; + + case EVENT_C: + printf("[psicc2] %s received EVENT_C\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S2]); + break; + + case EVENT_D: + if (!obj->foo) + { + printf("[psicc2] %s received EVENT_D and acted on it\n", + __func__); + obj->foo = true; + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]); + } + else + { + printf("[psicc2] %s received EVENT_D and ignored it\n", + __func__); + } + break; + + case EVENT_F: + printf("[psicc2] %s received EVENT_F\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]); + break; + + case EVENT_I: + printf("[psicc2] %s received EVENT_I\n", __func__); + return SMF_EVENT_HANDLED; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s1_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * STATE_S11 + ****************************************************************************/ + +static void s11_entry(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +static enum smf_state_result s11_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_D: + if (obj->foo) + { + printf("[psicc2] %s received EVENT_D and acted upon it\n", + __func__); + obj->foo = false; + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]); + } + else + { + printf("[psicc2] %s received EVENT_D and ignored it\n", + __func__); + } + break; + + case EVENT_G: + printf("[psicc2] %s received EVENT_G\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]); + break; + + case EVENT_H: + printf("[psicc2] %s received EVENT_H\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]); + break; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s11_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * STATE_S2 + ****************************************************************************/ + +static void s2_entry(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +static enum smf_state_result s2_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_C: + printf("[psicc2] %s received EVENT_C\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]); + break; + + case EVENT_F: + printf("[psicc2] %s received EVENT_F\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S11]); + break; + + case EVENT_I: + if (!obj->foo) + { + printf("[psicc2] %s received EVENT_I and set foo true\n", + __func__); + obj->foo = true; + return SMF_EVENT_HANDLED; + } + else + { + printf("[psicc2] %s received EVENT_I and did nothing\n", + __func__); + } + break; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s2_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * STATE_S21 + ****************************************************************************/ + +static void s21_entry(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +static enum smf_state_result s21_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_A: + printf("[psicc2] %s received EVENT_A\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]); + break; + + case EVENT_B: + printf("[psicc2] %s received EVENT_B\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S211]); + break; + + case EVENT_G: + printf("[psicc2] %s received EVENT_G\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S1]); + break; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s21_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * STATE_S211 + ****************************************************************************/ + +static void s211_entry(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +static enum smf_state_result s211_run(void *o) +{ + struct s_object *obj = (struct s_object *)o; + + printf("[psicc2] %s\n", __func__); + + switch (obj->event.event_id) + { + case EVENT_D: + printf("[psicc2] %s received EVENT_D\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S21]); + break; + + case EVENT_H: + printf("[psicc2] %s received EVENT_H\n", __func__); + smf_set_state(SMF_CTX(obj), &demo_states[STATE_S]); + break; + + default: + break; + } + + return SMF_EVENT_PROPAGATE; +} + +static void s211_exit(void *o) +{ + (void)o; + + printf("[psicc2] %s\n", __func__); +} + +/**************************************************************************** + * State Table + ****************************************************************************/ + +static const struct smf_state demo_states[] = +{ + [STATE_INITIAL] = SMF_CREATE_STATE( + initial_entry, + initial_run, + initial_exit, + NULL, + &demo_states[STATE_S2] + ), + [STATE_S] = SMF_CREATE_STATE( + s_entry, + s_run, + s_exit, + &demo_states[STATE_INITIAL], + &demo_states[STATE_S11] + ), + [STATE_S1] = SMF_CREATE_STATE( + s1_entry, + s1_run, + s1_exit, + &demo_states[STATE_S], + &demo_states[STATE_S11] + ), + [STATE_S2] = SMF_CREATE_STATE( + s2_entry, + s2_run, + s2_exit, + &demo_states[STATE_S], + &demo_states[STATE_S211] + ), + [STATE_S11] = SMF_CREATE_STATE( + s11_entry, + s11_run, + s11_exit, + &demo_states[STATE_S1], + NULL + ), + [STATE_S21] = SMF_CREATE_STATE( + s21_entry, + s21_run, + s21_exit, + &demo_states[STATE_S2], + &demo_states[STATE_S211] + ), + [STATE_S211] = SMF_CREATE_STATE( + s211_entry, + s211_run, + s211_exit, + &demo_states[STATE_S21], + NULL + ) +}; + +/**************************************************************************** + * Name: hsm_psicc2_thread_main + ****************************************************************************/ + +static int hsm_psicc2_thread_main(int argc, char **argv) +{ + struct mq_attr attr; + mqd_t mq; + ssize_t n; + int rc; + + (void)argc; + (void)argv; + + memset(&attr, 0, sizeof(attr)); + attr.mq_maxmsg = CONFIG_EXAMPLES_SMF_EVENT_QUEUE_SIZE; + attr.mq_msgsize = sizeof(struct hsm_psicc2_event); + + mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_CREAT | O_RDONLY, 0666, &attr); + if (mq == (mqd_t)-1) + { + printf("psicc2][ERR] mq_open(%s) failed: %d\n", + CONFIG_EXAMPLES_SMF_MQ_NAME, errno); + return -1; + } + + printf("[psicc2] State Machine thread started\n"); + + memset(&s_obj, 0, sizeof(s_obj)); + smf_set_initial(SMF_CTX(&s_obj), &demo_states[STATE_INITIAL]); + + while (true) + { + n = mq_receive(mq, (char *)&s_obj.event, sizeof(s_obj.event), NULL); + if (n < 0) + { + printf("psicc2][ERR] mq_receive failed: %d\n", errno); + continue; + } + + rc = smf_run_state(SMF_CTX(&s_obj)); + if (rc != 0) + { + printf("[psicc2] SMF terminated (rc=%d). Exiting thread\n", rc); + break; + } + } + + mq_close(mq); + mq_unlink(CONFIG_EXAMPLES_SMF_MQ_NAME); + + g_started = false; + memset(&s_obj, 0, sizeof(s_obj)); + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: hsm_psicc2_thread_start + ****************************************************************************/ + +int hsm_psicc2_thread_start(void) +{ + int pid; + + if (g_started) + { + return 0; + } + + pid = task_create("psicc2_thread", + CONFIG_EXAMPLES_SMF_PRIORITY, + CONFIG_EXAMPLES_SMF_STACKSIZE, + hsm_psicc2_thread_main, + NULL); + if (pid < 0) + { + printf("psicc2][ERR] task_create failed: %d", errno); + return -1; + } + + g_started = true; + return 0; +} + +/**************************************************************************** + * Name: hsm_psicc2_post_event + ****************************************************************************/ + +int hsm_psicc2_post_event(uint32_t event_id) +{ + struct hsm_psicc2_event ev; + mqd_t mq; + int rc; + + ev.event_id = event_id; + + mq = mq_open(CONFIG_EXAMPLES_SMF_MQ_NAME, O_WRONLY | O_NONBLOCK); + if (mq == (mqd_t)-1) + { + printf("psicc2][ERR] mq_open(O_WRONLY) failed: %d " + "(did you run 'hsm_psicc2 start'?)\n", + errno); + return -1; + } + + rc = mq_send(mq, (const char *)&ev, sizeof(ev), 0); + if (rc < 0) + { + printf("psicc2][ERR] mq_send failed: %d\n", errno); + mq_close(mq); + return -1; + } + + mq_close(mq); + return 0; +} diff --git a/examples/smf/hsm_psicc2_thread.h b/examples/smf/hsm_psicc2_thread.h new file mode 100644 index 000000000..385a29703 --- /dev/null +++ b/examples/smf/hsm_psicc2_thread.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * apps/examples/smf/hsm_psicc2_thread.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Glenn Andrews + * State Machine example copyright (c) Miro Samek + * + * Implementation of the statechart in Figure 2.11 of + * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek + * https://www.state-machine.com/psicc2 + * Used with permission of the author. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H +#define __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct hsm_psicc2_event +{ + uint32_t event_id; +}; + +enum demo_events +{ + EVENT_A, + EVENT_B, + EVENT_C, + EVENT_D, + EVENT_E, + EVENT_F, + EVENT_G, + EVENT_H, + EVENT_I, + EVENT_TERMINATE +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int hsm_psicc2_thread_start(void); +int hsm_psicc2_post_event(uint32_t event_id); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_EXAMPLES_SMF_HSM_PSICC2_THREAD_H */ diff --git a/examples/smf/smf_main.c b/examples/smf/smf_main.c new file mode 100644 index 000000000..b6a9677a5 --- /dev/null +++ b/examples/smf/smf_main.c @@ -0,0 +1,127 @@ +/**************************************************************************** + * apps/examples/smf/smf_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Glenn Andrews + * State Machine example copyright (c) Miro Samek + * + * Implementation of the statechart in Figure 2.11 of + * Practical UML Statecharts in C/C++, 2nd Edition by Miro Samek + * https://www.state-machine.com/psicc2 + * Used with permission of the author. + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "hsm_psicc2_thread.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usage + ****************************************************************************/ + +static void usage(void) +{ + printf("Usage:\n"); + printf(" hsm_psicc2 start\n"); + printf(" hsm_psicc2 event \n"); + printf(" hsm_psicc2 terminate\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + usage(); + return 1; + } + + if (strcmp(argv[1], "start") == 0) + { + printf("State Machine Framework Demo\n"); + printf("See PSiCC2 Fig 2.11 for the statechart\n"); + printf("https://www.state-machine.com/psicc2\n\n"); + return hsm_psicc2_thread_start(); + } + + if (strcmp(argv[1], "terminate") == 0) + { + return hsm_psicc2_post_event(EVENT_TERMINATE); + } + + if (strcmp(argv[1], "event") == 0) + { + int c; + + if (argc < 3 || argv[2] == NULL || argv[2][0] == '\0') + { + usage(); + return 1; + } + + c = toupper((unsigned char)argv[2][0]); + switch (c) + { + case 'A': + return hsm_psicc2_post_event(EVENT_A); + case 'B': + return hsm_psicc2_post_event(EVENT_B); + case 'C': + return hsm_psicc2_post_event(EVENT_C); + case 'D': + return hsm_psicc2_post_event(EVENT_D); + case 'E': + return hsm_psicc2_post_event(EVENT_E); + case 'F': + return hsm_psicc2_post_event(EVENT_F); + case 'G': + return hsm_psicc2_post_event(EVENT_G); + case 'H': + return hsm_psicc2_post_event(EVENT_H); + case 'I': + return hsm_psicc2_post_event(EVENT_I); + default: + printf("Invalid event '%c'\n", c); + return 1; + } + } + + usage(); + return 1; +} diff --git a/examples/smps/smps_main.c b/examples/smps/smps_main.c index ba6a7b079..b9ddbd827 100644 --- a/examples/smps/smps_main.c +++ b/examples/smps/smps_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -476,16 +476,10 @@ int main(int argc, char *argv[]) goto errout; } -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - #ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -#endif #endif /* Set SMPS mode */ diff --git a/examples/sotest/Make.defs b/examples/sotest/Make.defs index c674e34e6..17afaca43 100644 --- a/examples/sotest/Make.defs +++ b/examples/sotest/Make.defs @@ -20,4 +20,11 @@ # ############################################################################ -include $(wildcard $(APPDIR)/examples/sotest/*/Make.defs) +ifeq ($(CONFIG_EXAMPLES_SOTEST),y) +include $(APPDIR)/examples/sotest/main/Make.defs +endif + +ifneq ($(CONFIG_EXAMPLES_SOTEST),n) +include $(APPDIR)/examples/sotest/modprint/Make.defs +include $(APPDIR)/examples/sotest/sotest/Make.defs +endif diff --git a/examples/sotest/main/CMakeLists.txt b/examples/sotest/main/CMakeLists.txt index caca43d22..5c5cef5a6 100644 --- a/examples/sotest/main/CMakeLists.txt +++ b/examples/sotest/main/CMakeLists.txt @@ -21,27 +21,107 @@ # ############################################################################## if(CONFIG_EXAMPLES_SOTEST) + set(SOTEST_SYMTAB ${CMAKE_CURRENT_BINARY_DIR}/sotest_symtab.c) + set(SOTEST_ROMFS_IMG ${CMAKE_CURRENT_BINARY_DIR}/sotest_romfs.img) + set(SOTEST_ROMFS_SRC ${CMAKE_CURRENT_BINARY_DIR}/sotest_romfs.c) + set(SOTEST_MODPRINT_ELF ${CMAKE_BINARY_DIR}/bin/modprint) + set(SOTEST_SHARED_ELF ${CMAKE_BINARY_DIR}/bin/sotest) - # FIXME: fix all empty a after the kernel build is implemented - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sotest_symtab.c - COMMAND - ${NUTTX_APPS_DIR}/tools/mksymtab.sh ${CMAKE_CURRENT_BINARY_DIR}/empty - g_sot > ${CMAKE_CURRENT_BINARY_DIR}/sotest_symtab.c) - - add_custom_target( - sotest_romfs - COMMAND genromfs -f sotest_romfs.img -d empty - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + # Dynamic applications are wrapped in ELF_* helper targets when the build uses + # the non-ELF-capable compiler path. + if(CMAKE_C_ELF_COMPILER) + set(SOTEST_MODPRINT_TARGET modprint) + set(SOTEST_SHARED_TARGET sotest) + else() + set(SOTEST_MODPRINT_TARGET ELF_modprint) + set(SOTEST_SHARED_TARGET ELF_sotest) + endif() add_custom_command( - OUTPUT sotest_romfs.c - COMMAND xxd -i sotest_romfs.img > sotest_romfs.c - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS sotest_romfs) + OUTPUT ${SOTEST_SYMTAB} + COMMAND ${NUTTX_APPS_DIR}/tools/mksymtab.sh ${SOTEST_MODPRINT_ELF} + ${SOTEST_SHARED_ELF} g_sot > ${SOTEST_SYMTAB} + DEPENDS ${SOTEST_MODPRINT_TARGET} ${SOTEST_SHARED_TARGET}) - nuttx_add_application( - NAME sotest SRCS sotest_main.c ${CMAKE_CURRENT_BINARY_DIR}/sotest_symtab.c - ${CMAKE_CURRENT_BINARY_DIR}/sotest_romfs.c) + set_source_files_properties(${SOTEST_SYMTAB} PROPERTIES GENERATED TRUE) + set(SOTEST_GENERATED_OUTPUTS ${SOTEST_SYMTAB}) + set(SOTEST_SRCS sotest_main.c ${SOTEST_SYMTAB}) + + if(CONFIG_EXAMPLES_SOTEST_BUILTINFS) + set(SOTEST_ROMFS_DEPS ${SOTEST_MODPRINT_TARGET} ${SOTEST_SHARED_TARGET}) + + if(CONFIG_SYSTEM_NXPKG) + set(SOTEST_SHARED_INDEX ${CMAKE_BINARY_DIR}/bin/shared-index.json) + set(SOTEST_SHARED_SCRIPT ${CMAKE_BINARY_DIR}/bin/pkgsotest.nsh) + set(SOTEST_FIXTURE_HOST_DIR ${CMAKE_CURRENT_BINARY_DIR}/host) + set(SOTEST_FIXTURE_HOST_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/host/CMakeLists.txt) + set(SOTEST_FIXTURE_TOOL + ${SOTEST_FIXTURE_HOST_DIR}/mk_pkg_fixture_shared${CMAKE_EXECUTABLE_SUFFIX} + ) + + if(CONFIG_ARCH_BOARD) + set(SOTEST_FIXTURE_COMPAT ${CONFIG_ARCH_BOARD}) + else() + set(SOTEST_FIXTURE_COMPAT ${CONFIG_ARCH_BOARD_CUSTOM_NAME}) + endif() + + add_custom_command( + OUTPUT ${SOTEST_FIXTURE_TOOL} + COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/host -B + ${SOTEST_FIXTURE_HOST_DIR} + COMMAND ${CMAKE_COMMAND} --build ${SOTEST_FIXTURE_HOST_DIR} --target + mk_pkg_fixture_shared + DEPENDS ${SOTEST_FIXTURE_HOST_SRC} + ${CMAKE_CURRENT_SOURCE_DIR}/mk_pkg_fixture_shared.c + VERBATIM) + + add_custom_command( + OUTPUT ${SOTEST_SHARED_INDEX} ${SOTEST_SHARED_SCRIPT} + COMMAND + ${SOTEST_FIXTURE_TOOL} ${SOTEST_MODPRINT_ELF} ${SOTEST_SHARED_ELF} + ${SOTEST_SHARED_INDEX} ${SOTEST_SHARED_SCRIPT} ${CONFIG_ARCH} + ${SOTEST_FIXTURE_COMPAT} + DEPENDS ${SOTEST_MODPRINT_TARGET} ${SOTEST_SHARED_TARGET} + ${SOTEST_FIXTURE_TOOL} + VERBATIM) + + list(APPEND SOTEST_ROMFS_DEPS ${SOTEST_SHARED_INDEX} + ${SOTEST_SHARED_SCRIPT}) + endif() + + add_custom_command( + OUTPUT ${SOTEST_ROMFS_IMG} + COMMAND genromfs -f ${SOTEST_ROMFS_IMG} -d ${CMAKE_BINARY_DIR}/bin + DEPENDS ${SOTEST_ROMFS_DEPS} + VERBATIM) + + get_filename_component(SOTEST_ROMFS_IMG_NAME ${SOTEST_ROMFS_IMG} NAME) + + # xxd -i derives the generated array/length symbol names from the exact path + # it is given - sotest_main.c declares them as plain + # "sotest_romfs_img"/"sotest_romfs_img_len", so xxd must be run against just + # the bare filename (via WORKING_DIRECTORY) rather than the absolute + # ${SOTEST_ROMFS_IMG} path, or every non- alphanumeric character in that + # path (e.g. every "/" in the build directory) gets folded into the symbol + # name instead, leaving the names sotest_main.c expects undefined at link + # time. + + add_custom_command( + OUTPUT ${SOTEST_ROMFS_SRC} + COMMAND xxd -i ${SOTEST_ROMFS_IMG_NAME} > ${SOTEST_ROMFS_SRC} + DEPENDS ${SOTEST_ROMFS_IMG} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + set_source_files_properties(${SOTEST_ROMFS_SRC} PROPERTIES GENERATED TRUE) + + list(APPEND SOTEST_GENERATED_OUTPUTS ${SOTEST_ROMFS_SRC}) + list(APPEND SOTEST_SRCS ${SOTEST_ROMFS_SRC}) + endif() + + add_custom_target(sotest_artifacts DEPENDS ${SOTEST_GENERATED_OUTPUTS}) + + nuttx_add_application(NAME sotest SRCS ${SOTEST_SRCS} DEPENDS + sotest_artifacts) endif() diff --git a/examples/sotest/main/Makefile b/examples/sotest/main/Makefile index 064b0b434..327b60c9e 100644 --- a/examples/sotest/main/Makefile +++ b/examples/sotest/main/Makefile @@ -50,8 +50,34 @@ ifeq ($(CONFIG_EXAMPLES_SOTEST_BUILTINFS),y) ROMFSIMG = sotest_romfs.img ROMFSSRC = sotest_romfs.c ROMFSOBJ = $(ROMFSSRC:.c=$(OBJEXT)) +ifdef CONFIG_SYSTEM_NXPKG +HOSTOBJSEXT ?= hobj +PKGSHAREDINDEX = $(BINDIR)/shared-index.json +PKGSHAREDTEST = $(BINDIR)/pkgsotest.nsh +PKG_FIXTURE_GEN = mk_pkg_fixture_shared$(HOSTEXEEXT) +PKG_FIXTURE_SRCS = mk_pkg_fixture_shared.c +PKG_FIXTURE_OBJS = $(PKG_FIXTURE_SRCS:.c=.$(HOSTOBJSEXT)) +PKG_FIXTURE_OUTPUTS = $(PKGSHAREDINDEX) $(PKGSHAREDTEST) +PKG_FIXTURE_STAMP = $(BINDIR)/.pkgsotest.stamp +PKG_FIXTURE_COMPAT = $(if $(CONFIG_ARCH_BOARD),$(CONFIG_ARCH_BOARD),$(CONFIG_ARCH_BOARD_CUSTOM_NAME)) -$(ROMFSIMG): +$(PKG_FIXTURE_OBJS): %.$(HOSTOBJSEXT): %.c + @echo "CC: $<" + $(Q) $(HOSTCC) -c $(HOSTCFLAGS) $< -o $@ + +$(PKG_FIXTURE_GEN): $(PKG_FIXTURE_OBJS) + $(Q) $(HOSTCC) $(HOSTLDFLAGS) $(PKG_FIXTURE_OBJS) -o $@ + +$(PKG_FIXTURE_OUTPUTS): $(PKG_FIXTURE_STAMP) + +$(PKG_FIXTURE_STAMP): $(BINDIR)/modprint $(BINDIR)/sotest $(PKG_FIXTURE_GEN) + $(Q) ./$(PKG_FIXTURE_GEN) $(BINDIR)/modprint $(BINDIR)/sotest \ + $(PKGSHAREDINDEX) $(PKGSHAREDTEST) $(CONFIG_ARCH) \ + $(PKG_FIXTURE_COMPAT) + $(Q) touch $@ +endif + +$(ROMFSIMG): $(PKG_FIXTURE_OUTPUTS) $(Q) genromfs -d $(BINDIR) -f $@ $(ROMFSSRC): $(ROMFSIMG) @@ -67,6 +93,6 @@ postinstall:: $(ROMFSOBJ) $(SYMTABOBJ) $(FSIMG_OBJ) $(call ARLOCK, $(call CONVERT_PATH,$(BIN)), $^) distclean:: - $(Q) $(call DELFILE, $(SYMTABSRC) $(SYMTABOBJ) $(ROMFSSRC) $(ROMFSIMG) $(ROMFSOBJ)) + $(Q) $(call DELFILE, $(SYMTABSRC) $(SYMTABOBJ) $(ROMFSSRC) $(ROMFSIMG) $(ROMFSOBJ) $(PKG_FIXTURE_GEN) $(PKG_FIXTURE_OBJS) $(PKG_FIXTURE_STAMP)) include $(APPDIR)/Application.mk diff --git a/examples/sotest/main/host/CMakeLists.txt b/examples/sotest/main/host/CMakeLists.txt new file mode 100644 index 000000000..e83524d29 --- /dev/null +++ b/examples/sotest/main/host/CMakeLists.txt @@ -0,0 +1,26 @@ +# ############################################################################## +# apps/examples/sotest/main/host/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +cmake_minimum_required(VERSION 3.16) +project(sotest_fixture_host LANGUAGES C) + +add_executable(mk_pkg_fixture_shared ../mk_pkg_fixture_shared.c) diff --git a/examples/sotest/main/mk_pkg_fixture_shared.c b/examples/sotest/main/mk_pkg_fixture_shared.c new file mode 100644 index 000000000..713cd7115 --- /dev/null +++ b/examples/sotest/main/mk_pkg_fixture_shared.c @@ -0,0 +1,433 @@ +/**************************************************************************** + * apps/examples/sotest/main/mk_pkg_fixture_shared.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SHA256_BLOCK_SIZE 64 +#define SHA256_DIGEST_SIZE 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sha256_state_s +{ + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_SIZE]; + size_t buffer_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint32_t g_sha256_init[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +static const uint32_t g_sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t sha256_rotr(uint32_t value, unsigned int bits) +{ + return (value >> bits) | (value << (32 - bits)); +} + +static uint32_t sha256_ch(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (~x & z); +} + +static uint32_t sha256_maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (x & z) ^ (y & z); +} + +static uint32_t sha256_bs0(uint32_t x) +{ + return sha256_rotr(x, 2) ^ sha256_rotr(x, 13) ^ sha256_rotr(x, 22); +} + +static uint32_t sha256_bs1(uint32_t x) +{ + return sha256_rotr(x, 6) ^ sha256_rotr(x, 11) ^ sha256_rotr(x, 25); +} + +static uint32_t sha256_ss0(uint32_t x) +{ + return sha256_rotr(x, 7) ^ sha256_rotr(x, 18) ^ (x >> 3); +} + +static uint32_t sha256_ss1(uint32_t x) +{ + return sha256_rotr(x, 17) ^ sha256_rotr(x, 19) ^ (x >> 10); +} + +static uint32_t sha256_load32(const uint8_t *src) +{ + return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | + ((uint32_t)src[2] << 8) | (uint32_t)src[3]; +} + +static void sha256_store32(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)(value >> 24); + dst[1] = (uint8_t)(value >> 16); + dst[2] = (uint8_t)(value >> 8); + dst[3] = (uint8_t)value; +} + +static void sha256_transform(struct sha256_state_s *ctx, + const uint8_t block[SHA256_BLOCK_SIZE]) +{ + uint32_t schedule[64]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + int i; + + for (i = 0; i < 16; i++) + { + schedule[i] = sha256_load32(block + i * 4); + } + + for (; i < 64; i++) + { + schedule[i] = sha256_ss1(schedule[i - 2]) + schedule[i - 7] + + sha256_ss0(schedule[i - 15]) + schedule[i - 16]; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; i++) + { + uint32_t t1 = h + sha256_bs1(e) + sha256_ch(e, f, g) + g_sha256_k[i] + + schedule[i]; + uint32_t t2 = sha256_bs0(a) + sha256_maj(a, b, c); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +static void sha256_init(struct sha256_state_s *ctx) +{ + memcpy(ctx->state, g_sha256_init, sizeof(g_sha256_init)); + ctx->bitcount = 0; + ctx->buffer_len = 0; +} + +static void sha256_update(struct sha256_state_s *ctx, const uint8_t *data, + size_t len) +{ + ctx->bitcount += (uint64_t)len * 8; + + while (len > 0) + { + size_t space = SHA256_BLOCK_SIZE - ctx->buffer_len; + size_t chunk = len < space ? len : space; + + memcpy(ctx->buffer + ctx->buffer_len, data, chunk); + ctx->buffer_len += chunk; + data += chunk; + len -= chunk; + + if (ctx->buffer_len == SHA256_BLOCK_SIZE) + { + sha256_transform(ctx, ctx->buffer); + ctx->buffer_len = 0; + } + } +} + +static void sha256_final(struct sha256_state_s *ctx, + uint8_t digest[SHA256_DIGEST_SIZE]) +{ + uint8_t length_bytes[8]; + uint8_t padding[SHA256_BLOCK_SIZE] = + { + 0x80 + }; + + int i; + + for (i = 0; i < 8; i++) + { + length_bytes[7 - i] = (uint8_t)(ctx->bitcount >> (i * 8)); + } + + if (ctx->buffer_len < 56) + { + sha256_update(ctx, padding, 56 - ctx->buffer_len); + } + else + { + sha256_update(ctx, padding, SHA256_BLOCK_SIZE - ctx->buffer_len); + sha256_update(ctx, padding + 1, 56); + } + + sha256_update(ctx, length_bytes, sizeof(length_bytes)); + + for (i = 0; i < 8; i++) + { + sha256_store32(digest + i * 4, ctx->state[i]); + } +} + +static int sha256_file(const char *path, char *digest_hex, size_t digest_len) +{ + struct sha256_state_s ctx; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t buffer[4096]; + static const char g_hex[] = "0123456789abcdef"; + FILE *stream; + size_t nread; + int i; + + if (digest_len < SHA256_DIGEST_SIZE * 2 + 1) + { + fprintf(stderr, "digest buffer is too small\n"); + return -1; + } + + stream = fopen(path, "rb"); + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + sha256_init(&ctx); + + while ((nread = fread(buffer, 1, sizeof(buffer), stream)) > 0) + { + sha256_update(&ctx, buffer, nread); + } + + if (ferror(stream) != 0) + { + fprintf(stderr, "failed to read %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + fclose(stream); + sha256_final(&ctx, digest); + + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + { + digest_hex[i * 2] = g_hex[digest[i] >> 4]; + digest_hex[i * 2 + 1] = g_hex[digest[i] & 0x0f]; + } + + digest_hex[SHA256_DIGEST_SIZE * 2] = '\0'; + return 0; +} + +static int write_text_file(const char *path, const char *content) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fputs(content, stream) == EOF) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static int write_index_file(const char *path, const char *arch, + const char *compat, const char *modprint_digest, + const char *sotest_digest) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fprintf(stream, + "{\"_nxpkg_source\":\"/etc/nxpkg/index.json\"," + "\"packages\":[" + "{\"name\":\"modprint\",\"version\":\"1.0.0\",\"arch\":\"%s\"," + "\"compat\":\"%s\"," + "\"artifact\":\"modprint\"," + "\"sha256\":\"%s\",\"type\":\"shared-lib\"}," + "{\"name\":\"modprint\",\"version\":\"9.9.9\"," + "\"arch\":\"arm\"," + "\"compat\":\"stm32f4discovery\"," + "\"artifact\":\"modprint\"," + "\"sha256\":\"%s\",\"type\":\"shared-lib\"}," + "{\"name\":\"sotest\",\"version\":\"1.0.0\",\"arch\":\"%s\"," + "\"compat\":\"%s\",\"artifact\":\"sotest\"," + "\"sha256\":\"%s\",\"type\":\"shared-lib\"}," + "{\"name\":\"sotest\",\"version\":\"9.9.9\",\"arch\":\"arm\"," + "\"compat\":\"stm32f4discovery\"," + "\"artifact\":\"sotest\"," + "\"sha256\":\"%s\",\"type\":\"shared-lib\"}" + "]}\n", + arch, compat, modprint_digest, modprint_digest, arch, compat, + sotest_digest, sotest_digest) < 0) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s " + " \n", + progname); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + char modprint_digest[SHA256_DIGEST_SIZE * 2 + 1]; + char sotest_digest[SHA256_DIGEST_SIZE * 2 + 1]; + static const char g_pkgsotest[] = + "mount -t tmpfs /etc\n" + "mount -t tmpfs /var\n" + "mkdir /etc/nxpkg\n" + "cp /mnt/sotest/romfs/shared-index.json /etc/nxpkg/index.json\n" + "cp /mnt/sotest/romfs/modprint /etc/nxpkg/modprint\n" + "cp /mnt/sotest/romfs/sotest /etc/nxpkg/sotest\n" + "mkdir /var/lib\n" + "mkdir /var/lib/nxpkg\n" + "cp /mnt/sotest/romfs/shared-index.json /var/lib/nxpkg/index.jsn\n" + "nxpkg install modprint\n" + "nxpkg install sotest\n" + "cp /var/lib/nxpkg/pkgs/modprint/1.0.0/modprint /etc/m\n" + "cp /var/lib/nxpkg/pkgs/sotest/1.0.0/sotest /etc/s\n" + "sotest /etc/m /etc/s\n" + "nxpkg list\n"; + + if (argc != 7) + { + usage(argv[0]); + return EXIT_FAILURE; + } + + if (sha256_file(argv[1], modprint_digest, sizeof(modprint_digest)) < 0 || + sha256_file(argv[2], sotest_digest, sizeof(sotest_digest)) < 0 || + write_index_file(argv[3], argv[5], argv[6], modprint_digest, + sotest_digest) < 0 || + write_text_file(argv[4], g_pkgsotest) < 0) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/examples/sotest/main/sotest_main.c b/examples/sotest/main/sotest_main.c index 98b11730c..6b2417ddb 100644 --- a/examples/sotest/main/sotest_main.c +++ b/examples/sotest/main/sotest_main.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -82,6 +82,21 @@ #define SOTEST_DEVPATH_FMT "/dev/ram%d" +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void sotest_show_usage(FAR const char *progname) +{ +#if CONFIG_LIBC_ELF_MAXDEPEND > 0 + fprintf(stderr, + "Usage: %s [--mount] [ ]\n", + progname); +#else + fprintf(stderr, "Usage: %s [--mount] []\n", progname); +#endif +} + /**************************************************************************** * Symbols from Auto-Generated Code ****************************************************************************/ @@ -104,7 +119,11 @@ extern const int g_sot_nexports; int main(int argc, FAR char *argv[]) { + FAR const char *modprint_path = NULL; + FAR const char *sotest_path = NULL; + bool mount_only = false; #ifdef CONFIG_EXAMPLES_SOTEST_BUILTINFS + bool mounted_builtinfs = false; char devname[32]; #endif #if CONFIG_LIBC_ELF_MAXDEPEND > 0 @@ -117,6 +136,38 @@ int main(int argc, FAR char *argv[]) #ifdef CONFIG_EXAMPLES_SOTEST_BUILTINFS struct boardioc_romdisk_s desc; #endif + +#if CONFIG_LIBC_ELF_MAXDEPEND > 0 + if (argc == 2 && strcmp(argv[1], "--mount") == 0) + { + mount_only = true; + } + else if (argc == 3) + { + modprint_path = argv[1]; + sotest_path = argv[2]; + } + else if (argc != 1) + { + sotest_show_usage(argv[0]); + return EXIT_FAILURE; + } +#else + if (argc == 2 && strcmp(argv[1], "--mount") == 0) + { + mount_only = true; + } + else if (argc == 2) + { + sotest_path = argv[1]; + } + else if (argc != 1) + { + sotest_show_usage(argv[0]); + return EXIT_FAILURE; + } +#endif + /* Set the shared library symbol table */ ret = dlsymtab((FAR struct symtab_s *)g_sot_exports, g_sot_nexports); @@ -127,6 +178,8 @@ int main(int argc, FAR char *argv[]) } #ifdef CONFIG_EXAMPLES_SOTEST_BUILTINFS + if (sotest_path == NULL || mount_only) + { /* Create a ROM disk for the ROMFS filesystem */ desc.minor = 0; /* Minor device number of the ROM disk. */ @@ -168,28 +221,48 @@ int main(int argc, FAR char *argv[]) devname, BINDIR, strerror(errno)); exit(EXIT_FAILURE); } + + mounted_builtinfs = true; + } #endif /* CONFIG_EXAMPLES_SOTEST_BUILTINFS */ + if (mount_only) + { + return EXIT_SUCCESS; + } + +#if CONFIG_LIBC_ELF_MAXDEPEND > 0 + if (modprint_path == NULL) + { + modprint_path = BINDIR "/modprint"; + } +#endif + + if (sotest_path == NULL) + { + sotest_path = BINDIR "/sotest"; + } + #if CONFIG_LIBC_ELF_MAXDEPEND > 0 /* Install the first test shared library. The first shared library only * verifies that symbols exported by one shared library can be used to * resolve undefined symbols in a second shared library. */ - handle1 = dlopen(BINDIR "/modprint", RTLD_NOW | RTLD_LOCAL); + handle1 = dlopen(modprint_path, RTLD_NOW | RTLD_LOCAL); if (handle1 == NULL) { - fprintf(stderr, "ERROR: dlopen(%s/modprint) failed\n", BINDIR); + fprintf(stderr, "ERROR: dlopen(%s) failed\n", modprint_path); exit(EXIT_FAILURE); } #endif /* Install the second test shared library */ - handle2 = dlopen(BINDIR "/sotest", RTLD_NOW | RTLD_LOCAL); + handle2 = dlopen(sotest_path, RTLD_NOW | RTLD_LOCAL); if (handle2 == NULL) { - fprintf(stderr, "ERROR: dlopen(%s/sotest) failed\n", BINDIR); + fprintf(stderr, "ERROR: dlopen(%s) failed\n", sotest_path); exit(EXIT_FAILURE); } @@ -288,15 +361,18 @@ int main(int argc, FAR char *argv[]) #endif #ifdef CONFIG_EXAMPLES_SOTEST_BUILTINFS - ret = umount(BINDIR); - if (ret < 0) + if (mounted_builtinfs) { - fprintf(stderr, "ERROR: umount(%s) failed: %d\n", - BINDIR, errno); - exit(EXIT_FAILURE); - } + ret = umount(BINDIR); + if (ret < 0) + { + fprintf(stderr, "ERROR: umount(%s) failed: %d\n", + BINDIR, errno); + exit(EXIT_FAILURE); + } - unlink(devname); + unlink(devname); + } #endif /* CONFIG_EXAMPLES_SOTEST_BUILTINFS */ return EXIT_SUCCESS; diff --git a/examples/sotest/modprint/CMakeLists.txt b/examples/sotest/modprint/CMakeLists.txt new file mode 100644 index 000000000..0fbeef868 --- /dev/null +++ b/examples/sotest/modprint/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/examples/sotest/modprint/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_SOTEST) + nuttx_add_application(NAME modprint SRCS modprint.c DYNLIB y) +endif() diff --git a/examples/sotest/sotest/CMakeLists.txt b/examples/sotest/sotest/CMakeLists.txt new file mode 100644 index 000000000..d4f30c1c5 --- /dev/null +++ b/examples/sotest/sotest/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/examples/sotest/sotest/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_SOTEST) + nuttx_add_application(NAME sotest SRCS sotest.c DYNLIB y) +endif() diff --git a/examples/spislv_test/Kconfig b/examples/spislv_test/Kconfig index ab273e0aa..54dd827c5 100644 --- a/examples/spislv_test/Kconfig +++ b/examples/spislv_test/Kconfig @@ -12,6 +12,12 @@ config EXAMPLES_SPISLV if EXAMPLES_SPISLV +config EXAMPLES_SPISLV_DEFAULT_PATH + string "Default SPI Slave device path" + default "/dev/spislv0" + ---help--- + Default path for SPI Slave device + config EXAMPLES_SPISLV_PROGNAME string "Program name" default "spislv" diff --git a/examples/spislv_test/spislv_test.c b/examples/spislv_test/spislv_test.c index 2ad13c236..dcde9fe3a 100644 --- a/examples/spislv_test/spislv_test.c +++ b/examples/spislv_test/spislv_test.c @@ -40,7 +40,7 @@ /* Define buffer sizes */ #define RX_BUFFER_SIZE 64 #define TX_BUFFER_SIZE 64 -#define SOURCE_FILE "dev/spislv2" /* SPI device path */ +#define DEFAULT_SPI_PATH CONFIG_EXAMPLES_SPISLV_DEFAULT_PATH /* Enumeration for operation modes */ @@ -61,6 +61,7 @@ typedef struct operation_mode_t mode; int num_bytes; /* Applicable for write mode */ int timeout_seconds; /* Applicable for all modes */ + const char *spi_path; /* SPI slave device path */ unsigned char tx_buffer[TX_BUFFER_SIZE]; } program_config_t; @@ -68,6 +69,33 @@ typedef struct * Private Functions ****************************************************************************/ +static void print_usage(const char *progname) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s -x [-p ] [-t ] \n", + progname); + fprintf(stderr, " %s -l [-p ] [-t ]\n", progname); + fprintf(stderr, " %s -e [-p ] [-t ]\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -p SPI slave device path [default: %s]\n", + DEFAULT_SPI_PATH); + fprintf(stderr, + " -t Timeout in seconds [default: 10]\n"); + fprintf(stderr, + " -x Number of bytes to queue \n"); + fprintf(stderr, + " -l Only listen for data from the master\n"); + fprintf(stderr, + " -e Echo received data back to the master\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, " %s -x 2 abba\n", progname); + fprintf(stderr, " %s -l -t 5\n", progname); + fprintf(stderr, " %s -e -t 10\n", progname); + fprintf(stderr, " %s -p /dev/spislv2 -l\n", progname); +} + /** * @brief Converts a single hexadecimal character to its byte value. * @@ -148,13 +176,25 @@ static int parse_arguments(int argc, char *argv[], config->mode = MODE_INVALID; config->num_bytes = 0; config->timeout_seconds = 10; /* Default timeout */ + config->spi_path = DEFAULT_SPI_PATH; /* Parse command-line options */ - while ((opt = getopt(argc, argv, "x:t:le")) != -1) + while ((opt = getopt(argc, argv, "p:x:t:le")) != -1) { switch (opt) { + case 'p': + if (optarg[0] == '\0') + { + fprintf(stderr, + "Error: SPI device path (-p) must not be empty.\n"); + return -1; + } + + config->spi_path = optarg; + break; + case 'x': if (config->mode != MODE_INVALID) { @@ -208,18 +248,7 @@ static int parse_arguments(int argc, char *argv[], break; default: - fprintf(stderr, "Usage:\n"); - fprintf(stderr, - " %s -x [-t ] \n", - argv[0]); - fprintf(stderr, - " %s -l [-t ]\n", argv[0]); - fprintf(stderr, - " %s -e [-t ]\n", argv[0]); - printf("Examples:\n"); - printf(" spislv -x 2 abba\n"); - printf(" spislv -l -t 5\n"); - printf(" spislv -e -t 10\n\n"); + print_usage(argv[0]); return -1; } } @@ -232,9 +261,7 @@ static int parse_arguments(int argc, char *argv[], { fprintf(stderr, "Error: Missing hexadecimal bytes to send.\n"); - fprintf(stderr, - "Usage: %s -x [-t ] \n", - argv[0]); + print_usage(argv[0]); return -1; } @@ -265,18 +292,7 @@ static int parse_arguments(int argc, char *argv[], else if (config->mode == MODE_INVALID) { fprintf(stderr, "Error: No operation mode specified.\n"); - fprintf(stderr, "Usage:\n"); - fprintf(stderr, - " %s -x [-t ] \n", - argv[0]); - fprintf(stderr, - " %s -l [-t ]\n", argv[0]); - fprintf(stderr, - " %s -e [-t ]\n", argv[0]); - printf("Examples:\n"); - printf(" spislv -x 2 abba\n"); - printf(" spislv -l -t 5\n"); - printf(" spislv -e -t 10\n"); + print_usage(argv[0]); return -1; } @@ -320,7 +336,7 @@ static int write_mode(program_config_t *config, int fd) if (bytes_written < 0) { fprintf(stderr, "Error: Failed to write to %s: %s\n", - SOURCE_FILE, strerror(errno)); + config->spi_path, strerror(errno)); return -1; } @@ -426,7 +442,7 @@ static int read_with_timeout(program_config_t *config, int fd) if (bytes_read < 0) { fprintf(stderr, "Error: Failed to read from %s: %s\n", - SOURCE_FILE, strerror(errno)); + config->spi_path, strerror(errno)); return -1; } @@ -453,7 +469,7 @@ static int read_with_timeout(program_config_t *config, int fd) { fprintf(stderr, "Error: Failed to write to %s: %s\n", - SOURCE_FILE, strerror(errno)); + config->spi_path, strerror(errno)); return -1; } else if (bytes_written != bytes_read) @@ -503,11 +519,11 @@ int main(int argc, char *argv[]) return -1; } - fd = open(SOURCE_FILE, O_RDWR); + fd = open(config.spi_path, O_RDWR); if (fd < 0) { fprintf(stderr, "Error: Failed to open %s: %s\n", - SOURCE_FILE, strerror(errno)); + config.spi_path, strerror(errno)); return -1; } diff --git a/examples/sx127x_demo/sx127x_demo.c b/examples/sx127x_demo/sx127x_demo.c index 05175a6ed..a93fe74fe 100644 --- a/examples/sx127x_demo/sx127x_demo.c +++ b/examples/sx127x_demo/sx127x_demo.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/tcpblaster/tcpblaster.h b/examples/tcpblaster/tcpblaster.h index 9fc7c4dcc..5d645f4ab 100644 --- a/examples/tcpblaster/tcpblaster.h +++ b/examples/tcpblaster/tcpblaster.h @@ -31,7 +31,7 @@ #ifdef TCPBLASTER_HOST #else -# include +# include #endif /**************************************************************************** diff --git a/examples/tcpblaster/tcpblaster_netinit.c b/examples/tcpblaster/tcpblaster_netinit.c index 6007674f4..f9719d295 100644 --- a/examples/tcpblaster/tcpblaster_netinit.c +++ b/examples/tcpblaster/tcpblaster_netinit.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/examples/tcpblaster/tcpblaster_target1.c b/examples/tcpblaster/tcpblaster_target1.c index f46fd0da8..827fe3a7d 100644 --- a/examples/tcpblaster/tcpblaster_target1.c +++ b/examples/tcpblaster/tcpblaster_target1.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "tcpblaster.h" diff --git a/examples/tcpecho/tcpecho_main.c b/examples/tcpecho/tcpecho_main.c index 96aba8096..2de49f6af 100644 --- a/examples/tcpecho/tcpecho_main.c +++ b/examples/tcpecho/tcpecho_main.c @@ -28,7 +28,7 @@ * Included Files ****************************************************************************/ -#include +#include #include #include #include diff --git a/examples/thttpd/content/tasks/tasks.c b/examples/thttpd/content/tasks/tasks.c index da7347678..089a751b6 100644 --- a/examples/thttpd/content/tasks/tasks.c +++ b/examples/thttpd/content/tasks/tasks.c @@ -26,48 +26,36 @@ #include +#include +#include +#include +#include +#include #include -#include +#include #include -#include - -#include /**************************************************************************** - * Private Data + * Pre-processor Definitions ****************************************************************************/ -static const char *g_statenames[] = -{ - "INVALID ", - "PENDING ", - "READY ", - "RUNNING ", - "INACTIVE", - "WAITSEM ", -#ifndef CONFIG_DISABLE_MQUEUE - "WAITSIG ", +#ifndef THTTPD_PROCFS_MOUNTPOINT +# define THTTPD_PROCFS_MOUNTPOINT "/proc" #endif -#ifndef CONFIG_DISABLE_MQUEUE - "MQNEMPTY", - "MQNFULL " -#endif -}; -static const char *g_ttypenames[4] = -{ - "TASK ", - "PTHREAD", - "KTHREAD", - "--?-- " -}; +#define THTTPD_STATUS_SIZE 512 -static FAR const char *g_policynames[4] = +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct thttpd_task_status_s { - "FIFO", - "RR ", - "SPOR", - "OTHR" + FAR const char *name; + FAR const char *type; + FAR const char *state; + FAR const char *priority; + FAR const char *policy; }; /**************************************************************************** @@ -78,38 +66,210 @@ static FAR const char *g_policynames[4] = * Private Functions ****************************************************************************/ -/* NOTEs: - * - * 1. One limitation in the use of NXFLAT is that functions that are - * referenced as a pointer-to-a-function must have global scope. - * Otherwise ARM GCC will generate some bad logic. - * 2. In general, when called back, there is no guarantee to that PIC - * registers will be valid and, unless you take special precautions, it - * could be dangerous to reference global variables in the callback - * function. - */ +/**************************************************************************** + * Name: tasks_trim_value + ****************************************************************************/ -void show_task(FAR struct tcb_s *tcb, FAR void *arg) +static FAR char *tasks_trim_value(FAR char *line) { - FAR const char *policy; + FAR char *end; - /* Show task/thread status */ + while (isblank((unsigned char)*line) && *line != '\0') + { + line++; + } - policy = g_policynames[(tcb->flags & TCB_FLAG_POLICY_MASK) >> - TCB_FLAG_POLICY_SHIFT]; -#if CONFIG_TASK_NAME_SIZE > 0 - printf("%5d %3d %4s %7s %8s %s\n", -#else - printf("%5d %3d %4s %7s %8s\n", -#endif - tcb->pid, tcb->sched_priority, policy, - g_ttypenames[(tcb->flags & TCB_FLAG_TTYPE_MASK) >> - TCB_FLAG_TTYPE_SHIFT], - g_statenames[tcb->task_state] -#if CONFIG_TASK_NAME_SIZE > 0 - , tcb->name -#endif - ); + end = line + strlen(line); + while (end > line && + (*(end - 1) == '\n' || *(end - 1) == '\r' || + isblank((unsigned char)*(end - 1)))) + { + end--; + } + + *end = '\0'; + return line; +} + +/**************************************************************************** + * Name: tasks_is_pid_dir + ****************************************************************************/ + +static bool tasks_is_pid_dir(FAR const struct dirent *entryp) +{ + FAR const char *name = entryp->d_name; + + if (!DIRENT_ISDIRECTORY(entryp->d_type) || *name == '\0') + { + return false; + } + + for (; *name != '\0'; name++) + { + if (!isdigit((unsigned char)*name)) + { + return false; + } + } + + return true; +} + +/**************************************************************************** + * Name: tasks_parse_status_line + ****************************************************************************/ + +static void tasks_parse_status_line(FAR char *line, + FAR struct thttpd_task_status_s *status) +{ + if (strncmp(line, "Name:", 5) == 0) + { + status->name = tasks_trim_value(&line[5]); + } + else if (strncmp(line, "Type:", 5) == 0) + { + status->type = tasks_trim_value(&line[5]); + } + else if (strncmp(line, "State:", 6) == 0) + { + status->state = tasks_trim_value(&line[6]); + } + else if (strncmp(line, "Priority:", 9) == 0) + { + status->priority = tasks_trim_value(&line[9]); + } + else if (strncmp(line, "Scheduler:", 10) == 0) + { + status->policy = tasks_trim_value(&line[10]); + } +} + +/**************************************************************************** + * Name: tasks_parse_status + ****************************************************************************/ + +static void tasks_parse_status(FAR char *buffer, + FAR struct thttpd_task_status_s *status) +{ + FAR char *line = buffer; + + while (line != NULL && *line != '\0') + { + FAR char *next = strchr(line, '\n'); + if (next != NULL) + { + *next++ = '\0'; + } + + tasks_parse_status_line(line, status); + line = next; + } +} + +/**************************************************************************** + * Name: tasks_read_status + ****************************************************************************/ + +static int tasks_read_status(FAR const char *pid, FAR char *buffer, + size_t buflen, + FAR struct thttpd_task_status_s *status) +{ + char filepath[sizeof(THTTPD_PROCFS_MOUNTPOINT) + NAME_MAX + + sizeof("/status") + 1]; + ssize_t nread; + size_t total = 0; + int ret; + int fd; + + ret = snprintf(filepath, sizeof(filepath), "%s/%s/status", + THTTPD_PROCFS_MOUNTPOINT, pid); + if (ret < 0 || ret >= sizeof(filepath)) + { + return -ENAMETOOLONG; + } + + fd = open(filepath, O_RDONLY); + if (fd < 0) + { + return -errno; + } + + while (total < buflen - 1) + { + nread = read(fd, &buffer[total], buflen - 1 - total); + if (nread < 0) + { + ret = -errno; + close(fd); + return ret; + } + else if (nread == 0) + { + break; + } + + total += nread; + } + + close(fd); + buffer[total] = '\0'; + tasks_parse_status(buffer, status); + return OK; +} + +/**************************************************************************** + * Name: show_task + ****************************************************************************/ + +static void show_task(FAR const char *pid, + FAR const struct thttpd_task_status_s *status) +{ + printf("%5s %-12s %-14s %-7s %-18s %s\n", + pid, status->priority, status->policy, status->type, + status->state, status->name); +} + +/**************************************************************************** + * Name: show_tasks + ****************************************************************************/ + +static int show_tasks(void) +{ + FAR struct dirent *entryp; + DIR *dirp; + + dirp = opendir(THTTPD_PROCFS_MOUNTPOINT); + if (dirp == NULL) + { + printf("Unable to open %s (is procfs mounted?): %d\n", + THTTPD_PROCFS_MOUNTPOINT, errno); + return -errno; + } + + while ((entryp = readdir(dirp)) != NULL) + { + char buffer[THTTPD_STATUS_SIZE]; + struct thttpd_task_status_s status = + { + "", "", "", "", "" + }; + + if (!tasks_is_pid_dir(entryp)) + { + continue; + } + + if (tasks_read_status(entryp->d_name, buffer, sizeof(buffer), + &status) < 0) + { + continue; + } + + show_task(entryp->d_name, &status); + } + + closedir(dirp); + return OK; } /**************************************************************************** @@ -139,10 +299,12 @@ int main(int argc, char *argv[]) "
\r\n" "\r\n" "
\r\n" - "
\r\n"
-    "PID   PRI SCHD TYPE   NP STATE    NAME\r\n");
+    "
\r\n");
 
-  nxsched_foreach(show_task, NULL);
+  printf("%5s %-12s %-14s %-7s %-18s %s\n",
+         "PID", "PRI", "SCHEDULER", "TYPE", "STATE", "NAME");
+
+  show_tasks();
 
   puts(
     "
\r\n" diff --git a/examples/thttpd/thttpd_main.c b/examples/thttpd/thttpd_main.c index 291e7552c..5ee01b455 100644 --- a/examples/thttpd/thttpd_main.c +++ b/examples/thttpd/thttpd_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/touchscreen/tc_main.c b/examples/touchscreen/tc_main.c index 129de8d27..b2f40a8f7 100644 --- a/examples/touchscreen/tc_main.c +++ b/examples/touchscreen/tc_main.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #ifdef CONFIG_EXAMPLES_TOUCHSCREEN_MOUSE # include diff --git a/examples/txmorse/CMakeLists.txt b/examples/txmorse/CMakeLists.txt new file mode 100644 index 000000000..469455762 --- /dev/null +++ b/examples/txmorse/CMakeLists.txt @@ -0,0 +1,33 @@ +# ############################################################################## +# apps/examples/txmorse/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_TXMORSE) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_TXMORSE_PROGNAME} + SRCS + txmorse_main.c + STACKSIZE + ${CONFIG_EXAMPLES_TXMORSE_STACKSIZE} + PRIORITY + ${CONFIG_EXAMPLES_TXMORSE_PRIORITY}) +endif() diff --git a/examples/txmorse/Kconfig b/examples/txmorse/Kconfig new file mode 100644 index 000000000..3754a9ffe --- /dev/null +++ b/examples/txmorse/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_TXMORSE + tristate "txmorse (Morse code transmitter)" + default n + depends on AUDIOUTILS_MORSEY + ---help--- + Enable the Morse code transmission example. + +if EXAMPLES_TXMORSE + +config EXAMPLES_TXMORSE_PROGNAME + string "Program name" + default "txmorse" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_TXMORSE_PRIORITY + int "txmorse task priority" + default 100 + +config EXAMPLES_TXMORSE_STACKSIZE + int "txmorse stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/txmorse/Make.defs b/examples/txmorse/Make.defs new file mode 100644 index 000000000..0c52d4fb8 --- /dev/null +++ b/examples/txmorse/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/txmorse/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_TXMORSE),) +CONFIGURED_APPS += $(APPDIR)/examples/txmorse +endif diff --git a/examples/txmorse/Makefile b/examples/txmorse/Makefile new file mode 100644 index 000000000..8d9bcefff --- /dev/null +++ b/examples/txmorse/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/examples/txmorse/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Application info + +PROGNAME = $(CONFIG_EXAMPLES_TXMORSE_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_TXMORSE_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_TXMORSE_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_TXMORSE) + +MAINSRC = txmorse_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/txmorse/txmorse_main.c b/examples/txmorse/txmorse_main.c new file mode 100644 index 000000000..cc2a3f957 --- /dev/null +++ b/examples/txmorse/txmorse_main.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * apps/examples/txmorse/txmorse_main.c + * + * SPDX-License-Identifer: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DEV_GPIO +#include +#endif + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Default millisecond duration of dot (2.5WPM) */ + +#define DEFAULT_DURATION (240) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: transmit_console + * + * Description: + * Transmit Morse code as printed characters to the console. + ****************************************************************************/ + +static void transmit_console(int on, unsigned duration_ms, void *arg) +{ + unsigned ms_per_unit = *((unsigned *)arg); + + if (on) + { + /* We are printing a dot or a dash */ + + if (duration_ms == ms_per_unit * MORSEY_UNITS_DOT) + { + putchar('.'); + } + else if (duration_ms == ms_per_unit * MORSEY_UNITS_DASH) + { + putchar('-'); + } + } + else + { + /* We are indicating separation */ + + if (duration_ms == ms_per_unit * MORSEY_UNITS_INTERELEM) + { + return; /* Inter-element gap, do nothing */ + } + else if (duration_ms == ms_per_unit * MORSEY_UNITS_INTERLET) + { + putchar(' '); /* Inter-letter gap, print a space */ + } + else if (duration_ms == ms_per_unit * MORSEY_UNITS_INTERWORD) + { + putchar('/'); /* Inter-word gap, print / delimiter */ + } + } +} + +/**************************************************************************** + * Name: cleanup_console + * + * Description: + * Default cleanup function; does nothing. + * + * Input Parameters: + * arg - User argument. + * + ****************************************************************************/ + +static void cleanup_console(void *arg) +{ + UNUSED(arg); + putchar('\n'); /* Newline character after message is done */ +} + +#ifdef CONFIG_DEV_GPIO + +/**************************************************************************** + * Name: transmit_gpio + * + * Description: + * Transmit Morse over a GPIO pin. + * + * Input Parameters: + * arg - Pointer to file descriptor of GPIO device + * + ****************************************************************************/ + +static void transmit_gpio(int on, unsigned duration_ms, void *arg) +{ + int err; + int fd = *((int *)arg); + + if (on) + { + err = ioctl(fd, GPIOC_WRITE, 1); + if (err < 0) + { + fprintf(stderr, "Failed to turn on GPIO: %d\n", errno); + return; + } + } + + usleep(duration_ms * 1000); + + err = ioctl(fd, GPIOC_WRITE, 0); + if (err < 0) + { + fprintf(stderr, "Failed to turn off GPIO: %d\n", errno); + } +} + +/**************************************************************************** + * Name: cleanup_gpio + * + * Description: + * Clean up a GPIO Morse sink. + * + * Input Parameters: + * arg - User argument (pointer to file descriptor) + * + ****************************************************************************/ + +static void cleanup_gpio(void *arg) +{ + int fd = *((int *)arg); + close(fd); +} + +#endif /* CONFIG_DEV_GPIO */ + +/**************************************************************************** + * Name: print_usage + * + * Description: + * Prints out the usage information for the program. + * + * Input Parameters: + * sink - The stream to output the usage information + * + ****************************************************************************/ + +static void print_usage(FILE *sink) +{ + fprintf(sink, "USAGE: " CONFIG_EXAMPLES_TXMORSE_PROGNAME + " [-d dot_duration_ms] \"my text\" [path/to/device]\n"); + fprintf(sink, + "Ex: " CONFIG_EXAMPLES_TXMORSE_PROGNAME " \"sos\" /dev/gpio1\n"); + fprintf(sink, "Default dot duration is 240 ms\n"); + fprintf(sink, "Default device is output to console\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv) +{ + int err; + int c; + int fd; + struct morsey_state_s state; + + transmit_f txfunc = transmit_console; /* Default transmit */ + void (*cleanupfunc)(void *) = cleanup_console; /* Default cleanup */ + void *arg = NULL; + + unsigned duration = DEFAULT_DURATION; + char *devpath = NULL; + char *message = NULL; + + arg = &duration; /* Default argument */ + + /* Parse command line arguments */ + + while ((c = getopt(argc, argv, ":hd:")) != -1) + { + switch (c) + { + case 'h': + print_usage(stdout); + return EXIT_SUCCESS; + case 'd': + duration = strtoul(optarg, NULL, 10); + break; + case '?': + fprintf(stderr, "Unknown option -%c\n", optopt); + print_usage(stderr); + return EXIT_FAILURE; + } + } + + /* Get text to encode */ + + if (argc <= optind) + { + fprintf(stderr, "Missing text to encode!\n"); + return EXIT_FAILURE; + } + + message = argv[optind++]; + + /* Check for optional device path */ + + if (argc > optind) + { + devpath = argv[optind++]; + } + + /* Choose transmit function based on device path */ + + if (devpath == NULL) + { + txfunc = transmit_console; + cleanupfunc = cleanup_console; + arg = &duration; + } +#ifdef CONFIG_DEV_GPIO + else if (strstr(devpath, "gpio")) + { + txfunc = transmit_gpio; + cleanupfunc = cleanup_gpio; + fd = open(devpath, O_WRONLY); + if (fd < 0) + { + fprintf(stderr, "Couldn't open '%s': %d\n", devpath, errno); + return EXIT_FAILURE; + } + + arg = &fd; + } +#endif + else + { + fprintf(stderr, "Unrecognized device '%s', defaulting to console.\n", + devpath); + } + + /* Set up library to play */ + + morsey_init(&state, duration, 0); + + /* Transmit the text as Morse code */ + + err = morsey_transmit(message, strlen(message), &state, txfunc, arg); + if (err == 0) + { + cleanupfunc(arg); + return 0; + } + + fprintf(stderr, "Error transmitting: %d\n", err); + cleanupfunc(arg); + return err; +} diff --git a/examples/udgram/udgram_client.c b/examples/udgram/udgram_client.c index f60abedf6..c0fd63a4f 100644 --- a/examples/udgram/udgram_client.c +++ b/examples/udgram/udgram_client.c @@ -100,7 +100,7 @@ int main(int argc, FAR char *argv[]) server.sun_family = AF_LOCAL; strlcpy(server.sun_path, CONFIG_EXAMPLES_UDGRAM_ADDR, addrlen); - addrlen += sizeof(sa_family_t) + 1; + addrlen += sizeof(sa_family_t); /* Send the message */ diff --git a/examples/udgram/udgram_server.c b/examples/udgram/udgram_server.c index 49674ff36..cd1cc928c 100644 --- a/examples/udgram/udgram_server.c +++ b/examples/udgram/udgram_server.c @@ -102,7 +102,7 @@ int main(int argc, FAR char *argv[]) server.sun_family = AF_LOCAL; strlcpy(server.sun_path, CONFIG_EXAMPLES_UDGRAM_ADDR, addrlen); - addrlen += sizeof(sa_family_t) + 1; + addrlen += sizeof(sa_family_t); if (bind(sockfd, (struct sockaddr *)&server, addrlen) < 0) { diff --git a/examples/udp/CMakeLists.txt b/examples/udp/CMakeLists.txt index 996b9a9be..b3cbdf166 100644 --- a/examples/udp/CMakeLists.txt +++ b/examples/udp/CMakeLists.txt @@ -21,5 +21,60 @@ # ############################################################################## if(CONFIG_EXAMPLES_UDP) - nuttx_add_application(NAME udp) + + # Basic UDP networking test + + set(CSRCS udp_cmdline.c) + + if(CONFIG_EXAMPLES_UDP_NETINIT) + list(APPEND CSRCS udp_netinit.c) + endif() + + # Target 1 Files + + if(CONFIG_EXAMPLES_UDP_SERVER1) + list(APPEND CSRCS udp_server.c) + else() + list(APPEND CSRCS udp_client.c) + endif() + + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_UDP_PROGNAME1} + PRIORITY + ${CONFIG_EXAMPLES_UDP_PRIORITY1} + STACKSIZE + ${CONFIG_EXAMPLES_UDP_STACKSIZE1} + MODULE + ${CONFIG_EXAMPLES_UDP} + INCLUDE_DIRECTORIES + ${CMAKE_BINARY_DIR}/include/nuttx + SRCS + udp_target1.c + ${CSRCS}) + + # Target 2 Files + + if(CONFIG_EXAMPLES_UDP_TARGET2) + if(CONFIG_EXAMPLES_UDP_SERVER1) + list(APPEND CSRCS udp_client.c) + else() + list(APPEND CSRCS udp_server.c) + endif() + + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_UDP_PROGNAME2} + PRIORITY + ${CONFIG_EXAMPLES_UDP_PRIORITY2} + STACKSIZE + ${CONFIG_EXAMPLES_UDP_STACKSIZE2} + MODULE + ${CONFIG_EXAMPLES_UDP} + INCLUDE_DIRECTORIES + ${CMAKE_BINARY_DIR}/include/nuttx + SRCS + udp_target2.c + ${CSRCS}) + endif() endif() diff --git a/examples/udp/udp.h b/examples/udp/udp.h index 415317f50..2f15d8ea3 100644 --- a/examples/udp/udp.h +++ b/examples/udp/udp.h @@ -29,7 +29,7 @@ #ifdef EXAMPLES_UDP_HOST #else -# include +# include #endif #include diff --git a/examples/udp/udp_netinit.c b/examples/udp/udp_netinit.c index 3cd2625b7..d5851b22a 100644 --- a/examples/udp/udp_netinit.c +++ b/examples/udp/udp_netinit.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/examples/udpblaster/udpblaster_target.c b/examples/udpblaster/udpblaster_target.c index 0df0072d4..be5908fbd 100644 --- a/examples/udpblaster/udpblaster_target.c +++ b/examples/udpblaster/udpblaster_target.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/uid/uid_main.c b/examples/uid/uid_main.c index cc4bfc6fd..9792114be 100644 --- a/examples/uid/uid_main.c +++ b/examples/uid/uid_main.c @@ -51,10 +51,14 @@ static void show_usage(FAR const char *progname, FAR FILE *stream, int exit_code) { fprintf(stream, "USAGE:\n"); - fprintf(stream, "\t%s -uid - Show user info by ID\n", progname); - fprintf(stream, "\t%s -uname - Show user info by name\n", progname); - fprintf(stream, "\t%s -gid - Show group info by ID\n", progname); - fprintf(stream, "\t%s -gname - Show group info by name\n", progname); + fprintf(stream, "\t%s -uid - Show user info by ID\n", + progname); + fprintf(stream, "\t%s -uname - Show user info by name\n", + progname); + fprintf(stream, "\t%s -gid - Show group info by ID\n", + progname); + fprintf(stream, "\t%s -gname - Show group info by name\n", + progname); fprintf(stream, "\t%s -h - Show this help info\n", progname); exit(exit_code); } diff --git a/examples/usbserial/usbserial_main.c b/examples/usbserial/usbserial_main.c index 66e4790b9..e1eec27c8 100644 --- a/examples/usbserial/usbserial_main.c +++ b/examples/usbserial/usbserial_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/userfs/userfs_main.c b/examples/userfs/userfs_main.c index a2cf97ea5..d1d64fa80 100644 --- a/examples/userfs/userfs_main.c +++ b/examples/userfs/userfs_main.c @@ -142,19 +142,28 @@ static char g_file3_data[UFSTEST_FILE3_MXSIZE] = "This is file 3"; static struct ufstest_file_s g_rootdir[UFSTEST_NFILES] = { { - { DTYPE_FILE, "File1" }, + { + .d_type = DTYPE_FILE, + .d_name = "File1" + }, UFSTEST_INIT_FILE1_SIZE, UFSTEST_FILE1_MXSIZE, g_file1_data }, { - { DTYPE_FILE, "File2" }, + { + .d_type = DTYPE_FILE, + .d_name = "File2" + }, UFSTEST_INIT_FILE2_SIZE, UFSTEST_FILE2_MXSIZE, g_file2_data }, { - { DTYPE_FILE, "File3" }, + { + .d_type = DTYPE_FILE, + .d_name = "File3" + }, UFSTEST_INIT_FILE3_SIZE, UFSTEST_FILE3_MXSIZE, g_file3_data @@ -236,7 +245,7 @@ static int ufstest_open(FAR void *volinfo, FAR const char *relpath, file->inuse = 0; } - if ((oflags & (O_WROK | O_APPEND)) == (O_WROK | O_APPEND)) + if ((oflags & (O_WRONLY | O_APPEND)) == (O_WRONLY | O_APPEND)) { opriv->pos = file->inuse; } @@ -247,7 +256,7 @@ static int ufstest_open(FAR void *volinfo, FAR const char *relpath, opriv->file = file; - /* Initiallly, there is one reference count on the open data. This may + /* Initially, there is one reference count on the open data. This may * be incremented in the event that the file is dup'ed. */ diff --git a/examples/usrsocktest/usrsocktest_daemon.c b/examples/usrsocktest/usrsocktest_daemon.c index 3d84d8396..efa543591 100644 --- a/examples/usrsocktest/usrsocktest_daemon.c +++ b/examples/usrsocktest/usrsocktest_daemon.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/examples/usrsocktest/usrsocktest_main.c b/examples/usrsocktest/usrsocktest_main.c index d09036c38..c6109423e 100644 --- a/examples/usrsocktest/usrsocktest_main.c +++ b/examples/usrsocktest/usrsocktest_main.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/examples/usrsocktest/usrsocktest_noblock_connect.c b/examples/usrsocktest/usrsocktest_noblock_connect.c index bc7264bfe..32906e2a2 100644 --- a/examples/usrsocktest/usrsocktest_noblock_connect.c +++ b/examples/usrsocktest/usrsocktest_noblock_connect.c @@ -120,13 +120,13 @@ TEST(no_block_connect, instant_connect) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Do connect, should succeed instantly. */ @@ -188,13 +188,13 @@ TEST(no_block_connect, delayed_connect) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until @@ -290,13 +290,13 @@ TEST(no_block_connect, close_not_connected) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until @@ -363,13 +363,13 @@ TEST(no_block_connect, early_drop) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until @@ -443,24 +443,24 @@ TEST(no_block_connect, multiple) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempts, daemon delays actual connection until @@ -552,24 +552,24 @@ TEST(no_block_connect, multiple) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempts, daemon delays actual connection until @@ -637,24 +637,24 @@ TEST(no_block_connect, multiple) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd2, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until @@ -758,13 +758,13 @@ TEST(no_block_connect, basic_daemon_dup2) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until diff --git a/examples/usrsocktest/usrsocktest_noblock_recv.c b/examples/usrsocktest/usrsocktest_noblock_recv.c index 146471d6f..ffacefde3 100644 --- a/examples/usrsocktest/usrsocktest_noblock_recv.c +++ b/examples/usrsocktest/usrsocktest_noblock_recv.c @@ -114,13 +114,13 @@ static void receive(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Do connect, should succeed instantly. */ @@ -310,13 +310,13 @@ static void delayed_connect(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until diff --git a/examples/usrsocktest/usrsocktest_noblock_send.c b/examples/usrsocktest/usrsocktest_noblock_send.c index 7b039f3ae..8526c29b2 100644 --- a/examples/usrsocktest/usrsocktest_noblock_send.c +++ b/examples/usrsocktest/usrsocktest_noblock_send.c @@ -110,13 +110,13 @@ static void _send(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Do connect, should succeed instantly. */ @@ -231,13 +231,13 @@ static void connect_send(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Launch connect attempt, daemon delays actual connection until diff --git a/examples/usrsocktest/usrsocktest_poll.c b/examples/usrsocktest/usrsocktest_poll.c index 8ab7f6ce0..66cda8aa6 100644 --- a/examples/usrsocktest/usrsocktest_poll.c +++ b/examples/usrsocktest/usrsocktest_poll.c @@ -115,13 +115,13 @@ static void connectreceive(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* poll for input (instant timeout). */ @@ -321,13 +321,13 @@ static void connectsend(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* poll for input (instant timeout). */ @@ -454,13 +454,13 @@ static void daemonabort(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* poll for input (instant timeout). */ diff --git a/examples/usrsocktest/usrsocktest_remote_disconnect.c b/examples/usrsocktest/usrsocktest_remote_disconnect.c index 1e3681f9d..a49acc03f 100644 --- a/examples/usrsocktest/usrsocktest_remote_disconnect.c +++ b/examples/usrsocktest/usrsocktest_remote_disconnect.c @@ -151,13 +151,13 @@ static void unreachable(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Try connect, connection in progress. */ @@ -670,13 +670,13 @@ static void remote_disconnect_poll(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Poll for input (instant timeout). */ @@ -797,13 +797,13 @@ static void remote_disconnect_poll2(struct usrsocktest_daemon_conf_s *dconf) flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); TEST_ASSERT_EQUAL(0, ret); flags = fcntl(sd, F_GETFL, 0); TEST_ASSERT_TRUE(flags >= 0); - TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_ACCMODE); TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); /* Poll for input (instant timeout). */ @@ -871,7 +871,6 @@ static void remote_disconnect_poll2(struct usrsocktest_daemon_conf_s *dconf) TEST_ASSERT_EQUAL(1, ret); TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); - TEST_ASSERT_EQUAL(*events & POLLIN, pfd.revents & POLLIN); TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); diff --git a/examples/usrsocktest/usrsocktest_wake_with_signal.c b/examples/usrsocktest/usrsocktest_wake_with_signal.c index 800eb571d..da460ab15 100644 --- a/examples/usrsocktest/usrsocktest_wake_with_signal.c +++ b/examples/usrsocktest/usrsocktest_wake_with_signal.c @@ -146,11 +146,20 @@ static void do_usrsock_blocking_socket_thread(FAR void *param) TEST_ASSERT_TRUE(test_hang); TEST_ASSERT_TRUE(test_abort); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Allow main thread to hang usrsock daemon at this point. */ sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt hanging open socket. */ sem_post(&tid_startsem); @@ -176,6 +185,11 @@ static void do_usrsock_blocking_close_thread(FAR void *param) TEST_ASSERT_TRUE(test_hang); TEST_ASSERT_TRUE(test_abort); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -190,6 +204,10 @@ static void do_usrsock_blocking_close_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt hanging close socket. */ sem_post(&tid_startsem); @@ -214,6 +232,11 @@ static void do_usrsock_blocking_connect_thread(FAR void *param) TEST_ASSERT_TRUE(test_hang || !test_hang); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -228,6 +251,10 @@ static void do_usrsock_blocking_connect_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt blocking connect. */ sem_post(&tid_startsem); @@ -257,6 +284,11 @@ static void do_usrsock_blocking_setsockopt_thread(FAR void *param) bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -274,6 +306,10 @@ static void do_usrsock_blocking_setsockopt_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt hanging setsockopt. */ sem_post(&tid_startsem); @@ -304,6 +340,11 @@ static void do_usrsock_blocking_getsockopt_thread(FAR void *param) bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -321,6 +362,10 @@ static void do_usrsock_blocking_getsockopt_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt hanging getsockopt. */ sem_post(&tid_startsem); @@ -352,6 +397,11 @@ static void do_usrsock_blocking_send_thread(FAR void *param) TEST_ASSERT_TRUE(test_hang || !test_hang); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -372,6 +422,10 @@ static void do_usrsock_blocking_send_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt blocking send. */ sem_post(&tid_startsem); @@ -401,6 +455,11 @@ static void do_usrsock_blocking_recv_thread(FAR void *param) TEST_ASSERT_TRUE(test_hang || !test_hang); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -421,6 +480,10 @@ static void do_usrsock_blocking_recv_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt blocking recv. */ sem_post(&tid_startsem); @@ -454,6 +517,11 @@ static void do_usrsock_blocking_poll_thread(FAR void *param) TEST_ASSERT_TRUE(test_abort); TEST_ASSERT_TRUE(test_hang || !test_hang); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + /* Open socket. */ test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); @@ -474,6 +542,10 @@ static void do_usrsock_blocking_poll_thread(FAR void *param) sem_post(&tid_startsem); sem_wait(&tid_releasesem); +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_testcancel(); +#endif + /* Attempt poll. */ pfd.fd = test_sd[tidx]; @@ -560,8 +632,11 @@ static void do_wake_test(enum e_test_type type, int flags) for (tidx = 0; tidx < nthreads; tidx++) { +#ifdef CONFIG_DISABLE_ALL_SIGNALS + pthread_cancel(tid[tidx]); +#else pthread_kill(tid[tidx], SIGUSR1); - +#endif /* Wait threads to complete work. */ ret = pthread_join(tid[tidx], NULL); diff --git a/examples/ustream/ustream_client.c b/examples/ustream/ustream_client.c index c7e69a56b..a80e185aa 100644 --- a/examples/ustream/ustream_client.c +++ b/examples/ustream/ustream_client.c @@ -88,10 +88,9 @@ int main(int argc, FAR char *argv[]) myaddr.sun_family = AF_LOCAL; strlcpy(myaddr.sun_path, CONFIG_EXAMPLES_USTREAM_ADDR, addrlen); - myaddr.sun_path[addrlen] = '\0'; printf("client: Connecting to %s...\n", CONFIG_EXAMPLES_USTREAM_ADDR); - addrlen += sizeof(sa_family_t) + 1; + addrlen += sizeof(sa_family_t); ret = connect(sockfd, (struct sockaddr *)&myaddr, addrlen); if (ret < 0) { diff --git a/examples/ustream/ustream_server.c b/examples/ustream/ustream_server.c index 8f6fffe40..a14c39574 100644 --- a/examples/ustream/ustream_server.c +++ b/examples/ustream/ustream_server.c @@ -88,9 +88,8 @@ int main(int argc, char *argv[]) myaddr.sun_family = AF_LOCAL; strlcpy(myaddr.sun_path, CONFIG_EXAMPLES_USTREAM_ADDR, addrlen); - myaddr.sun_path[addrlen] = '\0'; - addrlen += sizeof(sa_family_t) + 1; + addrlen += sizeof(sa_family_t); ret = bind(listensd, (struct sockaddr *)&myaddr, addrlen); if (ret < 0) { diff --git a/examples/uvc_cam/CMakeLists.txt b/examples/uvc_cam/CMakeLists.txt new file mode 100644 index 000000000..952fd3e4b --- /dev/null +++ b/examples/uvc_cam/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/examples/uvc_cam/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_UVC_CAM) + nuttx_add_application( + NAME + ${CONFIG_EXAMPLES_UVC_CAM_PROGNAME} + PRIORITY + ${CONFIG_EXAMPLES_UVC_CAM_PRIORITY} + STACKSIZE + ${CONFIG_EXAMPLES_UVC_CAM_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_UVC_CAM} + SRCS + uvc_cam_main.c) +endif() diff --git a/examples/uvc_cam/Kconfig b/examples/uvc_cam/Kconfig new file mode 100644 index 000000000..83e6ca52d --- /dev/null +++ b/examples/uvc_cam/Kconfig @@ -0,0 +1,32 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_UVC_CAM + tristate "UVC Camera streaming example" + default n + depends on USBUVC && VIDEO + ---help--- + Captures frames from V4L2 camera and streams to USB host + via UVC gadget driver (/dev/uvc0). + +if EXAMPLES_UVC_CAM + +config EXAMPLES_UVC_CAM_PROGNAME + string "Program name" + default "uvc_cam" + +config EXAMPLES_UVC_CAM_PRIORITY + int "Task priority" + default 100 + +config EXAMPLES_UVC_CAM_STACKSIZE + int "Stack size" + default 4096 + +config EXAMPLES_UVC_CAM_NFRAMES + int "Number of frames (0=infinite)" + default 0 + +endif diff --git a/examples/uvc_cam/Make.defs b/examples/uvc_cam/Make.defs new file mode 100644 index 000000000..8a5c46a47 --- /dev/null +++ b/examples/uvc_cam/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/uvc_cam/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_UVC_CAM),) +CONFIGURED_APPS += $(APPDIR)/examples/uvc_cam +endif diff --git a/examples/uvc_cam/Makefile b/examples/uvc_cam/Makefile new file mode 100644 index 000000000..409bc2494 --- /dev/null +++ b/examples/uvc_cam/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/examples/uvc_cam/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# UVC Camera streaming application + +PROGNAME = $(CONFIG_EXAMPLES_UVC_CAM_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_UVC_CAM_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_UVC_CAM_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_UVC_CAM) + +MAINSRC = uvc_cam_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/uvc_cam/uvc_cam_main.c b/examples/uvc_cam/uvc_cam_main.c new file mode 100644 index 000000000..a47d97e84 --- /dev/null +++ b/examples/uvc_cam/uvc_cam_main.c @@ -0,0 +1,482 @@ +/**************************************************************************** + * apps/examples/uvc_cam/uvc_cam_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * USB Video Class camera streaming application. + * Captures frames from V4L2 camera and streams to /dev/uvc0. + * + * Queries the sensor's native pixel format, resolution and frame rate + * via V4L2, then streams frames to the UVC gadget. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define UVC_DEV_DEFAULT "/dev/uvc0" +#define VIDEO_DEV_DEFAULT "/dev/video0" + +#ifndef CONFIG_EXAMPLES_UVC_CAM_NFRAMES +# define CONFIG_EXAMPLES_UVC_CAM_NFRAMES 0 /* 0 = infinite */ +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pixfmt_to_bpp + * + * Description: + * Return bytes-per-pixel for common V4L2 pixel formats. + * + ****************************************************************************/ + +static int pixfmt_to_bpp(uint32_t pixfmt) +{ + switch (pixfmt) + { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_RGB565: + return 2; + case V4L2_PIX_FMT_RGB24: + return 3; + default: + return 0; + } +} + +/**************************************************************************** + * Name: camera_init + * + * Description: + * Open and configure the V4L2 camera device. + * Queries the sensor's native pixel format, resolution and frame rate + * and returns them via output parameters. + * + ****************************************************************************/ + +static int camera_init(int fd, FAR uint32_t *pixfmt, FAR uint16_t *width, + FAR uint16_t *height, FAR uint16_t *fps) +{ + struct v4l2_fmtdesc fmtdesc; + struct v4l2_frmivalenum frmival; + struct v4l2_frmsizeenum frmsize; + struct v4l2_format fmt; + struct v4l2_requestbuffers req; + int ret; + + /* Query native pixel format from sensor */ + + memset(&fmtdesc, 0, sizeof(fmtdesc)); + fmtdesc.index = 0; + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc); + if (ret < 0) + { + printf("VIDIOC_ENUM_FMT failed: %d\n", errno); + return -errno; + } + + printf("Sensor format: %.4s\n", (FAR const char *)&fmtdesc.pixelformat); + + /* Query native resolution from sensor */ + + memset(&frmsize, 0, sizeof(frmsize)); + frmsize.index = 0; + frmsize.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + frmsize.pixel_format = fmtdesc.pixelformat; + + ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize); + if (ret < 0) + { + printf("VIDIOC_ENUM_FRAMESIZES failed: %d\n", errno); + return -errno; + } + + printf("Sensor native: %dx%d\n", + frmsize.discrete.width, frmsize.discrete.height); + + /* Set format — capture at native resolution */ + + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = frmsize.discrete.width; + fmt.fmt.pix.height = frmsize.discrete.height; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + fmt.fmt.pix.pixelformat = fmtdesc.pixelformat; + + ret = ioctl(fd, VIDIOC_S_FMT, &fmt); + if (ret < 0) + { + printf("VIDIOC_S_FMT failed: %d\n", errno); + return -errno; + } + + /* Request buffers — match camcap: USERPTR + FIFO */ + + memset(&req, 0, sizeof(req)); + req.count = 1; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + req.mode = V4L2_BUF_MODE_FIFO; + + ret = ioctl(fd, VIDIOC_REQBUFS, &req); + if (ret < 0) + { + printf("VIDIOC_REQBUFS failed: %d\n", errno); + return -errno; + } + + /* NOTE: Do NOT call VIDIOC_STREAMON here. + * With USERPTR + FIFO mode, STREAMON would start DMA capture + * immediately using the container's uninitialized userptr (0), + * causing DMA to write 153KB of video data to address 0 and + * corrupt memory. STREAMON must be called AFTER the first QBUF + * so that userptr points to a valid buffer. + */ + + /* Query frame interval from sensor */ + + memset(&frmival, 0, sizeof(frmival)); + frmival.index = 0; + frmival.buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + frmival.pixel_format = fmtdesc.pixelformat; + frmival.width = frmsize.discrete.width; + frmival.height = frmsize.discrete.height; + + ret = ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); + if (ret < 0) + { + printf("VIDIOC_ENUM_FRAMEINTERVALS failed: %d\n", errno); + return -errno; + } + + *pixfmt = fmtdesc.pixelformat; + *width = frmsize.discrete.width; + *height = frmsize.discrete.height; + *fps = frmival.discrete.numerator > 0 + ? frmival.discrete.denominator / frmival.discrete.numerator + : 0; + return OK; +} + +/**************************************************************************** + * Name: camera_capture_frame + * + * Description: + * Capture one frame from V4L2 into the provided buffer. + * + ****************************************************************************/ + +static int camera_capture_frame(int camfd, FAR uint8_t *buf, size_t buflen, + bool do_streamon) +{ + struct v4l2_buffer vbuf; + int ret; + + memset(&vbuf, 0, sizeof(vbuf)); + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vbuf.memory = V4L2_MEMORY_USERPTR; + vbuf.index = 0; + vbuf.m.userptr = (uintptr_t)buf; + vbuf.length = buflen; + + ret = ioctl(camfd, VIDIOC_QBUF, &vbuf); + if (ret < 0) + { + printf("VIDIOC_QBUF failed: %d\n", errno); + return -errno; + } + + /* Start streaming after the first QBUF so DMA has a valid buffer */ + + if (do_streamon) + { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = ioctl(camfd, VIDIOC_STREAMON, &type); + if (ret < 0) + { + printf("VIDIOC_STREAMON failed: %d\n", errno); + return -errno; + } + } + + ret = ioctl(camfd, VIDIOC_DQBUF, &vbuf); + if (ret < 0) + { + printf("VIDIOC_DQBUF failed: %d\n", errno); + return -errno; + } + + return vbuf.bytesused > 0 ? (int)vbuf.bytesused : (int)buflen; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct boardioc_usbdev_ctrl_s ctrl; + struct uvc_params_s uvc_params; + FAR void *uvc_handle = NULL; + FAR const char *video_dev = VIDEO_DEV_DEFAULT; + FAR const char *uvc_dev = UVC_DEV_DEFAULT; + FAR uint8_t *framebuf; + ssize_t written; + enum v4l2_buf_type cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + uint32_t pixfmt = 0; + uint16_t width = 0; + uint16_t height = 0; + uint16_t fps = 0; + uint32_t frame_size; + int bpp; + int camfd; + int uvcfd; + int nframes; + int count = 0; + int ret; + + nframes = CONFIG_EXAMPLES_UVC_CAM_NFRAMES; + if (argc > 1) + { + nframes = atoi(argv[1]); + } + + if (argc > 2) + { + video_dev = argv[2]; + } + + if (argc > 3) + { + uvc_dev = argv[3]; + } + + /* Create capture device */ + + ret = capture_initialize(video_dev); + if (ret < 0 && ret != -EEXIST) + { + printf("Failed to initialize capture: %d\n", ret); + return EXIT_FAILURE; + } + + /* Open camera and query resolution */ + + camfd = open(video_dev, O_RDWR); + if (camfd < 0) + { + printf("Failed to open %s: %d\n", video_dev, errno); + return EXIT_FAILURE; + } + + ret = camera_init(camfd, &pixfmt, &width, &height, &fps); + if (ret < 0) + { + printf("Camera init failed: %d\n", ret); + close(camfd); + return EXIT_FAILURE; + } + + bpp = pixfmt_to_bpp(pixfmt); + if (bpp == 0) + { + printf("Unsupported pixel format: 0x%08lx\n", + (unsigned long)pixfmt); + close(camfd); + return EXIT_FAILURE; + } + + frame_size = (uint32_t)width * height * bpp; + printf("UVC Camera: %dx%d %.4s @%dfps, frames=%d (0=infinite)\n", + width, height, (FAR const char *)&pixfmt, fps, nframes); + + /* Allocate frame buffer — 32-byte aligned for DMA (same as camcap) */ + + framebuf = memalign(32, frame_size); + if (!framebuf) + { + printf("Failed to allocate frame buffer (%lu bytes)\n", + (unsigned long)frame_size); + close(camfd); + return EXIT_FAILURE; + } + + /* Initialize and connect UVC gadget via boardctl. + * Pass video params queried from camera so USB descriptors + * match the actual sensor resolution. + */ + + uvc_params.width = width; + uvc_params.height = height; + uvc_params.fps = fps; + + uvc_handle = (FAR void *)&uvc_params; +#ifdef CONFIG_USBUVC_COMPOSITE + ctrl.usbdev = BOARDIOC_USBDEV_COMPOSITE; +#else + ctrl.usbdev = BOARDIOC_USBDEV_UVC; +#endif + ctrl.action = BOARDIOC_USBDEV_CONNECT; + ctrl.instance = 0; + ctrl.config = 0; + ctrl.handle = &uvc_handle; + + ret = boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + + if (ret < 0) + { + printf("Failed to initialize UVC device: %d\n", ret); + close(camfd); + free(framebuf); + return EXIT_FAILURE; + } + + /* Open UVC device */ + + uvcfd = open(uvc_dev, O_WRONLY); + if (uvcfd < 0) + { + printf("Failed to open %s: %d\n", uvc_dev, errno); + ctrl.action = BOARDIOC_USBDEV_DISCONNECT; + boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + close(camfd); + free(framebuf); + return EXIT_FAILURE; + } + + /* Wait for USB host to enumerate and start streaming */ + + struct pollfd pfd; + pfd.fd = uvcfd; + pfd.events = POLLOUT; + + printf("Waiting for USB host to connect...\n"); + while (poll(&pfd, 1, 500) <= 0 || !(pfd.revents & POLLOUT)) + { + } + + printf("Streaming started...\n"); + + /* Main capture-stream loop */ + + while (nframes == 0 || count < nframes) + { + ret = camera_capture_frame(camfd, framebuf, frame_size, + count == 0); + if (ret < 0) + { + printf("Capture failed: %d\n", ret); + break; + } + + /* Write frame to UVC directly from framebuf. + * DMA won't touch framebuf until next QBUF. + */ + + written = 0; + while (written < (ssize_t)frame_size) + { + ret = write(uvcfd, framebuf + written, + frame_size - written); + if (ret < 0) + { + if (errno == EAGAIN) + { + /* Host stopped streaming - wait for reconnect. + * + * NOTE: The printf inside the loop is required + * on boards where the USB-UART bridge (CH341) + * and the ESP32-S3 OTG share an on-board USB + * hub (e.g. lckfb-szpi). Continuous UART TX + * keeps the hub active and prevents the host + * from power-cycling the hub port, which would + * cause a full board POWERON reset. + */ + + printf("Host disconnected, waiting...\n"); + pfd.revents = 0; + while (poll(&pfd, 1, 500) <= 0 + || !(pfd.revents & POLLOUT)) + { + printf(".\n"); + } + + printf("Streaming restarted.\n"); + written = 0; + break; + } + + printf("UVC write failed: %d\n", errno); + goto out; + } + + written += ret; + } + + count++; + if ((count % 10) == 0) + { + printf("Streamed %d frames\n", count); + } + } + + printf("Done. Streamed %d frames total.\n", count); + +out: + ioctl(camfd, VIDIOC_STREAMOFF, &cap_type); + + close(uvcfd); + close(camfd); + + /* Disconnect UVC gadget */ + + ctrl.action = BOARDIOC_USBDEV_DISCONNECT; + boardctl(BOARDIOC_USBDEV_CONTROL, (uintptr_t)&ctrl); + + free(framebuf); + return EXIT_SUCCESS; +} diff --git a/examples/watchdog/watchdog_main.c b/examples/watchdog/watchdog_main.c index 06a36c75d..1167f3116 100644 --- a/examples/watchdog/watchdog_main.c +++ b/examples/watchdog/watchdog_main.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include #include @@ -308,8 +308,7 @@ int main(int argc, FAR char *argv[]) /* Get current time to calculate the elapsed time */ clock_gettime(CLOCK_REALTIME, &tnow); - current_time_ms = (uint64_t)((tnow.tv_sec * 1000) - + (tnow.tv_nsec / 1000000)); + current_time_ms = (tnow.tv_sec * 1000) + (tnow.tv_nsec / 1000000); } /* Then stop pinging */ @@ -345,8 +344,7 @@ int main(int argc, FAR char *argv[]) /* Get current time to calculate the elapsed time */ clock_gettime(CLOCK_REALTIME, &tnow); - current_time_ms = (uint64_t)((tnow.tv_sec * 1000) - + (tnow.tv_nsec / 1000000)); + current_time_ms = (tnow.tv_sec * 1000) + (tnow.tv_nsec / 1000000); } /* We should not get here */ diff --git a/examples/watcher/wdt.c b/examples/watcher/wdt.c index ccfbeaa98..667240dee 100644 --- a/examples/watcher/wdt.c +++ b/examples/watcher/wdt.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/webpanel/.gitignore b/examples/webpanel/.gitignore new file mode 100644 index 000000000..583a0928d --- /dev/null +++ b/examples/webpanel/.gitignore @@ -0,0 +1,2 @@ +# ROMFS build artifacts (generated by Makefile) +/content diff --git a/examples/webpanel/CMakeLists.txt b/examples/webpanel/CMakeLists.txt new file mode 100644 index 000000000..ed47cabbd --- /dev/null +++ b/examples/webpanel/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/examples/webpanel/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_WEBPANEL) + nuttx_add_application(NAME ${CONFIG_EXAMPLES_WEBPANEL_PROGNAME}) +endif() diff --git a/examples/webpanel/Kconfig b/examples/webpanel/Kconfig new file mode 100644 index 000000000..b1392c725 --- /dev/null +++ b/examples/webpanel/Kconfig @@ -0,0 +1,109 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_WEBPANEL + tristate "Web Panel for device management" + default n + depends on NET_TCP + depends on NETUTILS_THTTPD + depends on NETUTILS_LIBWEBSOCKETS + depends on NETUTILS_LIBWEBSOCKETS_SERVER + depends on FS_BINFS + depends on FS_UNIONFS + depends on PSEUDOTERM + depends on PSEUDOTERM_SUSV1 + ---help--- + Enable the Web Panel application, a Tasmota-inspired web + interface for NuttX devices. Provides a landing page served + via THTTPD from ROMFS, CGI handlers for system info, file + management, and a WebSocket-based NSH terminal (powered + by libwebsockets). + +if EXAMPLES_WEBPANEL + +config EXAMPLES_WEBPANEL_PROGNAME + string "Program name" + default "webpanel" + +config EXAMPLES_WEBPANEL_PRIORITY + int "Web Panel task priority" + default 100 + +config EXAMPLES_WEBPANEL_STACKSIZE + int "Web Panel stack size" + default 4096 + +config EXAMPLES_WEBPANEL_NETIF + string "Network interface name" + default "eth0" + ---help--- + Name of the network interface used by the web panel for + system info queries and DHCP renew (e.g. eth0, wlan0). + +config EXAMPLES_WEBPANEL_CGI_SYSINFO_PROGNAME + string "CGI sysinfo program name" + default "sysinfo" + +config EXAMPLES_WEBPANEL_CGI_SYSINFO_PRIORITY + int "CGI sysinfo task priority" + default 100 + +config EXAMPLES_WEBPANEL_CGI_SYSINFO_STACKSIZE + int "CGI sysinfo stack size" + default 4096 + +config EXAMPLES_WEBPANEL_CGI_FILES_PROGNAME + string "CGI files program name" + default "files" + +config EXAMPLES_WEBPANEL_CGI_FILES_PRIORITY + int "CGI files task priority" + default 100 + +config EXAMPLES_WEBPANEL_CGI_FILES_STACKSIZE + int "CGI files stack size" + default 4096 + +config EXAMPLES_WEBPANEL_CGI_UPLOAD_PROGNAME + string "CGI upload program name" + default "upload" + +config EXAMPLES_WEBPANEL_CGI_UPLOAD_PRIORITY + int "CGI upload task priority" + default 100 + +config EXAMPLES_WEBPANEL_CGI_UPLOAD_STACKSIZE + int "CGI upload stack size" + default 8192 + +config EXAMPLES_WEBPANEL_CGI_RENEW_PROGNAME + string "CGI DHCP renew program name" + default "dhcprenew" + +config EXAMPLES_WEBPANEL_CGI_RENEW_PRIORITY + int "CGI DHCP renew task priority" + default 100 + +config EXAMPLES_WEBPANEL_CGI_RENEW_STACKSIZE + int "CGI DHCP renew stack size" + default 4096 + +config EXAMPLES_WEBPANEL_WS_PORT + int "WebSocket terminal TCP port" + default 8080 + +config EXAMPLES_WEBPANEL_WS_PRIORITY + int "WebSocket server task priority" + default 100 + +config EXAMPLES_WEBPANEL_WS_STACKSIZE + int "WebSocket daemon stack size" + default 8192 + +config EXAMPLES_WEBPANEL_WS_NSH_STACKSIZE + int "WebSocket NSH session stack size" + default 4096 + +endif diff --git a/examples/webpanel/Make.defs b/examples/webpanel/Make.defs new file mode 100644 index 000000000..bf880eb82 --- /dev/null +++ b/examples/webpanel/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/webpanel/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_WEBPANEL),) +CONFIGURED_APPS += $(APPDIR)/examples/webpanel +endif diff --git a/examples/webpanel/Makefile b/examples/webpanel/Makefile new file mode 100644 index 000000000..12d6be8e6 --- /dev/null +++ b/examples/webpanel/Makefile @@ -0,0 +1,94 @@ +############################################################################ +# apps/examples/webpanel/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Web Panel application + CGI handlers +# NuttX multi-program build: space-separated lists, matched by position. + +CSRCS = romfs.c ws_terminal.c +MAINSRC = webpanel_main.c cgi_sysinfo.c cgi_files.c cgi_upload.c cgi_renew.c + +PROGNAME = $(CONFIG_EXAMPLES_WEBPANEL_PROGNAME) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_SYSINFO_PROGNAME) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_FILES_PROGNAME) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_UPLOAD_PROGNAME) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_RENEW_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_WEBPANEL_PRIORITY) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_SYSINFO_PRIORITY) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_FILES_PRIORITY) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_UPLOAD_PRIORITY) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_RENEW_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_WEBPANEL_STACKSIZE) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_SYSINFO_STACKSIZE) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_FILES_STACKSIZE) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_UPLOAD_STACKSIZE) \ + $(CONFIG_EXAMPLES_WEBPANEL_CGI_RENEW_STACKSIZE) + +LWS_DIR = $(APPDIR)/netutils/libwebsockets +CFLAGS += -I$(LWS_DIR) \ + -I$(LWS_DIR)/libwebsockets/include + +MODULE = $(CONFIG_EXAMPLES_WEBPANEL) + +VPATH += content +DEPPATH += --dep-path content + +# ROMFS image generation +# Static web content goes directly into the ROMFS root (no www/ subdir) + +WEBPANEL_DIR = $(APPDIR)/examples/webpanel +CONTENT_DIR = $(WEBPANEL_DIR)/content +ROMFS_DIR = $(CONTENT_DIR)/romfs +ROMFS_IMG = $(CONTENT_DIR)/romfs.img +ROMFS_SRC = $(CONTENT_DIR)/romfs.c + +WWW_FILES = $(wildcard $(CONTENT_DIR)/www/*) + +$(CONTENT_DIR)/.romfs_stamp: $(WWW_FILES) + $(Q) mkdir -p $(ROMFS_DIR) + $(Q) cp -a $(CONTENT_DIR)/www/* $(ROMFS_DIR)/ + $(Q) touch $@ + +$(ROMFS_IMG): $(CONTENT_DIR)/.romfs_stamp + $(Q) genromfs -f $@ -d $(ROMFS_DIR) -V "WebPanelROMFS" + +$(ROMFS_SRC): $(ROMFS_IMG) + $(Q) (cd $(CONTENT_DIR) && \ + echo "#include " >$@ && \ + xxd -i romfs.img | \ + sed -e "s/romfs_img/webpanel_romfs_img/g" | \ + sed -e "s/^unsigned char/const unsigned char aligned_data(4)/g" >>$@) + +content/romfs.c: $(ROMFS_SRC) + +context:: + +clean:: + $(call DELFILE, $(ROMFS_SRC)) + $(call DELFILE, $(ROMFS_IMG)) + $(call DELFILE, $(CONTENT_DIR)/.romfs_stamp) + $(Q) rm -rf $(ROMFS_DIR) + +distclean:: clean + +include $(APPDIR)/Application.mk diff --git a/examples/webpanel/cgi_files.c b/examples/webpanel/cgi_files.c new file mode 100644 index 000000000..fdc67b868 --- /dev/null +++ b/examples/webpanel/cgi_files.c @@ -0,0 +1,314 @@ +/**************************************************************************** + * apps/examples/webpanel/cgi_files.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FILES_DIR "/mnt" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void url_decode(char *dst, const char *src, size_t dstsize); +static const char *get_query_param(const char *qs, const char *key, + char *val, size_t vallen); +static void handle_list(void); +static void handle_delete(const char *name); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: url_decode + * + * Description: + * Decode a URL-encoded string into a destination buffer. + * + * Input Parameters: + * dst - Destination buffer. + * src - Source URL-encoded string. + * dstsize - Size of destination buffer. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void url_decode(char *dst, const char *src, size_t dstsize) +{ + size_t i = 0; + + while (*src && i < dstsize - 1) + { + if (*src == '%' && src[1] && src[2]) + { + char hex[3]; + + hex[0] = src[1]; + hex[1] = src[2]; + hex[2] = '\0'; + dst[i++] = (char)strtol(hex, NULL, 16); + src += 3; + } + else if (*src == '+') + { + dst[i++] = ' '; + src++; + } + else + { + dst[i++] = *src++; + } + } + + dst[i] = '\0'; +} + +/**************************************************************************** + * Name: get_query_param + * + * Description: + * Find and decode a key=value parameter from the QUERY_STRING. + * + * Input Parameters: + * qs - Query string. + * key - Parameter name to search for. + * val - Output buffer for decoded value. + * vallen - Size of output buffer. + * + * Returned Value: + * Pointer to val on success; NULL if not found or invalid input. + * + ****************************************************************************/ + +static const char *get_query_param(const char *qs, const char *key, + char *val, size_t vallen) +{ + const char *p; + size_t klen; + + if (qs == NULL || key == NULL) + { + return NULL; + } + + klen = strlen(key); + p = qs; + + while (*p) + { + if (strncmp(p, key, klen) == 0 && p[klen] == '=') + { + const char *start = p + klen + 1; + const char *end = strchr(start, '&'); + size_t len; + + if (end == NULL) + { + end = start + strlen(start); + } + + len = end - start; + if (len >= vallen) + { + len = vallen - 1; + } + + url_decode(val, start, len + 1); + return val; + } + + p = strchr(p, '&'); + if (p == NULL) + { + break; + } + + p++; + } + + return NULL; +} + +/**************************************************************************** + * Name: handle_list + * + * Description: + * Emit a JSON response listing files in FILES_DIR. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void handle_list(void) +{ + DIR *dir; + struct dirent *ent; + struct stat st; + char path[128]; + int first = 1; + + puts("Content-type: application/json\r\n" + "\r\n"); + + printf("{\"files\":["); + + dir = opendir(FILES_DIR); + if (dir != NULL) + { + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_name[0] == '.') + { + continue; + } + + snprintf(path, sizeof(path), "%s/%s", FILES_DIR, ent->d_name); + + if (!first) + { + printf(","); + } + + first = 0; + + if (stat(path, &st) == 0) + { + printf("{\"name\":\"%s\",\"size\":%ld}", + ent->d_name, (long)st.st_size); + } + else + { + printf("{\"name\":\"%s\",\"size\":0}", ent->d_name); + } + } + + closedir(dir); + } + + printf("]}\n"); +} + +/**************************************************************************** + * Name: handle_delete + * + * Description: + * Delete the requested file from FILES_DIR and emit a JSON response. + * + * Input Parameters: + * name - File name to delete. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void handle_delete(const char *name) +{ + char path[128]; + + if (name == NULL || name[0] == '\0' || strchr(name, '/') != NULL) + { + puts("Content-type: application/json\r\n" + "Status: 400\r\n" + "\r\n"); + printf("{\"error\":\"Invalid filename\"}\n"); + return; + } + + snprintf(path, sizeof(path), "%s/%s", FILES_DIR, name); + + if (unlink(path) == 0) + { + puts("Content-type: application/json\r\n" + "\r\n"); + printf("{\"ok\":true}\n"); + } + else + { + puts("Content-type: application/json\r\n" + "Status: 404\r\n" + "\r\n"); + printf("{\"error\":\"File not found\"}\n"); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: files_main + * + * Description: + * Entry point for file list/delete CGI handling. + * + * Input Parameters: + * argc - Number of arguments. + * argv - Argument vector. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int files_main(int argc, FAR char *argv[]) +{ + const char *qs; + char action[16]; + char name[64]; + + qs = getenv("QUERY_STRING"); + + if (qs != NULL && get_query_param(qs, "action", action, sizeof(action))) + { + if (strcmp(action, "delete") == 0) + { + get_query_param(qs, "name", name, sizeof(name)); + handle_delete(name); + return 0; + } + } + + handle_list(); + return 0; +} diff --git a/examples/webpanel/cgi_renew.c b/examples/webpanel/cgi_renew.c new file mode 100644 index 000000000..098995094 --- /dev/null +++ b/examples/webpanel/cgi_renew.c @@ -0,0 +1,72 @@ +/**************************************************************************** + * apps/examples/webpanel/cgi_renew.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "netutils/netlib.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dhcprenew_main + * + * Description: + * CGI entry point that renews the IPv4 address on the configured + * interface and returns the result as JSON. + * + * Input Parameters: + * argc - Number of arguments. + * argv - Argument vector. + * + * Returned Value: + * Zero on success; one on failure. + * + ****************************************************************************/ + +int dhcprenew_main(int argc, FAR char *argv[]) +{ + int ret; + + ret = netlib_obtain_ipv4addr(CONFIG_EXAMPLES_WEBPANEL_NETIF); + + puts("Content-type: application/json\r\n" + "\r\n"); + + if (ret >= 0) + { + puts("{\"status\":\"ok\"}\n"); + } + else + { + printf("{\"status\":\"error\",\"code\":%d}\n", ret); + } + + return ret < 0 ? 1 : 0; +} diff --git a/examples/webpanel/cgi_sysinfo.c b/examples/webpanel/cgi_sysinfo.c new file mode 100644 index 000000000..619eb4cf1 --- /dev/null +++ b/examples/webpanel/cgi_sysinfo.c @@ -0,0 +1,414 @@ +/**************************************************************************** + * apps/examples/webpanel/cgi_sysinfo.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void get_version(char *ver, size_t vlen, + char *build, size_t blen, + char *board, size_t boardlen); +static void get_arch(char *arch, size_t archlen); +static unsigned long get_uptime(char *buf, size_t len); +static void get_net_info(char *ip, size_t iplen, + char *mask, size_t masklen, + char *gw, size_t gwlen, + char *mac, size_t maclen); +static int count_files(const char *path); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: get_version + * + * Description: + * Read version, build hash, and board information from /proc/version. + * + * Input Parameters: + * ver - Output buffer for version string. + * vlen - Size of ver buffer. + * build - Output buffer for build/hash string. + * blen - Size of build buffer. + * board - Output buffer for board string. + * boardlen - Size of board buffer. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void get_version(char *ver, size_t vlen, + char *build, size_t blen, + char *board, size_t boardlen) +{ + FILE *fp; + char line[256]; + char *p; + char *sp; + char *last; + char *tok; + size_t n; + + strncpy(ver, "unknown", vlen); + strncpy(build, "unknown", blen); + strncpy(board, "unknown", boardlen); + + fp = fopen("/proc/version", "r"); + if (fp == NULL) + { + return; + } + + if (fgets(line, sizeof(line), fp) != NULL) + { + /* Format: "NuttX version X.Y.Z HASH DATE TIME BOARD:CONFIG" */ + + p = strstr(line, "version "); + if (p != NULL) + { + p += 8; + + /* version token */ + + sp = strchr(p, ' '); + if (sp != NULL) + { + n = sp - p; + if (n >= vlen) + { + n = vlen - 1; + } + + memcpy(ver, p, n); + ver[n] = '\0'; + + /* build (git hash) token */ + + p = sp + 1; + sp = strchr(p, ' '); + if (sp != NULL) + { + n = sp - p; + if (n >= blen) + { + n = blen - 1; + } + + memcpy(build, p, n); + build[n] = '\0'; + } + } + } + + /* Board name is the last whitespace-separated token; trim :config */ + + last = NULL; + tok = strtok(line, " \t\r\n"); + while (tok != NULL) + { + last = tok; + tok = strtok(NULL, " \t\r\n"); + } + + if (last != NULL) + { + char *colon = strchr(last, ':'); + if (colon != NULL) + { + *colon = '\0'; + } + + strncpy(board, last, boardlen - 1); + board[boardlen - 1] = '\0'; + } + } + + fclose(fp); +} + +/**************************************************************************** + * Name: get_arch + * + * Description: + * Query architecture information via uname(). + * + * Input Parameters: + * arch - Output buffer for architecture string. + * archlen - Size of arch buffer. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void get_arch(char *arch, size_t archlen) +{ + struct utsname uts; + + strncpy(arch, "unknown", archlen); + + if (uname(&uts) == 0) + { + strncpy(arch, uts.machine, archlen - 1); + arch[archlen - 1] = '\0'; + } +} + +/**************************************************************************** + * Name: get_uptime + * + * Description: + * Read system uptime based on CLOCK_MONOTONIC and format it. + * + * Input Parameters: + * buf - Output buffer for formatted uptime. + * len - Size of buf. + * + * Returned Value: + * Uptime in seconds; zero on error. + * + ****************************************************************************/ + +static unsigned long get_uptime(char *buf, size_t len) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + unsigned long secs = ts.tv_sec; + unsigned int days = secs / 86400; + unsigned int hrs = (secs % 86400) / 3600; + unsigned int mins = (secs % 3600) / 60; + unsigned int s = secs % 60; + + if (days > 0) + { + snprintf(buf, len, "%ud %02u:%02u:%02u", days, hrs, mins, s); + } + else + { + snprintf(buf, len, "%02u:%02u:%02u", hrs, mins, s); + } + + return secs; + } + + snprintf(buf, len, "unknown"); + return 0; +} + +/**************************************************************************** + * Name: get_net_info + * + * Description: + * Query IP, netmask, gateway, and MAC for the configured interface. + * + * Input Parameters: + * ip - Output buffer for IPv4 address. + * iplen - Size of ip buffer. + * mask - Output buffer for netmask. + * masklen - Size of mask buffer. + * gw - Output buffer for gateway. + * gwlen - Size of gw buffer. + * mac - Output buffer for MAC address. + * maclen - Size of mac buffer. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void get_net_info(char *ip, size_t iplen, + char *mask, size_t masklen, + char *gw, size_t gwlen, + char *mac, size_t maclen) +{ + int sockfd; + struct ifreq ifr; + + strncpy(ip, "N/A", iplen); + strncpy(mask, "N/A", masklen); + strncpy(gw, "N/A", gwlen); + strncpy(mac, "N/A", maclen); + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + return; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, CONFIG_EXAMPLES_WEBPANEL_NETIF, IFNAMSIZ); + + if (ioctl(sockfd, SIOCGIFADDR, &ifr) == 0) + { + struct sockaddr_in *sa = (struct sockaddr_in *)&ifr.ifr_addr; + inet_ntop(AF_INET, &sa->sin_addr, ip, iplen); + } + + if (ioctl(sockfd, SIOCGIFNETMASK, &ifr) == 0) + { + struct sockaddr_in *sa = (struct sockaddr_in *)&ifr.ifr_netmask; + inet_ntop(AF_INET, &sa->sin_addr, mask, masklen); + } + + if (ioctl(sockfd, SIOCGIFDSTADDR, &ifr) == 0) + { + struct sockaddr_in *sa = (struct sockaddr_in *)&ifr.ifr_dstaddr; + inet_ntop(AF_INET, &sa->sin_addr, gw, gwlen); + } + + if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == 0) + { + unsigned char *hw = (unsigned char *)ifr.ifr_hwaddr.sa_data; + snprintf(mac, maclen, "%02x:%02x:%02x:%02x:%02x:%02x", + hw[0], hw[1], hw[2], hw[3], hw[4], hw[5]); + } + + close(sockfd); +} + +/**************************************************************************** + * Name: count_files + * + * Description: + * Count visible files in a directory. + * + * Input Parameters: + * path - Directory path. + * + * Returned Value: + * Number of visible entries. + * + ****************************************************************************/ + +static int count_files(const char *path) +{ + DIR *dir; + struct dirent *ent; + int count = 0; + + dir = opendir(path); + if (dir == NULL) + { + return 0; + } + + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_name[0] != '.') + { + count++; + } + } + + closedir(dir); + return count; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sysinfo_main + * + * Description: + * CGI entry point that returns board and runtime information as JSON. + * + * Input Parameters: + * argc - Number of arguments. + * argv - Argument vector. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int sysinfo_main(int argc, FAR char *argv[]) +{ + char version[16]; + char build[16]; + char board[64]; + char arch[32]; + char uptime[32]; + char ip[16]; + char mask[16]; + char gw[16]; + char mac[24]; + unsigned long uptime_sec; + int nfiles; + + get_version(version, sizeof(version), build, sizeof(build), + board, sizeof(board)); + get_arch(arch, sizeof(arch)); + uptime_sec = get_uptime(uptime, sizeof(uptime)); + get_net_info(ip, sizeof(ip), mask, sizeof(mask), + gw, sizeof(gw), mac, sizeof(mac)); + nfiles = count_files("/mnt"); + + puts("Content-type: application/json\r\n" + "\r\n"); + + printf("{" + "\"chip\":\"" CONFIG_ARCH_CHIP "\"," + "\"board\":\"%s\"," + "\"os\":\"NuttX\"," + "\"version\":\"%s\"," + "\"build\":\"%s\"," + "\"arch\":\"%s\"," + "\"ifname\":\"" CONFIG_EXAMPLES_WEBPANEL_NETIF "\"," + "\"ip\":\"%s\"," + "\"mask\":\"%s\"," + "\"gw\":\"%s\"," + "\"mac\":\"%s\"," + "\"uptime\":\"%s\"," + "\"uptime_sec\":%lu," + "\"files\":%d" + "}\n", + board, version, build, arch, + ip, mask, gw, mac, + uptime, uptime_sec, nfiles); + + return 0; +} diff --git a/examples/webpanel/cgi_upload.c b/examples/webpanel/cgi_upload.c new file mode 100644 index 000000000..4dfd137ad --- /dev/null +++ b/examples/webpanel/cgi_upload.c @@ -0,0 +1,637 @@ +/**************************************************************************** + * apps/examples/webpanel/cgi_upload.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define UPLOAD_DIR "/mnt" +#define BUF_SIZE 512 +#define MAX_FILENAME 64 +#define MAX_BOUNDARY 80 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void cgi_error(int status, const char *msg); +static void cgi_ok(const char *filename); +static int extract_boundary(const char *content_type, char *boundary, + size_t blen); +static int extract_filename(const char *line, char *name, size_t nlen); +static size_t read_stdin(char *buf, size_t n); +static char *memmem_local(const char *haystack, size_t hlen, + const char *needle, size_t nlen); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cgi_error + * + * Description: + * Emit a JSON error response with HTTP status. + * + * Input Parameters: + * status - HTTP status code. + * msg - Error message string. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void cgi_error(int status, const char *msg) +{ + printf("Content-type: application/json\r\n" + "Status: %d\r\n" + "\r\n" + "{\"error\":\"%s\"}\n", status, msg); +} + +/**************************************************************************** + * Name: cgi_ok + * + * Description: + * Emit a JSON success response. + * + * Input Parameters: + * filename - Uploaded file name. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void cgi_ok(const char *filename) +{ + printf("Content-type: application/json\r\n" + "\r\n" + "{\"ok\":true,\"name\":\"%s\"}\n", filename); +} + +/**************************************************************************** + * Name: extract_boundary + * + * Description: + * Extract multipart boundary from CONTENT_TYPE. + * + * Input Parameters: + * content_type - CONTENT_TYPE value. + * boundary - Output buffer for boundary token. + * blen - Size of boundary buffer. + * + * Returned Value: + * Zero on success; negated value on failure. + * + ****************************************************************************/ + +static int extract_boundary(const char *content_type, char *boundary, + size_t blen) +{ + const char *p; + char *q; + size_t len; + + p = strstr(content_type, "boundary="); + if (p == NULL) + { + return -1; + } + + p += 9; + + /* Skip optional quotes */ + + if (*p == '"') + { + p++; + } + + strncpy(boundary, p, blen - 1); + boundary[blen - 1] = '\0'; + + /* Remove trailing quote if present */ + + q = strchr(boundary, '"'); + if (q != NULL) + { + *q = '\0'; + } + + /* Remove trailing whitespace/CR/LF */ + + len = strlen(boundary); + while (len > 0 && + (boundary[len - 1] == '\r' || boundary[len - 1] == '\n' || + boundary[len - 1] == ' ')) + { + boundary[--len] = '\0'; + } + + return len > 0 ? 0 : -1; +} + +/**************************************************************************** + * Name: extract_filename + * + * Description: + * Extract a safe filename from a Content-Disposition header line. + * + * Input Parameters: + * line - Header line. + * name - Output buffer for filename. + * nlen - Size of name buffer. + * + * Returned Value: + * Zero on success; negated value on failure. + * + ****************************************************************************/ + +static int extract_filename(const char *line, char *name, size_t nlen) +{ + const char *p; + const char *end; + size_t len; + + p = strstr(line, "filename=\""); + if (p == NULL) + { + return -1; + } + + p += 10; + end = strchr(p, '"'); + if (end == NULL) + { + return -1; + } + + len = end - p; + if (len == 0 || len >= nlen) + { + return -1; + } + + /* Reject path separators in filename */ + + if (memchr(p, '/', len) != NULL || memchr(p, '\\', len) != NULL) + { + return -1; + } + + memcpy(name, p, len); + name[len] = '\0'; + return 0; +} + +/**************************************************************************** + * Name: read_stdin + * + * Description: + * Read exactly n bytes from standard input unless EOF/error occurs. + * + * Input Parameters: + * buf - Destination buffer. + * n - Requested number of bytes. + * + * Returned Value: + * Number of bytes actually read. + * + ****************************************************************************/ + +static size_t read_stdin(char *buf, size_t n) +{ + size_t total = 0; + while (total < n) + { + ssize_t r = read(STDIN_FILENO, buf + total, n - total); + if (r <= 0) + { + break; + } + + total += r; + } + + return total; +} + +/**************************************************************************** + * Name: memmem_local + * + * Description: + * Find a byte sequence inside a bounded memory region. + * + * Input Parameters: + * haystack - Input buffer. + * hlen - Size of haystack. + * needle - Pattern to search. + * nlen - Size of needle. + * + * Returned Value: + * Pointer to first match; NULL if not found. + * + ****************************************************************************/ + +static char *memmem_local(const char *haystack, size_t hlen, + const char *needle, size_t nlen) +{ + const char *p; + if (nlen == 0) + { + return (char *)haystack; + } + + if (hlen < nlen) + { + return NULL; + } + + p = haystack; + while (p <= haystack + hlen - nlen) + { + if (memcmp(p, needle, nlen) == 0) + { + return (char *)p; + } + + p++; + } + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: upload_main + * + * Description: + * CGI entry point for multipart file upload. + * + * Input Parameters: + * argc - Number of arguments. + * argv - Argument vector. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int upload_main(int argc, FAR char *argv[]) +{ + const char *content_type; + const char *content_length_str; + char boundary_raw[MAX_BOUNDARY]; + char boundary[MAX_BOUNDARY + 4]; + size_t boundary_len; + char filename[MAX_FILENAME]; + char filepath[MAX_FILENAME + 8]; + char buf[BUF_SIZE]; + size_t content_length; + size_t total_read; + int fd = -1; + int state; + char linebuf[256]; + size_t linepos; + int headers_done; + + content_type = getenv("CONTENT_TYPE"); + content_length_str = getenv("CONTENT_LENGTH"); + + if (content_type == NULL || content_length_str == NULL) + { + cgi_error(400, "Missing Content-Type or Content-Length"); + return 0; + } + + content_length = strtoul(content_length_str, NULL, 10); + if (content_length == 0 || content_length > 1024 * 1024) + { + cgi_error(400, "Invalid content length (max 1MB)"); + return 0; + } + + if (extract_boundary(content_type, boundary_raw, sizeof(boundary_raw)) < 0) + { + cgi_error(400, "No boundary in Content-Type"); + return 0; + } + + /* Multipart boundaries in the body are prefixed with "--" */ + + snprintf(boundary, sizeof(boundary), "--%s", boundary_raw); + boundary_len = strlen(boundary); + + /* State machine for multipart parsing: + * 0 = looking for first boundary + * 1 = reading headers after boundary + * 2 = reading file data + * 3 = done + */ + + state = 0; + total_read = 0; + filename[0] = '\0'; + linepos = 0; + headers_done = 0; + + /* Read the entire POST body in chunks and process inline. + * We accumulate a line buffer for header parsing, and stream + * file data directly to disk. + */ + + while (total_read < content_length && state != 3) + { + size_t toread = content_length - total_read; + size_t nread; + + if (toread > sizeof(buf)) + { + toread = sizeof(buf); + } + + nread = read_stdin(buf, toread); + if (nread == 0) + { + break; + } + + total_read += nread; + + size_t i = 0; + while (i < nread && state != 3) + { + switch (state) + { + case 0: + + /* Accumulate until we find the first boundary line */ + + while (i < nread) + { + if (buf[i] == '\n') + { + linebuf[linepos] = '\0'; + + /* Strip trailing \r */ + + if (linepos > 0 && linebuf[linepos - 1] == '\r') + { + linebuf[--linepos] = '\0'; + } + + if (strncmp(linebuf, boundary, boundary_len) == 0) + { + state = 1; + headers_done = 0; + linepos = 0; + i++; + break; + } + + linepos = 0; + i++; + } + else + { + if (linepos < sizeof(linebuf) - 1) + { + linebuf[linepos++] = buf[i]; + } + + i++; + } + } + + break; + + case 1: + /* Parse headers after boundary, looking for + * Content-Disposition with filename. + * Headers end with an empty line. + */ + + while (i < nread && !headers_done) + { + if (buf[i] == '\n') + { + linebuf[linepos] = '\0'; + + if (linepos > 0 && linebuf[linepos - 1] == '\r') + { + linebuf[--linepos] = '\0'; + } + + if (linepos == 0) + { + /* Empty line = end of headers */ + + headers_done = 1; + + if (filename[0] == '\0') + { + cgi_error(400, "No filename in upload"); + if (fd >= 0) + { + close(fd); + } + + return 0; + } + + snprintf(filepath, sizeof(filepath), "%s/%s", + UPLOAD_DIR, filename); + + fd = open(filepath, + O_WRONLY | O_CREAT | O_TRUNC, + 0666); + if (fd < 0) + { + cgi_error(500, "Cannot create file"); + return 0; + } + + state = 2; + i++; + break; + } + + if (strstr(linebuf, "Content-Disposition") != NULL) + { + extract_filename(linebuf, filename, + sizeof(filename)); + } + + linepos = 0; + i++; + } + else + { + if (linepos < sizeof(linebuf) - 1) + { + linebuf[linepos++] = buf[i]; + } + + i++; + } + } + + break; + + case 2: + { + /* Write file data. We need to detect the closing boundary + * which appears as "\r\n--BOUNDARY" in the stream. + * Buffer the last boundary_len+4 bytes to check for the + * boundary. + */ + + size_t remaining = nread - i; + char *bnd; + + bnd = memmem_local(buf + i, remaining, + boundary, boundary_len); + if (bnd != NULL) + { + /* Found boundary. Write everything before it, + * minus the preceding \r\n. + */ + + size_t datalen = bnd - (buf + i); + if (datalen >= 2) + { + datalen -= 2; + } + + if (datalen > 0) + { + write(fd, buf + i, datalen); + } + + close(fd); + fd = -1; + state = 3; + break; + } + else + { + /* No boundary in this chunk. Write data but hold back + * enough bytes to avoid splitting a boundary across + * chunks. + */ + + size_t safe; + + if (remaining > boundary_len + 4) + { + safe = remaining - boundary_len - 4; + } + else + { + safe = 0; + } + + if (safe > 0) + { + write(fd, buf + i, safe); + i += safe; + } + else + { + /* Not enough data to be safe. This means we're near + * the end of a chunk and need more data. Just write + * what we have and hope the boundary comes in the + * next read. For simplicity, write it all - worst + * case we include a few extra bytes at end of file + * which will be fixed when the boundary is found. + */ + + write(fd, buf + i, remaining); + i = nread; + } + } + + break; + } + } + } + } + + /* Drain any remaining data from stdin */ + + while (total_read < content_length) + { + size_t toread = content_length - total_read; + size_t nread; + + if (toread > sizeof(buf)) + { + toread = sizeof(buf); + } + + nread = read_stdin(buf, toread); + if (nread == 0) + { + break; + } + + total_read += nread; + } + + if (fd >= 0) + { + close(fd); + } + + if (state == 3 && filename[0] != '\0') + { + cgi_ok(filename); + } + else if (filename[0] != '\0') + { + cgi_ok(filename); + } + else + { + cgi_error(400, "Upload incomplete or no file received"); + } + + return 0; +} diff --git a/examples/webpanel/content/www/index.html b/examples/webpanel/content/www/index.html new file mode 100644 index 000000000..7d4928e31 --- /dev/null +++ b/examples/webpanel/content/www/index.html @@ -0,0 +1,454 @@ + + + + + +NuttX Web Panel + + + + + +
+

NuttX Web Panel --

+ Uptime: -- +
+ + + + +
+
+
+

System

+
Board--
+
OSNuttX
+
Version--
+
Build--
+
Arch--
+
+
+

🔌 Network

+
Interface--
+
IP--
+
Mask--
+
Gateway--
+
MAC--
+
+
+

💾 Storage

+
SmartFS/mnt
+
Web Root/data/www (ROMFS)
+
Files--
+
+
+

Quick Actions

+
+ + + +
+
+
+
+ + +
+
+

NSH Terminal

+
+ + + Disconnected +
+
+
+
+ + +
+
+

📁 File Manager — /mnt

+
+ /mnt + +
+
+
Loading file list…
+
+
+ + Drop file here or click to upload to /mnt +
+
+
+ + +
+
+
+

🔌 --

+
+
StatusRUNNING
+
IP Address--
+
Netmask--
+
Gateway--
+
MAC--
+
MTU1500
+
+
+
+

📜 DHCP

+
ModeClient (auto)
+
Last Renew--
+
+ + +
+
+
+
+ +
+ + + + + diff --git a/examples/webpanel/content/www/xterm.css b/examples/webpanel/content/www/xterm.css new file mode 100644 index 000000000..e97b64390 --- /dev/null +++ b/examples/webpanel/content/www/xterm.css @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2014 The xterm.js authors. All rights reserved. + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * https://github.com/chjj/term.js + * @license MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + */ + +/** + * Default styles for xterm.js + */ + +.xterm { + cursor: text; + position: relative; + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; +} + +.xterm.focus, +.xterm:focus { + outline: none; +} + +.xterm .xterm-helpers { + position: absolute; + top: 0; + /** + * The z-index of the helpers must be higher than the canvases in order for + * IMEs to appear on top. + */ + z-index: 5; +} + +.xterm .xterm-helper-textarea { + padding: 0; + border: 0; + margin: 0; + /* Move textarea out of the screen to the far left, so that the cursor is not visible */ + position: absolute; + opacity: 0; + left: -9999em; + top: 0; + width: 0; + height: 0; + z-index: -5; + /** Prevent wrapping so the IME appears against the textarea at the correct position */ + white-space: nowrap; + overflow: hidden; + resize: none; +} + +.xterm .composition-view { + /* TODO: Composition position got messed up somewhere */ + background: #000; + color: #FFF; + display: none; + position: absolute; + white-space: nowrap; + z-index: 1; +} + +.xterm .composition-view.active { + display: block; +} + +.xterm .xterm-viewport { + /* On OS X this is required in order for the scroll bar to appear fully opaque */ + background-color: #000; + overflow-y: scroll; + cursor: default; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; +} + +.xterm .xterm-screen { + position: relative; +} + +.xterm .xterm-screen canvas { + position: absolute; + left: 0; + top: 0; +} + +.xterm .xterm-scroll-area { + visibility: hidden; +} + +.xterm-char-measure-element { + display: inline-block; + visibility: hidden; + position: absolute; + top: 0; + left: -9999em; + line-height: normal; +} + +.xterm.enable-mouse-events { + /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ + cursor: default; +} + +.xterm.xterm-cursor-pointer, +.xterm .xterm-cursor-pointer { + cursor: pointer; +} + +.xterm.column-select.focus { + /* Column selection mode */ + cursor: crosshair; +} + +.xterm .xterm-accessibility:not(.debug), +.xterm .xterm-message { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 10; + color: transparent; + pointer-events: none; +} + +.xterm .xterm-accessibility-tree:not(.debug) *::selection { + color: transparent; +} + +.xterm .xterm-accessibility-tree { + user-select: text; + white-space: pre; +} + +.xterm .live-region { + position: absolute; + left: -9999px; + width: 1px; + height: 1px; + overflow: hidden; +} + +.xterm-dim { + /* Dim should not apply to background, so the opacity of the foreground color is applied + * explicitly in the generated class and reset to 1 here */ + opacity: 1 !important; +} + +.xterm-underline-1 { text-decoration: underline; } +.xterm-underline-2 { text-decoration: double underline; } +.xterm-underline-3 { text-decoration: wavy underline; } +.xterm-underline-4 { text-decoration: dotted underline; } +.xterm-underline-5 { text-decoration: dashed underline; } + +.xterm-overline { + text-decoration: overline; +} + +.xterm-overline.xterm-underline-1 { text-decoration: overline underline; } +.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; } +.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; } +.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; } +.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; } + +.xterm-strikethrough { + text-decoration: line-through; +} + +.xterm-screen .xterm-decoration-container .xterm-decoration { + z-index: 6; + position: absolute; +} + +.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer { + z-index: 7; +} + +.xterm-decoration-overview-ruler { + z-index: 8; + position: absolute; + top: 0; + right: 0; + pointer-events: none; +} + +.xterm-decoration-top { + z-index: 2; + position: relative; +} diff --git a/examples/webpanel/content/www/xterm.min.js b/examples/webpanel/content/www/xterm.min.js new file mode 100644 index 000000000..0a51bfb69 --- /dev/null +++ b/examples/webpanel/content/www/xterm.min.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@xterm/xterm@5.5.0/lib/xterm.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(globalThis,(()=>(()=>{"use strict";var e={4567:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(9042),o=i(9924),a=i(844),h=i(4725),c=i(2585),l=i(3656);let d=t.AccessibilityManager=class extends a.Disposable{constructor(e,t,i,s){super(),this._terminal=e,this._coreBrowserService=i,this._renderService=s,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;ethis._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new o.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this.register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this.register(this._terminal.onLineFeed((()=>this._handleChar("\n")))),this.register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this.register(this._terminal.onKey((e=>this._handleKey(e.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,l.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,a.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const i=this._terminal.buffer,s=i.lines.length.toString();for(let r=e;r<=t;r++){const e=i.lines.get(i.ydisp+r),t=[],n=e?.translateToString(!0,void 0,void 0,t)||"",o=(i.ydisp+r+1).toString(),a=this._rowElements[r];a&&(0===n.length?(a.innerText=" ",this._rowColumns.set(a,[0,1])):(a.textContent=n,this._rowColumns.set(a,t)),a.setAttribute("aria-posinset",o),a.setAttribute("aria-setsize",s))}this._announceCharacters()}_announceCharacters(){0!==this._charsToAnnounce.length&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const i=e.target,s=this._rowElements[0===t?1:this._rowElements.length-2];if(i.getAttribute("aria-posinset")===(0===t?"1":`${this._terminal.buffer.lines.length}`))return;if(e.relatedTarget!==s)return;let r,n;if(0===t?(r=i,n=this._rowElements.pop(),this._rowContainer.removeChild(n)):(r=this._rowElements.shift(),n=i,this._rowContainer.removeChild(r)),r.removeEventListener("focus",this._topBoundaryFocusListener),n.removeEventListener("focus",this._bottomBoundaryFocusListener),0===t){const e=this._createAccessibilityTreeNode();this._rowElements.unshift(e),this._rowContainer.insertAdjacentElement("afterbegin",e)}else{const e=this._createAccessibilityTreeNode();this._rowElements.push(e),this._rowContainer.appendChild(e)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(0===t?-1:1),this._rowElements[0===t?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(0===this._rowElements.length)return;const e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let t={node:e.anchorNode,offset:e.anchorOffset},i={node:e.focusNode,offset:e.focusOffset};if((t.node.compareDocumentPosition(i.node)&Node.DOCUMENT_POSITION_PRECEDING||t.node===i.node&&t.offset>i.offset)&&([t,i]=[i,t]),t.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(t={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(t.node))return;const s=this._rowElements.slice(-1)[0];if(i.node.compareDocumentPosition(s)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(i={node:s,offset:s.textContent?.length??0}),!this._rowContainer.contains(i.node))return;const r=({node:e,offset:t})=>{const i=e instanceof Text?e.parentNode:e;let s=parseInt(i?.getAttribute("aria-posinset"),10)-1;if(isNaN(s))return console.warn("row is invalid. Race condition?"),null;const r=this._rowColumns.get(i);if(!r)return console.warn("columns is null. Race condition?"),null;let n=t=this._terminal.cols&&(++s,n=0),{row:s,column:n}},n=r(t),o=r(i);if(n&&o){if(n.row>o.row||n.row===o.row&&n.column>=o.column)throw new Error("invalid range");this._terminal.select(n.column,n.row,(o.row-n.row)*this._terminal.cols-n.column+o.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let e=this._rowContainer.children.length;ee;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e{function i(e){return e.replace(/\r?\n/g,"\r")}function s(e,t){return t?"[200~"+e+"[201~":e}function r(e,t,r,n){e=s(e=i(e),r.decPrivateModes.bracketedPasteMode&&!0!==n.rawOptions.ignoreBracketedPasteMode),r.triggerDataEvent(e,!0),t.value=""}function n(e,t,i){const s=i.getBoundingClientRect(),r=e.clientX-s.left-10,n=e.clientY-s.top-10;t.style.width="20px",t.style.height="20px",t.style.left=`${r}px`,t.style.top=`${n}px`,t.style.zIndex="1000",t.focus()}Object.defineProperty(t,"__esModule",{value:!0}),t.rightClickHandler=t.moveTextAreaUnderMouseCursor=t.paste=t.handlePasteEvent=t.copyHandler=t.bracketTextForPaste=t.prepareTextForTerminal=void 0,t.prepareTextForTerminal=i,t.bracketTextForPaste=s,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,i,s){e.stopPropagation(),e.clipboardData&&r(e.clipboardData.getData("text/plain"),t,i,s)},t.paste=r,t.moveTextAreaUnderMouseCursor=n,t.rightClickHandler=function(e,t,i,s,r){n(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}},7239:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorContrastCache=void 0;const s=i(1505);t.ColorContrastCache=class{constructor(){this._color=new s.TwoKeyMap,this._css=new s.TwoKeyMap}setCss(e,t,i){this._css.set(e,t,i)}getCss(e,t){return this._css.get(e,t)}setColor(e,t,i){this._color.set(e,t,i)}getColor(e,t){return this._color.get(e,t)}clear(){this._color.clear(),this._css.clear()}}},3656:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.addDisposableDomListener=void 0,t.addDisposableDomListener=function(e,t,i,s){e.addEventListener(t,i,s);let r=!1;return{dispose:()=>{r||(r=!0,e.removeEventListener(t,i,s))}}}},3551:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Linkifier=void 0;const n=i(3656),o=i(8460),a=i(844),h=i(2585),c=i(4725);let l=t.Linkifier=class extends a.Disposable{get currentLink(){return this._currentLink}constructor(e,t,i,s,r){super(),this._element=e,this._mouseService=t,this._renderService=i,this._bufferService=s,this._linkProviderService=r,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new o.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new o.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,a.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,a.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(e){this._lastMouseEvent=e;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);if(!t)return;this._isMouseOut=!1;const i=e.composedPath();for(let e=0;e{e?.forEach((e=>{e.link.dispose&&e.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=e.y);let i=!1;for(const[s,r]of this._linkProviderService.linkProviders.entries())if(t){const t=this._activeProviderReplies?.get(s);t&&(i=this._checkLinkProviderResult(s,e,i))}else r.provideLinks(e.y,(t=>{if(this._isMouseOut)return;const r=t?.map((e=>({link:e})));this._activeProviderReplies?.set(s,r),i=this._checkLinkProviderResult(s,e,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(e.y,this._activeProviderReplies)}))}_removeIntersectingLinks(e,t){const i=new Set;for(let s=0;se?this._bufferService.cols:s.link.range.end.x;for(let e=n;e<=o;e++){if(i.has(e)){r.splice(t--,1);break}i.add(e)}}}}_checkLinkProviderResult(e,t,i){if(!this._activeProviderReplies)return i;const s=this._activeProviderReplies.get(e);let r=!1;for(let t=0;tthis._linkAtPosition(e.link,t)));e&&(i=!0,this._handleNewLink(e))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let e=0;ethis._linkAtPosition(e.link,t)));if(s){i=!0,this._handleNewLink(s);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(e){if(!this._currentLink)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);t&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,t)&&this._currentLink.link.activate(e,this._currentLink.link.text)}_clearCurrentLink(e,t){this._currentLink&&this._lastMouseEvent&&(!e||!t||this._currentLink.link.range.start.y>=e&&this._currentLink.link.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,a.disposeArray)(this._linkCacheDisposables))}_handleNewLink(e){if(!this._lastMouseEvent)return;const t=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);t&&this._linkAtPosition(e.link,t)&&(this._currentLink=e,this._currentLink.state={decorations:{underline:void 0===e.link.decorations||e.link.decorations.underline,pointerCursor:void 0===e.link.decorations||e.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,e.link,this._lastMouseEvent),e.link.decorations={},Object.defineProperties(e.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:e=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==e&&(this._currentLink.state.decorations.pointerCursor=e,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",e))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:t=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(e.link,t))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((e=>{if(!this._currentLink)return;const t=0===e.start?0:e.start+1+this._bufferService.buffer.ydisp,i=this._bufferService.buffer.ydisp+1+e.end;if(this._currentLink.link.range.start.y>=t&&this._currentLink.link.range.end.y<=i&&(this._clearCurrentLink(t,i),this._lastMouseEvent)){const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._askForLink(e,!1)}}))))}_linkHover(e,t,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!0),this._currentLink.state.decorations.pointerCursor&&e.classList.add("xterm-cursor-pointer")),t.hover&&t.hover(i,t.text)}_fireUnderlineEvent(e,t){const i=e.range,s=this._bufferService.buffer.ydisp,r=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-s-1,i.end.x,i.end.y-s-1,void 0);(t?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(r)}_linkLeave(e,t,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!1),this._currentLink.state.decorations.pointerCursor&&e.classList.remove("xterm-cursor-pointer")),t.leave&&t.leave(i,t.text)}_linkAtPosition(e,t){const i=e.range.start.y*this._bufferService.cols+e.range.start.x,s=e.range.end.y*this._bufferService.cols+e.range.end.x,r=t.y*this._bufferService.cols+t.x;return i<=r&&r<=s}_positionFromMouseEvent(e,t,i){const s=i.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(s)return{x:s[0],y:s[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(e,t,i,s,r){return{x1:e,y1:t,x2:i,y2:s,cols:this._bufferService.cols,fg:r}}};t.Linkifier=l=s([r(1,c.IMouseService),r(2,c.IRenderService),r(3,h.IBufferService),r(4,c.ILinkProviderService)],l)},9042:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tooMuchOutput=t.promptLabel=void 0,t.promptLabel="Terminal input",t.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkProvider=void 0;const n=i(511),o=i(2585);let a=t.OscLinkProvider=class{constructor(e,t,i){this._bufferService=e,this._optionsService=t,this._oscLinkService=i}provideLinks(e,t){const i=this._bufferService.buffer.lines.get(e-1);if(!i)return void t(void 0);const s=[],r=this._optionsService.rawOptions.linkHandler,o=new n.CellData,a=i.getTrimmedLength();let c=-1,l=-1,d=!1;for(let t=0;tr?r.activate(e,t,n):h(0,t),hover:(e,t)=>r?.hover?.(e,t,n),leave:(e,t)=>r?.leave?.(e,t,n)})}d=!1,o.hasExtendedAttrs()&&o.extended.urlId?(l=t,c=o.extended.urlId):(l=-1,c=-1)}}t(s)}};function h(e,t){if(confirm(`Do you want to navigate to ${t}?\n\nWARNING: This link could potentially be dangerous`)){const e=window.open();if(e){try{e.opener=null}catch{}e.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}}t.OscLinkProvider=a=s([r(0,o.IBufferService),r(1,o.IOptionsService),r(2,o.IOscLinkService)],a)},6193:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RenderDebouncer=void 0,t.RenderDebouncer=class{constructor(e,t){this._renderCallback=e,this._coreBrowserService=t,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(e){return this._refreshCallbacks.push(e),this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh()))),this._animationFrame}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t,this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._animationFrame=void 0,void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return void this._runRefreshCallbacks();const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(const e of this._refreshCallbacks)e(0);this._refreshCallbacks=[]}}},3236:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Terminal=void 0;const s=i(3614),r=i(3656),n=i(3551),o=i(9042),a=i(3730),h=i(1680),c=i(3107),l=i(5744),d=i(2950),_=i(1296),u=i(428),f=i(4269),v=i(5114),p=i(8934),g=i(3230),m=i(9312),S=i(4725),C=i(6731),b=i(8055),w=i(8969),y=i(8460),E=i(844),k=i(6114),L=i(8437),D=i(2584),R=i(7399),x=i(5941),A=i(9074),B=i(2585),T=i(5435),M=i(4567),O=i(779);class P extends w.CoreTerminal{get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(e={}){super(e),this.browser=k,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this.register(new E.MutableDisposable),this._onCursorMove=this.register(new y.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this.register(new y.EventEmitter),this.onKey=this._onKey.event,this._onRender=this.register(new y.EventEmitter),this.onRender=this._onRender.event,this._onSelectionChange=this.register(new y.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this.register(new y.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this.register(new y.EventEmitter),this.onBell=this._onBell.event,this._onFocus=this.register(new y.EventEmitter),this._onBlur=this.register(new y.EventEmitter),this._onA11yCharEmitter=this.register(new y.EventEmitter),this._onA11yTabEmitter=this.register(new y.EventEmitter),this._onWillOpen=this.register(new y.EventEmitter),this._setup(),this._decorationService=this._instantiationService.createInstance(A.DecorationService),this._instantiationService.setService(B.IDecorationService,this._decorationService),this._linkProviderService=this._instantiationService.createInstance(O.LinkProviderService),this._instantiationService.setService(S.ILinkProviderService,this._linkProviderService),this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(a.OscLinkProvider)),this.register(this._inputHandler.onRequestBell((()=>this._onBell.fire()))),this.register(this._inputHandler.onRequestRefreshRows(((e,t)=>this.refresh(e,t)))),this.register(this._inputHandler.onRequestSendFocus((()=>this._reportFocus()))),this.register(this._inputHandler.onRequestReset((()=>this.reset()))),this.register(this._inputHandler.onRequestWindowsOptionsReport((e=>this._reportWindowsOptions(e)))),this.register(this._inputHandler.onColor((e=>this._handleColorEvent(e)))),this.register((0,y.forwardEvent)(this._inputHandler.onCursorMove,this._onCursorMove)),this.register((0,y.forwardEvent)(this._inputHandler.onTitleChange,this._onTitleChange)),this.register((0,y.forwardEvent)(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this.register((0,y.forwardEvent)(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this.register(this._bufferService.onResize((e=>this._afterResize(e.cols,e.rows)))),this.register((0,E.toDisposable)((()=>{this._customKeyEventHandler=void 0,this.element?.parentNode?.removeChild(this.element)})))}_handleColorEvent(e){if(this._themeService)for(const t of e){let e,i="";switch(t.index){case 256:e="foreground",i="10";break;case 257:e="background",i="11";break;case 258:e="cursor",i="12";break;default:e="ansi",i="4;"+t.index}switch(t.type){case 0:const s=b.color.toColorRGB("ansi"===e?this._themeService.colors.ansi[t.index]:this._themeService.colors[e]);this.coreService.triggerDataEvent(`${D.C0.ESC}]${i};${(0,x.toRgbString)(s)}${D.C1_ESCAPED.ST}`);break;case 1:if("ansi"===e)this._themeService.modifyColors((e=>e.ansi[t.index]=b.channels.toColor(...t.color)));else{const i=e;this._themeService.modifyColors((e=>e[i]=b.channels.toColor(...t.color)))}break;case 2:this._themeService.restoreColor(t.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(e){e?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(e){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[I"),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){return this.textarea?.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(D.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;const e=this.buffer.ybase+this.buffer.y,t=this.buffer.lines.get(e);if(!t)return;const i=Math.min(this.buffer.x,this.cols-1),s=this._renderService.dimensions.css.cell.height,r=t.getWidth(i),n=this._renderService.dimensions.css.cell.width*r,o=this.buffer.y*this._renderService.dimensions.css.cell.height,a=i*this._renderService.dimensions.css.cell.width;this.textarea.style.left=a+"px",this.textarea.style.top=o+"px",this.textarea.style.width=n+"px",this.textarea.style.height=s+"px",this.textarea.style.lineHeight=s+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this.register((0,r.addDisposableDomListener)(this.element,"copy",(e=>{this.hasSelection()&&(0,s.copyHandler)(e,this._selectionService)})));const e=e=>(0,s.handlePasteEvent)(e,this.textarea,this.coreService,this.optionsService);this.register((0,r.addDisposableDomListener)(this.textarea,"paste",e)),this.register((0,r.addDisposableDomListener)(this.element,"paste",e)),k.isFirefox?this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>{2===e.button&&(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))):this.register((0,r.addDisposableDomListener)(this.element,"contextmenu",(e=>{(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))),k.isLinux&&this.register((0,r.addDisposableDomListener)(this.element,"auxclick",(e=>{1===e.button&&(0,s.moveTextAreaUnderMouseCursor)(e,this.textarea,this.screenElement)})))}_bindKeys(){this.register((0,r.addDisposableDomListener)(this.textarea,"keyup",(e=>this._keyUp(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keydown",(e=>this._keyDown(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"keypress",(e=>this._keyPress(e)),!0)),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionstart",(()=>this._compositionHelper.compositionstart()))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionupdate",(e=>this._compositionHelper.compositionupdate(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"compositionend",(()=>this._compositionHelper.compositionend()))),this.register((0,r.addDisposableDomListener)(this.textarea,"input",(e=>this._inputEvent(e)),!0)),this.register(this.onRender((()=>this._compositionHelper.updateCompositionElements())))}open(e){if(!e)throw new Error("Terminal requires a parent element.");if(e.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this.element?.ownerDocument.defaultView&&this._coreBrowserService)return void(this.element.ownerDocument.defaultView!==this._coreBrowserService.window&&(this._coreBrowserService.window=this.element.ownerDocument.defaultView));this._document=e.ownerDocument,this.options.documentOverride&&this.options.documentOverride instanceof Document&&(this._document=this.optionsService.rawOptions.documentOverride),this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),e.appendChild(this.element);const t=this._document.createDocumentFragment();this._viewportElement=this._document.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),t.appendChild(this._viewportElement),this._viewportScrollArea=this._document.createElement("div"),this._viewportScrollArea.classList.add("xterm-scroll-area"),this._viewportElement.appendChild(this._viewportScrollArea),this.screenElement=this._document.createElement("div"),this.screenElement.classList.add("xterm-screen"),this.register((0,r.addDisposableDomListener)(this.screenElement,"mousemove",(e=>this.updateCursorStyle(e)))),this._helperContainer=this._document.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),t.appendChild(this.screenElement),this.textarea=this._document.createElement("textarea"),this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",o.promptLabel),k.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._coreBrowserService=this.register(this._instantiationService.createInstance(v.CoreBrowserService,this.textarea,e.ownerDocument.defaultView??window,this._document??"undefined"!=typeof window?window.document:null)),this._instantiationService.setService(S.ICoreBrowserService,this._coreBrowserService),this.register((0,r.addDisposableDomListener)(this.textarea,"focus",(e=>this._handleTextAreaFocus(e)))),this.register((0,r.addDisposableDomListener)(this.textarea,"blur",(()=>this._handleTextAreaBlur()))),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(u.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(S.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(C.ThemeService),this._instantiationService.setService(S.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(f.CharacterJoinerService),this._instantiationService.setService(S.ICharacterJoinerService,this._characterJoinerService),this._renderService=this.register(this._instantiationService.createInstance(g.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(S.IRenderService,this._renderService),this.register(this._renderService.onRenderedViewportChange((e=>this._onRender.fire(e)))),this.onResize((e=>this._renderService.resize(e.cols,e.rows))),this._compositionView=this._document.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(d.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this._mouseService=this._instantiationService.createInstance(p.MouseService),this._instantiationService.setService(S.IMouseService,this._mouseService),this.linkifier=this.register(this._instantiationService.createInstance(n.Linkifier,this.screenElement)),this.element.appendChild(t);try{this._onWillOpen.fire(this.element)}catch{}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this.viewport=this._instantiationService.createInstance(h.Viewport,this._viewportElement,this._viewportScrollArea),this.viewport.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent,1))),this.register(this._inputHandler.onRequestSyncScrollBar((()=>this.viewport.syncScrollArea()))),this.register(this.viewport),this.register(this.onCursorMove((()=>{this._renderService.handleCursorMove(),this._syncTextArea()}))),this.register(this.onResize((()=>this._renderService.handleResize(this.cols,this.rows)))),this.register(this.onBlur((()=>this._renderService.handleBlur()))),this.register(this.onFocus((()=>this._renderService.handleFocus()))),this.register(this._renderService.onDimensionsChange((()=>this.viewport.syncScrollArea()))),this._selectionService=this.register(this._instantiationService.createInstance(m.SelectionService,this.element,this.screenElement,this.linkifier)),this._instantiationService.setService(S.ISelectionService,this._selectionService),this.register(this._selectionService.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent)))),this.register(this._selectionService.onSelectionChange((()=>this._onSelectionChange.fire()))),this.register(this._selectionService.onRequestRedraw((e=>this._renderService.handleSelectionChanged(e.start,e.end,e.columnSelectMode)))),this.register(this._selectionService.onLinuxMouseSelection((e=>{this.textarea.value=e,this.textarea.focus(),this.textarea.select()}))),this.register(this._onScroll.event((e=>{this.viewport.syncScrollArea(),this._selectionService.refresh()}))),this.register((0,r.addDisposableDomListener)(this._viewportElement,"scroll",(()=>this._selectionService.refresh()))),this.register(this._instantiationService.createInstance(c.BufferDecorationRenderer,this.screenElement)),this.register((0,r.addDisposableDomListener)(this.element,"mousedown",(e=>this._selectionService.handleMouseDown(e)))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(M.AccessibilityManager,this)),this.register(this.optionsService.onSpecificOptionChange("screenReaderMode",(e=>this._handleScreenReaderModeOptionChange(e)))),this.options.overviewRulerWidth&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRulerWidth",(e=>{!this._overviewRulerRenderer&&e&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this.register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement)))})),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(_.DomRenderer,this,this._document,this.element,this.screenElement,this._viewportElement,this._helperContainer,this.linkifier)}bindMouse(){const e=this,t=this.element;function i(t){const i=e._mouseService.getMouseReportCoords(t,e.screenElement);if(!i)return!1;let s,r;switch(t.overrideType||t.type){case"mousemove":r=32,void 0===t.buttons?(s=3,void 0!==t.button&&(s=t.button<3?t.button:3)):s=1&t.buttons?0:4&t.buttons?1:2&t.buttons?2:3;break;case"mouseup":r=0,s=t.button<3?t.button:3;break;case"mousedown":r=1,s=t.button<3?t.button:3;break;case"wheel":if(e._customWheelEventHandler&&!1===e._customWheelEventHandler(t))return!1;if(0===e.viewport.getLinesScrolled(t))return!1;r=t.deltaY<0?0:1,s=4;break;default:return!1}return!(void 0===r||void 0===s||s>4)&&e.coreMouseService.triggerMouseEvent({col:i.col,row:i.row,x:i.x,y:i.y,button:s,action:r,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey})}const s={mouseup:null,wheel:null,mousedrag:null,mousemove:null},n={mouseup:e=>(i(e),e.buttons||(this._document.removeEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.removeEventListener("mousemove",s.mousedrag)),this.cancel(e)),wheel:e=>(i(e),this.cancel(e,!0)),mousedrag:e=>{e.buttons&&i(e)},mousemove:e=>{e.buttons||i(e)}};this.register(this.coreMouseService.onProtocolChange((e=>{e?("debug"===this.optionsService.rawOptions.logLevel&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(e)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&e?s.mousemove||(t.addEventListener("mousemove",n.mousemove),s.mousemove=n.mousemove):(t.removeEventListener("mousemove",s.mousemove),s.mousemove=null),16&e?s.wheel||(t.addEventListener("wheel",n.wheel,{passive:!1}),s.wheel=n.wheel):(t.removeEventListener("wheel",s.wheel),s.wheel=null),2&e?s.mouseup||(s.mouseup=n.mouseup):(this._document.removeEventListener("mouseup",s.mouseup),s.mouseup=null),4&e?s.mousedrag||(s.mousedrag=n.mousedrag):(this._document.removeEventListener("mousemove",s.mousedrag),s.mousedrag=null)}))),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this.register((0,r.addDisposableDomListener)(t,"mousedown",(e=>{if(e.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(e))return i(e),s.mouseup&&this._document.addEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.addEventListener("mousemove",s.mousedrag),this.cancel(e)}))),this.register((0,r.addDisposableDomListener)(t,"wheel",(e=>{if(!s.wheel){if(this._customWheelEventHandler&&!1===this._customWheelEventHandler(e))return!1;if(!this.buffer.hasScrollback){const t=this.viewport.getLinesScrolled(e);if(0===t)return;const i=D.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(e.deltaY<0?"A":"B");let s="";for(let e=0;e{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchStart(e),this.cancel(e)}),{passive:!0})),this.register((0,r.addDisposableDomListener)(t,"touchmove",(e=>{if(!this.coreMouseService.areMouseEventsActive)return this.viewport.handleTouchMove(e)?void 0:this.cancel(e)}),{passive:!1}))}refresh(e,t){this._renderService?.refreshRows(e,t)}updateCursorStyle(e){this._selectionService?.shouldColumnSelect(e)?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(e,t,i=0){1===i?(super.scrollLines(e,t,i),this.refresh(0,this.rows-1)):this.viewport?.scrollLines(e)}paste(e){(0,s.paste)(e,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(e){this._customKeyEventHandler=e}attachCustomWheelEventHandler(e){this._customWheelEventHandler=e}registerLinkProvider(e){return this._linkProviderService.registerLinkProvider(e)}registerCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");const t=this._characterJoinerService.register(e);return this.refresh(0,this.rows-1),t}deregisterCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(e)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(e){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)}registerDecoration(e){return this._decorationService.registerDecoration(e)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(e,t,i){this._selectionService.setSelection(e,t,i)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){this._selectionService?.clearSelection()}selectAll(){this._selectionService?.selectAll()}selectLines(e,t){this._selectionService?.selectLines(e,t)}_keyDown(e){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;const t=this.browser.isMac&&this.options.macOptionIsMeta&&e.altKey;if(!t&&!this._compositionHelper.keydown(e))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(),!1;t||"Dead"!==e.key&&"AltGraph"!==e.key||(this._unprocessedDeadKey=!0);const i=(0,R.evaluateKeyboardEvent)(e,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),3===i.type||2===i.type){const t=this.rows-1;return this.scrollLines(2===i.type?-t:t),this.cancel(e,!0)}return 1===i.type&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(i.cancel&&this.cancel(e,!0),!i.key||!!(e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&1===e.key.length&&e.key.charCodeAt(0)>=65&&e.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(i.key!==D.C0.ETX&&i.key!==D.C0.CR||(this.textarea.value=""),this._onKey.fire({key:i.key,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(i.key,!0),!this.optionsService.rawOptions.screenReaderMode||e.altKey||e.ctrlKey?this.cancel(e,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(e,t){const i=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey||e.isWindows&&t.getModifierState("AltGraph");return"keypress"===t.type?i:i&&(!t.keyCode||t.keyCode>47)}_keyUp(e){this._keyDownSeen=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return 16===e.keyCode||17===e.keyCode||18===e.keyCode}(e)||this.focus(),this.updateCursorStyle(e),this._keyPressHandled=!1)}_keyPress(e){let t;if(this._keyPressHandled=!1,this._keyDownHandled)return!1;if(this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(null===e.which||void 0===e.which)t=e.keyCode;else{if(0===e.which||0===e.charCode)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e)||(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(t,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(e){if(e.data&&"insertText"===e.inputType&&(!e.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;const t=e.data;return this.coreService.triggerDataEvent(t,!0),this.cancel(e),!0}return!1}resize(e,t){e!==this.cols||t!==this.rows?super.resize(e,t):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(e,t){this._charSizeService?.measure(),this.viewport?.syncScrollArea(!0)}clear(){if(0!==this.buffer.ybase||0!==this.buffer.y){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let e=1;e{Object.defineProperty(t,"__esModule",{value:!0}),t.TimeBasedDebouncer=void 0,t.TimeBasedDebouncer=class{constructor(e,t=1e3){this._renderCallback=e,this._debounceThresholdMS=t,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t;const s=Date.now();if(s-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=s,this._innerRefresh();else if(!this._additionalRefreshRequested){const e=s-this._lastRefreshMs,t=this._debounceThresholdMS-e;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout((()=>{this._lastRefreshMs=Date.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0}),t)}}_innerRefresh(){if(void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return;const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t)}}},1680:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Viewport=void 0;const n=i(3656),o=i(4725),a=i(8460),h=i(844),c=i(2585);let l=t.Viewport=class extends h.Disposable{constructor(e,t,i,s,r,o,h,c){super(),this._viewportElement=e,this._scrollArea=t,this._bufferService=i,this._optionsService=s,this._charSizeService=r,this._renderService=o,this._coreBrowserService=h,this.scrollBarWidth=0,this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._wheelPartialScroll=0,this._refreshAnimationFrame=null,this._ignoreNextScrollEvent=!1,this._smoothScrollState={startTime:0,origin:-1,target:-1},this._onRequestScrollLines=this.register(new a.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this.scrollBarWidth=this._viewportElement.offsetWidth-this._scrollArea.offsetWidth||15,this.register((0,n.addDisposableDomListener)(this._viewportElement,"scroll",this._handleScroll.bind(this))),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._renderDimensions=this._renderService.dimensions,this.register(this._renderService.onDimensionsChange((e=>this._renderDimensions=e))),this._handleThemeChange(c.colors),this.register(c.onChangeColors((e=>this._handleThemeChange(e)))),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.syncScrollArea()))),setTimeout((()=>this.syncScrollArea()))}_handleThemeChange(e){this._viewportElement.style.backgroundColor=e.background.css}reset(){this._currentRowHeight=0,this._currentDeviceCellHeight=0,this._lastRecordedBufferLength=0,this._lastRecordedViewportHeight=0,this._lastRecordedBufferHeight=0,this._lastTouchY=0,this._lastScrollTop=0,this._coreBrowserService.window.requestAnimationFrame((()=>this.syncScrollArea()))}_refresh(e){if(e)return this._innerRefresh(),void(null!==this._refreshAnimationFrame&&this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame));null===this._refreshAnimationFrame&&(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._charSizeService.height>0){this._currentRowHeight=this._renderDimensions.device.cell.height/this._coreBrowserService.dpr,this._currentDeviceCellHeight=this._renderDimensions.device.cell.height,this._lastRecordedViewportHeight=this._viewportElement.offsetHeight;const e=Math.round(this._currentRowHeight*this._lastRecordedBufferLength)+(this._lastRecordedViewportHeight-this._renderDimensions.css.canvas.height);this._lastRecordedBufferHeight!==e&&(this._lastRecordedBufferHeight=e,this._scrollArea.style.height=this._lastRecordedBufferHeight+"px")}const e=this._bufferService.buffer.ydisp*this._currentRowHeight;this._viewportElement.scrollTop!==e&&(this._ignoreNextScrollEvent=!0,this._viewportElement.scrollTop=e),this._refreshAnimationFrame=null}syncScrollArea(e=!1){if(this._lastRecordedBufferLength!==this._bufferService.buffer.lines.length)return this._lastRecordedBufferLength=this._bufferService.buffer.lines.length,void this._refresh(e);this._lastRecordedViewportHeight===this._renderService.dimensions.css.canvas.height&&this._lastScrollTop===this._activeBuffer.ydisp*this._currentRowHeight&&this._renderDimensions.device.cell.height===this._currentDeviceCellHeight||this._refresh(e)}_handleScroll(e){if(this._lastScrollTop=this._viewportElement.scrollTop,!this._viewportElement.offsetParent)return;if(this._ignoreNextScrollEvent)return this._ignoreNextScrollEvent=!1,void this._onRequestScrollLines.fire({amount:0,suppressScrollEvent:!0});const t=Math.round(this._lastScrollTop/this._currentRowHeight)-this._bufferService.buffer.ydisp;this._onRequestScrollLines.fire({amount:t,suppressScrollEvent:!0})}_smoothScroll(){if(this._isDisposed||-1===this._smoothScrollState.origin||-1===this._smoothScrollState.target)return;const e=this._smoothScrollPercent();this._viewportElement.scrollTop=this._smoothScrollState.origin+Math.round(e*(this._smoothScrollState.target-this._smoothScrollState.origin)),e<1?this._coreBrowserService.window.requestAnimationFrame((()=>this._smoothScroll())):this._clearSmoothScrollState()}_smoothScrollPercent(){return this._optionsService.rawOptions.smoothScrollDuration&&this._smoothScrollState.startTime?Math.max(Math.min((Date.now()-this._smoothScrollState.startTime)/this._optionsService.rawOptions.smoothScrollDuration,1),0):1}_clearSmoothScrollState(){this._smoothScrollState.startTime=0,this._smoothScrollState.origin=-1,this._smoothScrollState.target=-1}_bubbleScroll(e,t){const i=this._viewportElement.scrollTop+this._lastRecordedViewportHeight;return!(t<0&&0!==this._viewportElement.scrollTop||t>0&&i0&&(i=e),s=""}}return{bufferElements:r,cursorElement:i}}getLinesScrolled(e){if(0===e.deltaY||e.shiftKey)return 0;let t=this._applyScrollModifier(e.deltaY,e);return e.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(t/=this._currentRowHeight+0,this._wheelPartialScroll+=t,t=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._bufferService.rows),t}_applyScrollModifier(e,t){const i=this._optionsService.rawOptions.fastScrollModifier;return"alt"===i&&t.altKey||"ctrl"===i&&t.ctrlKey||"shift"===i&&t.shiftKey?e*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:e*this._optionsService.rawOptions.scrollSensitivity}handleTouchStart(e){this._lastTouchY=e.touches[0].pageY}handleTouchMove(e){const t=this._lastTouchY-e.touches[0].pageY;return this._lastTouchY=e.touches[0].pageY,0!==t&&(this._viewportElement.scrollTop+=t,this._bubbleScroll(e,t))}};t.Viewport=l=s([r(2,c.IBufferService),r(3,c.IOptionsService),r(4,o.ICharSizeService),r(5,o.IRenderService),r(6,o.ICoreBrowserService),r(7,o.IThemeService)],l)},3107:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferDecorationRenderer=void 0;const n=i(4725),o=i(844),a=i(2585);let h=t.BufferDecorationRenderer=class extends o.Disposable{constructor(e,t,i,s,r){super(),this._screenElement=e,this._bufferService=t,this._coreBrowserService=i,this._decorationService=s,this._renderService=r,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this.register(this._renderService.onRenderedViewportChange((()=>this._doRefreshDecorations()))),this.register(this._renderService.onDimensionsChange((()=>{this._dimensionsChanged=!0,this._queueRefresh()}))),this.register(this._coreBrowserService.onDprChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt}))),this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh()))),this.register(this._decorationService.onDecorationRemoved((e=>this._removeDecoration(e)))),this.register((0,o.toDisposable)((()=>{this._container.remove(),this._decorationElements.clear()})))}_queueRefresh(){void 0===this._animationFrame&&(this._animationFrame=this._renderService.addRefreshCallback((()=>{this._doRefreshDecorations(),this._animationFrame=void 0})))}_doRefreshDecorations(){for(const e of this._decorationService.decorations)this._renderDecoration(e);this._dimensionsChanged=!1}_renderDecoration(e){this._refreshStyle(e),this._dimensionsChanged&&this._refreshXPosition(e)}_createElement(e){const t=this._coreBrowserService.mainDocument.createElement("div");t.classList.add("xterm-decoration"),t.classList.toggle("xterm-decoration-top-layer","top"===e?.options?.layer),t.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,t.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+"px",t.style.top=(e.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",t.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;const i=e.options.x??0;return i&&i>this._bufferService.cols&&(t.style.display="none"),this._refreshXPosition(e,t),t}_refreshStyle(e){const t=e.marker.line-this._bufferService.buffers.active.ydisp;if(t<0||t>=this._bufferService.rows)e.element&&(e.element.style.display="none",e.onRenderEmitter.fire(e.element));else{let i=this._decorationElements.get(e);i||(i=this._createElement(e),e.element=i,this._decorationElements.set(e,i),this._container.appendChild(i),e.onDispose((()=>{this._decorationElements.delete(e),i.remove()}))),i.style.top=t*this._renderService.dimensions.css.cell.height+"px",i.style.display=this._altBufferIsActive?"none":"block",e.onRenderEmitter.fire(i)}}_refreshXPosition(e,t=e.element){if(!t)return;const i=e.options.x??0;"right"===(e.options.anchor||"left")?t.style.right=i?i*this._renderService.dimensions.css.cell.width+"px":"":t.style.left=i?i*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(e){this._decorationElements.get(e)?.remove(),this._decorationElements.delete(e),e.dispose()}};t.BufferDecorationRenderer=h=s([r(1,a.IBufferService),r(2,n.ICoreBrowserService),r(3,a.IDecorationService),r(4,n.IRenderService)],h)},5871:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorZoneStore=void 0,t.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(e){if(e.options.overviewRulerOptions){for(const t of this._zones)if(t.color===e.options.overviewRulerOptions.color&&t.position===e.options.overviewRulerOptions.position){if(this._lineIntersectsZone(t,e.marker.line))return;if(this._lineAdjacentToZone(t,e.marker.line,e.options.overviewRulerOptions.position))return void this._addLineToZone(t,e.marker.line)}if(this._zonePoolIndex=e.startBufferLine&&t<=e.endBufferLine}_lineAdjacentToZone(e,t,i){return t>=e.startBufferLine-this._linePadding[i||"full"]&&t<=e.endBufferLine+this._linePadding[i||"full"]}_addLineToZone(e,t){e.startBufferLine=Math.min(e.startBufferLine,t),e.endBufferLine=Math.max(e.endBufferLine,t)}}},5744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OverviewRulerRenderer=void 0;const n=i(5871),o=i(4725),a=i(844),h=i(2585),c={full:0,left:0,center:0,right:0},l={full:0,left:0,center:0,right:0},d={full:0,left:0,center:0,right:0};let _=t.OverviewRulerRenderer=class extends a.Disposable{get _width(){return this._optionsService.options.overviewRulerWidth||0}constructor(e,t,i,s,r,o,h){super(),this._viewportElement=e,this._screenElement=t,this._bufferService=i,this._decorationService=s,this._renderService=r,this._optionsService=o,this._coreBrowserService=h,this._colorZoneStore=new n.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=this._coreBrowserService.mainDocument.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),this._viewportElement.parentElement?.insertBefore(this._canvas,this._viewportElement);const c=this._canvas.getContext("2d");if(!c)throw new Error("Ctx cannot be null");this._ctx=c,this._registerDecorationListeners(),this._registerBufferChangeListeners(),this._registerDimensionChangeListeners(),this.register((0,a.toDisposable)((()=>{this._canvas?.remove()})))}_registerDecorationListeners(){this.register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh(void 0,!0)))),this.register(this._decorationService.onDecorationRemoved((()=>this._queueRefresh(void 0,!0))))}_registerBufferChangeListeners(){this.register(this._renderService.onRenderedViewportChange((()=>this._queueRefresh()))),this.register(this._bufferService.buffers.onBufferActivate((()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"}))),this.register(this._bufferService.onScroll((()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())})))}_registerDimensionChangeListeners(){this.register(this._renderService.onRender((()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)}))),this.register(this._optionsService.onSpecificOptionChange("overviewRulerWidth",(()=>this._queueRefresh(!0)))),this.register(this._coreBrowserService.onDprChange((()=>this._queueRefresh(!0)))),this._queueRefresh(!0)}_refreshDrawConstants(){const e=Math.floor(this._canvas.width/3),t=Math.ceil(this._canvas.width/3);l.full=this._canvas.width,l.left=e,l.center=t,l.right=e,this._refreshDrawHeightConstants(),d.full=0,d.left=0,d.center=l.left,d.right=l.left+l.center}_refreshDrawHeightConstants(){c.full=Math.round(2*this._coreBrowserService.dpr);const e=this._canvas.height/this._bufferService.buffer.lines.length,t=Math.round(Math.max(Math.min(e,12),6)*this._coreBrowserService.dpr);c.left=t,c.center=t,c.right=t}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*c.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*c.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*c.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*c.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowserService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowserService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(const e of this._decorationService.decorations)this._colorZoneStore.addDecoration(e);this._ctx.lineWidth=1;const e=this._colorZoneStore.zones;for(const t of e)"full"!==t.position&&this._renderColorZone(t);for(const t of e)"full"===t.position&&this._renderColorZone(t);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderColorZone(e){this._ctx.fillStyle=e.color,this._ctx.fillRect(d[e.position||"full"],Math.round((this._canvas.height-1)*(e.startBufferLine/this._bufferService.buffers.active.lines.length)-c[e.position||"full"]/2),l[e.position||"full"],Math.round((this._canvas.height-1)*((e.endBufferLine-e.startBufferLine)/this._bufferService.buffers.active.lines.length)+c[e.position||"full"]))}_queueRefresh(e,t){this._shouldUpdateDimensions=e||this._shouldUpdateDimensions,this._shouldUpdateAnchor=t||this._shouldUpdateAnchor,void 0===this._animationFrame&&(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>{this._refreshDecorations(),this._animationFrame=void 0})))}};t.OverviewRulerRenderer=_=s([r(2,h.IBufferService),r(3,h.IDecorationService),r(4,o.IRenderService),r(5,h.IOptionsService),r(6,o.ICoreBrowserService)],_)},2950:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CompositionHelper=void 0;const n=i(4725),o=i(2585),a=i(2584);let h=t.CompositionHelper=class{get isComposing(){return this._isComposing}constructor(e,t,i,s,r,n){this._textarea=e,this._compositionView=t,this._bufferService=i,this._optionsService=s,this._coreService=r,this._renderService=n,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(e){this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((()=>{this._compositionPosition.end=this._textarea.value.length}),0)}compositionend(){this._finalizeComposition(!0)}keydown(e){if(this._isComposing||this._isSendingComposition){if(229===e.keyCode)return!1;if(16===e.keyCode||17===e.keyCode||18===e.keyCode)return!1;this._finalizeComposition(!1)}return 229!==e.keyCode||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(e){if(this._compositionView.classList.remove("active"),this._isComposing=!1,e){const e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((()=>{if(this._isSendingComposition){let t;this._isSendingComposition=!1,e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,e.end):this._textarea.value.substring(e.start),t.length>0&&this._coreService.triggerDataEvent(t,!0)}}),0)}else{this._isSendingComposition=!1;const e=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(e,!0)}}_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.lengththis.updateCompositionElements(!0)),0)}}};t.CompositionHelper=h=s([r(2,o.IBufferService),r(3,o.IOptionsService),r(4,o.ICoreService),r(5,n.IRenderService)],h)},9806:(e,t)=>{function i(e,t,i){const s=i.getBoundingClientRect(),r=e.getComputedStyle(i),n=parseInt(r.getPropertyValue("padding-left")),o=parseInt(r.getPropertyValue("padding-top"));return[t.clientX-s.left-n,t.clientY-s.top-o]}Object.defineProperty(t,"__esModule",{value:!0}),t.getCoords=t.getCoordsRelativeToElement=void 0,t.getCoordsRelativeToElement=i,t.getCoords=function(e,t,s,r,n,o,a,h,c){if(!o)return;const l=i(e,t,s);return l?(l[0]=Math.ceil((l[0]+(c?a/2:0))/a),l[1]=Math.ceil(l[1]/h),l[0]=Math.min(Math.max(l[0],1),r+(c?1:0)),l[1]=Math.min(Math.max(l[1],1),n),l):void 0}},9504:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.moveToCellSequence=void 0;const s=i(2584);function r(e,t,i,s){const r=e-n(e,i),a=t-n(t,i),l=Math.abs(r-a)-function(e,t,i){let s=0;const r=e-n(e,i),a=t-n(t,i);for(let n=0;n=0&&et?"A":"B"}function a(e,t,i,s,r,n){let o=e,a=t,h="";for(;o!==i||a!==s;)o+=r?1:-1,r&&o>n.cols-1?(h+=n.buffer.translateBufferLineToString(a,!1,e,o),o=0,e=0,a++):!r&&o<0&&(h+=n.buffer.translateBufferLineToString(a,!1,0,e+1),o=n.cols-1,e=o,a--);return h+n.buffer.translateBufferLineToString(a,!1,e,o)}function h(e,t){const i=t?"O":"[";return s.C0.ESC+i+e}function c(e,t){e=Math.floor(e);let i="";for(let s=0;s0?s-n(s,o):t;const _=s,u=function(e,t,i,s,o,a){let h;return h=r(i,s,o,a).length>0?s-n(s,o):t,e=i&&he?"D":"C",c(Math.abs(o-e),h(d,s));d=l>t?"D":"C";const _=Math.abs(l-t);return c(function(e,t){return t.cols-e}(l>t?e:o,i)+(_-1)*i.cols+1+((l>t?o:e)-1),h(d,s))}},1296:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRenderer=void 0;const n=i(3787),o=i(2550),a=i(2223),h=i(6171),c=i(6052),l=i(4725),d=i(8055),_=i(8460),u=i(844),f=i(2585),v="xterm-dom-renderer-owner-",p="xterm-rows",g="xterm-fg-",m="xterm-bg-",S="xterm-focus",C="xterm-selection";let b=1,w=t.DomRenderer=class extends u.Disposable{constructor(e,t,i,s,r,a,l,d,f,g,m,S,w){super(),this._terminal=e,this._document=t,this._element=i,this._screenElement=s,this._viewportElement=r,this._helperContainer=a,this._linkifier2=l,this._charSizeService=f,this._optionsService=g,this._bufferService=m,this._coreBrowserService=S,this._themeService=w,this._terminalClass=b++,this._rowElements=[],this._selectionRenderModel=(0,c.createSelectionRenderModel)(),this.onRequestRedraw=this.register(new _.EventEmitter).event,this._rowContainer=this._document.createElement("div"),this._rowContainer.classList.add(p),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=this._document.createElement("div"),this._selectionContainer.classList.add(C),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,h.createRenderDimensions)(),this._updateDimensions(),this.register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._themeService.onChangeColors((e=>this._injectCss(e)))),this._injectCss(this._themeService.colors),this._rowFactory=d.createInstance(n.DomRendererRowFactory,document),this._element.classList.add(v+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this.register(this._linkifier2.onShowLinkUnderline((e=>this._handleLinkHover(e)))),this.register(this._linkifier2.onHideLinkUnderline((e=>this._handleLinkLeave(e)))),this.register((0,u.toDisposable)((()=>{this._element.classList.remove(v+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()}))),this._widthCache=new o.WidthCache(this._document,this._helperContainer),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){const e=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*e,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*e),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/e),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/e),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(const e of this._rowElements)e.style.width=`${this.dimensions.css.canvas.width}px`,e.style.height=`${this.dimensions.css.cell.height}px`,e.style.lineHeight=`${this.dimensions.css.cell.height}px`,e.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));const t=`${this._terminalSelector} .${p} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(e){this._themeStyleElement||(this._themeStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let t=`${this._terminalSelector} .${p} { color: ${e.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;t+=`${this._terminalSelector} .${p} .xterm-dim { color: ${d.color.multiplyOpacity(e.foreground,.5).css};}`,t+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`;const i=`blink_underline_${this._terminalClass}`,s=`blink_bar_${this._terminalClass}`,r=`blink_block_${this._terminalClass}`;t+=`@keyframes ${i} { 50% { border-bottom-style: hidden; }}`,t+=`@keyframes ${s} { 50% { box-shadow: none; }}`,t+=`@keyframes ${r} { 0% { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css}; } 50% { background-color: inherit; color: ${e.cursor.css}; }}`,t+=`${this._terminalSelector} .${p}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-underline { animation: ${i} 1s step-end infinite;}${this._terminalSelector} .${p}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-bar { animation: ${s} 1s step-end infinite;}${this._terminalSelector} .${p}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: ${r} 1s step-end infinite;}${this._terminalSelector} .${p} .xterm-cursor.xterm-cursor-block { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css};}${this._terminalSelector} .${p} .xterm-cursor.xterm-cursor-block:not(.xterm-cursor-blink) { background-color: ${e.cursor.css} !important; color: ${e.cursorAccent.css} !important;}${this._terminalSelector} .${p} .xterm-cursor.xterm-cursor-outline { outline: 1px solid ${e.cursor.css}; outline-offset: -1px;}${this._terminalSelector} .${p} .xterm-cursor.xterm-cursor-bar { box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${e.cursor.css} inset;}${this._terminalSelector} .${p} .xterm-cursor.xterm-cursor-underline { border-bottom: 1px ${e.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,t+=`${this._terminalSelector} .${C} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${C} div { position: absolute; background-color: ${e.selectionBackgroundOpaque.css};}${this._terminalSelector} .${C} div { position: absolute; background-color: ${e.selectionInactiveBackgroundOpaque.css};}`;for(const[i,s]of e.ansi.entries())t+=`${this._terminalSelector} .${g}${i} { color: ${s.css}; }${this._terminalSelector} .${g}${i}.xterm-dim { color: ${d.color.multiplyOpacity(s,.5).css}; }${this._terminalSelector} .${m}${i} { background-color: ${s.css}; }`;t+=`${this._terminalSelector} .${g}${a.INVERTED_DEFAULT_COLOR} { color: ${d.color.opaque(e.background).css}; }${this._terminalSelector} .${g}${a.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${d.color.multiplyOpacity(d.color.opaque(e.background),.5).css}; }${this._terminalSelector} .${m}${a.INVERTED_DEFAULT_COLOR} { background-color: ${e.foreground.css}; }`,this._themeStyleElement.textContent=t}_setDefaultSpacing(){const e=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${e}px`,this._rowFactory.defaultSpacing=e}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(e,t){for(let e=this._rowElements.length;e<=t;e++){const e=this._document.createElement("div");this._rowContainer.appendChild(e),this._rowElements.push(e)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(e,t){this._refreshRowElements(e,t),this._updateDimensions(),this.handleSelectionChanged(this._selectionRenderModel.selectionStart,this._selectionRenderModel.selectionEnd,this._selectionRenderModel.columnSelectMode)}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(S),this.renderRows(0,this._bufferService.rows-1)}handleFocus(){this._rowContainer.classList.add(S),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(e,t,i){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(e,t,i),this.renderRows(0,this._bufferService.rows-1),!e||!t)return;this._selectionRenderModel.update(this._terminal,e,t,i);const s=this._selectionRenderModel.viewportStartRow,r=this._selectionRenderModel.viewportEndRow,n=this._selectionRenderModel.viewportCappedStartRow,o=this._selectionRenderModel.viewportCappedEndRow;if(n>=this._bufferService.rows||o<0)return;const a=this._document.createDocumentFragment();if(i){const i=e[0]>t[0];a.appendChild(this._createSelectionElement(n,i?t[0]:e[0],i?e[0]:t[0],o-n+1))}else{const i=s===n?e[0]:0,h=n===r?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(n,i,h));const c=o-n-1;if(a.appendChild(this._createSelectionElement(n+1,0,this._bufferService.cols,c)),n!==o){const e=r===o?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(o,0,e))}}this._selectionContainer.appendChild(a)}_createSelectionElement(e,t,i,s=1){const r=this._document.createElement("div"),n=t*this.dimensions.css.cell.width;let o=this.dimensions.css.cell.width*(i-t);return n+o>this.dimensions.css.canvas.width&&(o=this.dimensions.css.canvas.width-n),r.style.height=s*this.dimensions.css.cell.height+"px",r.style.top=e*this.dimensions.css.cell.height+"px",r.style.left=`${n}px`,r.style.width=`${o}px`,r}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(const e of this._rowElements)e.replaceChildren()}renderRows(e,t){const i=this._bufferService.buffer,s=i.ybase+i.y,r=Math.min(i.x,this._bufferService.cols-1),n=this._optionsService.rawOptions.cursorBlink,o=this._optionsService.rawOptions.cursorStyle,a=this._optionsService.rawOptions.cursorInactiveStyle;for(let h=e;h<=t;h++){const e=h+i.ydisp,t=this._rowElements[h],c=i.lines.get(e);if(!t||!c)break;t.replaceChildren(...this._rowFactory.createRow(c,e,e===s,o,a,r,n,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${v}${this._terminalClass}`}_handleLinkHover(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)}_handleLinkLeave(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)}_setCellUnderline(e,t,i,s,r,n){i<0&&(e=0),s<0&&(t=0);const o=this._bufferService.rows-1;i=Math.max(Math.min(i,o),0),s=Math.max(Math.min(s,o),0),r=Math.min(r,this._bufferService.cols);const a=this._bufferService.buffer,h=a.ybase+a.y,c=Math.min(a.x,r-1),l=this._optionsService.rawOptions.cursorBlink,d=this._optionsService.rawOptions.cursorStyle,_=this._optionsService.rawOptions.cursorInactiveStyle;for(let o=i;o<=s;++o){const u=o+a.ydisp,f=this._rowElements[o],v=a.lines.get(u);if(!f||!v)break;f.replaceChildren(...this._rowFactory.createRow(v,u,u===h,d,_,c,l,this.dimensions.css.cell.width,this._widthCache,n?o===i?e:0:-1,n?(o===s?t:r)-1:-1))}}};t.DomRenderer=w=s([r(7,f.IInstantiationService),r(8,l.ICharSizeService),r(9,f.IOptionsService),r(10,f.IBufferService),r(11,l.ICoreBrowserService),r(12,l.IThemeService)],w)},3787:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRendererRowFactory=void 0;const n=i(2223),o=i(643),a=i(511),h=i(2585),c=i(8055),l=i(4725),d=i(4269),_=i(6171),u=i(3734);let f=t.DomRendererRowFactory=class{constructor(e,t,i,s,r,n,o){this._document=e,this._characterJoinerService=t,this._optionsService=i,this._coreBrowserService=s,this._coreService=r,this._decorationService=n,this._themeService=o,this._workCell=new a.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(e,t,i){this._selectionStart=e,this._selectionEnd=t,this._columnSelectMode=i}createRow(e,t,i,s,r,a,h,l,_,f,p){const g=[],m=this._characterJoinerService.getJoinedCharacters(t),S=this._themeService.colors;let C,b=e.getNoBgTrimmedLength();i&&b0&&M===m[0][0]){O=!0;const t=m.shift();I=new d.JoinedCellData(this._workCell,e.translateToString(!0,t[0],t[1]),t[1]-t[0]),P=t[1]-1,b=I.getWidth()}const H=this._isCellInSelection(M,t),F=i&&M===a,W=T&&M>=f&&M<=p;let U=!1;this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{U=!0}));let N=I.getChars()||o.WHITESPACE_CELL_CHAR;if(" "===N&&(I.isUnderline()||I.isOverline())&&(N=" "),A=b*l-_.get(N,I.isBold(),I.isItalic()),C){if(w&&(H&&x||!H&&!x&&I.bg===E)&&(H&&x&&S.selectionForeground||I.fg===k)&&I.extended.ext===L&&W===D&&A===R&&!F&&!O&&!U){I.isInvisible()?y+=o.WHITESPACE_CELL_CHAR:y+=N,w++;continue}w&&(C.textContent=y),C=this._document.createElement("span"),w=0,y=""}else C=this._document.createElement("span");if(E=I.bg,k=I.fg,L=I.extended.ext,D=W,R=A,x=H,O&&a>=M&&a<=P&&(a=M),!this._coreService.isCursorHidden&&F&&this._coreService.isCursorInitialized)if(B.push("xterm-cursor"),this._coreBrowserService.isFocused)h&&B.push("xterm-cursor-blink"),B.push("bar"===s?"xterm-cursor-bar":"underline"===s?"xterm-cursor-underline":"xterm-cursor-block");else if(r)switch(r){case"outline":B.push("xterm-cursor-outline");break;case"block":B.push("xterm-cursor-block");break;case"bar":B.push("xterm-cursor-bar");break;case"underline":B.push("xterm-cursor-underline")}if(I.isBold()&&B.push("xterm-bold"),I.isItalic()&&B.push("xterm-italic"),I.isDim()&&B.push("xterm-dim"),y=I.isInvisible()?o.WHITESPACE_CELL_CHAR:I.getChars()||o.WHITESPACE_CELL_CHAR,I.isUnderline()&&(B.push(`xterm-underline-${I.extended.underlineStyle}`)," "===y&&(y=" "),!I.isUnderlineColorDefault()))if(I.isUnderlineColorRGB())C.style.textDecorationColor=`rgb(${u.AttributeData.toColorRGB(I.getUnderlineColor()).join(",")})`;else{let e=I.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&I.isBold()&&e<8&&(e+=8),C.style.textDecorationColor=S.ansi[e].css}I.isOverline()&&(B.push("xterm-overline")," "===y&&(y=" ")),I.isStrikethrough()&&B.push("xterm-strikethrough"),W&&(C.style.textDecoration="underline");let $=I.getFgColor(),j=I.getFgColorMode(),z=I.getBgColor(),K=I.getBgColorMode();const q=!!I.isInverse();if(q){const e=$;$=z,z=e;const t=j;j=K,K=t}let V,G,X,J=!1;switch(this._decorationService.forEachDecorationAtCell(M,t,void 0,(e=>{"top"!==e.options.layer&&J||(e.backgroundColorRGB&&(K=50331648,z=e.backgroundColorRGB.rgba>>8&16777215,V=e.backgroundColorRGB),e.foregroundColorRGB&&(j=50331648,$=e.foregroundColorRGB.rgba>>8&16777215,G=e.foregroundColorRGB),J="top"===e.options.layer)})),!J&&H&&(V=this._coreBrowserService.isFocused?S.selectionBackgroundOpaque:S.selectionInactiveBackgroundOpaque,z=V.rgba>>8&16777215,K=50331648,J=!0,S.selectionForeground&&(j=50331648,$=S.selectionForeground.rgba>>8&16777215,G=S.selectionForeground)),J&&B.push("xterm-decoration-top"),K){case 16777216:case 33554432:X=S.ansi[z],B.push(`xterm-bg-${z}`);break;case 50331648:X=c.channels.toColor(z>>16,z>>8&255,255&z),this._addStyle(C,`background-color:#${v((z>>>0).toString(16),"0",6)}`);break;default:q?(X=S.foreground,B.push(`xterm-bg-${n.INVERTED_DEFAULT_COLOR}`)):X=S.background}switch(V||I.isDim()&&(V=c.color.multiplyOpacity(X,.5)),j){case 16777216:case 33554432:I.isBold()&&$<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&($+=8),this._applyMinimumContrast(C,X,S.ansi[$],I,V,void 0)||B.push(`xterm-fg-${$}`);break;case 50331648:const e=c.channels.toColor($>>16&255,$>>8&255,255&$);this._applyMinimumContrast(C,X,e,I,V,G)||this._addStyle(C,`color:#${v($.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(C,X,S.foreground,I,V,G)||q&&B.push(`xterm-fg-${n.INVERTED_DEFAULT_COLOR}`)}B.length&&(C.className=B.join(" "),B.length=0),F||O||U?C.textContent=y:w++,A!==this.defaultSpacing&&(C.style.letterSpacing=`${A}px`),g.push(C),M=P}return C&&w&&(C.textContent=y),g}_applyMinimumContrast(e,t,i,s,r,n){if(1===this._optionsService.rawOptions.minimumContrastRatio||(0,_.treatGlyphAsBackgroundColor)(s.getCode()))return!1;const o=this._getContrastCache(s);let a;if(r||n||(a=o.getColor(t.rgba,i.rgba)),void 0===a){const e=this._optionsService.rawOptions.minimumContrastRatio/(s.isDim()?2:1);a=c.color.ensureContrastRatio(r||t,n||i,e),o.setColor((r||t).rgba,(n||i).rgba,a??null)}return!!a&&(this._addStyle(e,`color:${a.css}`),!0)}_getContrastCache(e){return e.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(e,t){e.setAttribute("style",`${e.getAttribute("style")||""}${t};`)}_isCellInSelection(e,t){const i=this._selectionStart,s=this._selectionEnd;return!(!i||!s)&&(this._columnSelectMode?i[0]<=s[0]?e>=i[0]&&t>=i[1]&&e=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t=i[0]&&e=i[0])}};function v(e,t,i){for(;e.length{Object.defineProperty(t,"__esModule",{value:!0}),t.WidthCache=void 0,t.WidthCache=class{constructor(e,t){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=e.createElement("div"),this._container.classList.add("xterm-width-cache-measure-container"),this._container.setAttribute("aria-hidden","true"),this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";const i=e.createElement("span");i.classList.add("xterm-char-measure-element");const s=e.createElement("span");s.classList.add("xterm-char-measure-element"),s.style.fontWeight="bold";const r=e.createElement("span");r.classList.add("xterm-char-measure-element"),r.style.fontStyle="italic";const n=e.createElement("span");n.classList.add("xterm-char-measure-element"),n.style.fontWeight="bold",n.style.fontStyle="italic",this._measureElements=[i,s,r,n],this._container.appendChild(i),this._container.appendChild(s),this._container.appendChild(r),this._container.appendChild(n),t.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(e,t,i,s){e===this._font&&t===this._fontSize&&i===this._weight&&s===this._weightBold||(this._font=e,this._fontSize=t,this._weight=i,this._weightBold=s,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${i}`,this._measureElements[1].style.fontWeight=`${s}`,this._measureElements[2].style.fontWeight=`${i}`,this._measureElements[3].style.fontWeight=`${s}`,this.clear())}get(e,t,i){let s=0;if(!t&&!i&&1===e.length&&(s=e.charCodeAt(0))<256){if(-9999!==this._flat[s])return this._flat[s];const t=this._measure(e,0);return t>0&&(this._flat[s]=t),t}let r=e;t&&(r+="B"),i&&(r+="I");let n=this._holey.get(r);if(void 0===n){let s=0;t&&(s|=1),i&&(s|=2),n=this._measure(e,s),n>0&&this._holey.set(r,n)}return n}_measure(e,t){const i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}}},2223:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.TEXT_BASELINE=t.DIM_OPACITY=t.INVERTED_DEFAULT_COLOR=void 0;const s=i(6114);t.INVERTED_DEFAULT_COLOR=257,t.DIM_OPACITY=.5,t.TEXT_BASELINE=s.isFirefox||s.isLegacyEdge?"bottom":"ideographic"},6171:(e,t)=>{function i(e){return 57508<=e&&e<=57558}function s(e){return e>=128512&&e<=128591||e>=127744&&e<=128511||e>=128640&&e<=128767||e>=9728&&e<=9983||e>=9984&&e<=10175||e>=65024&&e<=65039||e>=129280&&e<=129535||e>=127462&&e<=127487}Object.defineProperty(t,"__esModule",{value:!0}),t.computeNextVariantOffset=t.createRenderDimensions=t.treatGlyphAsBackgroundColor=t.allowRescaling=t.isEmoji=t.isRestrictedPowerlineGlyph=t.isPowerlineGlyph=t.throwIfFalsy=void 0,t.throwIfFalsy=function(e){if(!e)throw new Error("value must not be falsy");return e},t.isPowerlineGlyph=i,t.isRestrictedPowerlineGlyph=function(e){return 57520<=e&&e<=57527},t.isEmoji=s,t.allowRescaling=function(e,t,r,n){return 1===t&&r>Math.ceil(1.5*n)&&void 0!==e&&e>255&&!s(e)&&!i(e)&&!function(e){return 57344<=e&&e<=63743}(e)},t.treatGlyphAsBackgroundColor=function(e){return i(e)||function(e){return 9472<=e&&e<=9631}(e)},t.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}},t.computeNextVariantOffset=function(e,t,i=0){return(e-(2*Math.round(t)-i))%(2*Math.round(t))}},6052:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createSelectionRenderModel=void 0;class i{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(e,t,i,s=!1){if(this.selectionStart=t,this.selectionEnd=i,!t||!i||t[0]===i[0]&&t[1]===i[1])return void this.clear();const r=e.buffers.active.ydisp,n=t[1]-r,o=i[1]-r,a=Math.max(n,0),h=Math.min(o,e.rows-1);a>=e.rows||h<0?this.clear():(this.hasSelection=!0,this.columnSelectMode=s,this.viewportStartRow=n,this.viewportEndRow=o,this.viewportCappedStartRow=a,this.viewportCappedEndRow=h,this.startCol=t[0],this.endCol=i[0])}isCellSelected(e,t,i){return!!this.hasSelection&&(i-=e.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?t>=this.startCol&&i>=this.viewportCappedStartRow&&t=this.viewportCappedStartRow&&t>=this.endCol&&i<=this.viewportCappedEndRow:i>this.viewportStartRow&&i=this.startCol&&t=this.startCol)}}t.createSelectionRenderModel=function(){return new i}},456:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionModel=void 0,t.SelectionModel=class{constructor(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?e%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)-1]:[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[Math.max(e,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){const e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])}handleTrim(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},428:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharSizeService=void 0;const n=i(2585),o=i(8460),a=i(844);let h=t.CharSizeService=class extends a.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,i){super(),this._optionsService=i,this.width=0,this.height=0,this._onCharSizeChange=this.register(new o.EventEmitter),this.onCharSizeChange=this._onCharSizeChange.event;try{this._measureStrategy=this.register(new d(this._optionsService))}catch{this._measureStrategy=this.register(new l(e,t,this._optionsService))}this.register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],(()=>this.measure())))}measure(){const e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};t.CharSizeService=h=s([r(2,n.IOptionsService)],h);class c extends a.Disposable{constructor(){super(...arguments),this._result={width:0,height:0}}_validateAndSet(e,t){void 0!==e&&e>0&&void 0!==t&&t>0&&(this._result.width=e,this._result.height=t)}}class l extends c{constructor(e,t,i){super(),this._document=e,this._parentElement=t,this._optionsService=i,this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){return this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`,this._validateAndSet(Number(this._measureElement.offsetWidth)/32,Number(this._measureElement.offsetHeight)),this._result}}class d extends c{constructor(e){super(),this._optionsService=e,this._canvas=new OffscreenCanvas(100,100),this._ctx=this._canvas.getContext("2d");const t=this._ctx.measureText("W");if(!("width"in t&&"fontBoundingBoxAscent"in t&&"fontBoundingBoxDescent"in t))throw new Error("Required font metrics not supported")}measure(){this._ctx.font=`${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;const e=this._ctx.measureText("W");return this._validateAndSet(e.width,e.fontBoundingBoxAscent+e.fontBoundingBoxDescent),this._result}}},4269:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharacterJoinerService=t.JoinedCellData=void 0;const n=i(3734),o=i(643),a=i(511),h=i(2585);class c extends n.AttributeData{constructor(e,t,i){super(),this.content=0,this.combinedData="",this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=i}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.JoinedCellData=c;let l=t.CharacterJoinerService=class e{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new a.CellData}register(e){const t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t1){const e=this._getJoinedRanges(s,a,n,t,r);for(let t=0;t{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserService=void 0;const s=i(844),r=i(8460),n=i(3656);class o extends s.Disposable{constructor(e,t,i){super(),this._textarea=e,this._window=t,this.mainDocument=i,this._isFocused=!1,this._cachedIsFocused=void 0,this._screenDprMonitor=new a(this._window),this._onDprChange=this.register(new r.EventEmitter),this.onDprChange=this._onDprChange.event,this._onWindowChange=this.register(new r.EventEmitter),this.onWindowChange=this._onWindowChange.event,this.register(this.onWindowChange((e=>this._screenDprMonitor.setWindow(e)))),this.register((0,r.forwardEvent)(this._screenDprMonitor.onDprChange,this._onDprChange)),this._textarea.addEventListener("focus",(()=>this._isFocused=!0)),this._textarea.addEventListener("blur",(()=>this._isFocused=!1))}get window(){return this._window}set window(e){this._window!==e&&(this._window=e,this._onWindowChange.fire(this._window))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return void 0===this._cachedIsFocused&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask((()=>this._cachedIsFocused=void 0))),this._cachedIsFocused}}t.CoreBrowserService=o;class a extends s.Disposable{constructor(e){super(),this._parentWindow=e,this._windowResizeListener=this.register(new s.MutableDisposable),this._onDprChange=this.register(new r.EventEmitter),this.onDprChange=this._onDprChange.event,this._outerListener=()=>this._setDprAndFireIfDiffers(),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._updateDpr(),this._setWindowResizeListener(),this.register((0,s.toDisposable)((()=>this.clearListener())))}setWindow(e){this._parentWindow=e,this._setWindowResizeListener(),this._setDprAndFireIfDiffers()}_setWindowResizeListener(){this._windowResizeListener.value=(0,n.addDisposableDomListener)(this._parentWindow,"resize",(()=>this._setDprAndFireIfDiffers()))}_setDprAndFireIfDiffers(){this._parentWindow.devicePixelRatio!==this._currentDevicePixelRatio&&this._onDprChange.fire(this._parentWindow.devicePixelRatio),this._updateDpr()}_updateDpr(){this._outerListener&&(this._resolutionMediaMatchList?.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._outerListener=void 0)}}},779:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkProviderService=void 0;const s=i(844);class r extends s.Disposable{constructor(){super(),this.linkProviders=[],this.register((0,s.toDisposable)((()=>this.linkProviders.length=0)))}registerLinkProvider(e){return this.linkProviders.push(e),{dispose:()=>{const t=this.linkProviders.indexOf(e);-1!==t&&this.linkProviders.splice(t,1)}}}}t.LinkProviderService=r},8934:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.MouseService=void 0;const n=i(4725),o=i(9806);let a=t.MouseService=class{constructor(e,t){this._renderService=e,this._charSizeService=t}getCoords(e,t,i,s,r){return(0,o.getCoords)(window,e,t,i,s,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,r)}getMouseReportCoords(e,t){const i=(0,o.getCoordsRelativeToElement)(window,e,t);if(this._charSizeService.hasValidSize)return i[0]=Math.min(Math.max(i[0],0),this._renderService.dimensions.css.canvas.width-1),i[1]=Math.min(Math.max(i[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(i[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(i[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(i[0]),y:Math.floor(i[1])}}};t.MouseService=a=s([r(0,n.IRenderService),r(1,n.ICharSizeService)],a)},3230:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.RenderService=void 0;const n=i(6193),o=i(4725),a=i(8460),h=i(844),c=i(7226),l=i(2585);let d=t.RenderService=class extends h.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,i,s,r,o,l,d){super(),this._rowCount=e,this._charSizeService=s,this._renderer=this.register(new h.MutableDisposable),this._pausedResizeTask=new c.DebouncedIdleTask,this._observerDisposable=this.register(new h.MutableDisposable),this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this.register(new a.EventEmitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this.register(new a.EventEmitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this.register(new a.EventEmitter),this.onRender=this._onRender.event,this._onRefreshRequest=this.register(new a.EventEmitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new n.RenderDebouncer(((e,t)=>this._renderRows(e,t)),l),this.register(this._renderDebouncer),this.register(l.onDprChange((()=>this.handleDevicePixelRatioChange()))),this.register(o.onResize((()=>this._fullRefresh()))),this.register(o.buffers.onBufferActivate((()=>this._renderer.value?.clear()))),this.register(i.onOptionChange((()=>this._handleOptionsChanged()))),this.register(this._charSizeService.onCharSizeChange((()=>this.handleCharSizeChanged()))),this.register(r.onDecorationRegistered((()=>this._fullRefresh()))),this.register(r.onDecorationRemoved((()=>this._fullRefresh()))),this.register(i.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio","rescaleOverlappingGlyphs"],(()=>{this.clear(),this.handleResize(o.cols,o.rows),this._fullRefresh()}))),this.register(i.onMultipleOptionChange(["cursorBlink","cursorStyle"],(()=>this.refreshRows(o.buffer.y,o.buffer.y,!0)))),this.register(d.onChangeColors((()=>this._fullRefresh()))),this._registerIntersectionObserver(l.window,t),this.register(l.onWindowChange((e=>this._registerIntersectionObserver(e,t))))}_registerIntersectionObserver(e,t){if("IntersectionObserver"in e){const i=new e.IntersectionObserver((e=>this._handleIntersectionChange(e[e.length-1])),{threshold:0});i.observe(t),this._observerDisposable.value=(0,h.toDisposable)((()=>i.disconnect()))}}_handleIntersectionChange(e){this._isPaused=void 0===e.isIntersecting?0===e.intersectionRatio:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,i=!1){this._isPaused?this._needsFullRefresh=!0:(i||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount))}_renderRows(e,t){this._renderer.value&&(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0)}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value&&(this._renderer.value.onRequestRedraw((e=>this.refreshRows(e.start,e.end,!0))),this._needsSelectionRefresh=!0,this._fullRefresh())}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){this._renderer.value&&(this._renderer.value.clearTextureAtlas?.(),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set((()=>this._renderer.value?.handleResize(e,t))):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){this._renderer.value?.handleCharSizeChanged()}handleBlur(){this._renderer.value?.handleBlur()}handleFocus(){this._renderer.value?.handleFocus()}handleSelectionChanged(e,t,i){this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=i,this._renderer.value?.handleSelectionChanged(e,t,i)}handleCursorMove(){this._renderer.value?.handleCursorMove()}clear(){this._renderer.value?.clear()}};t.RenderService=d=s([r(2,l.IOptionsService),r(3,o.ICharSizeService),r(4,l.IDecorationService),r(5,l.IBufferService),r(6,o.ICoreBrowserService),r(7,o.IThemeService)],d)},9312:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionService=void 0;const n=i(9806),o=i(9504),a=i(456),h=i(4725),c=i(8460),l=i(844),d=i(6114),_=i(4841),u=i(511),f=i(2585),v=String.fromCharCode(160),p=new RegExp(v,"g");let g=t.SelectionService=class extends l.Disposable{constructor(e,t,i,s,r,n,o,h,d){super(),this._element=e,this._screenElement=t,this._linkifier=i,this._bufferService=s,this._coreService=r,this._mouseService=n,this._optionsService=o,this._renderService=h,this._coreBrowserService=d,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new u.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this.register(new c.EventEmitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this.register(new c.EventEmitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this.register(new c.EventEmitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this.register(new c.EventEmitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=e=>this._handleMouseMove(e),this._mouseUpListener=e=>this._handleMouseUp(e),this._coreService.onUserInput((()=>{this.hasSelection&&this.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((e=>this._handleTrim(e))),this.register(this._bufferService.buffers.onBufferActivate((e=>this._handleBufferActivate(e)))),this.enable(),this._model=new a.SelectionModel(this._bufferService),this._activeSelectionMode=0,this.register((0,l.toDisposable)((()=>{this._removeMouseDownListeners()})))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t||e[0]===t[0]&&e[1]===t[1])}get selectionText(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return"";const i=this._bufferService.buffer,s=[];if(3===this._activeSelectionMode){if(e[0]===t[0])return"";const r=e[0]e.replace(p," "))).join(d.isWindows?"\r\n":"\n")}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(e){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._refresh()))),d.isLinux&&e&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:3===this._activeSelectionMode})}_isClickInSelection(e){const t=this._getMouseBufferCoords(e),i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!!(i&&s&&t)&&this._areCoordsInSelection(t,i,s)}isCellInSelection(e,t){const i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!(!i||!s)&&this._areCoordsInSelection([e,t],i,s)}_areCoordsInSelection(e,t,i){return e[1]>t[1]&&e[1]=t[0]&&e[0]=t[0]}_selectWordAtCursor(e,t){const i=this._linkifier.currentLink?.link?.range;if(i)return this._model.selectionStart=[i.start.x-1,i.start.y-1],this._model.selectionStartLength=(0,_.getRangeLength)(i,this._bufferService.cols),this._model.selectionEnd=void 0,!0;const s=this._getMouseBufferCoords(e);return!!s&&(this._selectWordAt(s,t),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()}_handleTrim(e){this._model.handleTrim(e)&&this.refresh()}_getMouseBufferCoords(e){const t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t}_getMouseEventScrollAmount(e){let t=(0,n.getCoordsRelativeToElement)(this._coreBrowserService.window,e,this._screenElement)[1];const i=this._renderService.dimensions.css.canvas.height;return t>=0&&t<=i?0:(t>i&&(t-=i),t=Math.min(Math.max(t,-50),50),t/=50,t/Math.abs(t)+Math.round(14*t))}shouldForceSelection(e){return d.isMac?e.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:e.shiftKey}handleMouseDown(e){if(this._mouseDownTimeStamp=e.timeStamp,(2!==e.button||!this.hasSelection)&&0===e.button){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._handleIncrementalClick(e):1===e.detail?this._handleSingleClick(e):2===e.detail?this._handleDoubleClick(e):3===e.detail&&this._handleTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval((()=>this._dragScroll()),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))}_handleSingleClick(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),!this._model.selectionStart)return;this._model.selectionEnd=void 0;const t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&0===t.hasWidth(this._model.selectionStart[0])&&this._model.selectionStart[0]++}_handleDoubleClick(e){this._selectWordAtCursor(e,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(e){const t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))}shouldColumnSelect(e){return e.altKey&&!(d.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(e){if(e.stopImmediatePropagation(),!this._model.selectionStart)return;const t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),!this._model.selectionEnd)return void this.refresh(!0);2===this._activeSelectionMode?this._model.selectionEnd[1]0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));const i=this._bufferService.buffer;if(this._model.selectionEnd[1]0?(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}}_handleMouseUp(e){const t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500&&e.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){const t=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(t&&void 0!==t[0]&&void 0!==t[1]){const e=(0,o.moveToCellSequence)(t[0]-1,t[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(e,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd,i=!(!e||!t||e[0]===t[0]&&e[1]===t[1]);i?e&&t&&(this._oldSelectionStart&&this._oldSelectionEnd&&e[0]===this._oldSelectionStart[0]&&e[1]===this._oldSelectionStart[1]&&t[0]===this._oldSelectionEnd[0]&&t[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(e,t,i)):this._oldHasSelection&&this._fireOnSelectionChange(e,t,i)}_fireOnSelectionChange(e,t,i){this._oldSelectionStart=e,this._oldSelectionEnd=t,this._oldHasSelection=i,this._onSelectionChange.fire()}_handleBufferActivate(e){this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((e=>this._handleTrim(e)))}_convertViewportColToCharacterIndex(e,t){let i=t;for(let s=0;t>=s;s++){const r=e.loadCell(s,this._workCell).getChars().length;0===this._workCell.getWidth()?i--:r>1&&t!==s&&(i+=r-1)}return i}setSelection(e,t,i){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=i,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(e){this._isClickInSelection(e)||(this._selectWordAtCursor(e,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(e,t,i=!0,s=!0){if(e[0]>=this._bufferService.cols)return;const r=this._bufferService.buffer,n=r.lines.get(e[1]);if(!n)return;const o=r.translateBufferLineToString(e[1],!1);let a=this._convertViewportColToCharacterIndex(n,e[0]),h=a;const c=e[0]-a;let l=0,d=0,_=0,u=0;if(" "===o.charAt(a)){for(;a>0&&" "===o.charAt(a-1);)a--;for(;h1&&(u+=s-1,h+=s-1);t>0&&a>0&&!this._isCharWordSeparator(n.loadCell(t-1,this._workCell));){n.loadCell(t-1,this._workCell);const e=this._workCell.getChars().length;0===this._workCell.getWidth()?(l++,t--):e>1&&(_+=e-1,a-=e-1),a--,t--}for(;i1&&(u+=e-1,h+=e-1),h++,i++}}h++;let f=a+c-l+_,v=Math.min(this._bufferService.cols,h-a+l+d-_-u);if(t||""!==o.slice(a,h).trim()){if(i&&0===f&&32!==n.getCodePoint(0)){const t=r.lines.get(e[1]-1);if(t&&n.isWrapped&&32!==t.getCodePoint(this._bufferService.cols-1)){const t=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(t){const e=this._bufferService.cols-t.start;f-=e,v+=e}}}if(s&&f+v===this._bufferService.cols&&32!==n.getCodePoint(this._bufferService.cols-1)){const t=r.lines.get(e[1]+1);if(t?.isWrapped&&32!==t.getCodePoint(0)){const t=this._getWordAt([0,e[1]+1],!1,!1,!0);t&&(v+=t.length)}}return{start:f,length:v}}}_selectWordAt(e,t){const i=this._getWordAt(e,t);if(i){for(;i.start<0;)i.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[i.start,e[1]],this._model.selectionStartLength=i.length}}_selectToWordAt(e){const t=this._getWordAt(e,!0);if(t){let i=e[1];for(;t.start<0;)t.start+=this._bufferService.cols,i--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,i++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,i]}}_isCharWordSeparator(e){return 0!==e.getWidth()&&this._optionsService.rawOptions.wordSeparator.indexOf(e.getChars())>=0}_selectLineAt(e){const t=this._bufferService.buffer.getWrappedRangeForLine(e),i={start:{x:0,y:t.first},end:{x:this._bufferService.cols-1,y:t.last}};this._model.selectionStart=[0,t.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,_.getRangeLength)(i,this._bufferService.cols)}};t.SelectionService=g=s([r(3,f.IBufferService),r(4,f.ICoreService),r(5,h.IMouseService),r(6,f.IOptionsService),r(7,h.IRenderService),r(8,h.ICoreBrowserService)],g)},4725:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ILinkProviderService=t.IThemeService=t.ICharacterJoinerService=t.ISelectionService=t.IRenderService=t.IMouseService=t.ICoreBrowserService=t.ICharSizeService=void 0;const s=i(8343);t.ICharSizeService=(0,s.createDecorator)("CharSizeService"),t.ICoreBrowserService=(0,s.createDecorator)("CoreBrowserService"),t.IMouseService=(0,s.createDecorator)("MouseService"),t.IRenderService=(0,s.createDecorator)("RenderService"),t.ISelectionService=(0,s.createDecorator)("SelectionService"),t.ICharacterJoinerService=(0,s.createDecorator)("CharacterJoinerService"),t.IThemeService=(0,s.createDecorator)("ThemeService"),t.ILinkProviderService=(0,s.createDecorator)("LinkProviderService")},6731:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.ThemeService=t.DEFAULT_ANSI_COLORS=void 0;const n=i(7239),o=i(8055),a=i(8460),h=i(844),c=i(2585),l=o.css.toColor("#ffffff"),d=o.css.toColor("#000000"),_=o.css.toColor("#ffffff"),u=o.css.toColor("#000000"),f={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117};t.DEFAULT_ANSI_COLORS=Object.freeze((()=>{const e=[o.css.toColor("#2e3436"),o.css.toColor("#cc0000"),o.css.toColor("#4e9a06"),o.css.toColor("#c4a000"),o.css.toColor("#3465a4"),o.css.toColor("#75507b"),o.css.toColor("#06989a"),o.css.toColor("#d3d7cf"),o.css.toColor("#555753"),o.css.toColor("#ef2929"),o.css.toColor("#8ae234"),o.css.toColor("#fce94f"),o.css.toColor("#729fcf"),o.css.toColor("#ad7fa8"),o.css.toColor("#34e2e2"),o.css.toColor("#eeeeec")],t=[0,95,135,175,215,255];for(let i=0;i<216;i++){const s=t[i/36%6|0],r=t[i/6%6|0],n=t[i%6];e.push({css:o.channels.toCss(s,r,n),rgba:o.channels.toRgba(s,r,n)})}for(let t=0;t<24;t++){const i=8+10*t;e.push({css:o.channels.toCss(i,i,i),rgba:o.channels.toRgba(i,i,i)})}return e})());let v=t.ThemeService=class extends h.Disposable{get colors(){return this._colors}constructor(e){super(),this._optionsService=e,this._contrastCache=new n.ColorContrastCache,this._halfContrastCache=new n.ColorContrastCache,this._onChangeColors=this.register(new a.EventEmitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:l,background:d,cursor:_,cursorAccent:u,selectionForeground:void 0,selectionBackgroundTransparent:f,selectionBackgroundOpaque:o.color.blend(d,f),selectionInactiveBackgroundTransparent:f,selectionInactiveBackgroundOpaque:o.color.blend(d,f),ansi:t.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this.register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",(()=>this._contrastCache.clear()))),this.register(this._optionsService.onSpecificOptionChange("theme",(()=>this._setTheme(this._optionsService.rawOptions.theme))))}_setTheme(e={}){const i=this._colors;if(i.foreground=p(e.foreground,l),i.background=p(e.background,d),i.cursor=p(e.cursor,_),i.cursorAccent=p(e.cursorAccent,u),i.selectionBackgroundTransparent=p(e.selectionBackground,f),i.selectionBackgroundOpaque=o.color.blend(i.background,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundTransparent=p(e.selectionInactiveBackground,i.selectionBackgroundTransparent),i.selectionInactiveBackgroundOpaque=o.color.blend(i.background,i.selectionInactiveBackgroundTransparent),i.selectionForeground=e.selectionForeground?p(e.selectionForeground,o.NULL_COLOR):void 0,i.selectionForeground===o.NULL_COLOR&&(i.selectionForeground=void 0),o.color.isOpaque(i.selectionBackgroundTransparent)){const e=.3;i.selectionBackgroundTransparent=o.color.opacity(i.selectionBackgroundTransparent,e)}if(o.color.isOpaque(i.selectionInactiveBackgroundTransparent)){const e=.3;i.selectionInactiveBackgroundTransparent=o.color.opacity(i.selectionInactiveBackgroundTransparent,e)}if(i.ansi=t.DEFAULT_ANSI_COLORS.slice(),i.ansi[0]=p(e.black,t.DEFAULT_ANSI_COLORS[0]),i.ansi[1]=p(e.red,t.DEFAULT_ANSI_COLORS[1]),i.ansi[2]=p(e.green,t.DEFAULT_ANSI_COLORS[2]),i.ansi[3]=p(e.yellow,t.DEFAULT_ANSI_COLORS[3]),i.ansi[4]=p(e.blue,t.DEFAULT_ANSI_COLORS[4]),i.ansi[5]=p(e.magenta,t.DEFAULT_ANSI_COLORS[5]),i.ansi[6]=p(e.cyan,t.DEFAULT_ANSI_COLORS[6]),i.ansi[7]=p(e.white,t.DEFAULT_ANSI_COLORS[7]),i.ansi[8]=p(e.brightBlack,t.DEFAULT_ANSI_COLORS[8]),i.ansi[9]=p(e.brightRed,t.DEFAULT_ANSI_COLORS[9]),i.ansi[10]=p(e.brightGreen,t.DEFAULT_ANSI_COLORS[10]),i.ansi[11]=p(e.brightYellow,t.DEFAULT_ANSI_COLORS[11]),i.ansi[12]=p(e.brightBlue,t.DEFAULT_ANSI_COLORS[12]),i.ansi[13]=p(e.brightMagenta,t.DEFAULT_ANSI_COLORS[13]),i.ansi[14]=p(e.brightCyan,t.DEFAULT_ANSI_COLORS[14]),i.ansi[15]=p(e.brightWhite,t.DEFAULT_ANSI_COLORS[15]),e.extendedAnsi){const s=Math.min(i.ansi.length-16,e.extendedAnsi.length);for(let r=0;r{Object.defineProperty(t,"__esModule",{value:!0}),t.CircularList=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._maxLength=e,this.onDeleteEmitter=this.register(new s.EventEmitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this.register(new s.EventEmitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this.register(new s.EventEmitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(e){if(this._maxLength===e)return;const t=new Array(e);for(let i=0;ithis._length)for(let t=this._length;t=e;t--)this._array[this._getCyclicIndex(t+i.length)]=this._array[this._getCyclicIndex(t)];for(let t=0;tthis._maxLength){const e=this._length+i.length-this._maxLength;this._startIndex+=e,this._length=this._maxLength,this.onTrimEmitter.fire(e)}else this._length+=i.length}trimStart(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)}shiftElements(e,t,i){if(!(t<=0)){if(e<0||e>=this._length)throw new Error("start argument out of range");if(e+i<0)throw new Error("Cannot shift elements in list beyond index 0");if(i>0){for(let s=t-1;s>=0;s--)this.set(e+s+i,this.get(e+s));const s=e+t+i-this._length;if(s>0)for(this._length+=s;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let s=0;s{Object.defineProperty(t,"__esModule",{value:!0}),t.clone=void 0,t.clone=function e(t,i=5){if("object"!=typeof t)return t;const s=Array.isArray(t)?[]:{};for(const r in t)s[r]=i<=1?t[r]:t[r]&&e(t[r],i-1);return s}},8055:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.contrastRatio=t.toPaddedHex=t.rgba=t.rgb=t.css=t.color=t.channels=t.NULL_COLOR=void 0;let i=0,s=0,r=0,n=0;var o,a,h,c,l;function d(e){const t=e.toString(16);return t.length<2?"0"+t:t}function _(e,t){return e>>0},e.toColor=function(t,i,s,r){return{css:e.toCss(t,i,s,r),rgba:e.toRgba(t,i,s,r)}}}(o||(t.channels=o={})),function(e){function t(e,t){return n=Math.round(255*t),[i,s,r]=l.toChannels(e.rgba),{css:o.toCss(i,s,r,n),rgba:o.toRgba(i,s,r,n)}}e.blend=function(e,t){if(n=(255&t.rgba)/255,1===n)return{css:t.css,rgba:t.rgba};const a=t.rgba>>24&255,h=t.rgba>>16&255,c=t.rgba>>8&255,l=e.rgba>>24&255,d=e.rgba>>16&255,_=e.rgba>>8&255;return i=l+Math.round((a-l)*n),s=d+Math.round((h-d)*n),r=_+Math.round((c-_)*n),{css:o.toCss(i,s,r),rgba:o.toRgba(i,s,r)}},e.isOpaque=function(e){return 255==(255&e.rgba)},e.ensureContrastRatio=function(e,t,i){const s=l.ensureContrastRatio(e.rgba,t.rgba,i);if(s)return o.toColor(s>>24&255,s>>16&255,s>>8&255)},e.opaque=function(e){const t=(255|e.rgba)>>>0;return[i,s,r]=l.toChannels(t),{css:o.toCss(i,s,r),rgba:t}},e.opacity=t,e.multiplyOpacity=function(e,i){return n=255&e.rgba,t(e,n*i/255)},e.toColorRGB=function(e){return[e.rgba>>24&255,e.rgba>>16&255,e.rgba>>8&255]}}(a||(t.color=a={})),function(e){let t,a;try{const e=document.createElement("canvas");e.width=1,e.height=1;const i=e.getContext("2d",{willReadFrequently:!0});i&&(t=i,t.globalCompositeOperation="copy",a=t.createLinearGradient(0,0,1,1))}catch{}e.toColor=function(e){if(e.match(/#[\da-f]{3,8}/i))switch(e.length){case 4:return i=parseInt(e.slice(1,2).repeat(2),16),s=parseInt(e.slice(2,3).repeat(2),16),r=parseInt(e.slice(3,4).repeat(2),16),o.toColor(i,s,r);case 5:return i=parseInt(e.slice(1,2).repeat(2),16),s=parseInt(e.slice(2,3).repeat(2),16),r=parseInt(e.slice(3,4).repeat(2),16),n=parseInt(e.slice(4,5).repeat(2),16),o.toColor(i,s,r,n);case 7:return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0};case 9:return{css:e,rgba:parseInt(e.slice(1),16)>>>0}}const h=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(h)return i=parseInt(h[1]),s=parseInt(h[2]),r=parseInt(h[3]),n=Math.round(255*(void 0===h[5]?1:parseFloat(h[5]))),o.toColor(i,s,r,n);if(!t||!a)throw new Error("css.toColor: Unsupported css format");if(t.fillStyle=a,t.fillStyle=e,"string"!=typeof t.fillStyle)throw new Error("css.toColor: Unsupported css format");if(t.fillRect(0,0,1,1),[i,s,r,n]=t.getImageData(0,0,1,1).data,255!==n)throw new Error("css.toColor: Unsupported css format");return{rgba:o.toRgba(i,s,r,n),css:e}}}(h||(t.css=h={})),function(e){function t(e,t,i){const s=e/255,r=t/255,n=i/255;return.2126*(s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(c||(t.rgb=c={})),function(e){function t(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,l=_(c.relativeLuminance2(o,a,h),c.relativeLuminance2(s,r,n));for(;l0||a>0||h>0);)o-=Math.max(0,Math.ceil(.1*o)),a-=Math.max(0,Math.ceil(.1*a)),h-=Math.max(0,Math.ceil(.1*h)),l=_(c.relativeLuminance2(o,a,h),c.relativeLuminance2(s,r,n));return(o<<24|a<<16|h<<8|255)>>>0}function a(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,h=t>>8&255,l=_(c.relativeLuminance2(o,a,h),c.relativeLuminance2(s,r,n));for(;l>>0}e.blend=function(e,t){if(n=(255&t)/255,1===n)return t;const a=t>>24&255,h=t>>16&255,c=t>>8&255,l=e>>24&255,d=e>>16&255,_=e>>8&255;return i=l+Math.round((a-l)*n),s=d+Math.round((h-d)*n),r=_+Math.round((c-_)*n),o.toRgba(i,s,r)},e.ensureContrastRatio=function(e,i,s){const r=c.relativeLuminance(e>>8),n=c.relativeLuminance(i>>8);if(_(r,n)>8));if(o_(r,c.relativeLuminance(t>>8))?n:t}return n}const o=a(e,i,s),h=_(r,c.relativeLuminance(o>>8));if(h_(r,c.relativeLuminance(n>>8))?o:n}return o}},e.reduceLuminance=t,e.increaseLuminance=a,e.toChannels=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]}}(l||(t.rgba=l={})),t.toPaddedHex=d,t.contrastRatio=_},8969:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreTerminal=void 0;const s=i(844),r=i(2585),n=i(4348),o=i(7866),a=i(744),h=i(7302),c=i(6975),l=i(8460),d=i(1753),_=i(1480),u=i(7994),f=i(9282),v=i(5435),p=i(5981),g=i(2660);let m=!1;class S extends s.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this.register(new l.EventEmitter),this._onScroll.event((e=>{this._onScrollApi?.fire(e.position)}))),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(e){for(const t in e)this.optionsService.options[t]=e[t]}constructor(e){super(),this._windowsWrappingHeuristics=this.register(new s.MutableDisposable),this._onBinary=this.register(new l.EventEmitter),this.onBinary=this._onBinary.event,this._onData=this.register(new l.EventEmitter),this.onData=this._onData.event,this._onLineFeed=this.register(new l.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this.register(new l.EventEmitter),this.onResize=this._onResize.event,this._onWriteParsed=this.register(new l.EventEmitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this.register(new l.EventEmitter),this._instantiationService=new n.InstantiationService,this.optionsService=this.register(new h.OptionsService(e)),this._instantiationService.setService(r.IOptionsService,this.optionsService),this._bufferService=this.register(this._instantiationService.createInstance(a.BufferService)),this._instantiationService.setService(r.IBufferService,this._bufferService),this._logService=this.register(this._instantiationService.createInstance(o.LogService)),this._instantiationService.setService(r.ILogService,this._logService),this.coreService=this.register(this._instantiationService.createInstance(c.CoreService)),this._instantiationService.setService(r.ICoreService,this.coreService),this.coreMouseService=this.register(this._instantiationService.createInstance(d.CoreMouseService)),this._instantiationService.setService(r.ICoreMouseService,this.coreMouseService),this.unicodeService=this.register(this._instantiationService.createInstance(_.UnicodeService)),this._instantiationService.setService(r.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(u.CharsetService),this._instantiationService.setService(r.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(g.OscLinkService),this._instantiationService.setService(r.IOscLinkService,this._oscLinkService),this._inputHandler=this.register(new v.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this.register((0,l.forwardEvent)(this._inputHandler.onLineFeed,this._onLineFeed)),this.register(this._inputHandler),this.register((0,l.forwardEvent)(this._bufferService.onResize,this._onResize)),this.register((0,l.forwardEvent)(this.coreService.onData,this._onData)),this.register((0,l.forwardEvent)(this.coreService.onBinary,this._onBinary)),this.register(this.coreService.onRequestScrollToBottom((()=>this.scrollToBottom()))),this.register(this.coreService.onUserInput((()=>this._writeBuffer.handleUserInput()))),this.register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],(()=>this._handleWindowsPtyOptionChange()))),this.register(this._bufferService.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this.register(this._inputHandler.onScroll((e=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp,source:0}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this._writeBuffer=this.register(new p.WriteBuffer(((e,t)=>this._inputHandler.parse(e,t)))),this.register((0,l.forwardEvent)(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(e,t){this._writeBuffer.write(e,t)}writeSync(e,t){this._logService.logLevel<=r.LogLevelEnum.WARN&&!m&&(this._logService.warn("writeSync is unreliable and will be removed soon."),m=!0),this._writeBuffer.writeSync(e,t)}input(e,t=!0){this.coreService.triggerDataEvent(e,t)}resize(e,t){isNaN(e)||isNaN(t)||(e=Math.max(e,a.MINIMUM_COLS),t=Math.max(t,a.MINIMUM_ROWS),this._bufferService.resize(e,t))}scroll(e,t=!1){this._bufferService.scroll(e,t)}scrollLines(e,t,i){this._bufferService.scrollLines(e,t,i)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){const t=e-this._bufferService.buffer.ydisp;0!==t&&this.scrollLines(t)}registerEscHandler(e,t){return this._inputHandler.registerEscHandler(e,t)}registerDcsHandler(e,t){return this._inputHandler.registerDcsHandler(e,t)}registerCsiHandler(e,t){return this._inputHandler.registerCsiHandler(e,t)}registerOscHandler(e,t){return this._inputHandler.registerOscHandler(e,t)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let e=!1;const t=this.optionsService.rawOptions.windowsPty;t&&void 0!==t.buildNumber&&void 0!==t.buildNumber?e=!!("conpty"===t.backend&&t.buildNumber<21376):this.optionsService.rawOptions.windowsMode&&(e=!0),e?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){const e=[];e.push(this.onLineFeed(f.updateWindowsModeWrappedState.bind(null,this._bufferService))),e.push(this.registerCsiHandler({final:"H"},(()=>((0,f.updateWindowsModeWrappedState)(this._bufferService),!1)))),this._windowsWrappingHeuristics.value=(0,s.toDisposable)((()=>{for(const t of e)t.dispose()}))}}}t.CoreTerminal=S},8460:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.runAndSubscribe=t.forwardEvent=t.EventEmitter=void 0,t.EventEmitter=class{constructor(){this._listeners=[],this._disposed=!1}get event(){return this._event||(this._event=e=>(this._listeners.push(e),{dispose:()=>{if(!this._disposed)for(let t=0;tt.fire(e)))},t.runAndSubscribe=function(e,t){return t(void 0),e((e=>t(e)))}},5435:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.InputHandler=t.WindowsOptionsReportType=void 0;const n=i(2584),o=i(7116),a=i(2015),h=i(844),c=i(482),l=i(8437),d=i(8460),_=i(643),u=i(511),f=i(3734),v=i(2585),p=i(1480),g=i(6242),m=i(6351),S=i(5941),C={"(":0,")":1,"*":2,"+":3,"-":1,".":2},b=131072;function w(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var y;!function(e){e[e.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",e[e.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"}(y||(t.WindowsOptionsReportType=y={}));let E=0;class k extends h.Disposable{getAttrData(){return this._curAttrData}constructor(e,t,i,s,r,h,_,f,v=new a.EscapeSequenceParser){super(),this._bufferService=e,this._charsetService=t,this._coreService=i,this._logService=s,this._optionsService=r,this._oscLinkService=h,this._coreMouseService=_,this._unicodeService=f,this._parser=v,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new c.StringToUtf32,this._utf8Decoder=new c.Utf8ToUtf32,this._workCell=new u.CellData,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this.register(new d.EventEmitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this.register(new d.EventEmitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this.register(new d.EventEmitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this.register(new d.EventEmitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this.register(new d.EventEmitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this.register(new d.EventEmitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this.register(new d.EventEmitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this.register(new d.EventEmitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this.register(new d.EventEmitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this.register(new d.EventEmitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this.register(new d.EventEmitter),this.onScroll=this._onScroll.event,this._onTitleChange=this.register(new d.EventEmitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this.register(new d.EventEmitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this.register(this._parser),this._dirtyRowTracker=new L(this._bufferService),this._activeBuffer=this._bufferService.buffer,this.register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._parser.setCsiHandlerFallback(((e,t)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(e),params:t.toArray()})})),this._parser.setEscHandlerFallback((e=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(e)})})),this._parser.setExecuteHandlerFallback((e=>{this._logService.debug("Unknown EXECUTE code: ",{code:e})})),this._parser.setOscHandlerFallback(((e,t,i)=>{this._logService.debug("Unknown OSC code: ",{identifier:e,action:t,data:i})})),this._parser.setDcsHandlerFallback(((e,t,i)=>{"HOOK"===t&&(i=i.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(e),action:t,payload:i})})),this._parser.setPrintHandler(((e,t,i)=>this.print(e,t,i))),this._parser.registerCsiHandler({final:"@"},(e=>this.insertChars(e))),this._parser.registerCsiHandler({intermediates:" ",final:"@"},(e=>this.scrollLeft(e))),this._parser.registerCsiHandler({final:"A"},(e=>this.cursorUp(e))),this._parser.registerCsiHandler({intermediates:" ",final:"A"},(e=>this.scrollRight(e))),this._parser.registerCsiHandler({final:"B"},(e=>this.cursorDown(e))),this._parser.registerCsiHandler({final:"C"},(e=>this.cursorForward(e))),this._parser.registerCsiHandler({final:"D"},(e=>this.cursorBackward(e))),this._parser.registerCsiHandler({final:"E"},(e=>this.cursorNextLine(e))),this._parser.registerCsiHandler({final:"F"},(e=>this.cursorPrecedingLine(e))),this._parser.registerCsiHandler({final:"G"},(e=>this.cursorCharAbsolute(e))),this._parser.registerCsiHandler({final:"H"},(e=>this.cursorPosition(e))),this._parser.registerCsiHandler({final:"I"},(e=>this.cursorForwardTab(e))),this._parser.registerCsiHandler({final:"J"},(e=>this.eraseInDisplay(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"J"},(e=>this.eraseInDisplay(e,!0))),this._parser.registerCsiHandler({final:"K"},(e=>this.eraseInLine(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"K"},(e=>this.eraseInLine(e,!0))),this._parser.registerCsiHandler({final:"L"},(e=>this.insertLines(e))),this._parser.registerCsiHandler({final:"M"},(e=>this.deleteLines(e))),this._parser.registerCsiHandler({final:"P"},(e=>this.deleteChars(e))),this._parser.registerCsiHandler({final:"S"},(e=>this.scrollUp(e))),this._parser.registerCsiHandler({final:"T"},(e=>this.scrollDown(e))),this._parser.registerCsiHandler({final:"X"},(e=>this.eraseChars(e))),this._parser.registerCsiHandler({final:"Z"},(e=>this.cursorBackwardTab(e))),this._parser.registerCsiHandler({final:"`"},(e=>this.charPosAbsolute(e))),this._parser.registerCsiHandler({final:"a"},(e=>this.hPositionRelative(e))),this._parser.registerCsiHandler({final:"b"},(e=>this.repeatPrecedingCharacter(e))),this._parser.registerCsiHandler({final:"c"},(e=>this.sendDeviceAttributesPrimary(e))),this._parser.registerCsiHandler({prefix:">",final:"c"},(e=>this.sendDeviceAttributesSecondary(e))),this._parser.registerCsiHandler({final:"d"},(e=>this.linePosAbsolute(e))),this._parser.registerCsiHandler({final:"e"},(e=>this.vPositionRelative(e))),this._parser.registerCsiHandler({final:"f"},(e=>this.hVPosition(e))),this._parser.registerCsiHandler({final:"g"},(e=>this.tabClear(e))),this._parser.registerCsiHandler({final:"h"},(e=>this.setMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"h"},(e=>this.setModePrivate(e))),this._parser.registerCsiHandler({final:"l"},(e=>this.resetMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"l"},(e=>this.resetModePrivate(e))),this._parser.registerCsiHandler({final:"m"},(e=>this.charAttributes(e))),this._parser.registerCsiHandler({final:"n"},(e=>this.deviceStatus(e))),this._parser.registerCsiHandler({prefix:"?",final:"n"},(e=>this.deviceStatusPrivate(e))),this._parser.registerCsiHandler({intermediates:"!",final:"p"},(e=>this.softReset(e))),this._parser.registerCsiHandler({intermediates:" ",final:"q"},(e=>this.setCursorStyle(e))),this._parser.registerCsiHandler({final:"r"},(e=>this.setScrollRegion(e))),this._parser.registerCsiHandler({final:"s"},(e=>this.saveCursor(e))),this._parser.registerCsiHandler({final:"t"},(e=>this.windowOptions(e))),this._parser.registerCsiHandler({final:"u"},(e=>this.restoreCursor(e))),this._parser.registerCsiHandler({intermediates:"'",final:"}"},(e=>this.insertColumns(e))),this._parser.registerCsiHandler({intermediates:"'",final:"~"},(e=>this.deleteColumns(e))),this._parser.registerCsiHandler({intermediates:'"',final:"q"},(e=>this.selectProtected(e))),this._parser.registerCsiHandler({intermediates:"$",final:"p"},(e=>this.requestMode(e,!0))),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},(e=>this.requestMode(e,!1))),this._parser.setExecuteHandler(n.C0.BEL,(()=>this.bell())),this._parser.setExecuteHandler(n.C0.LF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.VT,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.FF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.CR,(()=>this.carriageReturn())),this._parser.setExecuteHandler(n.C0.BS,(()=>this.backspace())),this._parser.setExecuteHandler(n.C0.HT,(()=>this.tab())),this._parser.setExecuteHandler(n.C0.SO,(()=>this.shiftOut())),this._parser.setExecuteHandler(n.C0.SI,(()=>this.shiftIn())),this._parser.setExecuteHandler(n.C1.IND,(()=>this.index())),this._parser.setExecuteHandler(n.C1.NEL,(()=>this.nextLine())),this._parser.setExecuteHandler(n.C1.HTS,(()=>this.tabSet())),this._parser.registerOscHandler(0,new g.OscHandler((e=>(this.setTitle(e),this.setIconName(e),!0)))),this._parser.registerOscHandler(1,new g.OscHandler((e=>this.setIconName(e)))),this._parser.registerOscHandler(2,new g.OscHandler((e=>this.setTitle(e)))),this._parser.registerOscHandler(4,new g.OscHandler((e=>this.setOrReportIndexedColor(e)))),this._parser.registerOscHandler(8,new g.OscHandler((e=>this.setHyperlink(e)))),this._parser.registerOscHandler(10,new g.OscHandler((e=>this.setOrReportFgColor(e)))),this._parser.registerOscHandler(11,new g.OscHandler((e=>this.setOrReportBgColor(e)))),this._parser.registerOscHandler(12,new g.OscHandler((e=>this.setOrReportCursorColor(e)))),this._parser.registerOscHandler(104,new g.OscHandler((e=>this.restoreIndexedColor(e)))),this._parser.registerOscHandler(110,new g.OscHandler((e=>this.restoreFgColor(e)))),this._parser.registerOscHandler(111,new g.OscHandler((e=>this.restoreBgColor(e)))),this._parser.registerOscHandler(112,new g.OscHandler((e=>this.restoreCursorColor(e)))),this._parser.registerEscHandler({final:"7"},(()=>this.saveCursor())),this._parser.registerEscHandler({final:"8"},(()=>this.restoreCursor())),this._parser.registerEscHandler({final:"D"},(()=>this.index())),this._parser.registerEscHandler({final:"E"},(()=>this.nextLine())),this._parser.registerEscHandler({final:"H"},(()=>this.tabSet())),this._parser.registerEscHandler({final:"M"},(()=>this.reverseIndex())),this._parser.registerEscHandler({final:"="},(()=>this.keypadApplicationMode())),this._parser.registerEscHandler({final:">"},(()=>this.keypadNumericMode())),this._parser.registerEscHandler({final:"c"},(()=>this.fullReset())),this._parser.registerEscHandler({final:"n"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"o"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"|"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"}"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"~"},(()=>this.setgLevel(1))),this._parser.registerEscHandler({intermediates:"%",final:"@"},(()=>this.selectDefaultCharset())),this._parser.registerEscHandler({intermediates:"%",final:"G"},(()=>this.selectDefaultCharset()));for(const e in o.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:e},(()=>this.selectCharset("("+e))),this._parser.registerEscHandler({intermediates:")",final:e},(()=>this.selectCharset(")"+e))),this._parser.registerEscHandler({intermediates:"*",final:e},(()=>this.selectCharset("*"+e))),this._parser.registerEscHandler({intermediates:"+",final:e},(()=>this.selectCharset("+"+e))),this._parser.registerEscHandler({intermediates:"-",final:e},(()=>this.selectCharset("-"+e))),this._parser.registerEscHandler({intermediates:".",final:e},(()=>this.selectCharset("."+e))),this._parser.registerEscHandler({intermediates:"/",final:e},(()=>this.selectCharset("/"+e)));this._parser.registerEscHandler({intermediates:"#",final:"8"},(()=>this.screenAlignmentPattern())),this._parser.setErrorHandler((e=>(this._logService.error("Parsing error: ",e),e))),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new m.DcsHandler(((e,t)=>this.requestStatusString(e,t))))}_preserveStack(e,t,i,s){this._parseStack.paused=!0,this._parseStack.cursorStartX=e,this._parseStack.cursorStartY=t,this._parseStack.decodedLength=i,this._parseStack.position=s}_logSlowResolvingAsync(e){this._logService.logLevel<=v.LogLevelEnum.WARN&&Promise.race([e,new Promise(((e,t)=>setTimeout((()=>t("#SLOW_TIMEOUT")),5e3)))]).catch((e=>{if("#SLOW_TIMEOUT"!==e)throw e;console.warn("async parser handler taking longer than 5000 ms")}))}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(e,t){let i,s=this._activeBuffer.x,r=this._activeBuffer.y,n=0;const o=this._parseStack.paused;if(o){if(i=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,t))return this._logSlowResolvingAsync(i),i;s=this._parseStack.cursorStartX,r=this._parseStack.cursorStartY,this._parseStack.paused=!1,e.length>b&&(n=this._parseStack.position+b)}if(this._logService.logLevel<=v.LogLevelEnum.DEBUG&&this._logService.debug("parsing data"+("string"==typeof e?` "${e}"`:` "${Array.prototype.map.call(e,(e=>String.fromCharCode(e))).join("")}"`),"string"==typeof e?e.split("").map((e=>e.charCodeAt(0))):e),this._parseBuffer.lengthb)for(let t=n;t0&&2===f.getWidth(this._activeBuffer.x-1)&&f.setCellFromCodepoint(this._activeBuffer.x-1,0,1,u);let v=this._parser.precedingJoinState;for(let g=t;ga)if(h){const e=f;let t=this._activeBuffer.x-m;for(this._activeBuffer.x=m,this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData(),!0)):(this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),f=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y),m>0&&f instanceof l.BufferLine&&f.copyCellsFrom(e,t,0,m,!1);t=0;)f.setCellFromCodepoint(this._activeBuffer.x++,0,0,u)}else if(d&&(f.insertCells(this._activeBuffer.x,r-m,this._activeBuffer.getNullCell(u)),2===f.getWidth(a-1)&&f.setCellFromCodepoint(a-1,_.NULL_CELL_CODE,_.NULL_CELL_WIDTH,u)),f.setCellFromCodepoint(this._activeBuffer.x++,s,r,u),r>0)for(;--r;)f.setCellFromCodepoint(this._activeBuffer.x++,0,0,u)}this._parser.precedingJoinState=v,this._activeBuffer.x0&&0===f.getWidth(this._activeBuffer.x)&&!f.hasContent(this._activeBuffer.x)&&f.setCellFromCodepoint(this._activeBuffer.x,0,1,u),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(e,t){return"t"!==e.final||e.prefix||e.intermediates?this._parser.registerCsiHandler(e,t):this._parser.registerCsiHandler(e,(e=>!w(e.params[0],this._optionsService.rawOptions.windowOptions)||t(e)))}registerDcsHandler(e,t){return this._parser.registerDcsHandler(e,new m.DcsHandler(t))}registerEscHandler(e,t){return this._parser.registerEscHandler(e,t)}registerOscHandler(e,t){return this._parser.registerOscHandler(e,new g.OscHandler(t))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(0===this._activeBuffer.x&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)?.isWrapped){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;const e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);e.hasWidth(this._activeBuffer.x)&&!e.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;const e=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-e),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(e=this._bufferService.cols-1){this._activeBuffer.x=Math.min(e,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(e,t){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=e,this._activeBuffer.y=this._activeBuffer.scrollTop+t):(this._activeBuffer.x=e,this._activeBuffer.y=t),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(e,t){this._restrictCursor(),this._setCursor(this._activeBuffer.x+e,this._activeBuffer.y+t)}cursorUp(e){const t=this._activeBuffer.y-this._activeBuffer.scrollTop;return t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1)),!0}cursorDown(e){const t=this._activeBuffer.scrollBottom-this._activeBuffer.y;return t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1),!0}cursorForward(e){return this._moveCursor(e.params[0]||1,0),!0}cursorBackward(e){return this._moveCursor(-(e.params[0]||1),0),!0}cursorNextLine(e){return this.cursorDown(e),this._activeBuffer.x=0,!0}cursorPrecedingLine(e){return this.cursorUp(e),this._activeBuffer.x=0,!0}cursorCharAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(e){return this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1),!0}charPosAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(e){return this._moveCursor(e.params[0]||1,0),!0}linePosAbsolute(e){return this._setCursor(this._activeBuffer.x,(e.params[0]||1)-1),!0}vPositionRelative(e){return this._moveCursor(0,e.params[0]||1),!0}hVPosition(e){return this.cursorPosition(e),!0}tabClear(e){const t=e.params[0];return 0===t?delete this._activeBuffer.tabs[this._activeBuffer.x]:3===t&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(e){const t=e.params[0];return 1===t&&(this._curAttrData.bg|=536870912),2!==t&&0!==t||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(e,t,i,s=!1,r=!1){const n=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);n.replaceCells(t,i,this._activeBuffer.getNullCell(this._eraseAttrData()),r),s&&(n.isWrapped=!1)}_resetBufferLine(e,t=!1){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i&&(i.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),t),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+e),i.isWrapped=!1)}eraseInDisplay(e,t=!1){let i;switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:for(i=this._activeBuffer.y,this._dirtyRowTracker.markDirty(i),this._eraseInBufferLine(i++,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);i=this._bufferService.cols&&(this._activeBuffer.lines.get(i+1).isWrapped=!1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 2:for(i=this._bufferService.rows,this._dirtyRowTracker.markDirty(i-1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 3:const e=this._activeBuffer.lines.length-this._bufferService.rows;e>0&&(this._activeBuffer.lines.trimStart(e),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-e,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-e,0),this._onScroll.fire(0))}return!0}eraseInLine(e,t=!1){switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,t);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,t)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.ythis._activeBuffer.scrollBottom||this._activeBuffer.y65535?2:1}let h=a;for(let e=1;e0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(n.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(n.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(e){return e.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(n.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(n.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(e.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(n.C0.ESC+"[>83;40003;0c")),!0}_is(e){return 0===(this._optionsService.rawOptions.termName+"").indexOf(e)}setMode(e){for(let t=0;te?1:2,u=e.params[0];return f=u,v=t?2===u?4:4===u?_(o.modes.insertMode):12===u?3:20===u?_(d.convertEol):0:1===u?_(i.applicationCursorKeys):3===u?d.windowOptions.setWinLines?80===h?2:132===h?1:0:0:6===u?_(i.origin):7===u?_(i.wraparound):8===u?3:9===u?_("X10"===s):12===u?_(d.cursorBlink):25===u?_(!o.isCursorHidden):45===u?_(i.reverseWraparound):66===u?_(i.applicationKeypad):67===u?4:1e3===u?_("VT200"===s):1002===u?_("DRAG"===s):1003===u?_("ANY"===s):1004===u?_(i.sendFocus):1005===u?4:1006===u?_("SGR"===r):1015===u?4:1016===u?_("SGR_PIXELS"===r):1048===u?1:47===u||1047===u||1049===u?_(c===l):2004===u?_(i.bracketedPasteMode):0,o.triggerDataEvent(`${n.C0.ESC}[${t?"":"?"}${f};${v}$y`),!0;var f,v}_updateAttrColor(e,t,i,s,r){return 2===t?(e|=50331648,e&=-16777216,e|=f.AttributeData.fromColorRGB([i,s,r])):5===t&&(e&=-50331904,e|=33554432|255&i),e}_extractColor(e,t,i){const s=[0,0,-1,0,0,0];let r=0,n=0;do{if(s[n+r]=e.params[t+n],e.hasSubParams(t+n)){const i=e.getSubParams(t+n);let o=0;do{5===s[1]&&(r=1),s[n+o+1+r]=i[o]}while(++o=2||2===s[1]&&n+r>=5)break;s[1]&&(r=1)}while(++n+t5)&&(e=1),t.extended.underlineStyle=e,t.fg|=268435456,0===e&&(t.fg&=-268435457),t.updateExtended()}_processSGR0(e){e.fg=l.DEFAULT_ATTR_DATA.fg,e.bg=l.DEFAULT_ATTR_DATA.bg,e.extended=e.extended.clone(),e.extended.underlineStyle=0,e.extended.underlineColor&=-67108864,e.updateExtended()}charAttributes(e){if(1===e.length&&0===e.params[0])return this._processSGR0(this._curAttrData),!0;const t=e.length;let i;const s=this._curAttrData;for(let r=0;r=30&&i<=37?(s.fg&=-50331904,s.fg|=16777216|i-30):i>=40&&i<=47?(s.bg&=-50331904,s.bg|=16777216|i-40):i>=90&&i<=97?(s.fg&=-50331904,s.fg|=16777224|i-90):i>=100&&i<=107?(s.bg&=-50331904,s.bg|=16777224|i-100):0===i?this._processSGR0(s):1===i?s.fg|=134217728:3===i?s.bg|=67108864:4===i?(s.fg|=268435456,this._processUnderline(e.hasSubParams(r)?e.getSubParams(r)[0]:1,s)):5===i?s.fg|=536870912:7===i?s.fg|=67108864:8===i?s.fg|=1073741824:9===i?s.fg|=2147483648:2===i?s.bg|=134217728:21===i?this._processUnderline(2,s):22===i?(s.fg&=-134217729,s.bg&=-134217729):23===i?s.bg&=-67108865:24===i?(s.fg&=-268435457,this._processUnderline(0,s)):25===i?s.fg&=-536870913:27===i?s.fg&=-67108865:28===i?s.fg&=-1073741825:29===i?s.fg&=2147483647:39===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg):49===i?(s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):38===i||48===i||58===i?r+=this._extractColor(e,r,s):53===i?s.bg|=1073741824:55===i?s.bg&=-1073741825:59===i?(s.extended=s.extended.clone(),s.extended.underlineColor=-1,s.updateExtended()):100===i?(s.fg&=-67108864,s.fg|=16777215&l.DEFAULT_ATTR_DATA.fg,s.bg&=-67108864,s.bg|=16777215&l.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",i);return!0}deviceStatus(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(`${n.C0.ESC}[0n`);break;case 6:const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[${e};${t}R`)}return!0}deviceStatusPrivate(e){if(6===e.params[0]){const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[?${e};${t}R`)}return!0}softReset(e){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(e){const t=e.params[0]||1;switch(t){case 1:case 2:this._optionsService.options.cursorStyle="block";break;case 3:case 4:this._optionsService.options.cursorStyle="underline";break;case 5:case 6:this._optionsService.options.cursorStyle="bar"}const i=t%2==1;return this._optionsService.options.cursorBlink=i,!0}setScrollRegion(e){const t=e.params[0]||1;let i;return(e.length<2||(i=e.params[1])>this._bufferService.rows||0===i)&&(i=this._bufferService.rows),i>t&&(this._activeBuffer.scrollTop=t-1,this._activeBuffer.scrollBottom=i-1,this._setCursor(0,0)),!0}windowOptions(e){if(!w(e.params[0],this._optionsService.rawOptions.windowOptions))return!0;const t=e.length>1?e.params[1]:0;switch(e.params[0]){case 14:2!==t&&this._onRequestWindowsOptionsReport.fire(y.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(y.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${n.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:0!==t&&2!==t||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),0!==t&&1!==t||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:0!==t&&2!==t||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),0!==t&&1!==t||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(e){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(e){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(e){return this._windowTitle=e,this._onTitleChange.fire(e),!0}setIconName(e){return this._iconName=e,!0}setOrReportIndexedColor(e){const t=[],i=e.split(";");for(;i.length>1;){const e=i.shift(),s=i.shift();if(/^\d+$/.exec(e)){const i=parseInt(e);if(D(i))if("?"===s)t.push({type:0,index:i});else{const e=(0,S.parseColor)(s);e&&t.push({type:1,index:i,color:e})}}}return t.length&&this._onColor.fire(t),!0}setHyperlink(e){const t=e.split(";");return!(t.length<2)&&(t[1]?this._createHyperlink(t[0],t[1]):!t[0]&&this._finishHyperlink())}_createHyperlink(e,t){this._getCurrentLinkId()&&this._finishHyperlink();const i=e.split(":");let s;const r=i.findIndex((e=>e.startsWith("id=")));return-1!==r&&(s=i[r].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:s,uri:t}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(e,t){const i=e.split(";");for(let e=0;e=this._specialColors.length);++e,++t)if("?"===i[e])this._onColor.fire([{type:0,index:this._specialColors[t]}]);else{const s=(0,S.parseColor)(i[e]);s&&this._onColor.fire([{type:1,index:this._specialColors[t],color:s}])}return!0}setOrReportFgColor(e){return this._setOrReportSpecialColor(e,0)}setOrReportBgColor(e){return this._setOrReportSpecialColor(e,1)}setOrReportCursorColor(e){return this._setOrReportSpecialColor(e,2)}restoreIndexedColor(e){if(!e)return this._onColor.fire([{type:2}]),!0;const t=[],i=e.split(";");for(let e=0;e=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){const e=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,e,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=l.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=l.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(e){return this._charsetService.setgLevel(e),!0}screenAlignmentPattern(){const e=new u.CellData;e.content=1<<22|"E".charCodeAt(0),e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg,this._setCursor(0,0);for(let t=0;t(this._coreService.triggerDataEvent(`${n.C0.ESC}${e}${n.C0.ESC}\\`),!0))('"q'===e?`P1$r${this._curAttrData.isProtected()?1:0}"q`:'"p'===e?'P1$r61;1"p':"r"===e?`P1$r${i.scrollTop+1};${i.scrollBottom+1}r`:"m"===e?"P1$r0m":" q"===e?`P1$r${{block:2,underline:4,bar:6}[s.cursorStyle]-(s.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(e,t){this._dirtyRowTracker.markRangeDirty(e,t)}}t.InputHandler=k;let L=class{constructor(e){this._bufferService=e,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(e){ethis.end&&(this.end=e)}markRangeDirty(e,t){e>t&&(E=e,e=t,t=E),ethis.end&&(this.end=t)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function D(e){return 0<=e&&e<256}L=s([r(0,v.IBufferService)],L)},844:(e,t)=>{function i(e){for(const t of e)t.dispose();e.length=0}Object.defineProperty(t,"__esModule",{value:!0}),t.getDisposeArrayDisposable=t.disposeArray=t.toDisposable=t.MutableDisposable=t.Disposable=void 0,t.Disposable=class{constructor(){this._disposables=[],this._isDisposed=!1}dispose(){this._isDisposed=!0;for(const e of this._disposables)e.dispose();this._disposables.length=0}register(e){return this._disposables.push(e),e}unregister(e){const t=this._disposables.indexOf(e);-1!==t&&this._disposables.splice(t,1)}},t.MutableDisposable=class{constructor(){this._isDisposed=!1}get value(){return this._isDisposed?void 0:this._value}set value(e){this._isDisposed||e===this._value||(this._value?.dispose(),this._value=e)}clear(){this.value=void 0}dispose(){this._isDisposed=!0,this._value?.dispose(),this._value=void 0}},t.toDisposable=function(e){return{dispose:e}},t.disposeArray=i,t.getDisposeArrayDisposable=function(e){return{dispose:()=>i(e)}}},1505:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FourKeyMap=t.TwoKeyMap=void 0;class i{constructor(){this._data={}}set(e,t,i){this._data[e]||(this._data[e]={}),this._data[e][t]=i}get(e,t){return this._data[e]?this._data[e][t]:void 0}clear(){this._data={}}}t.TwoKeyMap=i,t.FourKeyMap=class{constructor(){this._data=new i}set(e,t,s,r,n){this._data.get(e,t)||this._data.set(e,t,new i),this._data.get(e,t).set(s,r,n)}get(e,t,i,s){return this._data.get(e,t)?.get(i,s)}clear(){this._data.clear()}}},6114:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isChromeOS=t.isLinux=t.isWindows=t.isIphone=t.isIpad=t.isMac=t.getSafariVersion=t.isSafari=t.isLegacyEdge=t.isFirefox=t.isNode=void 0,t.isNode="undefined"!=typeof process&&"title"in process;const i=t.isNode?"node":navigator.userAgent,s=t.isNode?"node":navigator.platform;t.isFirefox=i.includes("Firefox"),t.isLegacyEdge=i.includes("Edge"),t.isSafari=/^((?!chrome|android).)*safari/i.test(i),t.getSafariVersion=function(){if(!t.isSafari)return 0;const e=i.match(/Version\/(\d+)/);return null===e||e.length<2?0:parseInt(e[1])},t.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(s),t.isIpad="iPad"===s,t.isIphone="iPhone"===s,t.isWindows=["Windows","Win16","Win32","WinCE"].includes(s),t.isLinux=s.indexOf("Linux")>=0,t.isChromeOS=/\bCrOS\b/.test(i)},6106:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SortedList=void 0;let i=0;t.SortedList=class{constructor(e){this._getKey=e,this._array=[]}clear(){this._array.length=0}insert(e){0!==this._array.length?(i=this._search(this._getKey(e)),this._array.splice(i,0,e)):this._array.push(e)}delete(e){if(0===this._array.length)return!1;const t=this._getKey(e);if(void 0===t)return!1;if(i=this._search(t),-1===i)return!1;if(this._getKey(this._array[i])!==t)return!1;do{if(this._array[i]===e)return this._array.splice(i,1),!0}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{yield this._array[i]}while(++i=this._array.length)&&this._getKey(this._array[i])===e))do{t(this._array[i])}while(++i=t;){let s=t+i>>1;const r=this._getKey(this._array[s]);if(r>e)i=s-1;else{if(!(r0&&this._getKey(this._array[s-1])===e;)s--;return s}t=s+1}}return t}}},7226:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DebouncedIdleTask=t.IdleTaskQueue=t.PriorityTaskQueue=void 0;const s=i(6114);class r{constructor(){this._tasks=[],this._i=0}enqueue(e){this._tasks.push(e),this._start()}flush(){for(;this._ir)return s-t<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(s-t))}ms`),void this._start();s=r}this.clear()}}class n extends r{_requestCallback(e){return setTimeout((()=>e(this._createDeadline(16))))}_cancelCallback(e){clearTimeout(e)}_createDeadline(e){const t=Date.now()+e;return{timeRemaining:()=>Math.max(0,t-Date.now())}}}t.PriorityTaskQueue=n,t.IdleTaskQueue=!s.isNode&&"requestIdleCallback"in window?class extends r{_requestCallback(e){return requestIdleCallback(e)}_cancelCallback(e){cancelIdleCallback(e)}}:n,t.DebouncedIdleTask=class{constructor(){this._queue=new t.IdleTaskQueue}set(e){this._queue.clear(),this._queue.enqueue(e)}flush(){this._queue.flush()}}},9282:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.updateWindowsModeWrappedState=void 0;const s=i(643);t.updateWindowsModeWrappedState=function(e){const t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1),i=t?.get(e.cols-1),r=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);r&&i&&(r.isWrapped=i[s.CHAR_DATA_CODE_INDEX]!==s.NULL_CELL_CODE&&i[s.CHAR_DATA_CODE_INDEX]!==s.WHITESPACE_CELL_CODE)}},3734:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ExtendedAttrs=t.AttributeData=void 0;class i{constructor(){this.fg=0,this.bg=0,this.extended=new s}static toColorRGB(e){return[e>>>16&255,e>>>8&255,255&e]}static fromColorRGB(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]}clone(){const e=new i;return e.fg=this.fg,e.bg=this.bg,e.extended=this.extended.clone(),e}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&0!==this.extended.underlineStyle?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return 50331648==(50331648&this.fg)}isBgRGB(){return 50331648==(50331648&this.bg)}isFgPalette(){return 16777216==(50331648&this.fg)||33554432==(50331648&this.fg)}isBgPalette(){return 16777216==(50331648&this.bg)||33554432==(50331648&this.bg)}isFgDefault(){return 0==(50331648&this.fg)}isBgDefault(){return 0==(50331648&this.bg)}isAttributeDefault(){return 0===this.fg&&0===this.bg}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?50331648==(50331648&this.extended.underlineColor):this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?16777216==(50331648&this.extended.underlineColor)||33554432==(50331648&this.extended.underlineColor):this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?0==(50331648&this.extended.underlineColor):this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}}t.AttributeData=i;class s{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(e){this._ext&=-67108864,this._ext|=67108863&e}get urlId(){return this._urlId}set urlId(e){this._urlId=e}get underlineVariantOffset(){const e=(3758096384&this._ext)>>29;return e<0?4294967288^e:e}set underlineVariantOffset(e){this._ext&=536870911,this._ext|=e<<29&3758096384}constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}clone(){return new s(this._ext,this._urlId)}isEmpty(){return 0===this.underlineStyle&&0===this._urlId}}t.ExtendedAttrs=s},9092:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Buffer=t.MAX_BUFFER_SIZE=void 0;const s=i(6349),r=i(7226),n=i(3734),o=i(8437),a=i(4634),h=i(511),c=i(643),l=i(4863),d=i(7116);t.MAX_BUFFER_SIZE=4294967295,t.Buffer=class{constructor(e,t,i){this._hasScrollback=e,this._optionsService=t,this._bufferService=i,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=o.DEFAULT_ATTR_DATA.clone(),this.savedCharset=d.DEFAULT_CHARSET,this.markers=[],this._nullCell=h.CellData.fromCharData([0,c.NULL_CELL_CHAR,c.NULL_CELL_WIDTH,c.NULL_CELL_CODE]),this._whitespaceCell=h.CellData.fromCharData([0,c.WHITESPACE_CELL_CHAR,c.WHITESPACE_CELL_WIDTH,c.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new r.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new n.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new n.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new o.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){const e=this.ybase+this.y-this.ydisp;return e>=0&&et.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:i}fillViewportRows(e){if(0===this.lines.length){void 0===e&&(e=o.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){const i=this.getNullCell(o.DEFAULT_ATTR_DATA);let s=0;const r=this._getCorrectBufferLength(t);if(r>this.lines.maxLength&&(this.lines.maxLength=r),this.lines.length>0){if(this._cols0&&this.lines.length<=this.ybase+this.y+n+1?(this.ybase--,n++,this.ydisp>0&&this.ydisp--):this.lines.push(new o.BufferLine(e,i)));else for(let e=this._rows;e>t;e--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(r0&&(this.lines.trimStart(e),this.ybase=Math.max(this.ybase-e,0),this.ydisp=Math.max(this.ydisp-e,0),this.savedY=Math.max(this.savedY-e,0)),this.lines.maxLength=r}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),n&&(this.y+=n),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let t=0;t.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue((()=>this._batchedMemoryCleanup())))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition100)return!0;return e}get _isReflowEnabled(){const e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&"conpty"===e.backend&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){const i=(0,a.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(o.DEFAULT_ATTR_DATA));if(i.length>0){const s=(0,a.reflowLargerCreateNewLayout)(this.lines,i);(0,a.reflowLargerApplyNewLayout)(this.lines,s.layout),this._reflowLargerAdjustViewport(e,t,s.countRemoved)}}_reflowLargerAdjustViewport(e,t,i){const s=this.getNullCell(o.DEFAULT_ATTR_DATA);let r=i;for(;r-- >0;)0===this.ybase?(this.y>0&&this.y--,this.lines.length=0;n--){let h=this.lines.get(n);if(!h||!h.isWrapped&&h.getTrimmedLength()<=e)continue;const c=[h];for(;h.isWrapped&&n>0;)h=this.lines.get(--n),c.unshift(h);const l=this.ybase+this.y;if(l>=n&&l0&&(s.push({start:n+c.length+r,newLines:v}),r+=v.length),c.push(...v);let p=_.length-1,g=_[p];0===g&&(p--,g=_[p]);let m=c.length-u-1,S=d;for(;m>=0;){const e=Math.min(S,g);if(void 0===c[p])break;if(c[p].copyCellsFrom(c[m],S-e,g-e,e,!0),g-=e,0===g&&(p--,g=_[p]),S-=e,0===S){m--;const e=Math.max(m,0);S=(0,a.getWrappedLineTrimmedLength)(c,e,this._cols)}}for(let t=0;t0;)0===this.ybase?this.y0){const e=[],t=[];for(let e=0;e=0;c--)if(a&&a.start>n+h){for(let e=a.newLines.length-1;e>=0;e--)this.lines.set(c--,a.newLines[e]);c++,e.push({index:n+1,amount:a.newLines.length}),h+=a.newLines.length,a=s[++o]}else this.lines.set(c,t[n--]);let c=0;for(let t=e.length-1;t>=0;t--)e[t].index+=c,this.lines.onInsertEmitter.fire(e[t]),c+=e[t].amount;const l=Math.max(0,i+r-this.lines.maxLength);l>0&&this.lines.onTrimEmitter.fire(l)}}translateBufferLineToString(e,t,i=0,s){const r=this.lines.get(e);return r?r.translateToString(t,i,s):""}getWrappedRangeForLine(e){let t=e,i=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;i+10;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(null==e&&(e=this.x);!this.tabs[++e]&&e=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t{t.line-=e,t.line<0&&t.dispose()}))),t.register(this.lines.onInsert((e=>{t.line>=e.index&&(t.line+=e.amount)}))),t.register(this.lines.onDelete((e=>{t.line>=e.index&&t.linee.index&&(t.line-=e.amount)}))),t.register(t.onDispose((()=>this._removeMarker(t)))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},8437:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLine=t.DEFAULT_ATTR_DATA=void 0;const s=i(3734),r=i(511),n=i(643),o=i(482);t.DEFAULT_ATTR_DATA=Object.freeze(new s.AttributeData);let a=0;class h{constructor(e,t,i=!1){this.isWrapped=i,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*e);const s=t||r.CellData.fromCharData([0,n.NULL_CELL_CHAR,n.NULL_CELL_WIDTH,n.NULL_CELL_CODE]);for(let t=0;t>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):i]}set(e,t){this._data[3*e+1]=t[n.CHAR_DATA_ATTR_INDEX],t[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[n.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(e){return this._data[3*e+0]>>22}hasWidth(e){return 12582912&this._data[3*e+0]}getFg(e){return this._data[3*e+1]}getBg(e){return this._data[3*e+2]}hasContent(e){return 4194303&this._data[3*e+0]}getCodePoint(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t}isCombined(e){return 2097152&this._data[3*e+0]}getString(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?(0,o.stringFromCodePoint)(2097151&t):""}isProtected(e){return 536870912&this._data[3*e+2]}loadCell(e,t){return a=3*e,t.content=this._data[a+0],t.fg=this._data[a+1],t.bg=this._data[a+2],2097152&t.content&&(t.combinedData=this._combined[e]),268435456&t.bg&&(t.extended=this._extendedAttrs[e]),t}setCell(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),268435456&t.bg&&(this._extendedAttrs[e]=t.extended),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg}setCellFromCodepoint(e,t,i,s){268435456&s.bg&&(this._extendedAttrs[e]=s.extended),this._data[3*e+0]=t|i<<22,this._data[3*e+1]=s.fg,this._data[3*e+2]=s.bg}addCodepointToCell(e,t,i){let s=this._data[3*e+0];2097152&s?this._combined[e]+=(0,o.stringFromCodePoint)(t):2097151&s?(this._combined[e]=(0,o.stringFromCodePoint)(2097151&s)+(0,o.stringFromCodePoint)(t),s&=-2097152,s|=2097152):s=t|1<<22,i&&(s&=-12582913,s|=i<<22),this._data[3*e+0]=s}insertCells(e,t,i){if((e%=this.length)&&2===this.getWidth(e-1)&&this.setCellFromCodepoint(e-1,0,1,i),t=0;--i)this.setCell(e+t+i,this.loadCell(e+i,s));for(let s=0;sthis.length){if(this._data.buffer.byteLength>=4*i)this._data=new Uint32Array(this._data.buffer,0,i);else{const e=new Uint32Array(i);e.set(this._data),this._data=e}for(let i=this.length;i=e&&delete this._combined[s]}const s=Object.keys(this._extendedAttrs);for(let t=0;t=e&&delete this._extendedAttrs[i]}}return this.length=e,4*i*2=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0}getNoBgTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0]||50331648&this._data[3*e+2])return e+(this._data[3*e+0]>>22);return 0}copyCellsFrom(e,t,i,s,r){const n=e._data;if(r)for(let r=s-1;r>=0;r--){for(let e=0;e<3;e++)this._data[3*(i+r)+e]=n[3*(t+r)+e];268435456&n[3*(t+r)+2]&&(this._extendedAttrs[i+r]=e._extendedAttrs[t+r])}else for(let r=0;r=t&&(this._combined[r-t+i]=e._combined[r])}}translateToString(e,t,i,s){t=t??0,i=i??this.length,e&&(i=Math.min(i,this.getTrimmedLength())),s&&(s.length=0);let r="";for(;t>22||1}return s&&s.push(t),r}}t.BufferLine=h},4841:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getRangeLength=void 0,t.getRangeLength=function(e,t){if(e.start.y>e.end.y)throw new Error(`Buffer range end (${e.end.x}, ${e.end.y}) cannot be before start (${e.start.x}, ${e.start.y})`);return t*(e.end.y-e.start.y)+(e.end.x-e.start.x+1)}},4634:(e,t)=>{function i(e,t,i){if(t===e.length-1)return e[t].getTrimmedLength();const s=!e[t].hasContent(i-1)&&1===e[t].getWidth(i-1),r=2===e[t+1].getWidth(0);return s&&r?i-1:i}Object.defineProperty(t,"__esModule",{value:!0}),t.getWrappedLineTrimmedLength=t.reflowSmallerGetNewLineLengths=t.reflowLargerApplyNewLayout=t.reflowLargerCreateNewLayout=t.reflowLargerGetLinesToRemove=void 0,t.reflowLargerGetLinesToRemove=function(e,t,s,r,n){const o=[];for(let a=0;a=a&&r0&&(e>d||0===l[e].getTrimmedLength());e--)v++;v>0&&(o.push(a+l.length-v),o.push(v)),a+=l.length-1}return o},t.reflowLargerCreateNewLayout=function(e,t){const i=[];let s=0,r=t[s],n=0;for(let o=0;oi(e,r,t))).reduce(((e,t)=>e+t));let o=0,a=0,h=0;for(;hc&&(o-=c,a++);const l=2===e[a].getWidth(o-1);l&&o--;const d=l?s-1:s;r.push(d),h+=d}return r},t.getWrappedLineTrimmedLength=i},5295:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferSet=void 0;const s=i(8460),r=i(844),n=i(9092);class o extends r.Disposable{constructor(e,t){super(),this._optionsService=e,this._bufferService=t,this._onBufferActivate=this.register(new s.EventEmitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this.register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.resize(this._bufferService.cols,this._bufferService.rows)))),this.register(this._optionsService.onSpecificOptionChange("tabStopWidth",(()=>this.setupTabStops())))}reset(){this._normal=new n.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new n.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(e,t){this._normal.resize(e,t),this._alt.resize(e,t),this.setupTabStops(e)}setupTabStops(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)}}t.BufferSet=o},511:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CellData=void 0;const s=i(482),r=i(643),n=i(3734);class o extends n.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new n.ExtendedAttrs,this.combinedData=""}static fromCharData(e){const t=new o;return t.setFromCharData(e),t}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,s.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(e){this.fg=e[r.CHAR_DATA_ATTR_INDEX],this.bg=0;let t=!1;if(e[r.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(2===e[r.CHAR_DATA_CHAR_INDEX].length){const i=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=i&&i<=56319){const s=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=s&&s<=57343?this.content=1024*(i-55296)+s-56320+65536|e[r.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[r.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[r.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[r.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.CellData=o},643:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WHITESPACE_CELL_CODE=t.WHITESPACE_CELL_WIDTH=t.WHITESPACE_CELL_CHAR=t.NULL_CELL_CODE=t.NULL_CELL_WIDTH=t.NULL_CELL_CHAR=t.CHAR_DATA_CODE_INDEX=t.CHAR_DATA_WIDTH_INDEX=t.CHAR_DATA_CHAR_INDEX=t.CHAR_DATA_ATTR_INDEX=t.DEFAULT_EXT=t.DEFAULT_ATTR=t.DEFAULT_COLOR=void 0,t.DEFAULT_COLOR=0,t.DEFAULT_ATTR=256|t.DEFAULT_COLOR<<9,t.DEFAULT_EXT=0,t.CHAR_DATA_ATTR_INDEX=0,t.CHAR_DATA_CHAR_INDEX=1,t.CHAR_DATA_WIDTH_INDEX=2,t.CHAR_DATA_CODE_INDEX=3,t.NULL_CELL_CHAR="",t.NULL_CELL_WIDTH=1,t.NULL_CELL_CODE=0,t.WHITESPACE_CELL_CHAR=" ",t.WHITESPACE_CELL_WIDTH=1,t.WHITESPACE_CELL_CODE=32},4863:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Marker=void 0;const s=i(8460),r=i(844);class n{get id(){return this._id}constructor(e){this.line=e,this.isDisposed=!1,this._disposables=[],this._id=n._nextId++,this._onDispose=this.register(new s.EventEmitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,r.disposeArray)(this._disposables),this._disposables.length=0)}register(e){return this._disposables.push(e),e}}t.Marker=n,n._nextId=1},7116:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CHARSET=t.CHARSETS=void 0,t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":"◆",a:"▒",b:"␉",c:"␌",d:"␍",e:"␊",f:"°",g:"±",h:"␤",i:"␋",j:"┘",k:"┐",l:"┌",m:"└",n:"┼",o:"⎺",p:"⎻",q:"─",r:"⎼",s:"⎽",t:"├",u:"┤",v:"┴",w:"┬",x:"│",y:"≤",z:"≥","{":"π","|":"≠","}":"£","~":"·"},t.CHARSETS.A={"#":"£"},t.CHARSETS.B=void 0,t.CHARSETS[4]={"#":"£","@":"¾","[":"ij","\\":"½","]":"|","{":"¨","|":"f","}":"¼","~":"´"},t.CHARSETS.C=t.CHARSETS[5]={"[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS.R={"#":"£","@":"à","[":"°","\\":"ç","]":"§","{":"é","|":"ù","}":"è","~":"¨"},t.CHARSETS.Q={"@":"à","[":"â","\\":"ç","]":"ê","^":"î","`":"ô","{":"é","|":"ù","}":"è","~":"û"},t.CHARSETS.K={"@":"§","[":"Ä","\\":"Ö","]":"Ü","{":"ä","|":"ö","}":"ü","~":"ß"},t.CHARSETS.Y={"#":"£","@":"§","[":"°","\\":"ç","]":"é","`":"ù","{":"à","|":"ò","}":"è","~":"ì"},t.CHARSETS.E=t.CHARSETS[6]={"@":"Ä","[":"Æ","\\":"Ø","]":"Å","^":"Ü","`":"ä","{":"æ","|":"ø","}":"å","~":"ü"},t.CHARSETS.Z={"#":"£","@":"§","[":"¡","\\":"Ñ","]":"¿","{":"°","|":"ñ","}":"ç"},t.CHARSETS.H=t.CHARSETS[7]={"@":"É","[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS["="]={"#":"ù","@":"à","[":"é","\\":"ç","]":"ê","^":"î",_:"è","`":"ô","{":"ä","|":"ö","}":"ü","~":"û"}},2584:(e,t)=>{var i,s,r;Object.defineProperty(t,"__esModule",{value:!0}),t.C1_ESCAPED=t.C1=t.C0=void 0,function(e){e.NUL="\0",e.SOH="",e.STX="",e.ETX="",e.EOT="",e.ENQ="",e.ACK="",e.BEL="",e.BS="\b",e.HT="\t",e.LF="\n",e.VT="\v",e.FF="\f",e.CR="\r",e.SO="",e.SI="",e.DLE="",e.DC1="",e.DC2="",e.DC3="",e.DC4="",e.NAK="",e.SYN="",e.ETB="",e.CAN="",e.EM="",e.SUB="",e.ESC="",e.FS="",e.GS="",e.RS="",e.US="",e.SP=" ",e.DEL=""}(i||(t.C0=i={})),function(e){e.PAD="€",e.HOP="",e.BPH="‚",e.NBH="ƒ",e.IND="„",e.NEL="…",e.SSA="†",e.ESA="‡",e.HTS="ˆ",e.HTJ="‰",e.VTS="Š",e.PLD="‹",e.PLU="Œ",e.RI="",e.SS2="Ž",e.SS3="",e.DCS="",e.PU1="‘",e.PU2="’",e.STS="“",e.CCH="”",e.MW="•",e.SPA="–",e.EPA="—",e.SOS="˜",e.SGCI="™",e.SCI="š",e.CSI="›",e.ST="œ",e.OSC="",e.PM="ž",e.APC="Ÿ"}(s||(t.C1=s={})),function(e){e.ST=`${i.ESC}\\`}(r||(t.C1_ESCAPED=r={}))},7399:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.evaluateKeyboardEvent=void 0;const s=i(2584),r={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']};t.evaluateKeyboardEvent=function(e,t,i,n){const o={type:0,cancel:!1,key:void 0},a=(e.shiftKey?1:0)|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:"UIKeyInputUpArrow"===e.key?o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A":"UIKeyInputLeftArrow"===e.key?o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D":"UIKeyInputRightArrow"===e.key?o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C":"UIKeyInputDownArrow"===e.key&&(o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B");break;case 8:o.key=e.ctrlKey?"\b":s.C0.DEL,e.altKey&&(o.key=s.C0.ESC+o.key);break;case 9:if(e.shiftKey){o.key=s.C0.ESC+"[Z";break}o.key=s.C0.HT,o.cancel=!0;break;case 13:o.key=e.altKey?s.C0.ESC+s.C0.CR:s.C0.CR,o.cancel=!0;break;case 27:o.key=s.C0.ESC,e.altKey&&(o.key=s.C0.ESC+s.C0.ESC),o.cancel=!0;break;case 37:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"D",o.key===s.C0.ESC+"[1;3D"&&(o.key=s.C0.ESC+(i?"b":"[1;5D"))):o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D";break;case 39:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"C",o.key===s.C0.ESC+"[1;3C"&&(o.key=s.C0.ESC+(i?"f":"[1;5C"))):o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C";break;case 38:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"A",i||o.key!==s.C0.ESC+"[1;3A"||(o.key=s.C0.ESC+"[1;5A")):o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A";break;case 40:if(e.metaKey)break;a?(o.key=s.C0.ESC+"[1;"+(a+1)+"B",i||o.key!==s.C0.ESC+"[1;3B"||(o.key=s.C0.ESC+"[1;5B")):o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B";break;case 45:e.shiftKey||e.ctrlKey||(o.key=s.C0.ESC+"[2~");break;case 46:o.key=a?s.C0.ESC+"[3;"+(a+1)+"~":s.C0.ESC+"[3~";break;case 36:o.key=a?s.C0.ESC+"[1;"+(a+1)+"H":t?s.C0.ESC+"OH":s.C0.ESC+"[H";break;case 35:o.key=a?s.C0.ESC+"[1;"+(a+1)+"F":t?s.C0.ESC+"OF":s.C0.ESC+"[F";break;case 33:e.shiftKey?o.type=2:e.ctrlKey?o.key=s.C0.ESC+"[5;"+(a+1)+"~":o.key=s.C0.ESC+"[5~";break;case 34:e.shiftKey?o.type=3:e.ctrlKey?o.key=s.C0.ESC+"[6;"+(a+1)+"~":o.key=s.C0.ESC+"[6~";break;case 112:o.key=a?s.C0.ESC+"[1;"+(a+1)+"P":s.C0.ESC+"OP";break;case 113:o.key=a?s.C0.ESC+"[1;"+(a+1)+"Q":s.C0.ESC+"OQ";break;case 114:o.key=a?s.C0.ESC+"[1;"+(a+1)+"R":s.C0.ESC+"OR";break;case 115:o.key=a?s.C0.ESC+"[1;"+(a+1)+"S":s.C0.ESC+"OS";break;case 116:o.key=a?s.C0.ESC+"[15;"+(a+1)+"~":s.C0.ESC+"[15~";break;case 117:o.key=a?s.C0.ESC+"[17;"+(a+1)+"~":s.C0.ESC+"[17~";break;case 118:o.key=a?s.C0.ESC+"[18;"+(a+1)+"~":s.C0.ESC+"[18~";break;case 119:o.key=a?s.C0.ESC+"[19;"+(a+1)+"~":s.C0.ESC+"[19~";break;case 120:o.key=a?s.C0.ESC+"[20;"+(a+1)+"~":s.C0.ESC+"[20~";break;case 121:o.key=a?s.C0.ESC+"[21;"+(a+1)+"~":s.C0.ESC+"[21~";break;case 122:o.key=a?s.C0.ESC+"[23;"+(a+1)+"~":s.C0.ESC+"[23~";break;case 123:o.key=a?s.C0.ESC+"[24;"+(a+1)+"~":s.C0.ESC+"[24~";break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(i&&!n||!e.altKey||e.metaKey)!i||e.altKey||e.ctrlKey||e.shiftKey||!e.metaKey?e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&1===e.key.length?o.key=e.key:e.key&&e.ctrlKey&&("_"===e.key&&(o.key=s.C0.US),"@"===e.key&&(o.key=s.C0.NUL)):65===e.keyCode&&(o.type=1);else{const t=r[e.keyCode],i=t?.[e.shiftKey?1:0];if(i)o.key=s.C0.ESC+i;else if(e.keyCode>=65&&e.keyCode<=90){const t=e.ctrlKey?e.keyCode-64:e.keyCode+32;let i=String.fromCharCode(t);e.shiftKey&&(i=i.toUpperCase()),o.key=s.C0.ESC+i}else if(32===e.keyCode)o.key=s.C0.ESC+(e.ctrlKey?s.C0.NUL:" ");else if("Dead"===e.key&&e.code.startsWith("Key")){let t=e.code.slice(3,4);e.shiftKey||(t=t.toLowerCase()),o.key=s.C0.ESC+t,o.cancel=!0}}else e.keyCode>=65&&e.keyCode<=90?o.key=String.fromCharCode(e.keyCode-64):32===e.keyCode?o.key=s.C0.NUL:e.keyCode>=51&&e.keyCode<=55?o.key=String.fromCharCode(e.keyCode-51+27):56===e.keyCode?o.key=s.C0.DEL:219===e.keyCode?o.key=s.C0.ESC:220===e.keyCode?o.key=s.C0.FS:221===e.keyCode&&(o.key=s.C0.GS)}return o}},482:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Utf8ToUtf32=t.StringToUtf32=t.utf32ToString=t.stringFromCodePoint=void 0,t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t=0,i=e.length){let s="";for(let r=t;r65535?(t-=65536,s+=String.fromCharCode(55296+(t>>10))+String.fromCharCode(t%1024+56320)):s+=String.fromCharCode(t)}return s},t.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){const i=e.length;if(!i)return 0;let s=0,r=0;if(this._interim){const i=e.charCodeAt(r++);56320<=i&&i<=57343?t[s++]=1024*(this._interim-55296)+i-56320+65536:(t[s++]=this._interim,t[s++]=i),this._interim=0}for(let n=r;n=i)return this._interim=r,s;const o=e.charCodeAt(n);56320<=o&&o<=57343?t[s++]=1024*(r-55296)+o-56320+65536:(t[s++]=r,t[s++]=o)}else 65279!==r&&(t[s++]=r)}return s}},t.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){const i=e.length;if(!i)return 0;let s,r,n,o,a=0,h=0,c=0;if(this.interim[0]){let s=!1,r=this.interim[0];r&=192==(224&r)?31:224==(240&r)?15:7;let n,o=0;for(;(n=63&this.interim[++o])&&o<4;)r<<=6,r|=n;const h=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,l=h-o;for(;c=i)return 0;if(n=e[c++],128!=(192&n)){c--,s=!0;break}this.interim[o++]=n,r<<=6,r|=63&n}s||(2===h?r<128?c--:t[a++]=r:3===h?r<2048||r>=55296&&r<=57343||65279===r||(t[a++]=r):r<65536||r>1114111||(t[a++]=r)),this.interim.fill(0)}const l=i-4;let d=c;for(;d=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(h=(31&s)<<6|63&r,h<128){d--;continue}t[a++]=h}else if(224==(240&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(h=(15&s)<<12|(63&r)<<6|63&n,h<2048||h>=55296&&h<=57343||65279===h)continue;t[a++]=h}else if(240==(248&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,this.interim[2]=n,a;if(o=e[d++],128!=(192&o)){d--;continue}if(h=(7&s)<<18|(63&r)<<12|(63&n)<<6|63&o,h<65536||h>1114111)continue;t[a++]=h}}return a}}},225:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;const s=i(1480),r=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],n=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];let o;t.UnicodeV6=class{constructor(){if(this.version="6",!o){o=new Uint8Array(65536),o.fill(1),o[0]=0,o.fill(0,1,32),o.fill(0,127,160),o.fill(2,4352,4448),o[9001]=2,o[9002]=2,o.fill(2,11904,42192),o[12351]=1,o.fill(2,44032,55204),o.fill(2,63744,64256),o.fill(2,65040,65050),o.fill(2,65072,65136),o.fill(2,65280,65377),o.fill(2,65504,65511);for(let e=0;et[r][1])return!1;for(;r>=s;)if(i=s+r>>1,e>t[i][1])s=i+1;else{if(!(e=131072&&e<=196605||e>=196608&&e<=262141?2:1}charProperties(e,t){let i=this.wcwidth(e),r=0===i&&0!==t;if(r){const e=s.UnicodeService.extractWidth(t);0===e?r=!1:e>i&&(i=e)}return s.UnicodeService.createPropertyValue(0,i,r)}}},5981:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;const s=i(8460),r=i(844);class n extends r.Disposable{constructor(e){super(),this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this.register(new s.EventEmitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(e,t){if(void 0!==t&&this._syncCalls>t)return void(this._syncCalls=0);if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let i;for(this._isSyncWriting=!0;i=this._writeBuffer.shift();){this._action(i);const e=this._callbacks.shift();e&&e()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(e,t){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t),void this._innerWrite();setTimeout((()=>this._innerWrite()))}this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)}_innerWrite(e=0,t=!0){const i=e||Date.now();for(;this._writeBuffer.length>this._bufferOffset;){const e=this._writeBuffer[this._bufferOffset],s=this._action(e,t);if(s){const e=e=>Date.now()-i>=12?setTimeout((()=>this._innerWrite(0,e))):this._innerWrite(i,e);return void s.catch((e=>(queueMicrotask((()=>{throw e})),Promise.resolve(!1)))).then(e)}const r=this._callbacks[this._bufferOffset];if(r&&r(),this._bufferOffset++,this._pendingData-=e.length,Date.now()-i>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((()=>this._innerWrite()))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}t.WriteBuffer=n},5941:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.toRgbString=t.parseColor=void 0;const i=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,s=/^[\da-f]+$/;function r(e,t){const i=e.toString(16),s=i.length<2?"0"+i:i;switch(t){case 4:return i[0];case 8:return s;case 12:return(s+s).slice(0,3);default:return s+s}}t.parseColor=function(e){if(!e)return;let t=e.toLowerCase();if(0===t.indexOf("rgb:")){t=t.slice(4);const e=i.exec(t);if(e){const t=e[1]?15:e[4]?255:e[7]?4095:65535;return[Math.round(parseInt(e[1]||e[4]||e[7]||e[10],16)/t*255),Math.round(parseInt(e[2]||e[5]||e[8]||e[11],16)/t*255),Math.round(parseInt(e[3]||e[6]||e[9]||e[12],16)/t*255)]}}else if(0===t.indexOf("#")&&(t=t.slice(1),s.exec(t)&&[3,6,9,12].includes(t.length))){const e=t.length/3,i=[0,0,0];for(let s=0;s<3;++s){const r=parseInt(t.slice(e*s,e*s+e),16);i[s]=1===e?r<<4:2===e?r:3===e?r>>4:r>>8}return i}},t.toRgbString=function(e,t=16){const[i,s,n]=e;return`rgb:${r(i,t)}/${r(s,t)}/${r(n,t)}`}},5770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},6351:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;const s=i(482),r=i(8742),n=i(5770),o=[];t.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=o,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=o}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}reset(){if(this._active.length)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=o,this._ident=0}hook(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||o,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].hook(t);else this._handlerFb(this._ident,"HOOK",t)}put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._ident,"PUT",(0,s.utf32ToString)(e,t,i))}unhook(e,t=!0){if(this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].unhook(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].unhook(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._ident,"UNHOOK",e);this._active=o,this._ident=0}};const a=new r.Params;a.addParam(0),t.DcsHandler=class{constructor(e){this._handler=e,this._data="",this._params=a,this._hitLimit=!1}hook(e){this._params=e.length>1||e.params[0]?e.clone():a,this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,s.utf32ToString)(e,t,i),this._data.length>n.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data,this._params),t instanceof Promise))return t.then((e=>(this._params=a,this._data="",this._hitLimit=!1,e)));return this._params=a,this._data="",this._hitLimit=!1,t}}},2015:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;const s=i(844),r=i(8742),n=i(6242),o=i(6351);class a{constructor(e){this.table=new Uint8Array(e)}setDefault(e,t){this.table.fill(e<<4|t)}add(e,t,i,s){this.table[t<<8|e]=i<<4|s}addMany(e,t,i,s){for(let r=0;rt)),i=(e,i)=>t.slice(e,i),s=i(32,127),r=i(0,24);r.push(25),r.push.apply(r,i(28,32));const n=i(0,14);let o;for(o in e.setDefault(1,0),e.addMany(s,0,2,0),n)e.addMany([24,26,153,154],o,3,0),e.addMany(i(128,144),o,3,0),e.addMany(i(144,152),o,3,0),e.add(156,o,0,0),e.add(27,o,11,1),e.add(157,o,4,8),e.addMany([152,158,159],o,0,7),e.add(155,o,11,3),e.add(144,o,11,9);return e.addMany(r,0,3,0),e.addMany(r,1,3,1),e.add(127,1,0,1),e.addMany(r,8,0,8),e.addMany(r,3,3,3),e.add(127,3,0,3),e.addMany(r,4,3,4),e.add(127,4,0,4),e.addMany(r,6,3,6),e.addMany(r,5,3,5),e.add(127,5,0,5),e.addMany(r,2,3,2),e.add(127,2,0,2),e.add(93,1,4,8),e.addMany(s,8,5,8),e.add(127,8,5,8),e.addMany([156,27,24,26,7],8,6,0),e.addMany(i(28,32),8,0,8),e.addMany([88,94,95],1,0,7),e.addMany(s,7,0,7),e.addMany(r,7,0,7),e.add(156,7,0,0),e.add(127,7,0,7),e.add(91,1,11,3),e.addMany(i(64,127),3,7,0),e.addMany(i(48,60),3,8,4),e.addMany([60,61,62,63],3,9,4),e.addMany(i(48,60),4,8,4),e.addMany(i(64,127),4,7,0),e.addMany([60,61,62,63],4,0,6),e.addMany(i(32,64),6,0,6),e.add(127,6,0,6),e.addMany(i(64,127),6,0,0),e.addMany(i(32,48),3,9,5),e.addMany(i(32,48),5,9,5),e.addMany(i(48,64),5,0,6),e.addMany(i(64,127),5,7,0),e.addMany(i(32,48),4,9,5),e.addMany(i(32,48),1,9,2),e.addMany(i(32,48),2,9,2),e.addMany(i(48,127),2,10,0),e.addMany(i(48,80),1,10,0),e.addMany(i(81,88),1,10,0),e.addMany([89,90,92],1,10,0),e.addMany(i(96,127),1,10,0),e.add(80,1,11,9),e.addMany(r,9,0,9),e.add(127,9,0,9),e.addMany(i(28,32),9,0,9),e.addMany(i(32,48),9,9,12),e.addMany(i(48,60),9,8,10),e.addMany([60,61,62,63],9,9,10),e.addMany(r,11,0,11),e.addMany(i(32,128),11,0,11),e.addMany(i(28,32),11,0,11),e.addMany(r,10,0,10),e.add(127,10,0,10),e.addMany(i(28,32),10,0,10),e.addMany(i(48,60),10,8,10),e.addMany([60,61,62,63],10,0,11),e.addMany(i(32,48),10,9,12),e.addMany(r,12,0,12),e.add(127,12,0,12),e.addMany(i(28,32),12,0,12),e.addMany(i(32,48),12,9,12),e.addMany(i(48,64),12,0,11),e.addMany(i(64,127),12,12,13),e.addMany(i(64,127),10,12,13),e.addMany(i(64,127),9,12,13),e.addMany(r,13,13,13),e.addMany(s,13,13,13),e.add(127,13,0,13),e.addMany([27,156,24,26],13,14,0),e.add(h,0,2,0),e.add(h,8,5,8),e.add(h,6,0,6),e.add(h,11,0,11),e.add(h,13,13,13),e}();class c extends s.Disposable{constructor(e=t.VT500_TRANSITION_TABLE){super(),this._transitions=e,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new r.Params,this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._printHandlerFb=(e,t,i)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this.register((0,s.toDisposable)((()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)}))),this._oscParser=this.register(new n.OscParser),this._dcsParser=this.register(new o.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},(()=>!0))}_identifier(e,t=[64,126]){let i=0;if(e.prefix){if(e.prefix.length>1)throw new Error("only one byte as prefix supported");if(i=e.prefix.charCodeAt(0),i&&60>i||i>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let t=0;ts||s>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");i<<=8,i|=s}}if(1!==e.final.length)throw new Error("final must be a single byte");const s=e.final.charCodeAt(0);if(t[0]>s||s>t[1])throw new Error(`final must be in range ${t[0]} .. ${t[1]}`);return i<<=8,i|=s,i}identToString(e){const t=[];for(;e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")}setPrintHandler(e){this._printHandler=e}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(e,t){const i=this._identifier(e,[48,126]);void 0===this._escHandlers[i]&&(this._escHandlers[i]=[]);const s=this._escHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearEscHandler(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]}setEscHandlerFallback(e){this._escHandlerFb=e}setExecuteHandler(e,t){this._executeHandlers[e.charCodeAt(0)]=t}clearExecuteHandler(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]}setExecuteHandlerFallback(e){this._executeHandlerFb=e}registerCsiHandler(e,t){const i=this._identifier(e);void 0===this._csiHandlers[i]&&(this._csiHandlers[i]=[]);const s=this._csiHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearCsiHandler(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]}setCsiHandlerFallback(e){this._csiHandlerFb=e}registerDcsHandler(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)}clearDcsHandler(e){this._dcsParser.clearHandler(this._identifier(e))}setDcsHandlerFallback(e){this._dcsParser.setHandlerFallback(e)}registerOscHandler(e,t){return this._oscParser.registerHandler(e,t)}clearOscHandler(e){this._oscParser.clearHandler(e)}setOscHandlerFallback(e){this._oscParser.setHandlerFallback(e)}setErrorHandler(e){this._errorHandler=e}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0,0!==this._parseStack.state&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(e,t,i,s,r){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=i,this._parseStack.transition=s,this._parseStack.chunkPos=r}parse(e,t,i){let s,r=0,n=0,o=0;if(this._parseStack.state)if(2===this._parseStack.state)this._parseStack.state=0,o=this._parseStack.chunkPos+1;else{if(void 0===i||1===this._parseStack.state)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");const t=this._parseStack.handlers;let n=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](this._params),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 4:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 6:if(r=e[this._parseStack.chunkPos],s=this._dcsParser.unhook(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(r=e[this._parseStack.chunkPos],s=this._oscParser.end(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,o=this._parseStack.chunkPos+1,this.precedingJoinState=0,this.currentState=15&this._parseStack.transition}for(let i=o;i>4){case 2:for(let s=i+1;;++s){if(s>=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=t||(r=e[s])<32||r>126&&r=0&&(s=o[a](this._params),!0!==s);a--)if(s instanceof Promise)return this._preserveStack(3,o,a,n,i),s;a<0&&this._csiHandlerFb(this._collect<<8|r,this._params),this.precedingJoinState=0;break;case 8:do{switch(r){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(r-48)}}while(++i47&&r<60);i--;break;case 9:this._collect<<=8,this._collect|=r;break;case 10:const c=this._escHandlers[this._collect<<8|r];let l=c?c.length-1:-1;for(;l>=0&&(s=c[l](),!0!==s);l--)if(s instanceof Promise)return this._preserveStack(4,c,l,n,i),s;l<0&&this._escHandlerFb(this._collect<<8|r),this.precedingJoinState=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|r,this._params);break;case 13:for(let s=i+1;;++s)if(s>=t||24===(r=e[s])||26===r||27===r||r>127&&r=t||(r=e[s])<32||r>127&&r{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;const s=i(5770),r=i(482),n=[];t.OscParser=class{constructor(){this._state=0,this._active=n,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=n}reset(){if(2===this._state)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=n,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||n,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,"START")}_put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._id,"PUT",(0,r.utf32ToString)(e,t,i))}start(){this.reset(),this._state=1}put(e,t,i){if(3!==this._state){if(1===this._state)for(;t0&&this._put(e,t,i)}}end(e,t=!0){if(0!==this._state){if(3!==this._state)if(1===this._state&&this._start(),this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].end(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].end(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._id,"END",e);this._active=n,this._id=-1,this._state=0}}},t.OscHandler=class{constructor(e){this._handler=e,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,r.utf32ToString)(e,t,i),this._data.length>s.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data),t instanceof Promise))return t.then((e=>(this._data="",this._hitLimit=!1,e)));return this._data="",this._hitLimit=!1,t}}},8742:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;const i=2147483647;class s{static fromArray(e){const t=new s;if(!e.length)return t;for(let i=Array.isArray(e[0])?1:0;i256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){const e=new s(this.maxLength,this.maxSubParamsLength);return e.params.set(this.params),e.length=this.length,e._subParams.set(this._subParams),e._subParamsLength=this._subParamsLength,e._subParamsIdx.set(this._subParamsIdx),e._rejectDigits=this._rejectDigits,e._rejectSubDigits=this._rejectSubDigits,e._digitIsSub=this._digitIsSub,e}toArray(){const e=[];for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&e.push(Array.prototype.slice.call(this._subParams,i,s))}return e}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>i?i:e}}addSubParam(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>i?i:e,this._subParamsIdx[this.length-1]++}}hasSubParams(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0}getSubParams(e){const t=this._subParamsIdx[e]>>8,i=255&this._subParamsIdx[e];return i-t>0?this._subParams.subarray(t,i):null}getSubParamsAll(){const e={};for(let t=0;t>8,s=255&this._subParamsIdx[t];s-i>0&&(e[t]=this._subParams.slice(i,s))}return e}addDigit(e){let t;if(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;const s=this._digitIsSub?this._subParams:this.params,r=s[t-1];s[t-1]=~r?Math.min(10*r+e,i):e}}t.Params=s},5741:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0,t.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()}loadAddon(e,t){const i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=()=>this._wrappedAddonDispose(i),t.activate(e)}_wrappedAddonDispose(e){if(e.isDisposed)return;let t=-1;for(let i=0;i{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;const s=i(3785),r=i(511);t.BufferApiView=class{constructor(e,t){this._buffer=e,this.type=t}init(e){return this._buffer=e,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(e){const t=this._buffer.lines.get(e);if(t)return new s.BufferLineApiView(t)}getNullCell(){return new r.CellData}}},3785:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;const s=i(511);t.BufferLineApiView=class{constructor(e){this._line=e}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new s.CellData)}translateToString(e,t,i){return this._line.translateToString(e,t,i)}}},8285:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;const s=i(8771),r=i(8460),n=i(844);class o extends n.Disposable{constructor(e){super(),this._core=e,this._onBufferChange=this.register(new r.EventEmitter),this.onBufferChange=this._onBufferChange.event,this._normal=new s.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new s.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate((()=>this._onBufferChange.fire(this.active)))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}t.BufferNamespaceApi=o},7975:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0,t.ParserApi=class{constructor(e){this._core=e}registerCsiHandler(e,t){return this._core.registerCsiHandler(e,(e=>t(e.toArray())))}addCsiHandler(e,t){return this.registerCsiHandler(e,t)}registerDcsHandler(e,t){return this._core.registerDcsHandler(e,((e,i)=>t(e,i.toArray())))}addDcsHandler(e,t){return this.registerDcsHandler(e,t)}registerEscHandler(e,t){return this._core.registerEscHandler(e,t)}addEscHandler(e,t){return this.registerEscHandler(e,t)}registerOscHandler(e,t){return this._core.registerOscHandler(e,t)}addOscHandler(e,t){return this.registerOscHandler(e,t)}}},7090:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0,t.UnicodeApi=class{constructor(e){this._core=e}register(e){this._core.unicodeService.register(e)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(e){this._core.unicodeService.activeVersion=e}}},744:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;const n=i(8460),o=i(844),a=i(5295),h=i(2585);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;let c=t.BufferService=class extends o.Disposable{get buffer(){return this.buffers.active}constructor(e){super(),this.isUserScrolling=!1,this._onResize=this.register(new n.EventEmitter),this.onResize=this._onResize.event,this._onScroll=this.register(new n.EventEmitter),this.onScroll=this._onScroll.event,this.cols=Math.max(e.rawOptions.cols||0,t.MINIMUM_COLS),this.rows=Math.max(e.rawOptions.rows||0,t.MINIMUM_ROWS),this.buffers=this.register(new a.BufferSet(e,this))}resize(e,t){this.cols=e,this.rows=t,this.buffers.resize(e,t),this._onResize.fire({cols:e,rows:t})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(e,t=!1){const i=this.buffer;let s;s=this._cachedBlankLine,s&&s.length===this.cols&&s.getFg(0)===e.fg&&s.getBg(0)===e.bg||(s=i.getBlankLine(e,t),this._cachedBlankLine=s),s.isWrapped=t;const r=i.ybase+i.scrollTop,n=i.ybase+i.scrollBottom;if(0===i.scrollTop){const e=i.lines.isFull;n===i.lines.length-1?e?i.lines.recycle().copyFrom(s):i.lines.push(s.clone()):i.lines.splice(n+1,0,s.clone()),e?this.isUserScrolling&&(i.ydisp=Math.max(i.ydisp-1,0)):(i.ybase++,this.isUserScrolling||i.ydisp++)}else{const e=n-r+1;i.lines.shiftElements(r+1,e-1,-1),i.lines.set(n,s.clone())}this.isUserScrolling||(i.ydisp=i.ybase),this._onScroll.fire(i.ydisp)}scrollLines(e,t,i){const s=this.buffer;if(e<0){if(0===s.ydisp)return;this.isUserScrolling=!0}else e+s.ydisp>=s.ybase&&(this.isUserScrolling=!1);const r=s.ydisp;s.ydisp=Math.max(Math.min(s.ydisp+e,s.ybase),0),r!==s.ydisp&&(t||this._onScroll.fire(s.ydisp))}};t.BufferService=c=s([r(0,h.IOptionsService)],c)},7994:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0,t.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(e){this.glevel=e,this.charset=this._charsets[e]}setgCharset(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)}}},1753:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;const n=i(2585),o=i(8460),a=i(844),h={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:e=>4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)},VT200:{events:19,restrict:e=>32!==e.action},DRAG:{events:23,restrict:e=>32!==e.action||3!==e.button},ANY:{events:31,restrict:e=>!0}};function c(e,t){let i=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(i|=64,i|=e.action):(i|=3&e.button,4&e.button&&(i|=64),8&e.button&&(i|=128),32===e.action?i|=32:0!==e.action||t||(i|=3)),i}const l=String.fromCharCode,d={DEFAULT:e=>{const t=[c(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":`${l(t[0])}${l(t[1])}${l(t[2])}`},SGR:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.col};${e.row}${t}`},SGR_PIXELS:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${c(e,!0)};${e.x};${e.y}${t}`}};let _=t.CoreMouseService=class extends a.Disposable{constructor(e,t){super(),this._bufferService=e,this._coreService=t,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._onProtocolChange=this.register(new o.EventEmitter),this.onProtocolChange=this._onProtocolChange.event;for(const e of Object.keys(h))this.addProtocol(e,h[e]);for(const e of Object.keys(d))this.addEncoding(e,d[e]);this.reset()}addProtocol(e,t){this._protocols[e]=t}addEncoding(e,t){this._encodings[e]=t}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return 0!==this._protocols[this._activeProtocol].events}set activeProtocol(e){if(!this._protocols[e])throw new Error(`unknown protocol "${e}"`);this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(e){if(!this._encodings[e])throw new Error(`unknown encoding "${e}"`);this._activeEncoding=e}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null}triggerMouseEvent(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._equalEvents(this._lastEvent,e,"SGR_PIXELS"===this._activeEncoding))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;const t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0}explainEvents(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}}_equalEvents(e,t,i){if(i){if(e.x!==t.x)return!1;if(e.y!==t.y)return!1}else{if(e.col!==t.col)return!1;if(e.row!==t.row)return!1}return e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift}};t.CoreMouseService=_=s([r(0,n.IBufferService),r(1,n.ICoreService)],_)},6975:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;const n=i(1439),o=i(8460),a=i(844),h=i(2585),c=Object.freeze({insertMode:!1}),l=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,origin:!1,reverseWraparound:!1,sendFocus:!1,wraparound:!0});let d=t.CoreService=class extends a.Disposable{constructor(e,t,i){super(),this._bufferService=e,this._logService=t,this._optionsService=i,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this.register(new o.EventEmitter),this.onData=this._onData.event,this._onUserInput=this.register(new o.EventEmitter),this.onUserInput=this._onUserInput.event,this._onBinary=this.register(new o.EventEmitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this.register(new o.EventEmitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}reset(){this.modes=(0,n.clone)(c),this.decPrivateModes=(0,n.clone)(l)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;const i=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&i.ybase!==i.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`,(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onBinary.fire(e))}};t.CoreService=d=s([r(0,h.IBufferService),r(1,h.ILogService),r(2,h.IOptionsService)],d)},9074:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DecorationService=void 0;const s=i(8055),r=i(8460),n=i(844),o=i(6106);let a=0,h=0;class c extends n.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new o.SortedList((e=>e?.marker.line)),this._onDecorationRegistered=this.register(new r.EventEmitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this.register(new r.EventEmitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this.register((0,n.toDisposable)((()=>this.reset())))}registerDecoration(e){if(e.marker.isDisposed)return;const t=new l(e);if(t){const e=t.marker.onDispose((()=>t.dispose()));t.onDispose((()=>{t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),e.dispose())})),this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(const e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,i){let s=0,r=0;for(const n of this._decorations.getKeyIterator(t))s=n.options.x??0,r=s+(n.options.width??1),e>=s&&e{a=t.options.x??0,h=a+(t.options.width??1),e>=a&&e{Object.defineProperty(t,"__esModule",{value:!0}),t.InstantiationService=t.ServiceCollection=void 0;const s=i(2585),r=i(8343);class n{constructor(...e){this._entries=new Map;for(const[t,i]of e)this.set(t,i)}set(e,t){const i=this._entries.get(e);return this._entries.set(e,t),i}forEach(e){for(const[t,i]of this._entries.entries())e(t,i)}has(e){return this._entries.has(e)}get(e){return this._entries.get(e)}}t.ServiceCollection=n,t.InstantiationService=class{constructor(){this._services=new n,this._services.set(s.IInstantiationService,this)}setService(e,t){this._services.set(e,t)}getService(e){return this._services.get(e)}createInstance(e,...t){const i=(0,r.getServiceDependencies)(e).sort(((e,t)=>e.index-t.index)),s=[];for(const t of i){const i=this._services.get(t.id);if(!i)throw new Error(`[createInstance] ${e.name} depends on UNKNOWN service ${t.id}.`);s.push(i)}const n=i.length>0?i[0].index:t.length;if(t.length!==n)throw new Error(`[createInstance] First service dependency of ${e.name} at position ${n+1} conflicts with ${t.length} static arguments`);return new e(...[...t,...s])}}},7866:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.traceCall=t.setTraceLogger=t.LogService=void 0;const n=i(844),o=i(2585),a={trace:o.LogLevelEnum.TRACE,debug:o.LogLevelEnum.DEBUG,info:o.LogLevelEnum.INFO,warn:o.LogLevelEnum.WARN,error:o.LogLevelEnum.ERROR,off:o.LogLevelEnum.OFF};let h,c=t.LogService=class extends n.Disposable{get logLevel(){return this._logLevel}constructor(e){super(),this._optionsService=e,this._logLevel=o.LogLevelEnum.OFF,this._updateLogLevel(),this.register(this._optionsService.onSpecificOptionChange("logLevel",(()=>this._updateLogLevel()))),h=this}_updateLogLevel(){this._logLevel=a[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(e){for(let t=0;tJSON.stringify(e))).join(", ")})`);const t=s.apply(this,e);return h.trace(`GlyphRenderer#${s.name} return`,t),t}}},7302:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OptionsService=t.DEFAULT_OPTIONS=void 0;const s=i(8460),r=i(844),n=i(6114);t.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,documentOverride:null,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"courier-new, courier, monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},rescaleOverlappingGlyphs:!1,rightClickSelectsWord:n.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRulerWidth:0};const o=["normal","bold","100","200","300","400","500","600","700","800","900"];class a extends r.Disposable{constructor(e){super(),this._onOptionChange=this.register(new s.EventEmitter),this.onOptionChange=this._onOptionChange.event;const i={...t.DEFAULT_OPTIONS};for(const t in e)if(t in i)try{const s=e[t];i[t]=this._sanitizeAndValidateOption(t,s)}catch(e){console.error(e)}this.rawOptions=i,this.options={...i},this._setupOptions(),this.register((0,r.toDisposable)((()=>{this.rawOptions.linkHandler=null,this.rawOptions.documentOverride=null})))}onSpecificOptionChange(e,t){return this.onOptionChange((i=>{i===e&&t(this.rawOptions[e])}))}onMultipleOptionChange(e,t){return this.onOptionChange((i=>{-1!==e.indexOf(i)&&t()}))}_setupOptions(){const e=e=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);return this.rawOptions[e]},i=(e,i)=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);i=this._sanitizeAndValidateOption(e,i),this.rawOptions[e]!==i&&(this.rawOptions[e]=i,this._onOptionChange.fire(e))};for(const t in this.rawOptions){const s={get:e.bind(this,t),set:i.bind(this,t)};Object.defineProperty(this.options,t,s)}}_sanitizeAndValidateOption(e,i){switch(e){case"cursorStyle":if(i||(i=t.DEFAULT_OPTIONS[e]),!function(e){return"block"===e||"underline"===e||"bar"===e}(i))throw new Error(`"${i}" is not a valid value for ${e}`);break;case"wordSeparator":i||(i=t.DEFAULT_OPTIONS[e]);break;case"fontWeight":case"fontWeightBold":if("number"==typeof i&&1<=i&&i<=1e3)break;i=o.includes(i)?i:t.DEFAULT_OPTIONS[e];break;case"cursorWidth":i=Math.floor(i);case"lineHeight":case"tabStopWidth":if(i<1)throw new Error(`${e} cannot be less than 1, value: ${i}`);break;case"minimumContrastRatio":i=Math.max(1,Math.min(21,Math.round(10*i)/10));break;case"scrollback":if((i=Math.min(i,4294967295))<0)throw new Error(`${e} cannot be less than 0, value: ${i}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(i<=0)throw new Error(`${e} cannot be less than or equal to 0, value: ${i}`);break;case"rows":case"cols":if(!i&&0!==i)throw new Error(`${e} must be numeric, value: ${i}`);break;case"windowsPty":i=i??{}}return i}}t.OptionsService=a},2660:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkService=void 0;const n=i(2585);let o=t.OscLinkService=class{constructor(e){this._bufferService=e,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(e){const t=this._bufferService.buffer;if(void 0===e.id){const i=t.addMarker(t.ybase+t.y),s={data:e,id:this._nextId++,lines:[i]};return i.onDispose((()=>this._removeMarkerFromLink(s,i))),this._dataByLinkId.set(s.id,s),s.id}const i=e,s=this._getEntryIdKey(i),r=this._entriesWithId.get(s);if(r)return this.addLineToLink(r.id,t.ybase+t.y),r.id;const n=t.addMarker(t.ybase+t.y),o={id:this._nextId++,key:this._getEntryIdKey(i),data:i,lines:[n]};return n.onDispose((()=>this._removeMarkerFromLink(o,n))),this._entriesWithId.set(o.key,o),this._dataByLinkId.set(o.id,o),o.id}addLineToLink(e,t){const i=this._dataByLinkId.get(e);if(i&&i.lines.every((e=>e.line!==t))){const e=this._bufferService.buffer.addMarker(t);i.lines.push(e),e.onDispose((()=>this._removeMarkerFromLink(i,e)))}}getLinkData(e){return this._dataByLinkId.get(e)?.data}_getEntryIdKey(e){return`${e.id};;${e.uri}`}_removeMarkerFromLink(e,t){const i=e.lines.indexOf(t);-1!==i&&(e.lines.splice(i,1),0===e.lines.length&&(void 0!==e.data.id&&this._entriesWithId.delete(e.key),this._dataByLinkId.delete(e.id)))}};t.OscLinkService=o=s([r(0,n.IBufferService)],o)},8343:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createDecorator=t.getServiceDependencies=t.serviceRegistry=void 0;const i="di$target",s="di$dependencies";t.serviceRegistry=new Map,t.getServiceDependencies=function(e){return e[s]||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);const r=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");!function(e,t,r){t[i]===t?t[s].push({id:e,index:r}):(t[s]=[{id:e,index:r}],t[i]=t)}(r,e,n)};return r.toString=()=>e,t.serviceRegistry.set(e,r),r}},2585:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IDecorationService=t.IUnicodeService=t.IOscLinkService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;const s=i(8343);var r;t.IBufferService=(0,s.createDecorator)("BufferService"),t.ICoreMouseService=(0,s.createDecorator)("CoreMouseService"),t.ICoreService=(0,s.createDecorator)("CoreService"),t.ICharsetService=(0,s.createDecorator)("CharsetService"),t.IInstantiationService=(0,s.createDecorator)("InstantiationService"),function(e){e[e.TRACE=0]="TRACE",e[e.DEBUG=1]="DEBUG",e[e.INFO=2]="INFO",e[e.WARN=3]="WARN",e[e.ERROR=4]="ERROR",e[e.OFF=5]="OFF"}(r||(t.LogLevelEnum=r={})),t.ILogService=(0,s.createDecorator)("LogService"),t.IOptionsService=(0,s.createDecorator)("OptionsService"),t.IOscLinkService=(0,s.createDecorator)("OscLinkService"),t.IUnicodeService=(0,s.createDecorator)("UnicodeService"),t.IDecorationService=(0,s.createDecorator)("DecorationService")},1480:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;const s=i(8460),r=i(225);class n{static extractShouldJoin(e){return 0!=(1&e)}static extractWidth(e){return e>>1&3}static extractCharKind(e){return e>>3}static createPropertyValue(e,t,i=!1){return(16777215&e)<<3|(3&t)<<1|(i?1:0)}constructor(){this._providers=Object.create(null),this._active="",this._onChange=new s.EventEmitter,this.onChange=this._onChange.event;const e=new r.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(e){if(!this._providers[e])throw new Error(`unknown Unicode version "${e}"`);this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)}register(e){this._providers[e.version]=e}wcwidth(e){return this._activeProvider.wcwidth(e)}getStringCellWidth(e){let t=0,i=0;const s=e.length;for(let r=0;r=s)return t+this.wcwidth(o);const i=e.charCodeAt(r);56320<=i&&i<=57343?o=1024*(o-55296)+i-56320+65536:t+=this.wcwidth(i)}const a=this.charProperties(o,i);let h=n.extractWidth(a);n.extractShouldJoin(a)&&(h-=n.extractWidth(i)),t+=h,i=a}return t}charProperties(e,t){return this._activeProvider.charProperties(e,t)}}t.UnicodeService=n}},t={};function i(s){var r=t[s];if(void 0!==r)return r.exports;var n=t[s]={exports:{}};return e[s].call(n.exports,n,n.exports,i),n.exports}var s={};return(()=>{var e=s;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;const t=i(9042),r=i(3236),n=i(844),o=i(5741),a=i(8285),h=i(7975),c=i(7090),l=["cols","rows"];class d extends n.Disposable{constructor(e){super(),this._core=this.register(new r.Terminal(e)),this._addonManager=this.register(new o.AddonManager),this._publicOptions={...this._core.options};const t=e=>this._core.options[e],i=(e,t)=>{this._checkReadonlyOptions(e),this._core.options[e]=t};for(const e in this._core.options){const s={get:t.bind(this,e),set:i.bind(this,e)};Object.defineProperty(this._publicOptions,e,s)}}_checkReadonlyOptions(e){if(l.includes(e))throw new Error(`Option "${e}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new h.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new c.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this.register(new a.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){const e=this._core.coreService.decPrivateModes;let t="none";switch(this._core.coreMouseService.activeProtocol){case"X10":t="x10";break;case"VT200":t="vt200";break;case"DRAG":t="drag";break;case"ANY":t="any"}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,wraparoundMode:e.wraparound}}get options(){return this._publicOptions}set options(e){for(const t in e)this._publicOptions[t]=e[t]}blur(){this._core.blur()}focus(){this._core.focus()}input(e,t=!0){this._core.input(e,t)}resize(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)}open(e){this._core.open(e)}attachCustomKeyEventHandler(e){this._core.attachCustomKeyEventHandler(e)}attachCustomWheelEventHandler(e){this._core.attachCustomWheelEventHandler(e)}registerLinkProvider(e){return this._core.registerLinkProvider(e)}registerCharacterJoiner(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)}deregisterCharacterJoiner(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)}registerMarker(e=0){return this._verifyIntegers(e),this._core.registerMarker(e)}registerDecoration(e){return this._checkProposedApi(),this._verifyPositiveIntegers(e.x??0,e.width??0,e.height??0),this._core.registerDecoration(e)}hasSelection(){return this._core.hasSelection()}select(e,t,i){this._verifyIntegers(e,t,i),this._core.select(e,t,i)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)}dispose(){super.dispose()}scrollLines(e){this._verifyIntegers(e),this._core.scrollLines(e)}scrollPages(e){this._verifyIntegers(e),this._core.scrollPages(e)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(e){this._verifyIntegers(e),this._core.scrollToLine(e)}clear(){this._core.clear()}write(e,t){this._core.write(e,t)}writeln(e,t){this._core.write(e),this._core.write("\r\n",t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return t}_verifyIntegers(...e){for(const t of e)if(t===1/0||isNaN(t)||t%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(const t of e)if(t&&(t===1/0||isNaN(t)||t%1!=0||t<0))throw new Error("This API only accepts positive integers")}}e.Terminal=d})(),s})())); +//# sourceMappingURL=xterm.js.map \ No newline at end of file diff --git a/examples/webpanel/webpanel_main.c b/examples/webpanel/webpanel_main.c new file mode 100644 index 000000000..7831808f3 --- /dev/null +++ b/examples/webpanel/webpanel_main.c @@ -0,0 +1,207 @@ +/**************************************************************************** + * apps/examples/webpanel/webpanel_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "netutils/thttpd.h" +#include "fsutils/mksmartfs.h" +#include "ws_terminal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SECTORSIZE 64 +#define NSECTORS(b) (((b) + SECTORSIZE - 1) / SECTORSIZE) +#define WEBPANEL_RAMDISK_MINOR 2 +#define ROMFSDEV "/dev/ram2" + +#define ROMFS_MOUNTPT "/data/tmp_romfs" +#define BINFS_MOUNTPT "/data/tmp_binfs" +#define BINFS_PREFIX "cgi-bin" +#define UNIONFS_MOUNTPT CONFIG_THTTPD_PATH + +#define SMARTFS_DEV "/dev/smart0" +#define SMARTFS_MNT "/mnt" + +/**************************************************************************** + * External References + ****************************************************************************/ + +extern const unsigned char webpanel_romfs_img[]; +extern const unsigned int webpanel_romfs_img_len; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + * + * Description: + * Initialize WebPanel filesystem mounts, start the websocket daemon, + * then launch the THTTPD server. + * + * Input Parameters: + * argc - Number of arguments. + * argv - Argument vector. + * + * Returned Value: + * EXIT_SUCCESS on success; EXIT_FAILURE on setup error. + * + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct boardioc_romdisk_s desc; + char *thttpd_argv = "thttpd"; + struct statfs check_fs; + struct statfs sfs; + int ret; + + /* Prevent duplicate startup (e.g. rcS runs again in child NSH) */ + + if (statfs(UNIONFS_MOUNTPT, &check_fs) == 0) + { + return 0; + } + + /* Register the ROMFS image as a ramdisk */ + + printf("WebPanel: Registering ROMFS ramdisk\n"); + + desc.minor = WEBPANEL_RAMDISK_MINOR; + desc.nsectors = NSECTORS(webpanel_romfs_img_len); + desc.sectsize = SECTORSIZE; + desc.image = (FAR uint8_t *)webpanel_romfs_img; + + ret = boardctl(BOARDIOC_ROMDISK, (uintptr_t)&desc); + if (ret < 0 && errno != EEXIST) + { + printf("WebPanel: ERROR romdisk_register failed: %d\n", ret); + return EXIT_FAILURE; + } + + /* Create mount point directories */ + + mkdir("/data", 0777); + mkdir(ROMFS_MOUNTPT, 0777); + mkdir(BINFS_MOUNTPT, 0777); + + /* Mount ROMFS at temp location */ + + printf("WebPanel: Mounting ROMFS at %s\n", ROMFS_MOUNTPT); + + ret = mount(ROMFSDEV, ROMFS_MOUNTPT, "romfs", MS_RDONLY, NULL); + if (ret < 0 && errno != EBUSY) + { + printf("WebPanel: ERROR mount ROMFS failed: %d\n", errno); + return EXIT_FAILURE; + } + + /* Mount BINFS at temp location (exposes built-in apps as CGI) */ + + printf("WebPanel: Mounting BINFS at %s\n", BINFS_MOUNTPT); + + ret = mount(NULL, BINFS_MOUNTPT, "binfs", MS_RDONLY, NULL); + if (ret < 0 && errno != EBUSY) + { + printf("WebPanel: ERROR mount BINFS failed: %d\n", errno); + return EXIT_FAILURE; + } + + /* Create UNIONFS: ROMFS content + BINFS under cgi-bin/ prefix */ + + printf("WebPanel: Creating UNIONFS at %s\n", UNIONFS_MOUNTPT); + + ret = mount(NULL, UNIONFS_MOUNTPT, "unionfs", 0, + "fspath1=" ROMFS_MOUNTPT ",prefix1=" + ",fspath2=" BINFS_MOUNTPT ",prefix2=" BINFS_PREFIX); + if (ret < 0 && errno != EBUSY) + { + printf("WebPanel: ERROR mount UNIONFS failed: %d\n", errno); + return EXIT_FAILURE; + } + + /* Ensure SmartFS is formatted and mounted at /mnt for user files */ + + if (statfs(SMARTFS_MNT, &sfs) != 0) + { + printf("WebPanel: SmartFS not mounted, formatting %s\n", + SMARTFS_DEV); + ret = mksmartfs(SMARTFS_DEV, 0); + if (ret < 0) + { + printf("WebPanel: WARNING mksmartfs failed: %d\n", errno); + } + else + { + mkdir(SMARTFS_MNT, 0777); + ret = mount(SMARTFS_DEV, SMARTFS_MNT, "smartfs", 0, NULL); + if (ret < 0) + { + printf("WebPanel: WARNING mount SmartFS failed: %d\n", + errno); + } + else + { + printf("WebPanel: SmartFS mounted at %s\n", SMARTFS_MNT); + } + } + } + else + { + printf("WebPanel: SmartFS already mounted at %s\n", SMARTFS_MNT); + } + + /* Start WebSocket terminal server */ + + ret = ws_terminal_start(); + if (ret < 0) + { + printf("WebPanel: WARNING WebSocket server failed to start\n"); + } + + /* Start THTTPD */ + + printf("WebPanel: Starting THTTPD (serving from %s)\n", UNIONFS_MOUNTPT); + fflush(stdout); + + thttpd_main(1, &thttpd_argv); + + printf("WebPanel: THTTPD terminated\n"); + return 0; +} diff --git a/examples/webpanel/ws_terminal.c b/examples/webpanel/ws_terminal.c new file mode 100644 index 000000000..7db8d7392 --- /dev/null +++ b/examples/webpanel/ws_terminal.c @@ -0,0 +1,350 @@ +/**************************************************************************** + * apps/examples/webpanel/ws_terminal.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ws_terminal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_EXAMPLES_WEBPANEL_WS_PORT +# define CONFIG_EXAMPLES_WEBPANEL_WS_PORT 8080 +#endif + +#ifndef CONFIG_EXAMPLES_WEBPANEL_WS_PRIORITY +# define CONFIG_EXAMPLES_WEBPANEL_WS_PRIORITY 100 +#endif + +#ifndef CONFIG_EXAMPLES_WEBPANEL_WS_STACKSIZE +# define CONFIG_EXAMPLES_WEBPANEL_WS_STACKSIZE 8192 +#endif + +#ifndef CONFIG_EXAMPLES_WEBPANEL_WS_NSH_STACKSIZE +# define CONFIG_EXAMPLES_WEBPANEL_WS_NSH_STACKSIZE 4096 +#endif + +#define WS_IOBUF_SIZE 512 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ws_session +{ + int masterfd; + pid_t nshpid; + unsigned char txbuf[LWS_PRE + WS_IOBUF_SIZE]; + size_t txlen; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int ws_spawn_nsh(int masterfd, pid_t *pid); +static int ws_callback(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, void *in, size_t len); +static int ws_daemon(int argc, FAR char *argv[]); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct lws_protocols g_protocols[] = +{ + { + "", + ws_callback, + sizeof(struct ws_session), + WS_IOBUF_SIZE, + 0, NULL, 0 + }, + LWS_PROTOCOL_LIST_TERM +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ws_spawn_nsh + * + * Description: + * Spawn an NSH instance attached to a PTY slave. + * + * Input Parameters: + * masterfd - PTY master descriptor. + * pid - Location to receive spawned task PID. + * + * Returned Value: + * Zero on success; negated errno on failure. + * + ****************************************************************************/ + +static int ws_spawn_nsh(int masterfd, pid_t *pid) +{ + char slavepath[32]; + FAR char *nshargv[] = + { + "nsh", NULL + }; + + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + struct sched_param param; + int ret; + + ret = grantpt(masterfd); + if (ret < 0) + { + return ret; + } + + ret = unlockpt(masterfd); + if (ret < 0) + { + return ret; + } + + ret = ptsname_r(masterfd, slavepath, sizeof(slavepath)); + if (ret < 0) + { + return ret; + } + + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addopen(&actions, 0, slavepath, + O_RDWR, 0); + posix_spawn_file_actions_adddup2(&actions, 0, 1); + posix_spawn_file_actions_adddup2(&actions, 0, 2); + + posix_spawnattr_init(&attr); + param.sched_priority = CONFIG_EXAMPLES_WEBPANEL_WS_PRIORITY; + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDPARAM); + posix_spawnattr_setschedparam(&attr, ¶m); + posix_spawnattr_setstacksize(&attr, + CONFIG_EXAMPLES_WEBPANEL_WS_NSH_STACKSIZE); + + ret = posix_spawn(pid, "nsh", &actions, &attr, nshargv, NULL); + + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attr); + + return ret == 0 ? 0 : -ret; +} + +/**************************************************************************** + * Name: ws_callback + * + * Description: + * libwebsockets protocol callback for the NSH terminal relay. + * + ****************************************************************************/ + +static int ws_callback(struct lws *wsi, + enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct ws_session *sess = (struct ws_session *)user; + ssize_t n; + + switch (reason) + { + case LWS_CALLBACK_ESTABLISHED: + sess->masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (sess->masterfd < 0) + { + printf("ws_terminal: failed to open PTY\n"); + return -1; + } + + sess->nshpid = -1; + if (ws_spawn_nsh(sess->masterfd, &sess->nshpid) < 0) + { + printf("ws_terminal: failed to spawn NSH\n"); + close(sess->masterfd); + sess->masterfd = -1; + return -1; + } + + /* Make PTY master non-blocking for polling */ + + fcntl(sess->masterfd, F_SETFL, + fcntl(sess->masterfd, F_GETFL) | O_NONBLOCK); + + sess->txlen = 0; + printf("ws_terminal: client connected\n"); + + lws_set_timer_usecs(wsi, 50 * LWS_USEC_PER_SEC / 1000); + break; + + case LWS_CALLBACK_RECEIVE: + if (sess->masterfd >= 0 && len > 0) + { + write(sess->masterfd, in, len); + } + + break; + + case LWS_CALLBACK_SERVER_WRITEABLE: + if (sess->masterfd < 0) + { + break; + } + + n = read(sess->masterfd, + &sess->txbuf[LWS_PRE], + WS_IOBUF_SIZE); + if (n > 0) + { + lws_write(wsi, &sess->txbuf[LWS_PRE], n, LWS_WRITE_TEXT); + lws_set_timer_usecs(wsi, 10 * LWS_USEC_PER_SEC / 1000); + } + else + { + lws_set_timer_usecs(wsi, 50 * LWS_USEC_PER_SEC / 1000); + } + + break; + + case LWS_CALLBACK_TIMER: + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_CLOSED: + printf("ws_terminal: client disconnected\n"); + if (sess->nshpid > 0) + { + task_delete(sess->nshpid); + sess->nshpid = -1; + } + + if (sess->masterfd >= 0) + { + close(sess->masterfd); + sess->masterfd = -1; + } + + break; + + default: + break; + } + + return 0; +} + +/**************************************************************************** + * Name: ws_daemon + * + * Description: + * WebSocket terminal daemon using libwebsockets. + * + ****************************************************************************/ + +static int ws_daemon(int argc, FAR char *argv[]) +{ + struct lws_context_creation_info info; + struct lws_context *context; + int n; + + lws_set_log_level(LLL_ERR | LLL_WARN, NULL); + + memset(&info, 0, sizeof(info)); + info.port = CONFIG_EXAMPLES_WEBPANEL_WS_PORT; + info.protocols = g_protocols; + info.vhost_name = "localhost"; + info.options = 0; + + context = lws_create_context(&info); + if (context == NULL) + { + printf("ws_terminal: ERROR creating lws context\n"); + return EXIT_FAILURE; + } + + printf("WebPanel: WebSocket terminal listening on port %d\n", + CONFIG_EXAMPLES_WEBPANEL_WS_PORT); + + n = 0; + while (n >= 0) + { + n = lws_service(context, 100); + } + + lws_context_destroy(context); + return EXIT_FAILURE; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ws_terminal_start + * + * Description: + * Start the websocket terminal daemon task. + * + * Input Parameters: + * None. + * + * Returned Value: + * Task PID on success; negated errno value on failure. + * + ****************************************************************************/ + +int ws_terminal_start(void) +{ + int pid; + + pid = task_create("ws_daemon", + CONFIG_EXAMPLES_WEBPANEL_WS_PRIORITY, + CONFIG_EXAMPLES_WEBPANEL_WS_STACKSIZE, + ws_daemon, NULL); + if (pid < 0) + { + printf("WebPanel: ERROR failed to start WebSocket server: %d\n", + errno); + return -errno; + } + + return pid; +} diff --git a/examples/webpanel/ws_terminal.h b/examples/webpanel/ws_terminal.h new file mode 100644 index 000000000..91cc637d9 --- /dev/null +++ b/examples/webpanel/ws_terminal.h @@ -0,0 +1,63 @@ +/**************************************************************************** + * apps/examples/webpanel/ws_terminal.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_EXAMPLES_WEBPANEL_WS_TERMINAL_H +#define __APPS_EXAMPLES_WEBPANEL_WS_TERMINAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ws_terminal_start + * + * Description: + * Start the websocket terminal daemon task. + * + * Input Parameters: + * None. + * + * Returned Value: + * Task PID on success; negated errno value on failure. + * + ****************************************************************************/ + +int ws_terminal_start(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_EXAMPLES_WEBPANEL_WS_TERMINAL_H */ diff --git a/examples/webserver/webserver_main.c b/examples/webserver/webserver_main.c index 7a16c8c45..8309f59c8 100644 --- a/examples/webserver/webserver_main.c +++ b/examples/webserver/webserver_main.c @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include diff --git a/examples/wgetjson/wgetjson_main.c b/examples/wgetjson/wgetjson_main.c index 860a0fb5b..5e2679a6b 100644 --- a/examples/wgetjson/wgetjson_main.c +++ b/examples/wgetjson/wgetjson_main.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "netutils/netlib.h" #include "netutils/webclient.h" @@ -110,9 +110,14 @@ static int wgetjson_callback(FAR char **buffer, int offset, int datend, return 0; } - if (!g_json_buff) + if (g_json_buff == NULL) { g_json_buff = malloc(len + 1); + if (g_json_buff == NULL) + { + return -ENOMEM; + } + memcpy(g_json_buff, &((*buffer)[offset]), len); g_json_buff[len] = 0; g_json_bufflen = len; @@ -253,6 +258,11 @@ static void wgetjson_json_item_scan(cJSON *item, const char *prefix) const char *string = item->string ? item->string : "(null)"; size_t len = strlen(prefix) + strlen(string) + 2; newprefix = malloc(len); + if (newprefix == NULL) + { + return; + } + snprintf(newprefix, len, "%s/%s", prefix, string); dorecurse = wgetjson_json_item_callback(newprefix, item->type, item); @@ -370,6 +380,11 @@ int main(int argc, FAR char *argv[]) } buffer = malloc(buffer_len); + if (buffer == NULL) + { + return -ENOMEM; + } + wgetjson_json_release(); struct webclient_context ctx; @@ -388,6 +403,12 @@ int main(int argc, FAR char *argv[]) post_multi_values, MULTI_POST_NDATA); post_buff = malloc(post_buff_len); + if (post_buff == NULL) + { + ret = -ENOMEM; + goto out; + } + web_posts_str(post_buff, &post_buff_len, post_multi_names, post_multi_values, MULTI_POST_NDATA); } @@ -396,21 +417,24 @@ int main(int argc, FAR char *argv[]) post_buff_len = web_post_strlen(post_single_name, post_single_value); post_buff = malloc(post_buff_len); + if (post_buff == NULL) + { + ret = -ENOMEM; + goto out; + } + web_post_str(post_buff, &post_buff_len, post_single_name, post_single_value); } - if (post_buff) - { - const char *header = "Content-Type: " - "application/x-www-form-urlencoded"; - ctx.method = "POST"; - ctx.url = url; - ctx.headers = &header; - ctx.nheaders = 1; - webclient_set_static_body(&ctx, post_buff, strlen(post_buff)); - ret = webclient_perform(&ctx); - } + const char *header = "Content-Type: " + "application/x-www-form-urlencoded"; + ctx.method = "POST"; + ctx.url = url; + ctx.headers = &header; + ctx.nheaders = 1; + webclient_set_static_body(&ctx, post_buff, strlen(post_buff)); + ret = webclient_perform(&ctx); } else { @@ -439,6 +463,7 @@ int main(int argc, FAR char *argv[]) g_has_json = false; } +out: wgetjson_json_release(); free(buffer); if (post_buff) @@ -446,5 +471,5 @@ int main(int argc, FAR char *argv[]) free(post_buff); } - return 0; + return ret < 0 ? ret : 0; } diff --git a/examples/ws2812/ws2812_main.c b/examples/ws2812/ws2812_main.c index 438e68f11..ebb16b575 100644 --- a/examples/ws2812/ws2812_main.c +++ b/examples/ws2812/ws2812_main.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include #include @@ -77,7 +77,7 @@ static void help(FAR struct neo_config_s *conf) printf("\nArguments are \"sticky\". " "For example, once the device path is\n"); - printf("specified, that path will be re-used until it is changed.\n"); + printf("specified, that path will be reused until it is changed.\n"); printf(" [-p path] selects the ws2812 device. " "Default: %s Current: %s\n", diff --git a/examples/ws2812esp32rmt/ws2812esp32rmt_main.c b/examples/ws2812esp32rmt/ws2812esp32rmt_main.c index 3f451436e..7eb8dd6fe 100644 --- a/examples/ws2812esp32rmt/ws2812esp32rmt_main.c +++ b/examples/ws2812esp32rmt/ws2812esp32rmt_main.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include #include diff --git a/examples/xedge_demo/Kconfig b/examples/xedge_demo/Kconfig index 9bc02d0fa..183f18b28 100644 --- a/examples/xedge_demo/Kconfig +++ b/examples/xedge_demo/Kconfig @@ -5,11 +5,11 @@ config EXAMPLES_XEDGE_DEMO tristate "Xedge IoT Toolkit Demo" - depends on NETUTILS_XEDGE && ALLOW_GPL_COMPONENTS + depends on NETUTILS_XEDGE && ALLOW_GPL_COMPONENTS && ENABLE_ALL_SIGNALS default n ---help--- Simple demonstration of the Xedge IoT Toolkit library. - + This example shows how to integrate and use the Xedge library in your NuttX application. Xedge provides a high-level development environment for creating IoT and industrial device applications diff --git a/examples/xmlrpc/xmlrpc_main.c b/examples/xmlrpc/xmlrpc_main.c index feeb0bf4e..f170d7fc5 100644 --- a/examples/xmlrpc/xmlrpc_main.c +++ b/examples/xmlrpc/xmlrpc_main.c @@ -52,7 +52,7 @@ * Included Files ****************************************************************************/ -#include +#include #include #include #include @@ -134,6 +134,11 @@ static int xmlrpc_getheader(FAR char *buffer, FAR char *header, FAR char *temp; int i = 0; + if (size <= 0) + { + return -1; + } + temp = strstr(buffer, header); if (temp) { @@ -150,7 +155,7 @@ static int xmlrpc_getheader(FAR char *buffer, FAR char *header, /* Copy the rest to the value parameter */ - while ((*temp != ' ') && (*temp != '\n') && (i < size)) + while ((*temp != ' ') && (*temp != '\n') && (i < size - 1)) { value[i++] = *temp++; } @@ -213,7 +218,7 @@ static void xmlrpc_handler(int fd) buffer[max] = 0; ret = xmlrpc_getheader(buffer, "Content-Length:", value, - CONFIG_EXAMPLES_XMLRPC_BUFFERSIZE); + sizeof(value)); if (ret > 0) loadlen = atoi(value); } diff --git a/examples/zerocross/Kconfig b/examples/zerocross/Kconfig index b49614731..758c4781a 100644 --- a/examples/zerocross/Kconfig +++ b/examples/zerocross/Kconfig @@ -6,7 +6,7 @@ config EXAMPLES_ZEROCROSS tristate "Zero Cross Detection example" default n - depends on SENSORS_ZEROCROSS + depends on SENSORS_ZEROCROSS && ENABLE_ALL_SIGNALS ---help--- Enable the zero cross detection example diff --git a/examples/zerocross/zerocross_main.c b/examples/zerocross/zerocross_main.c index d2f49fa5b..2b6da5b62 100644 --- a/examples/zerocross/zerocross_main.c +++ b/examples/zerocross/zerocross_main.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/fsutils/flash_eraseall/flash_eraseall.c b/fsutils/flash_eraseall/flash_eraseall.c index 2dde3a1ee..fda7b7e04 100644 --- a/fsutils/flash_eraseall/flash_eraseall.c +++ b/fsutils/flash_eraseall/flash_eraseall.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "fsutils/flash_eraseall.h" @@ -71,13 +71,14 @@ int flash_eraseall(FAR const char *driver) if (ret < 0) { errcode = errno; - ferr("ERROR: MTD ioctl(%04x) failed: %d\n", MTDIOC_BULKERASE, errcode); + ferr("ERROR: MTD ioctl(%04x) failed: %d\n", + MTDIOC_BULKERASE, errcode); ret = -errcode; } /* Close the block driver */ - close(fd); + close(fd); } return ret; diff --git a/fsutils/flashtool/CMakeLists.txt b/fsutils/flashtool/CMakeLists.txt new file mode 100644 index 000000000..338c1a394 --- /dev/null +++ b/fsutils/flashtool/CMakeLists.txt @@ -0,0 +1,24 @@ +# ############################################################################## +# apps/fsutils/flashtool/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_FSUTILS_FLASHTOOL) + nuttx_add_application(MODULE ${CONFIG_FSUTILS_FLASHTOOL} NAME flashtool SRCS + flashtool_main.c) +endif() diff --git a/fsutils/flashtool/Kconfig b/fsutils/flashtool/Kconfig new file mode 100644 index 000000000..07f89671a --- /dev/null +++ b/fsutils/flashtool/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config FSUTILS_FLASHTOOL + tristate "FLASH Tool Command" + default n + depends on MTD + depends on BCH + ---help--- + Enable support for the FLASH tool. + +if FSUTILS_FLASHTOOL + +config FSUTILS_FLASHTOOL_STACKSIZE + int "flashtool stack size" + default DEFAULT_TASK_STACKSIZE + ---help--- + This is the stack size that will be used when starting the flashtool. + +config FSUTILS_FLASHTOOL_PRIORITY + int "flashtool priority" + default 100 + ---help--- + This is the task priority that will be used when starting the flashtool. + +endif + diff --git a/fsutils/flashtool/Make.defs b/fsutils/flashtool/Make.defs new file mode 100644 index 000000000..55f2c72c9 --- /dev/null +++ b/fsutils/flashtool/Make.defs @@ -0,0 +1,24 @@ +############################################################################ +# apps/fsutils/flashtool/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_FSUTILS_FLASHTOOL),) +CONFIGURED_APPS += $(APPDIR)/fsutils/flashtool +endif + diff --git a/fsutils/flashtool/Makefile b/fsutils/flashtool/Makefile new file mode 100644 index 000000000..deac9500a --- /dev/null +++ b/fsutils/flashtool/Makefile @@ -0,0 +1,33 @@ +############################################################################ +# apps/fsutils/flashtool/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# flashtool Application + +PROGNAME = flashtool +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_FSUTILS_FLASHTOOL) + +MAINSRC = flashtool_main.c + +include $(APPDIR)/Application.mk + diff --git a/fsutils/flashtool/flashtool_main.c b/fsutils/flashtool/flashtool_main.c new file mode 100644 index 000000000..bbb07a6d2 --- /dev/null +++ b/fsutils/flashtool/flashtool_main.c @@ -0,0 +1,374 @@ +/**************************************************************************** + * apps/fsutils/flashtool/flashtool_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum opr_list +{ + HELP, DEVICE_INFO, READ_PAGE, WRITE_PAGE, ERASE_BLOCK, ERASE_ALL, + CHECK_BAD_BLOCK +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void display_help(void) +{ + printf("Usage : flashtool [OPTION [ARG]] device_name ...\n"); + printf("-h show this help statement\n"); + printf("-i display device geometry information\n"); + printf("-r read the specified pages\n"); + printf("-w write the specified pages\n"); + printf("-e erase the specified blocks\n"); + printf("-n number of pages or blocks\n"); + printf("-f file name for read or write\n"); + printf("-b check whether is bad\n"); + printf("-c erase all of the flash\n"); + printf("-d device name\n"); +} + +static void display_device_info(FAR const struct mtd_geometry_s *geo) +{ + printf("Size of one read/write page: %lu\n", geo->blocksize); + printf("Size of one erase block: %lu\n", geo->erasesize); + printf("Number of erase blocks: %lu\n", geo->neraseblocks); +} + +static int read_page(int fd, FAR const struct mtd_geometry_s *geo, + off_t start_page, size_t npages) +{ + FAR unsigned char *buf; + ssize_t ret = OK; + off_t seek; + size_t i; + + buf = malloc(geo->blocksize); + if (!buf) + { + printf("ERROR: read_page cannot allocate memory %d\n", geo->blocksize); + return ERROR; + } + + seek = geo->blocksize * start_page; + if (lseek(fd, seek, SEEK_SET) != seek) + { + printf("ERROR: Cannot seek mtd to offset %lld: %d", seek, errno); + goto oops; + } + + printf("Flash pages contents:\n"); + for (i = 0; i < npages; i++) + { + ret = read(fd, buf, geo->blocksize); + if (ret < 0) + { + printf("ERROR: Read error: %d!\n", errno); + goto oops; + } + + printf("Flash page %lld:\n", start_page + i); + lib_dumpbuffer(NULL, buf, ret); + } + +oops: + free(buf); + return ret; +} + +/* Write npages data from img to flash + * If size of img is less than npags, only write img size + */ + +static int write_page(int fd, FAR const struct mtd_geometry_s *geo, + off_t start_page, size_t npages, FAR const char *img) +{ + FAR unsigned char *buf; + off_t imglen; + off_t seek; + off_t i; + int fd_src; + int ret = OK; + + if (img == NULL) + { + printf("ERROR: Need source file path!\n"); + return ERROR; + } + + printf("Write data from %s...\n", img); + seek = geo->blocksize * start_page; + if (lseek(fd, seek, SEEK_SET) != seek) + { + printf("ERROR: cannot seek mtd to offset %lld: %d", seek, errno); + return ERROR; + } + + fd_src = open(img, O_RDONLY); + if (fd_src < 0) + { + printf("ERROR: Open file %s failed: %d\n", img, errno); + return ERROR; + } + + imglen = lseek(fd_src, 0, SEEK_END); + if (lseek(fd_src, 0, SEEK_SET) != 0) + { + close(fd_src); + return ERROR; + } + + /* Malloc a size of page for buf */ + + buf = malloc(geo->blocksize); + if (!buf) + { + printf("ERROR: write_page cannot allocate memory %d\n", + geo->blocksize); + close(fd_src); + return ERROR; + } + + for (i = start_page; i < start_page + npages; i++) + { + ret = read(fd_src, buf, + (imglen < geo->blocksize) ? imglen : geo->blocksize); + if (ret < 0) + { + printf("ERROR: read source file error: %d!\n", errno); + goto oops; + } + + ret = write(fd, buf, geo->blocksize); + if (ret < 0) + { + printf("ERROR: write error: %d!\n", errno); + goto oops; + } + + imglen -= geo->blocksize; + if (imglen <= 0) + { + break; + } + } + +oops: + close(fd_src); + free(buf); + return ret; +} + +static int erase_block(int fd, off_t start_block, + size_t nblocks) +{ + struct mtd_erase_s erase; + int ret; + + printf("Erase block from %lld to %lld!\n", start_block, + start_block + nblocks -1); + erase.startblock = start_block; + erase.nblocks = nblocks; + ret = ioctl(fd, MTDIOC_ERASESECTORS, &erase); + if (ret < 0) + { + printf("ERROR: Erase failed: %d!\n", errno); + } + + return ret; +} + +static int erase_all(int fd) +{ + if (ioctl(fd, MTDIOC_BULKERASE, 0) < 0) + { + printf("ERROR: MTD ioctl(MTDIOC_BULKERASE) failed: %d\n", errno); + return ERROR; + } + + return OK; +} + +static int check_bad_block(int fd, FAR const struct mtd_geometry_s *geo) +{ + struct mtd_bad_block_s bad_block; + int flag = 0; + int ret; + int i; + + printf("Check bad blocks in flash......\n"); + for (i = 0; i < geo->neraseblocks; i++) + { + bad_block.block_num = i; + ret = ioctl(fd, MTDIOC_ISBAD, &bad_block); + if (ret != OK) + { + printf("ERROR: MTDIOC_ISBAD failed: %d!\n", errno); + return ERROR; + } + else + { + if (bad_block.bad_flag) + { + printf("Block %d is bad!\n", i); + flag = 1; + } + } + } + + if (flag == 0) + { + printf("No bad blocks!\n"); + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR char *mtddev = NULL; + FAR char *img = NULL; + struct mtd_geometry_s geo; + enum opr_list opr = HELP; + off_t npages = 1; + off_t start; + int option; + int mode = O_RDONLY; + int fd; + int ret; + + while ((option = getopt(argc, argv, ":hir:w:e:n:f:bcd:")) != -1) + { + switch (option) + { + case 'h': + display_help(); + exit(0); + case 'i': + opr = DEVICE_INFO; + break; + case 'r': + opr = READ_PAGE; + start = strtoul(optarg, NULL, 0); + break; + case 'w': + opr = WRITE_PAGE; + mode = O_RDWR; + start = strtoul(optarg, NULL, 0); + break; + case 'e': + opr = ERASE_BLOCK; + start = strtoul(optarg, NULL, 0); + break; + case 'n': + npages = strtoul(optarg, NULL, 0); + break; + case 'f': + img = optarg; + break; + case 'b': + opr = CHECK_BAD_BLOCK; + break; + case 'c': + opr = ERASE_ALL; + break; + case 'd': + mtddev = optarg; + break; + case ':': + printf("Error: Missing required argument\n"); + exit(1); + case '?': + printf("Error: Unrecognized option\n"); + exit(1); + } + } + + if (mtddev == NULL) + { + printf("Error: Device name is required!\n"); + return -1; + } + + fd = open(mtddev, mode, 0777); + if (fd < 0) + { + printf("ERROR: Failed to open '%s': %d\n", mtddev, errno); + return -1; + } + + ret = ioctl(fd, MTDIOC_GEOMETRY, &geo); + if (ret < 0) + { + printf("ERROR: Ioctl MTDIOC_GEOMETRY failed: %d\n", errno); + goto oops; + } + + switch (opr) + { + case DEVICE_INFO: + display_device_info(&geo); + break; + case READ_PAGE: + ret = read_page(fd, &geo, start, npages); + break; + case WRITE_PAGE: + ret = write_page(fd, &geo, start, npages, img); + break; + case ERASE_BLOCK: + ret = erase_block(fd, start, npages); + break; + case ERASE_ALL: + ret = erase_all(fd); + break; + case CHECK_BAD_BLOCK: + ret = check_bad_block(fd, &geo); + break; + default: + break; + } + +oops: + close(fd); + return ret; +} diff --git a/fsutils/inifile/inifile.c b/fsutils/inifile/inifile.c index 16c9da6af..8bb52326b 100644 --- a/fsutils/inifile/inifile.c +++ b/fsutils/inifile/inifile.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "fsutils/inifile.h" diff --git a/fsutils/ipcfg/ipcfg.c b/fsutils/ipcfg/ipcfg.c index c1d4f9f9d..9f6deab5b 100644 --- a/fsutils/ipcfg/ipcfg.c +++ b/fsutils/ipcfg/ipcfg.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "fsutils/ipcfg.h" #include "ipcfg.h" diff --git a/fsutils/ipcfg/ipcfg_binary.c b/fsutils/ipcfg/ipcfg_binary.c index 0247a4e88..c12a7ada4 100644 --- a/fsutils/ipcfg/ipcfg_binary.c +++ b/fsutils/ipcfg/ipcfg_binary.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "fsutils/ipcfg.h" #include "ipcfg.h" @@ -154,7 +154,7 @@ static int ipcfg_read_binary(int fd, FAR void *buffer, size_t nbytes) * Input Parameters: * fd - File descriptor of the open file to write to * buffer - Location to write to - * nbytes - Number of bytes to wrtie + * nbytes - Number of bytes to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -265,7 +265,7 @@ static int ipcfg_find_binary(int fd, sa_family_t af) * * Input Parameters: * path - The full path to the IP configuration file - * ipv4cfg - Location to read IPv4 configration to + * ipv4cfg - Location to read IPv4 configuration to * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -316,7 +316,7 @@ errout_with_fd: * * Input Parameters: * path - The full path to the IP configuration file - * ipv6cfg - Location to read IPv6 configration to + * ipv6cfg - Location to read IPv6 configuration to * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -367,7 +367,7 @@ errout_with_fd: * * Input Parameters: * path - The full path to the IP configuration file - * ipv4cfg - The IPv4 configration to write + * ipv4cfg - The IPv4 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -474,7 +474,7 @@ errout_with_fd: * * Input Parameters: * path - The full path to the IP configuration file - * ipv6cfg - The IPv6 configration to write + * ipv6cfg - The IPv6 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any diff --git a/fsutils/ipcfg/ipcfg_text.c b/fsutils/ipcfg/ipcfg_text.c index 738bde088..f0e64f8e8 100644 --- a/fsutils/ipcfg/ipcfg_text.c +++ b/fsutils/ipcfg/ipcfg_text.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include @@ -238,7 +238,7 @@ static int ipcfg_put_ipv6addr(FAR FILE *stream, FAR const char *variable, * * Input Parameters: * stream - Stream of the open file to write to - * ipv4cfg - The IPv4 configration to write + * ipv4cfg - The IPv4 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -278,7 +278,7 @@ static int ipcfg_write_ipv4(FAR FILE *stream, * * Input Parameters: * stream - Stream of the open file to write to - * ipv6cfg - The IPv6 configration to write + * ipv6cfg - The IPv6 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -322,7 +322,7 @@ static int ipcfg_write_ipv6(FAR FILE *stream, * Input Parameters: * path - The full path to the IP configuration file * netdev - Network device name string - * ipv4cfg - Location to read IPv4 configration to + * ipv4cfg - Location to read IPv4 configuration to * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -473,7 +473,7 @@ int ipcfg_read_text_ipv4(FAR const char *path, FAR const char *netdev, * Input Parameters: * path - The full path to the IP configuration file * netdev - Network device name string - * ipv6cfg - Location to read IPv6 configration to + * ipv6cfg - Location to read IPv6 configuration to * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -639,7 +639,7 @@ int ipcfg_read_text_ipv6(FAR const char *path, FAR const char *netdev, * Input Parameters: * path - The full path to the IP configuration file * netdev - Network device name string - * ipv4cfg - The IPv4 configration to write + * ipv4cfg - The IPv4 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any @@ -726,7 +726,7 @@ int ipcfg_write_text_ipv4(FAR const char *path, FAR const char *netdev, * Input Parameters: * path - The full path to the IP configuration file * netdev - Network device name string - * ipv6cfg - The IPv6 configration to write + * ipv6cfg - The IPv6 configuration to write * * Returned Value: * Zero is returned on success; a negated errno value is returned on any diff --git a/fsutils/mkfatfs/configfat.c b/fsutils/mkfatfs/configfat.c index cb6d41da4..20f54d5af 100644 --- a/fsutils/mkfatfs/configfat.c +++ b/fsutils/mkfatfs/configfat.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include "fsutils/mkfatfs.h" @@ -121,13 +121,8 @@ static inline uint32_t mkfatfs_nfatsect12(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var, uint32_t navailsects) { -#ifdef CONFIG_HAVE_LONG_LONG uint64_t denom; uint64_t number; -#else - uint32_t denom; - uint32_t number; -#endif /* For FAT12, the cluster number is held in a 12-bit number or 1.5 bytes * per cluster reference. So each FAT sector will hold sectorsize/1.5 @@ -156,25 +151,12 @@ mkfatfs_nfatsect12(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var, * navailsects > 0x55555555 - 2 * clustersize */ -#ifndef CONFIG_HAVE_LONG_LONG - if (navailsects <= (0x55555555 - (1 << (fmt->ff_clustshift + 1)))) - { -#endif - - denom = (fmt->ff_nfats << 1) + fmt->ff_nfats - + (var->fv_sectorsize << (fmt->ff_clustshift + 1)); - number = (navailsects << 1) + navailsects - + (1 << (fmt->ff_clustshift + 2)) - + (1 << (fmt->ff_clustshift + 1)); - return (uint32_t)((number + denom - 1) / denom); - -#ifndef CONFIG_HAVE_LONG_LONG - } - else - { - return 0; - } -#endif + denom = (fmt->ff_nfats << 1) + fmt->ff_nfats + + (var->fv_sectorsize << (fmt->ff_clustshift + 1)); + number = (navailsects << 1) + navailsects + + (1 << (fmt->ff_clustshift + 2)) + + (1 << (fmt->ff_clustshift + 1)); + return (uint32_t)((number + denom - 1) / denom); } /**************************************************************************** @@ -201,13 +183,8 @@ static inline uint32_t mkfatfs_nfatsect16(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var, uint32_t navailsects) { -#ifdef CONFIG_HAVE_LONG_LONG uint64_t denom; uint64_t number; -#else - uint32_t denom; - uint32_t number; -#endif /* For FAT16, the cluster number is held in a 16-bit number or 2 bytes per * cluster reference. So each FAT sector will hold sectorsize/2 cluster @@ -270,13 +247,8 @@ static inline uint32_t mkfatfs_nfatsect32(FAR struct fat_format_s *fmt, FAR struct fat_var_s *var, uint32_t navailsects) { -#ifdef CONFIG_HAVE_LONG_LONG uint64_t denom; uint64_t number; -#else - uint32_t denom; - uint32_t number; -#endif /* For FAT32, the cluster number is held in a 32-bit number or 4 bytes per * cluster reference. So each FAT sector will hold sectorsize/4 cluster diff --git a/fsutils/mkfatfs/mkfatfs.c b/fsutils/mkfatfs/mkfatfs.c index 2bf7e56b8..629d6bb76 100644 --- a/fsutils/mkfatfs/mkfatfs.c +++ b/fsutils/mkfatfs/mkfatfs.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -92,7 +92,7 @@ static uint32_t fat_systime2fattime(void) { /* Break done the seconds in date and time units */ - if (gmtime_r((FAR const time_t *)&ts.tv_sec, &tm) != NULL) + if (gmtime_r(&ts.tv_sec, &tm) != NULL) { /* FAT can only represent dates since 1980. struct tm can * represent dates since 1900. diff --git a/fsutils/mkfatfs/writefat.c b/fsutils/mkfatfs/writefat.c index d88e0bbb7..01837d05c 100644 --- a/fsutils/mkfatfs/writefat.c +++ b/fsutils/mkfatfs/writefat.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -206,7 +206,7 @@ static inline void mkfatfs_initmbr(FAR struct fat_format_s *fmt, /* The following fields are only valid for FAT12/16 */ - /* 1@36: Drive number for MSDOS bootstrap -- left zero */ + /* 1@36: Drive number for MS-DOS bootstrap -- left zero */ /* 1@37: Reserved (zero) */ @@ -268,7 +268,7 @@ static inline void mkfatfs_initmbr(FAR struct fat_format_s *fmt, /* 12@52: Reserved (zero) */ - /* 1@64: Drive number for MSDOS bootstrap -- left zero */ + /* 1@64: Drive number for MS-DOS bootstrap -- left zero */ /* 1@65: Reserved (zero) */ diff --git a/fsutils/mkgpt/mkgpt.c b/fsutils/mkgpt/mkgpt.c index e7621fab0..994902067 100644 --- a/fsutils/mkgpt/mkgpt.c +++ b/fsutils/mkgpt/mkgpt.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/fsutils/passwd/CMakeLists.txt b/fsutils/passwd/CMakeLists.txt index e2e0ebce4..16caf1258 100644 --- a/fsutils/passwd/CMakeLists.txt +++ b/fsutils/passwd/CMakeLists.txt @@ -23,7 +23,8 @@ if(CONFIG_FSUTILS_PASSWD) set(CSRCS) - list(APPEND CSRCS passwd_verify.c passwd_find.c passwd_encrypt.c) + list(APPEND CSRCS passwd_verify.c passwd_find.c passwd_encrypt.c + passwd_pbkdf2.c) if(NOT CONFIG_FSUTILS_PASSWD_READONLY) list( diff --git a/fsutils/passwd/Kconfig b/fsutils/passwd/Kconfig index 4f40349fc..9d6ba3944 100644 --- a/fsutils/passwd/Kconfig +++ b/fsutils/passwd/Kconfig @@ -6,8 +6,22 @@ config FSUTILS_PASSWD bool "Password file support" default n + depends on CRYPTO_CRYPTODEV + depends on CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO || CRYPTO_CRYPTODEV_HARDWARE + depends on NETUTILS_CODECS + depends on CODECS_BASE64 ---help--- - Enables support for /etc/passwd file access routines + Enables support for /etc/passwd file access routines. + + Requires CONFIG_CRYPTO=y, CRYPTO_CRYPTODEV (and + ALLOW_BSD_COMPONENTS), a cryptodev backend + (CONFIG_CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO or + CONFIG_CRYPTO_CRYPTODEV_HARDWARE), plus NETUTILS_CODECS/CODECS_BASE64 + for base64url hash encoding. + + NOTE: Password hashes use PBKDF2-HMAC-SHA256 (modular crypt format). + Existing TEA-encrypted /etc/passwd entries are NOT compatible and + must be regenerated. if FSUTILS_PASSWD @@ -23,20 +37,14 @@ config FSUTILS_PASSWD_IOBUFFER_SIZE int "Allocated I/O buffer size" default 512 -config FSUTILS_PASSWD_KEY1 - hex "Encryption key value 1" - default 0x12345678 - -config FSUTILS_PASSWD_KEY2 - hex "Encryption key value 2" - default 0x9abcdef0 - -config FSUTILS_PASSWD_KEY3 - hex "Encryption key value 3" - default 0x12345678 - -config FSUTILS_PASSWD_KEY4 - hex "Encryption key value 4" - default 0x9abcdef0 +config FSUTILS_PASSWD_PBKDF2_ITERATIONS + int "Default PBKDF2 iteration count for new passwords" + default 10000 + range 1000 200000 + ---help--- + Number of PBKDF2-HMAC-SHA256 iterations applied when setting a new + password. Higher values slow brute-force attacks but also increase + login latency on low-MHz MCUs. The iteration count is stored in each + hash string, so changing this option only affects newly-set passwords. endif # FSUTILS_PASSWD diff --git a/fsutils/passwd/Makefile b/fsutils/passwd/Makefile index b11148ffd..d15b1c6db 100644 --- a/fsutils/passwd/Makefile +++ b/fsutils/passwd/Makefile @@ -26,6 +26,7 @@ include $(APPDIR)/Make.defs ifeq ($(CONFIG_FSUTILS_PASSWD),y) CSRCS += passwd_verify.c passwd_find.c passwd_encrypt.c +CSRCS += passwd_pbkdf2.c ifneq ($(CONFIG_FSUTILS_PASSWD_READONLY),y) CSRCS += passwd_adduser.c passwd_deluser.c passwd_update.c passwd_append.c CSRCS += passwd_delete.c passwd_lock.c diff --git a/fsutils/passwd/passwd.h b/fsutils/passwd/passwd.h index 6dd1785e3..e617cef37 100644 --- a/fsutils/passwd/passwd.h +++ b/fsutils/passwd/passwd.h @@ -36,17 +36,18 @@ * Pre-processor Definitions ****************************************************************************/ -#define MAX_ENCRYPTED 48 /* Maximum size of a password (encrypted, ASCII) */ -#define MAX_USERNAME 48 /* Maximum size of a username */ -#define MAX_RECORD (MAX_USERNAME + MAX_ENCRYPTED + 1) +/* MCF format: $pbkdf2-sha256$$$ */ -/* The TEA incryption algorithm generates 8 bytes of encrypted data per - * 8 bytes of unencrypted data. The encrypted presentation is base64 which - * is 8-bits of ASCII for each 6 bits of data. That is a 3-to-4 expansion - * ratio. MAX_ENCRYPTED must be a multiple of 8 bytes. - */ +#define PASSWD_MCF_PREFIX "$pbkdf2-sha256$" +#define PASSWD_SALT_BYTES 16 +#define PASSWD_HASH_BYTES 32 -#define MAX_PASSWORD (3 * MAX_ENCRYPTED / 4) +/* 15 + 6 + 1 + 22 + 1 + 43 = 88 bytes for default parameters */ + +#define MAX_ENCRYPTED 96 +#define MAX_USERNAME 48 +#define MAX_RECORD (MAX_USERNAME + MAX_ENCRYPTED + 1) +#define MAX_PASSWORD 256 /**************************************************************************** * Public Types @@ -55,7 +56,7 @@ struct passwd_s { off_t offset; /* File offset (start of record) */ - char encrypted[MAX_ENCRYPTED + 1]; /* Encrtyped password in file */ + char encrypted[MAX_ENCRYPTED + 1]; /* Password hash in file */ }; /**************************************************************************** @@ -94,10 +95,11 @@ void passwd_unlock(FAR sem_t *sem); * Name: passwd_encrypt * * Description: - * Encrypt a password. Currently uses the Tiny Encryption Algorithm. + * Hash a password with PBKDF2-HMAC-SHA256 and encode the result in modular + * crypt format for storage in /etc/passwd. * * Input Parameters: - * password -- The password string to be encrypted + * password -- The password string to be hashed * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned on diff --git a/fsutils/passwd/passwd_append.c b/fsutils/passwd/passwd_append.c index be77c9aed..125a4cfe6 100644 --- a/fsutils/passwd/passwd_append.c +++ b/fsutils/passwd/passwd_append.c @@ -69,7 +69,7 @@ int passwd_append(FAR const char *username, FAR const char *password) { int errcode = errno; DEBUGASSERT(errcode > 0); - return errcode; + return -errcode; } /* The format of the password file is: diff --git a/fsutils/passwd/passwd_encrypt.c b/fsutils/passwd/passwd_encrypt.c index 50e516f77..c1e8b941e 100644 --- a/fsutils/passwd/passwd_encrypt.c +++ b/fsutils/passwd/passwd_encrypt.c @@ -25,85 +25,145 @@ ****************************************************************************/ #include +#include -#include +#include +#include +#include #include #include -#include +#include +#include -#include +#include #include "passwd.h" +#include "passwd_pbkdf2.h" /**************************************************************************** - * Private Data + * Pre-processor Definitions ****************************************************************************/ -/* This should be better protected */ +#ifndef CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS +# define CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS 10000 +#endif -static uint32_t g_tea_key[4] = -{ - CONFIG_FSUTILS_PASSWD_KEY1, - CONFIG_FSUTILS_PASSWD_KEY2, - CONFIG_FSUTILS_PASSWD_KEY3, - CONFIG_FSUTILS_PASSWD_KEY4 -}; +#define PASSWD_MIN_LENGTH 8 + +static const char g_password_specials[] = + "!@#$%^&*()_+-=[]{}|;:,.<>?"; /**************************************************************************** * Private Functions ****************************************************************************/ +static int validate_password_complexity(FAR const char *password) +{ + FAR const char *p; + size_t passlen; + int has_upper = 0; + int has_lower = 0; + int has_digit = 0; + int has_special = 0; + + passlen = strlen(password); + if (passlen < PASSWD_MIN_LENGTH) + { + _err("ERROR: password must be at least %d characters\n", + PASSWD_MIN_LENGTH); + return -EINVAL; + } + + if (passlen > MAX_PASSWORD) + { + _err("ERROR: password must be at most %d characters\n", MAX_PASSWORD); + return -EINVAL; + } + + for (p = password; *p != '\0'; p++) + { + if (isupper((unsigned char)*p)) + { + has_upper = 1; + } + else if (islower((unsigned char)*p)) + { + has_lower = 1; + } + else if (isdigit((unsigned char)*p)) + { + has_digit = 1; + } + else if (strchr(g_password_specials, *p) != NULL) + { + has_special = 1; + } + } + + if (!has_upper) + { + _err("ERROR: password must contain at least one uppercase " + "letter (A-Z)\n"); + return -EINVAL; + } + + if (!has_lower) + { + _err("ERROR: password must contain at least one lowercase " + "letter (a-z)\n"); + return -EINVAL; + } + + if (!has_digit) + { + _err("ERROR: password must contain at least one digit (0-9)\n"); + return -EINVAL; + } + + if (!has_special) + { + _err("ERROR: password must contain at least one special " + "character (!@#$%%^&*()_+-=[]{}|;:,.<>?)\n"); + return -EINVAL; + } + + return OK; +} + /**************************************************************************** - * Name: passwd_base64 + * Name: passwd_fill_random * * Description: - * Encode a 5 bit value as a base64 character. - * - * Input Parameters: - * binary - 5 bit value - * - * Returned Value: - * The ASCII base64 character. Must not return the field delimiter ':' + * Fill a buffer with random bytes using getrandom() or /dev/urandom. * ****************************************************************************/ -static char passwd_base64(uint8_t binary) +static int passwd_fill_random(FAR uint8_t *buf, size_t len) { - /* 0-26 -> 'A'-'Z' */ + ssize_t nread; + int fd; - binary &= 63; - if (binary < 26) + nread = getrandom(buf, len, 0); + if (nread == (ssize_t)len) { - return 'A' + binary; + return OK; } - /* 26-51 -> 'a'-'z' */ - - binary -= 26; - if (binary < 26) + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { - return 'a' + binary; + return -errno; } - /* 52->61 -> '0'-'9' */ + nread = read(fd, buf, len); + close(fd); - binary -= 26; - if (binary < 10) + if (nread != (ssize_t)len) { - return '0' + binary; + return nread < 0 ? -errno : -EIO; } - /* 62 -> '+' */ - - binary -= 10; - if (binary == 0) - { - return '+'; - } - - /* 63 -> '/' */ - - return '/'; + return OK; } /**************************************************************************** @@ -114,109 +174,66 @@ static char passwd_base64(uint8_t binary) * Name: passwd_encrypt * * Description: - * Encrypt a password. Currently uses the Tiny Encryption Algorithm. - * - * Input Parameters: - * password -- The password string to be encrypted - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * failure. + * Hash a password with PBKDF2-HMAC-SHA256 and encode as modular crypt + * format: $pbkdf2-sha256$$$ * ****************************************************************************/ int passwd_encrypt(FAR const char *password, char encrypted[MAX_ENCRYPTED + 1]) { - union - { - char b[8]; - uint16_t h[4]; - uint32_t l[2]; - } value; + uint8_t salt[PASSWD_SALT_BYTES]; + uint8_t hash[PASSWD_HASH_BYTES]; + char salt_b64[32]; + char hash_b64[48]; + size_t passlen; + int ret; - FAR const char *src; - FAR char *bptr; - FAR char *dest; - uint32_t tmp; - uint8_t remainder; - int remaining; - int gulpsize; - int nbits; - int i; + ret = validate_password_complexity(password); + if (ret < 0) + { + return ret; + } - /* How long is the password? */ + passlen = strlen(password); - remaining = strlen(password); - if (remaining > MAX_PASSWORD) + ret = passwd_fill_random(salt, sizeof(salt)); + if (ret < 0) + { + return ret; + } + + ret = passwd_pbkdf2_hmac_sha256((FAR const uint8_t *)password, passlen, + salt, sizeof(salt), + CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS, + hash, sizeof(hash)); + if (ret < 0) + { + return ret; + } + + ret = base64url_encode(salt, sizeof(salt), salt_b64, + sizeof(salt_b64)); + if (ret < 0) + { + return ret; + } + + ret = base64url_encode(hash, sizeof(hash), hash_b64, + sizeof(hash_b64)); + if (ret < 0) + { + return ret; + } + + ret = snprintf(encrypted, MAX_ENCRYPTED + 1, + PASSWD_MCF_PREFIX "%u$%s$%s", + CONFIG_FSUTILS_PASSWD_PBKDF2_ITERATIONS, + salt_b64, hash_b64); + if (ret < 0 || (size_t)ret > MAX_ENCRYPTED) { return -E2BIG; } - /* Convert the password in 8-byte TEA cycles */ - - src = password; - dest = encrypted; - *dest = '\0'; - - remainder = 0; - nbits = 0; - - for (; remaining > 0; remaining -= gulpsize) - { - /* Copy bytes */ - - gulpsize = sizeof(value.b); - if (gulpsize > remaining) - { - gulpsize = remaining; - } - - bptr = value.b; - for (i = 0; i < gulpsize; i++) - { - *bptr++ = *src++; - } - - /* Pad with spaces if necessary */ - - for (; i < sizeof(value.b); i++) - { - *bptr++ = ' '; - } - - /* Perform the conversion for this cycle */ - - tea_encrypt(value.l, g_tea_key); - - /* Generate the base64 output string from this cycle */ - - tmp = remainder; - - for (i = 0; i < 4; i++) - { - tmp = (uint32_t)value.h[i] << nbits | tmp; - nbits += 16; - - while (nbits >= 6) - { - *dest++ = passwd_base64((uint8_t)(tmp & 0x3f)); - tmp >>= 6; - nbits -= 6; - } - } - - remainder = (uint8_t)tmp; - *dest = '\0'; - } - - /* Handle any remainder */ - - if (nbits > 0) - { - *dest++ = passwd_base64(remainder); - *dest = '\0'; - } - return OK; } diff --git a/fsutils/passwd/passwd_pbkdf2.c b/fsutils/passwd/passwd_pbkdf2.c new file mode 100644 index 000000000..e070387ab --- /dev/null +++ b/fsutils/passwd/passwd_pbkdf2.c @@ -0,0 +1,114 @@ +/**************************************************************************** + * apps/fsutils/passwd/passwd_pbkdf2.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "passwd_pbkdf2.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int passwd_pbkdf2_hmac_sha256(FAR const uint8_t *pass, size_t passlen, + FAR const uint8_t *salt, size_t saltlen, + uint32_t iterations, + FAR uint8_t *out, size_t outlen) +{ + struct session_op session; + struct crypt_op cryp; + int cryptodev_fd = -1; + int fd = -1; + int ret = 0; + + if (pass == NULL || salt == NULL || out == NULL || passlen == 0 || + saltlen == 0 || iterations == 0 || outlen == 0) + { + return -EINVAL; + } + + fd = open("/dev/crypto", O_RDWR, 0); + if (fd < 0) + { + return -errno; + } + + if (ioctl(fd, CRIOGET, &cryptodev_fd) < 0) + { + ret = -errno; + goto errout; + } + + memset(&session, 0, sizeof(session)); + session.cipher = 0; + session.mac = CRYPTO_PBKDF2_HMAC_SHA256; + session.mackey = (caddr_t)pass; + session.mackeylen = passlen; + + if (ioctl(cryptodev_fd, CIOCGSESSION, &session) < 0) + { + ret = -errno; + goto errout; + } + + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = session.ses; + cryp.op = COP_ENCRYPT; + cryp.src = (caddr_t)salt; + cryp.len = saltlen; + cryp.mac = (caddr_t)out; + cryp.iterations = iterations; + cryp.olen = outlen; + + if (ioctl(cryptodev_fd, CIOCCRYPT, &cryp) < 0) + { + ret = -errno; + goto errout_with_session; + } + +errout_with_session: + ioctl(cryptodev_fd, CIOCFSESSION, &session.ses); + +errout: + if (cryptodev_fd >= 0) + { + close(cryptodev_fd); + } + + if (fd >= 0) + { + close(fd); + } + + return ret; +} diff --git a/fsutils/passwd/passwd_pbkdf2.h b/fsutils/passwd/passwd_pbkdf2.h new file mode 100644 index 000000000..1a66109fa --- /dev/null +++ b/fsutils/passwd/passwd_pbkdf2.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * apps/fsutils/passwd/passwd_pbkdf2.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H +#define __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int passwd_pbkdf2_hmac_sha256(FAR const uint8_t *pass, size_t passlen, + FAR const uint8_t *salt, size_t saltlen, + uint32_t iterations, + FAR uint8_t *out, size_t outlen); + +#endif /* __APPS_FSUTILS_PASSWD_PASSWD_PBKDF2_H */ diff --git a/fsutils/passwd/passwd_verify.c b/fsutils/passwd/passwd_verify.c index 2c63f3759..fef8da6df 100644 --- a/fsutils/passwd/passwd_verify.c +++ b/fsutils/passwd/passwd_verify.c @@ -24,11 +24,105 @@ * Included Files ****************************************************************************/ -#include -#include +#include + +#include +#include +#include +#include + +#include +#include -#include "fsutils/passwd.h" #include "passwd.h" +#include "passwd_pbkdf2.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: passwd_verify_hash + * + * Description: + * Verify a password against a stored PBKDF2-SHA256 modular crypt hash. + * Rejects any hash not in $pbkdf2-sha256$ format. + * + * Returned Value: + * 0 on match, -1 on mismatch or parse error. + * + ****************************************************************************/ + +static int passwd_verify_hash(FAR const char *stored, + FAR const char *password) +{ + FAR const char *p; + FAR const char *salt_b64; + FAR const char *hash_b64; + char *endptr; + uint8_t salt[PASSWD_SALT_BYTES]; + uint8_t expected[PASSWD_HASH_BYTES]; + uint8_t actual[PASSWD_HASH_BYTES]; + size_t saltlen; + size_t hashlen; + size_t passlen; + unsigned long iterations; + int ret; + + if (strncmp(stored, PASSWD_MCF_PREFIX, strlen(PASSWD_MCF_PREFIX)) != 0) + { + return -1; + } + + p = stored + strlen(PASSWD_MCF_PREFIX); + iterations = strtoul(p, &endptr, 10); + if (endptr == p || *endptr != '$' || iterations < 1 || + iterations > 200000) + { + return -1; + } + + salt_b64 = endptr + 1; + hash_b64 = strchr(salt_b64, '$'); + if (hash_b64 == NULL) + { + return -1; + } + + ret = base64url_decode(salt_b64, salt, sizeof(salt), &saltlen); + if (ret < 0 || saltlen == 0) + { + return -1; + } + + ret = base64url_decode(hash_b64 + 1, expected, sizeof(expected), + &hashlen); + if (ret < 0 || hashlen != PASSWD_HASH_BYTES) + { + return -1; + } + + passlen = strlen(password); + if (passlen == 0 || passlen > MAX_PASSWORD) + { + return -1; + } + + ret = passwd_pbkdf2_hmac_sha256((FAR const uint8_t *)password, passlen, + salt, saltlen, (uint32_t)iterations, + actual, sizeof(actual)); + if (ret < 0) + { + return -1; + } + + if (timingsafe_bcmp(actual, expected, sizeof(expected)) != 0) + { + return -1; + } + + return 0; +} /**************************************************************************** * Public Functions @@ -39,53 +133,33 @@ * * Description: * Return true if the username exists in the /etc/passwd file and if the - * password matches the user password in that failed. - * - * Input Parameters: + * password matches the user password in that file. * * Returned Value: - * One (1) is returned on success match, Zero (OK) is returned on an - * unsuccessful match; a negated errno value is returned on any other - * failure. + * Zero (0) is returned on a successful match, -1 on mismatch or invalid + * hash format; a negated errno value is returned on other failures. * ****************************************************************************/ int passwd_verify(FAR const char *username, FAR const char *password) { struct passwd_s passwd; - char encrypted[MAX_ENCRYPTED + 1]; PASSWD_SEM_DECL(sem); int ret; - /* Get exclusive access to the /etc/passwd file */ - ret = passwd_lock(&sem); if (ret < 0) { return ret; } - /* Verify that the username exists in the /etc/passwd file */ - ret = passwd_find(username, &passwd); if (ret < 0) { - /* The username does not exist in the /etc/passwd file */ - goto errout_with_lock; } - /* Encrypt the provided password */ - - ret = passwd_encrypt(password, encrypted); - if (ret < 0) - { - goto errout_with_lock; - } - - /* Compare the encrypted passwords */ - - ret = (strcmp(passwd.encrypted, encrypted) == 0) ? 1 : 0; + ret = passwd_verify_hash(passwd.encrypted, password); errout_with_lock: passwd_unlock(sem); diff --git a/games/NXDoom/.gitignore b/games/NXDoom/.gitignore new file mode 100644 index 000000000..58cf415e6 --- /dev/null +++ b/games/NXDoom/.gitignore @@ -0,0 +1,70 @@ +CMDLINE +INSTALL +Makefile.in +Make.dep +TAGS +aclocal.m4 +autom4te.cache +autotools +bin +config.hin +config.log +config.status +configure +lib +obj +rpm.spec +stamp-h +stamp-h.in +stamp-h1 +tags +\#*\# +DOOM*.png +HTIC*.png +HEXEN*.png +STRIFE*.png +DOOM*.pcx +HTIC*.pcx +HEXEN*.pcx +STRIFE*.pcx + +# CMake-specific ignores +/.vs +/build* +/CMakeSettings.json +/out + +# These are the default patterns globally ignored by Subversion: +.depend +.built +*.o +*.gcno +*.gcda +*.lo +*.la +*.al +.libs +*.so +*.so.[0-9]* +*.a +*.pyc +*.pyo +*.rej +*~ +.#* +.*.swp +.DS_store + +# Ignore GNU Global tags and html files +GPATH +GRTAGS +GTAGS +/HTML/ + +# VSCode settings + +/.vscode/ + +# clangd settings +/.cache/clangd +compile_commands.json diff --git a/games/NXDoom/AUTHORS b/games/NXDoom/AUTHORS new file mode 100644 index 000000000..6324caae4 --- /dev/null +++ b/games/NXDoom/AUTHORS @@ -0,0 +1,7 @@ +Simon Howard +James Haley +Samuel Villarreal +Fabian Greffrath +Jonathan Dowland +Alexey Khokholov +Turo Lamminen diff --git a/games/NXDoom/COPYING.md b/games/NXDoom/COPYING.md new file mode 100644 index 000000000..28fbecabf --- /dev/null +++ b/games/NXDoom/COPYING.md @@ -0,0 +1,361 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +### Preamble + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at +all. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work +based on the Program" means either the Program or any derivative work +under copyright law: that is to say, a work containing the Program or +a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is +included without limitation in the term "modification".) Each licensee +is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +**1.** You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Program or any +portion of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + +**a)** You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + + +**b)** You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + + +**c)** If the modified program normally reads commands interactively +when run, you must cause it, when started running for such interactive +use in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and telling +the user how to view a copy of this License. (Exception: if the +Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + +**a)** Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; or, + + +**b)** Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete machine-readable +copy of the corresponding source code, to be distributed under the +terms of Sections 1 and 2 above on a medium customarily used for +software interchange; or, + + +**c)** Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +**4.** You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and +will automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +**5.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +**6.** Each time you redistribute the Program (or any work based on +the Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +**7.** If, as a consequence of a court judgment or allegation of +patent infringement or for any other reason (not limited to patent +issues), conditions are imposed on you (whether by court order, +agreement or otherwise) that contradict the conditions of this +License, they do not excuse you from the conditions of this License. +If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, +then as a consequence you may not distribute the Program at all. For +example, if a patent license would not permit royalty-free +redistribution of the Program by all those who receive copies directly +or indirectly through you, then the only way you could satisfy both it +and this License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**8.** If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +**9.** The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +**10.** If you wish to incorporate parts of the Program into other +free programs whose distribution conditions are different, write to +the author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +**NO WARRANTY** + +**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +### END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + one line to give the program's name and an idea of what it does. + Copyright (C) yyyy name of author + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper +mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details + type `show w'. This is free software, and you are welcome + to redistribute it under certain conditions; type `show c' + for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than \`show w' and +\`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the program, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright + interest in the program `Gnomovision' + (which makes passes at compilers) written + by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +[GNU Lesser General Public +License](https://www.gnu.org/licenses/lgpl.html) instead of this +License. diff --git a/games/NXDoom/Kconfig b/games/NXDoom/Kconfig new file mode 100644 index 000000000..d2bd2080f --- /dev/null +++ b/games/NXDoom/Kconfig @@ -0,0 +1,262 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config GAMES_NXDOOM + bool "NXDoom" + default n + depends on ALLOW_GPL_COMPONENTS + depends on VIDEO_FB + depends on CRYPTO + depends on LIBC_LOCALE + ---help--- + Play DOOM on NuttX! + +if GAMES_NXDOOM + +comment "Program options" + +config GAMES_NXDOOM_PRIORITY + int "Priority" + default 100 + ---help--- + The task priority for the NXDoom application. + +config GAMES_NXDOOM_STACKSIZE + int "Stack size" + default 4096 + ---help--- + The stack size for the NXDoom application. + +comment "General game options" + +choice # Language + prompt "Game language" + default GAMES_NXDOOM_LANG_EN + + config GAMES_NXDOOM_LANG_EN + bool "English" + ---help--- + English language support (original). + + config GAMES_NXDOOM_LANG_FR + bool "French" + ---help--- + French language support. +endchoice # Language + +config GAMES_NXDOOM_PREFDIR + string "DOOM data directory" + default "/data" + ---help--- + Directory where DOOM WAD files are stored. + +config GAMES_NXDOOM_ENDOOM + bool "Show ENDOOM" + default n + ---help--- + Show the ENDOOM screen (https://www.doomwiki.org/wiki/ENDOOM). + +comment "Networking options" + +config GAMES_NXDOOM_NET + bool "Enable network features" + default n + ---help--- + Not ported! Will not work. + +if GAMES_NXDOOM_NET + +config GAMES_NXDOOM_NET_LOGS + bool "Enable network logging" + default n + ---help--- + Enables log information from the network features. + +config GAMES_NXDOOM_NET_MAXPLAYERS + int "Maximum number of players" + default 6 + ---help--- + The maximum number of players, multiplayer/networking. + This is the maximum supported by the networking code; individual games + have their own values for MAXPLAYERS that can be smaller. + ChocolateDOOM default was 8. + +config GAMES_NXDOOM_NET_BACKUPTICS + int "Maximum number of backup tics" + default 32 + ---help--- + The maximum number of backup tics to store locally during a net game. + ChocalateDOOM default was 128. + +config GAMES_NXDOOM_NET_MAXPLAYERNAME + int "Maximum player name length" + default 16 + ---help--- + The maximum number of characters in a player name for netgames. + ChocalateDOOM default was 30. + +endif # GAMES_NXDOOM_NET + +comment "Sound options" + +config GAMES_NXDOOM_SOUND + bool "Enable sound features" + default n + ---help--- + Not ported! Will not work. + +if GAMES_NXDOOM_SOUND + +config GAMES_NXDOOM_RTTTL_MUSIC + bool "RTTTL music" + default n + depends on AUDIOUTILS_RTTTL_C + depends on !DISABLE_PTHREAD + ---help--- + Play music using RTTTL. + +config GAMES_NXDOOM_RTTTL_DEVICE + string "RTTTL device" + default "/dev/pwm0" + depends on GAMES_NXDOOM_RTTTL_MUSIC + ---help--- + Device for RTTTL output (only PWM currently supported). + +endif # GAMES_NXDOOM_SOUND + +comment "Graphics options" + +config GAMES_NXDOOM_FBPATH + string "Framebuffer path" + default "/dev/fb0" + ---help--- + Path to the framebuffer character driver to use for rendering. + +comment "Input options" + +choice # Input method + prompt "NXDoom input method" + default GAMES_NXDOOM_KEYBOARD + + config GAMES_NXDOOM_KEYBOARD + bool "Keyboard" + ---help--- + Use a NuttX keyboard character driver. + +endchoice # Input method + +if GAMES_NXDOOM_KEYBOARD + +config GAMES_NXDOOM_KBDPATH + string "Keyboard device path" + default "/dev/kbd0" + ---help--- + Path to the keyboard device to use as input. + +endif # GAMES_NXDOOM_KEYBOARD + +config GAMES_NXDOOM_FB_CMAP + bool "Draw through the frame buffer colour map" + default n + ---help--- + DOOM draws into an 8-bit palettised buffer and normally converts + every pixel to the pixel format of the frame buffer as it blits. + A frame buffer that has a colour map of its own can do that + conversion in hardware while it scans the display out, so enable + this to load the DOOM palette into the colour map and blit the + palette indices unconverted. + + This needs a frame buffer that is 8 bits per pixel and supports + FBIOPUT_CMAP, such as an STM32 LTDC layer configured for L8. It + both removes the conversion and halves the amount of data written + per frame, which matters most when the frame buffer is external + memory. + +config GAMES_NXDOOM_FILLSCREEN + bool "Stretch the image to the whole display" + default n + ---help--- + By default the 320x200 image is scaled by the largest whole number + that fits, which leaves a border on a display whose size is not an + exact multiple of it. Enable this to stretch the image over the + entire frame buffer instead, which fills the display at the cost of + a scale factor that is not uniform across the image. + +config GAMES_NXDOOM_ROWSTAGE + bool "Build each output row in a staging buffer" + default n + ---help--- + Expand each row into a staging buffer and copy it to the frame + buffer, rather than writing the frame buffer a pixel at a time. + Every frame buffer access then becomes a burst-friendly copy, which + is worth a lot when the frame buffer is external memory and the + data cache is in write-through mode, and little otherwise. + + Costs a few kilobytes of .bss for the buffer. A row that does not + fit is written directly, as if this were disabled. + +config GAMES_NXDOOM_STATIC_SCRNBUF + bool "Place the render target in .bss" + default n + ---help--- + DOOM renders into a 320x200 buffer that is normally taken from the + heap. On a board whose heap is mostly slow external memory the + allocation lands there, which is close to the worst case: the + renderer draws in vertical columns, so consecutive writes are one + screen width apart and none of them coalesce. + + Enable this to make the buffer a static object instead, so the + linker places it in internal RAM along with the rest of .bss. It + costs 64000 bytes of RAM whether DOOM runs or not, so it is only + worth it when internal RAM is plentiful and the heap is not. + +comment "Optimizations" + +config GAMES_NXDOOM_MAXVISPLANES + int "Maximum number of visplanes" + default 96 + ---help--- + DOOM allocates a static number of 'visplanes' to use for gameplay. The + original ChocolateDOOM source uses 128 of these. They take a significant + amount of memory, so you may reduce the number. However, too few planes + may cause 'visplane overflow' errors, which is more common in complex + levels. + +config GAMES_NXDOOM_MAXOPENINGS + int "Maximum number of openings" + default 32 + ---help--- + DOOM allocates a static number of 'openings' to use for gameplay. These + are defined as multiples of the SCREENWIDTH (320). The original + ChocolateDOOM source uses 64 as the multiplier. They take a significant + amount of memory, so you may reduce the number. However, too few + openings will cause 'opening overflow' errors. + +config GAMES_NXDOOM_MAXVISSPRITES + int "Maximum number of visible sprites" + default 96 + ---help--- + DOOM allocates a static number of 'vissprites' to use for gameplay. The + original ChocolateDOOM source uses 128. They take a significant amount + of memory, so you may reduce the number. However, too few will cause + overflow errors. + +config GAMES_NXDOOM_MAXDRAWSEGS + int "Maximum number of drawsegs" + default 192 + ---help--- + DOOM allocates a static number of 'drawsegs' to use for gameplay. The + original ChocolateDOOM source uses 256. They take a significant amount of + memory, so you may reduce the number. However, too few will cause + rendering issues (overflow is checked to avoid crashes). + +config GAMES_NXDOOM_RANGECHECK + bool "Perform range checks" + default y + ---help--- + The ChocolateDOOM source includes the ability to optionally exclude + range checking on some array operations. + +endif # GAMES_NXDOOM diff --git a/games/NXDoom/Make.defs b/games/NXDoom/Make.defs new file mode 100644 index 000000000..b0fa11c3f --- /dev/null +++ b/games/NXDoom/Make.defs @@ -0,0 +1,3 @@ +ifneq ($(CONFIG_GAMES_NXDOOM),) +CONFIGURED_APPS += $(APPDIR)/games/NXDoom +endif diff --git a/games/NXDoom/Makefile b/games/NXDoom/Makefile new file mode 100644 index 000000000..b22390654 --- /dev/null +++ b/games/NXDoom/Makefile @@ -0,0 +1,209 @@ +include $(APPDIR)/Make.defs + +# Program options + +MODULE = $(CONFIG_GAMES_NXDOOM) +PRIORITY = $(CONFIG_GAMES_NXDOOM_PRIORITY) +STACKSIZE = $(CONFIG_GAMES_NXDOOM_STACKSIZE) +PROGNAME = nxdoom + +# Source files + +COMMONSRCDIR = src +TEXTSCREENSRCDIR = textscreen +DOOMSRCDIR = $(COMMONSRCDIR)/doom +PCSOUNDSRCDIR = pcsound + +# Includes + +CFLAGS += -I$(COMMONSRCDIR) -I$(DOOMSRCDIR) -I$(TEXTSCREENSRCDIR) +CFLAGS += -I$(PCSOUNDSRCDIR) + +# Source files + +COMMONSRCS = i_system.c m_argv.c m_misc.c # Excluding i_main.c +COMMONSRCS += z_native.c # Native memory allocation + +DEHACKEDSRCS = deh_io.c deh_main.c deh_mapping.c deh_text.c + +BASESRCS = \ + aes_prng.c \ + d_event.c \ + d_iwad.c \ + d_loop.c \ + d_mode.c \ + deh_str.c \ + gusconf.c \ + i_glob.c \ + i_input.c \ + i_joystick.c \ + i_timer.c \ + i_video.c \ + m_bbox.c \ + m_cheat.c \ + m_config.c \ + m_controls.c \ + m_fixed.c \ + p_rejectpad.c \ + memio.c \ + tables.c \ + v_diskicon.c \ + v_video.c \ + w_checksum.c \ + w_main.c \ + w_wad.c \ + w_file.c \ + w_file_stdc.c \ + w_merge.c \ + +# Only add ENDOOM stuff if the user wants it + +ifeq ($(CONFIG_GAMES_NXDOOM_ENDOOM),y) +BASESRCS += i_endoom.c +endif + +NETSRCS = \ + net_client.c \ + net_common.c \ + net_dedicated.c \ + net_gui.c \ + net_io.c \ + net_loop.c \ + net_packet.c \ + net_query.c \ + net_sdl.c \ + net_server.c \ + net_structrw.c \ + +DOOMSRCS = \ + am_map.c \ + deh_ammo.c \ + deh_bexstr.c \ + deh_cheat.c \ + deh_doom.c \ + deh_frame.c \ + deh_misc.c \ + deh_ptr.c \ + deh_thing.c \ + deh_weapon.c \ + d_items.c \ + d_main.c \ + d_net.c \ + doomdef.c \ + doomstat.c \ + dstrings.c \ + f_finale.c \ + f_wipe.c \ + g_game.c \ + hu_lib.c \ + hu_stuff.c \ + info.c \ + m_menu.c \ + m_random.c \ + p_ceilng.c \ + p_doors.c \ + p_enemy.c \ + p_floor.c \ + p_inter.c \ + p_lights.c \ + p_map.c \ + p_maputl.c \ + p_mobj.c \ + p_plats.c \ + p_pspr.c \ + p_saveg.c \ + p_setup.c \ + p_sight.c \ + p_spec.c \ + p_switch.c \ + p_telept.c \ + p_tick.c \ + p_user.c \ + r_bsp.c \ + r_data.c \ + r_draw.c \ + r_main.c \ + r_plane.c \ + r_segs.c \ + r_sky.c \ + r_things.c \ + statdump.c \ + st_lib.c \ + st_stuff.c \ + wi_stuff.c + +TXTSCREENSRCS = \ + txt_conditional.c \ + txt_checkbox.c \ + txt_desktop.c \ + txt_dropdown.c \ + txt_fileselect.c \ + txt_gui.c \ + txt_inputbox.c \ + txt_io.c \ + txt_button.c \ + txt_label.c \ + txt_radiobutton.c \ + txt_scrollpane.c \ + txt_separator.c \ + txt_spinctrl.c \ + txt_sdl.c \ + txt_strut.c \ + txt_table.c \ + txt_utf8.c \ + txt_widget.c \ + txt_window.c \ + txt_window_action.c + +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(COMMONSRCS)) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(DEHACKEDSRCS)) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(BASESRCS)) +CSRCS += $(patsubst %,$(DOOMSRCDIR)/%,$(DOOMSRCS)) + +# Textscreen stuff only needs to be included for networking stuff +# or if the user wants to see ENDOOM + +ifeq ($(CONFIG_GAMES_NXDOOM_ENDOOM),y) +ADDTXTSCREEN = y +endif + +ifeq ($(CONFIG_GAMES_NXDOOM_NET),y) +ADDTXTSCREEN = y +endif + +# Only include textscreen code if needed + +ifeq ($(ADDTXTSCREEN),y) +CSRCS += $(patsubst %,$(TEXTSCREENSRCDIR)/%,$(TXTSCREENSRCS)) +endif + +# Only include networking logic if enabled + +ifeq ($(CONFIG_GAMES_NXDOOM_NET),y) +CSRCS += $(patsubst %,$(COMMONSRCDIR)/%,$(NETSRCS)) +endif + +# Only include sound logic if enabled + +ifeq ($(CONFIG_GAMES_NXDOOM_SOUND),y) +CSRCS += $(COMMONSRCDIR)/i_musicpack.c +CSRCS += $(COMMONSRCDIR)/i_pcsound.c +CSRCS += $(COMMONSRCDIR)/i_sdlmusic.c +CSRCS += $(COMMONSRCDIR)/i_sound.c +CSRCS += $(COMMONSRCDIR)/midifile.c +CSRCS += $(COMMONSRCDIR)/mus2mid.c + +CSRCS += $(DOOMSRCDIR)/sounds.c +CSRCS += $(DOOMSRCDIR)/s_sound.c +CSRCS += $(DOOMSRCDIR)/deh_sound.c +endif + +# If we have chosen RTTTL music, then add that source + +ifeq ($(CONFIG_GAMES_NXDOOM_RTTTL_MUSIC),y) +CSRCS += $(COMMONSRCDIR)/i_rtttl_music.c +endif + +MAINSRC = $(COMMONSRCDIR)/i_main.c + +include $(APPDIR)/Application.mk diff --git a/games/NXDoom/pcsound/.gitignore b/games/NXDoom/pcsound/.gitignore new file mode 100644 index 000000000..022c663ee --- /dev/null +++ b/games/NXDoom/pcsound/.gitignore @@ -0,0 +1,8 @@ +Makefile.in +Makefile +.deps +libpcsound.a +*.rc +tags +TAGS + diff --git a/games/NXDoom/pcsound/pcsound.c b/games/NXDoom/pcsound/pcsound.c new file mode 100644 index 000000000..8e7b73bd6 --- /dev/null +++ b/games/NXDoom/pcsound/pcsound.c @@ -0,0 +1,129 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" +#include "pcsound.h" +#include "pcsound_internal.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pcsound_driver_t *g_drivers[] = +{ +#ifdef HAVE_LINUX_KD_H + &pcsound_linux_driver, +#endif + NULL, +}; + +static pcsound_driver_t *g_pcsound_driver = NULL; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int pcsound_sample_rate; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void pc_sound_set_sample_rate(int rate) +{ + pcsound_sample_rate = rate; +} + +int pc_sound_init(pcsound_callback_func callback_func) +{ + char *driver_name; + int i; + + if (g_pcsound_driver != NULL) + { + return 1; + } + + /* Check if the environment variable is set */ + + driver_name = getenv("PCSOUND_DRIVER"); + + if (driver_name != NULL) + { + for (i = 0; g_drivers[i] != NULL; ++i) + { + if (!strcmp(g_drivers[i]->name, driver_name)) + { + /* Found the driver! */ + + if (g_drivers[i]->init_func(callback_func)) + { + g_pcsound_driver = g_drivers[i]; + } + else + { + printf("Failed to initialize PC sound driver: %s\n", + g_drivers[i]->name); + break; + } + } + } + } + else + { + /* Try all drivers until we find a working one */ + + for (i = 0; g_drivers[i] != NULL; ++i) + { + if (g_drivers[i]->init_func(callback_func)) + { + g_pcsound_driver = g_drivers[i]; + break; + } + } + } + + if (g_pcsound_driver != NULL) + { + printf("Using PC sound driver: %s\n", g_pcsound_driver->name); + return 1; + } + else + { + printf("Failed to find a working PC sound driver.\n"); + return 0; + } +} + +void pc_sound_shutdown(void) +{ + g_pcsound_driver->shutdown_func(); + g_pcsound_driver = NULL; +} diff --git a/games/NXDoom/pcsound/pcsound.h b/games/NXDoom/pcsound/pcsound.h new file mode 100644 index 000000000..0d1e0f3ba --- /dev/null +++ b/games/NXDoom/pcsound/pcsound.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +#ifndef PCSOUND_H +#define PCSOUND_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*pcsound_callback_func)(int *duration, int *frequency); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialise the PC speaker subsystem. The given function is called + * periodically to request more sound data to play. + */ + +int pc_sound_init(pcsound_callback_func callback_func); + +/* Shut down the PC speaker subsystem. */ + +void pc_sound_shutdown(void); + +/* Set the preferred output sample rate when emulating a PC speaker. + * This must be called before pc_sound_init. + */ + +void pc_sound_set_sample_rate(int rate); + +#endif /* PCSOUND_H */ diff --git a/games/NXDoom/pcsound/pcsound_internal.h b/games/NXDoom/pcsound/pcsound_internal.h new file mode 100644 index 000000000..295ad6266 --- /dev/null +++ b/games/NXDoom/pcsound/pcsound_internal.h @@ -0,0 +1,65 @@ +/**************************************************************************** + * apps/games/NXDoom/pcsound/pcsound_internal.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * PC speaker interface. + * + ****************************************************************************/ + +#ifndef PCSOUND_INTERNAL_H +#define PCSOUND_INTERNAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "pcsound.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PCSOUND_8253_FREQUENCY (1193280) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct pcsound_driver_s pcsound_driver_t; +typedef int (*pcsound_init_func)(pcsound_callback_func callback); +typedef void (*pcsound_shutdown_func)(void); + +struct pcsound_driver_s +{ + const char *name; + pcsound_init_func init_func; + pcsound_shutdown_func shutdown_func; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int pcsound_sample_rate; + +#ifdef HAVE_LINUX_KD_H +extern pcsound_driver_t pcsound_linux_driver; +#endif + +extern pcsound_driver_t pcsound_sdl_driver; + +#endif /* PCSOUND_INTERNAL_H */ diff --git a/games/NXDoom/src/.gitignore b/games/NXDoom/src/.gitignore new file mode 100644 index 000000000..a3127e6bd --- /dev/null +++ b/games/NXDoom/src/.gitignore @@ -0,0 +1,22 @@ +Makefile +Makefile.in +.deps +*.rc +chocolate-doom +chocolate-heretic +chocolate-hexen +chocolate-server +chocolate-strife +chocolate-doom-setup +chocolate-heretic-setup +chocolate-hexen-setup +chocolate-strife-setup +chocolate-setup +*.cfg +*.exe +*.desktop +*.txt +!CMakeLists.txt +*.metainfo.xml +tags +TAGS diff --git a/games/NXDoom/src/aes_prng.c b/games/NXDoom/src/aes_prng.c new file mode 100644 index 000000000..04de172ef --- /dev/null +++ b/games/NXDoom/src/aes_prng.c @@ -0,0 +1,930 @@ +/**************************************************************************** + * apps/games/NXDoom/src/aes_prng.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * This implements a cryptographically secure pseudorandom number + * generator for implementing secure demos. The approach taken is to + * use the AES (Rijndael) stream cipher in "counter" mode, encrypting + * an incrementing counter. The cipher key acts as the random seed. + * Cryptanalysis of AES used in this way has shown it to be an + * effective PRNG (see: Empirical Evidence concerning AES, Hellekalek + * & Wegenkittl, 2003). + * + * AES implementation is taken from the Linux kernel's AES + * implementation, found in crypto/aes_generic.c. It has been hacked + * up to work independently. + * + * AES Cipher Algorithm. + * + * Based on Brian Gladman's code. + * + * Linux developers: + * Alexander Kjeldaas + * Herbert Valerio Riedel + * Kyle McMartin + * Adam J. Richter (conversion to 2.5 API). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (c) 2002, Dr Brian Gladman , Worcester, UK. + * All rights reserved. + * + * LICENSE TERMS + * + * The free distribution and use of this software in both source and binary + * form is allowed (with or without changes) provided that: + * + * 1. distributions of this source code include the above copyright + * notice, this list of conditions and the following disclaimer; + * + * 2. distributions in binary form include the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other associated materials; + * + * 3. the copyright holder's name is not used to endorse products + * built using this software without specific written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL), in which case the provisions of the GPL apply INSTEAD OF + * those given above. + * + * DISCLAIMER + * + * This software is provided 'as is' with no explicit or implied warranties + * in respect of its properties, including, but not limited to, correctness + * and/or fitness for purpose. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "aes_prng.h" +#include "doomtype.h" +#include "i_swap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define AES_MIN_KEY_SIZE 16 +#define AES_MAX_KEY_SIZE 32 +#define AES_KEYSIZE_128 16 +#define AES_KEYSIZE_192 24 +#define AES_KEYSIZE_256 32 +#define AES_BLOCK_SIZE 16 +#define AES_MAX_KEYLENGTH (15 * 16) +#define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(uint32_t)) + +#define cpu_to_le32(x) htole32(x) +#define le32_to_cpu(x) le32toh(x) + +#define star_x(x) \ + (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b) + +#define imix_col(y, x) \ + do \ + { \ + u = star_x(x); \ + v = star_x(u); \ + w = star_x(v); \ + t = w ^ (x); \ + (y) = u ^ v ^ w; \ + (y) ^= aes_ror32(u ^ t, 8) ^ aes_ror32(v ^ t, 16) ^ aes_ror32(t, 24); \ + } \ + while (0) + +#define ls_box(x) \ + crypto_fl_tab[0][get_byte(x, 0)] ^ crypto_fl_tab[1][get_byte(x, 1)] ^ \ + crypto_fl_tab[2][get_byte(x, 2)] ^ crypto_fl_tab[3][get_byte(x, 3)] + +#define loop4(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[4 * i]; \ + ctx->key_enc[4 * i + 4] = t; \ + t ^= ctx->key_enc[4 * i + 1]; \ + ctx->key_enc[4 * i + 5] = t; \ + t ^= ctx->key_enc[4 * i + 2]; \ + ctx->key_enc[4 * i + 6] = t; \ + t ^= ctx->key_enc[4 * i + 3]; \ + ctx->key_enc[4 * i + 7] = t; \ + } \ + while (0) + +#define loop6(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[6 * i]; \ + ctx->key_enc[6 * i + 6] = t; \ + t ^= ctx->key_enc[6 * i + 1]; \ + ctx->key_enc[6 * i + 7] = t; \ + t ^= ctx->key_enc[6 * i + 2]; \ + ctx->key_enc[6 * i + 8] = t; \ + t ^= ctx->key_enc[6 * i + 3]; \ + ctx->key_enc[6 * i + 9] = t; \ + t ^= ctx->key_enc[6 * i + 4]; \ + ctx->key_enc[6 * i + 10] = t; \ + t ^= ctx->key_enc[6 * i + 5]; \ + ctx->key_enc[6 * i + 11] = t; \ + } \ + while (0) + +#define loop8tophalf(i) \ + do \ + { \ + t = aes_ror32(t, 8); \ + t = ls_box(t) ^ rco_tab[i]; \ + t ^= ctx->key_enc[8 * i]; \ + ctx->key_enc[8 * i + 8] = t; \ + t ^= ctx->key_enc[8 * i + 1]; \ + ctx->key_enc[8 * i + 9] = t; \ + t ^= ctx->key_enc[8 * i + 2]; \ + ctx->key_enc[8 * i + 10] = t; \ + t ^= ctx->key_enc[8 * i + 3]; \ + ctx->key_enc[8 * i + 11] = t; \ + } \ + while (0) + +#define loop8(i) \ + do \ + { \ + loop8tophalf(i); \ + t = ctx->key_enc[8 * i + 4] ^ ls_box(t); \ + ctx->key_enc[8 * i + 12] = t; \ + t ^= ctx->key_enc[8 * i + 5]; \ + ctx->key_enc[8 * i + 13] = t; \ + t ^= ctx->key_enc[8 * i + 6]; \ + ctx->key_enc[8 * i + 14] = t; \ + t ^= ctx->key_enc[8 * i + 7]; \ + ctx->key_enc[8 * i + 15] = t; \ + } \ + while (0) + +/* encrypt a block of text */ + +#define f_rn(bo, bi, n, k) \ + do \ + { \ + bo[n] = crypto_ft_tab[0][get_byte(bi[n], 0)] ^ \ + crypto_ft_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ + crypto_ft_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ + crypto_ft_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ + } \ + while (0) + +#define f_nround(bo, bi, k) \ + do \ + { \ + f_rn(bo, bi, 0, k); \ + f_rn(bo, bi, 1, k); \ + f_rn(bo, bi, 2, k); \ + f_rn(bo, bi, 3, k); \ + k += 4; \ + } \ + while (0) + +#define f_rl(bo, bi, n, k) \ + do \ + { \ + bo[n] = crypto_fl_tab[0][get_byte(bi[n], 0)] ^ \ + crypto_fl_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \ + crypto_fl_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \ + crypto_fl_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \ + } \ + while (0) + +#define f_lround(bo, bi, k) \ + do \ + { \ + f_rl(bo, bi, 0, k); \ + f_rl(bo, bi, 1, k); \ + f_rl(bo, bi, 2, k); \ + f_rl(bo, bi, 3, k); \ + } \ + while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Please ensure that the first two fields are 16-byte aligned + * relative to the start of the structure, i.e., don't move them! + */ + +typedef struct +{ + uint32_t key_enc[AES_MAX_KEYLENGTH_U32]; + uint32_t key_dec[AES_MAX_KEYLENGTH_U32]; + uint32_t key_length; +} aes_context_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean prng_enabled = false; +static aes_context_t prng_context; +static uint32_t prng_input_counter; +static uint32_t prng_values[4]; +static unsigned int prng_value_index = 0; + +static const uint32_t rco_tab[10] = +{ + 1, 2, 4, 8, 16, 32, 64, 128, 27, 54, +}; + +static const uint32_t crypto_ft_tab[4][256] = +{ + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, + 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, + 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, + 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41, + 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, + 0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, + 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, + 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, + 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, + 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, + 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, + 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, + 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, + 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, + 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, + 0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a, + 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, + 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080, + 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, + 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, + 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, + 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, + 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, + 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, + 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, + 0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd, + 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, + 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, + 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, + 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a, + 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, + 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, + 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, + 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, + 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, + 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, + 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, + 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, + 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, + 0x3a16162c, + }, + { + 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, + 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, + 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, + 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, + 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec, + 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, + 0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae, + 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, + 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, + 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, + 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, + 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, + 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, + 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, + 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, + 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, + 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, + 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, + 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, + 0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, + 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf, + 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, + 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0, + 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, + 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, + 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, + 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, + 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, + 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, + 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, + 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, + 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, + 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, + 0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e, + 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, + 0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, + 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, + 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, + 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f, + 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, + 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, + 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, + 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, + 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, + 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, + 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, + 0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, + 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, + 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, + 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0, + 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, + 0x16162c3a, + }, + { + 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, + 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, + 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, + 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, + 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad, + 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, + 0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93, + 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, + 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, + 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, + 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, + 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, + 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, + 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, + 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, + 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, + 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, + 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, + 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, + 0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, + 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45, + 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, + 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040, + 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, + 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, + 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, + 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, + 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, + 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, + 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, + 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, + 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, + 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, + 0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3, + 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, + 0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, + 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, + 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, + 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25, + 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, + 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, + 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, + 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, + 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, + 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, + 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, + 0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, + 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, + 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, + 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099, + 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, + 0x162c3a16, + }, + { + 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, + 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, + 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, + 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, + 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad, + 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, + 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, + 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, + 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, + 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, + 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, + 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, + 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, + 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, + 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, + 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, + 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, + 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, + 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, + 0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, + 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545, + 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, + 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040, + 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, + 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, + 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, + 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, + 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, + 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, + 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, + 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, + 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, + 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, + 0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3, + 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, + 0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, + 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, + 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, + 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525, + 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, + 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, + 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, + 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, + 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, + 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, + 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, + 0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, + 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, + 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, + 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999, + 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, + 0x2c3a1616, + }, +}; + +static const uint32_t crypto_fl_tab[4][256] = +{ + { + 0x00000063, 0x0000007c, 0x00000077, 0x0000007b, 0x000000f2, + 0x0000006b, 0x0000006f, 0x000000c5, 0x00000030, 0x00000001, + 0x00000067, 0x0000002b, 0x000000fe, 0x000000d7, 0x000000ab, + 0x00000076, 0x000000ca, 0x00000082, 0x000000c9, 0x0000007d, + 0x000000fa, 0x00000059, 0x00000047, 0x000000f0, 0x000000ad, + 0x000000d4, 0x000000a2, 0x000000af, 0x0000009c, 0x000000a4, + 0x00000072, 0x000000c0, 0x000000b7, 0x000000fd, 0x00000093, + 0x00000026, 0x00000036, 0x0000003f, 0x000000f7, 0x000000cc, + 0x00000034, 0x000000a5, 0x000000e5, 0x000000f1, 0x00000071, + 0x000000d8, 0x00000031, 0x00000015, 0x00000004, 0x000000c7, + 0x00000023, 0x000000c3, 0x00000018, 0x00000096, 0x00000005, + 0x0000009a, 0x00000007, 0x00000012, 0x00000080, 0x000000e2, + 0x000000eb, 0x00000027, 0x000000b2, 0x00000075, 0x00000009, + 0x00000083, 0x0000002c, 0x0000001a, 0x0000001b, 0x0000006e, + 0x0000005a, 0x000000a0, 0x00000052, 0x0000003b, 0x000000d6, + 0x000000b3, 0x00000029, 0x000000e3, 0x0000002f, 0x00000084, + 0x00000053, 0x000000d1, 0x00000000, 0x000000ed, 0x00000020, + 0x000000fc, 0x000000b1, 0x0000005b, 0x0000006a, 0x000000cb, + 0x000000be, 0x00000039, 0x0000004a, 0x0000004c, 0x00000058, + 0x000000cf, 0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb, + 0x00000043, 0x0000004d, 0x00000033, 0x00000085, 0x00000045, + 0x000000f9, 0x00000002, 0x0000007f, 0x00000050, 0x0000003c, + 0x0000009f, 0x000000a8, 0x00000051, 0x000000a3, 0x00000040, + 0x0000008f, 0x00000092, 0x0000009d, 0x00000038, 0x000000f5, + 0x000000bc, 0x000000b6, 0x000000da, 0x00000021, 0x00000010, + 0x000000ff, 0x000000f3, 0x000000d2, 0x000000cd, 0x0000000c, + 0x00000013, 0x000000ec, 0x0000005f, 0x00000097, 0x00000044, + 0x00000017, 0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d, + 0x00000064, 0x0000005d, 0x00000019, 0x00000073, 0x00000060, + 0x00000081, 0x0000004f, 0x000000dc, 0x00000022, 0x0000002a, + 0x00000090, 0x00000088, 0x00000046, 0x000000ee, 0x000000b8, + 0x00000014, 0x000000de, 0x0000005e, 0x0000000b, 0x000000db, + 0x000000e0, 0x00000032, 0x0000003a, 0x0000000a, 0x00000049, + 0x00000006, 0x00000024, 0x0000005c, 0x000000c2, 0x000000d3, + 0x000000ac, 0x00000062, 0x00000091, 0x00000095, 0x000000e4, + 0x00000079, 0x000000e7, 0x000000c8, 0x00000037, 0x0000006d, + 0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9, 0x0000006c, + 0x00000056, 0x000000f4, 0x000000ea, 0x00000065, 0x0000007a, + 0x000000ae, 0x00000008, 0x000000ba, 0x00000078, 0x00000025, + 0x0000002e, 0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6, + 0x000000e8, 0x000000dd, 0x00000074, 0x0000001f, 0x0000004b, + 0x000000bd, 0x0000008b, 0x0000008a, 0x00000070, 0x0000003e, + 0x000000b5, 0x00000066, 0x00000048, 0x00000003, 0x000000f6, + 0x0000000e, 0x00000061, 0x00000035, 0x00000057, 0x000000b9, + 0x00000086, 0x000000c1, 0x0000001d, 0x0000009e, 0x000000e1, + 0x000000f8, 0x00000098, 0x00000011, 0x00000069, 0x000000d9, + 0x0000008e, 0x00000094, 0x0000009b, 0x0000001e, 0x00000087, + 0x000000e9, 0x000000ce, 0x00000055, 0x00000028, 0x000000df, + 0x0000008c, 0x000000a1, 0x00000089, 0x0000000d, 0x000000bf, + 0x000000e6, 0x00000042, 0x00000068, 0x00000041, 0x00000099, + 0x0000002d, 0x0000000f, 0x000000b0, 0x00000054, 0x000000bb, + 0x00000016, + }, + { + 0x00006300, 0x00007c00, 0x00007700, 0x00007b00, 0x0000f200, + 0x00006b00, 0x00006f00, 0x0000c500, 0x00003000, 0x00000100, + 0x00006700, 0x00002b00, 0x0000fe00, 0x0000d700, 0x0000ab00, + 0x00007600, 0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00, + 0x0000fa00, 0x00005900, 0x00004700, 0x0000f000, 0x0000ad00, + 0x0000d400, 0x0000a200, 0x0000af00, 0x00009c00, 0x0000a400, + 0x00007200, 0x0000c000, 0x0000b700, 0x0000fd00, 0x00009300, + 0x00002600, 0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00, + 0x00003400, 0x0000a500, 0x0000e500, 0x0000f100, 0x00007100, + 0x0000d800, 0x00003100, 0x00001500, 0x00000400, 0x0000c700, + 0x00002300, 0x0000c300, 0x00001800, 0x00009600, 0x00000500, + 0x00009a00, 0x00000700, 0x00001200, 0x00008000, 0x0000e200, + 0x0000eb00, 0x00002700, 0x0000b200, 0x00007500, 0x00000900, + 0x00008300, 0x00002c00, 0x00001a00, 0x00001b00, 0x00006e00, + 0x00005a00, 0x0000a000, 0x00005200, 0x00003b00, 0x0000d600, + 0x0000b300, 0x00002900, 0x0000e300, 0x00002f00, 0x00008400, + 0x00005300, 0x0000d100, 0x00000000, 0x0000ed00, 0x00002000, + 0x0000fc00, 0x0000b100, 0x00005b00, 0x00006a00, 0x0000cb00, + 0x0000be00, 0x00003900, 0x00004a00, 0x00004c00, 0x00005800, + 0x0000cf00, 0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00, + 0x00004300, 0x00004d00, 0x00003300, 0x00008500, 0x00004500, + 0x0000f900, 0x00000200, 0x00007f00, 0x00005000, 0x00003c00, + 0x00009f00, 0x0000a800, 0x00005100, 0x0000a300, 0x00004000, + 0x00008f00, 0x00009200, 0x00009d00, 0x00003800, 0x0000f500, + 0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100, 0x00001000, + 0x0000ff00, 0x0000f300, 0x0000d200, 0x0000cd00, 0x00000c00, + 0x00001300, 0x0000ec00, 0x00005f00, 0x00009700, 0x00004400, + 0x00001700, 0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00, + 0x00006400, 0x00005d00, 0x00001900, 0x00007300, 0x00006000, + 0x00008100, 0x00004f00, 0x0000dc00, 0x00002200, 0x00002a00, + 0x00009000, 0x00008800, 0x00004600, 0x0000ee00, 0x0000b800, + 0x00001400, 0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00, + 0x0000e000, 0x00003200, 0x00003a00, 0x00000a00, 0x00004900, + 0x00000600, 0x00002400, 0x00005c00, 0x0000c200, 0x0000d300, + 0x0000ac00, 0x00006200, 0x00009100, 0x00009500, 0x0000e400, + 0x00007900, 0x0000e700, 0x0000c800, 0x00003700, 0x00006d00, + 0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900, 0x00006c00, + 0x00005600, 0x0000f400, 0x0000ea00, 0x00006500, 0x00007a00, + 0x0000ae00, 0x00000800, 0x0000ba00, 0x00007800, 0x00002500, + 0x00002e00, 0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600, + 0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00, 0x00004b00, + 0x0000bd00, 0x00008b00, 0x00008a00, 0x00007000, 0x00003e00, + 0x0000b500, 0x00006600, 0x00004800, 0x00000300, 0x0000f600, + 0x00000e00, 0x00006100, 0x00003500, 0x00005700, 0x0000b900, + 0x00008600, 0x0000c100, 0x00001d00, 0x00009e00, 0x0000e100, + 0x0000f800, 0x00009800, 0x00001100, 0x00006900, 0x0000d900, + 0x00008e00, 0x00009400, 0x00009b00, 0x00001e00, 0x00008700, + 0x0000e900, 0x0000ce00, 0x00005500, 0x00002800, 0x0000df00, + 0x00008c00, 0x0000a100, 0x00008900, 0x00000d00, 0x0000bf00, + 0x0000e600, 0x00004200, 0x00006800, 0x00004100, 0x00009900, + 0x00002d00, 0x00000f00, 0x0000b000, 0x00005400, 0x0000bb00, + 0x00001600, + }, + { + 0x00630000, 0x007c0000, 0x00770000, 0x007b0000, 0x00f20000, + 0x006b0000, 0x006f0000, 0x00c50000, 0x00300000, 0x00010000, + 0x00670000, 0x002b0000, 0x00fe0000, 0x00d70000, 0x00ab0000, + 0x00760000, 0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000, + 0x00fa0000, 0x00590000, 0x00470000, 0x00f00000, 0x00ad0000, + 0x00d40000, 0x00a20000, 0x00af0000, 0x009c0000, 0x00a40000, + 0x00720000, 0x00c00000, 0x00b70000, 0x00fd0000, 0x00930000, + 0x00260000, 0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000, + 0x00340000, 0x00a50000, 0x00e50000, 0x00f10000, 0x00710000, + 0x00d80000, 0x00310000, 0x00150000, 0x00040000, 0x00c70000, + 0x00230000, 0x00c30000, 0x00180000, 0x00960000, 0x00050000, + 0x009a0000, 0x00070000, 0x00120000, 0x00800000, 0x00e20000, + 0x00eb0000, 0x00270000, 0x00b20000, 0x00750000, 0x00090000, + 0x00830000, 0x002c0000, 0x001a0000, 0x001b0000, 0x006e0000, + 0x005a0000, 0x00a00000, 0x00520000, 0x003b0000, 0x00d60000, + 0x00b30000, 0x00290000, 0x00e30000, 0x002f0000, 0x00840000, + 0x00530000, 0x00d10000, 0x00000000, 0x00ed0000, 0x00200000, + 0x00fc0000, 0x00b10000, 0x005b0000, 0x006a0000, 0x00cb0000, + 0x00be0000, 0x00390000, 0x004a0000, 0x004c0000, 0x00580000, + 0x00cf0000, 0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000, + 0x00430000, 0x004d0000, 0x00330000, 0x00850000, 0x00450000, + 0x00f90000, 0x00020000, 0x007f0000, 0x00500000, 0x003c0000, + 0x009f0000, 0x00a80000, 0x00510000, 0x00a30000, 0x00400000, + 0x008f0000, 0x00920000, 0x009d0000, 0x00380000, 0x00f50000, + 0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000, 0x00100000, + 0x00ff0000, 0x00f30000, 0x00d20000, 0x00cd0000, 0x000c0000, + 0x00130000, 0x00ec0000, 0x005f0000, 0x00970000, 0x00440000, + 0x00170000, 0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000, + 0x00640000, 0x005d0000, 0x00190000, 0x00730000, 0x00600000, + 0x00810000, 0x004f0000, 0x00dc0000, 0x00220000, 0x002a0000, + 0x00900000, 0x00880000, 0x00460000, 0x00ee0000, 0x00b80000, + 0x00140000, 0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000, + 0x00e00000, 0x00320000, 0x003a0000, 0x000a0000, 0x00490000, + 0x00060000, 0x00240000, 0x005c0000, 0x00c20000, 0x00d30000, + 0x00ac0000, 0x00620000, 0x00910000, 0x00950000, 0x00e40000, + 0x00790000, 0x00e70000, 0x00c80000, 0x00370000, 0x006d0000, + 0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000, 0x006c0000, + 0x00560000, 0x00f40000, 0x00ea0000, 0x00650000, 0x007a0000, + 0x00ae0000, 0x00080000, 0x00ba0000, 0x00780000, 0x00250000, + 0x002e0000, 0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000, + 0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000, 0x004b0000, + 0x00bd0000, 0x008b0000, 0x008a0000, 0x00700000, 0x003e0000, + 0x00b50000, 0x00660000, 0x00480000, 0x00030000, 0x00f60000, + 0x000e0000, 0x00610000, 0x00350000, 0x00570000, 0x00b90000, + 0x00860000, 0x00c10000, 0x001d0000, 0x009e0000, 0x00e10000, + 0x00f80000, 0x00980000, 0x00110000, 0x00690000, 0x00d90000, + 0x008e0000, 0x00940000, 0x009b0000, 0x001e0000, 0x00870000, + 0x00e90000, 0x00ce0000, 0x00550000, 0x00280000, 0x00df0000, + 0x008c0000, 0x00a10000, 0x00890000, 0x000d0000, 0x00bf0000, + 0x00e60000, 0x00420000, 0x00680000, 0x00410000, 0x00990000, + 0x002d0000, 0x000f0000, 0x00b00000, 0x00540000, 0x00bb0000, + 0x00160000, + }, + { + 0x63000000, 0x7c000000, 0x77000000, 0x7b000000, 0xf2000000, + 0x6b000000, 0x6f000000, 0xc5000000, 0x30000000, 0x01000000, + 0x67000000, 0x2b000000, 0xfe000000, 0xd7000000, 0xab000000, + 0x76000000, 0xca000000, 0x82000000, 0xc9000000, 0x7d000000, + 0xfa000000, 0x59000000, 0x47000000, 0xf0000000, 0xad000000, + 0xd4000000, 0xa2000000, 0xaf000000, 0x9c000000, 0xa4000000, + 0x72000000, 0xc0000000, 0xb7000000, 0xfd000000, 0x93000000, + 0x26000000, 0x36000000, 0x3f000000, 0xf7000000, 0xcc000000, + 0x34000000, 0xa5000000, 0xe5000000, 0xf1000000, 0x71000000, + 0xd8000000, 0x31000000, 0x15000000, 0x04000000, 0xc7000000, + 0x23000000, 0xc3000000, 0x18000000, 0x96000000, 0x05000000, + 0x9a000000, 0x07000000, 0x12000000, 0x80000000, 0xe2000000, + 0xeb000000, 0x27000000, 0xb2000000, 0x75000000, 0x09000000, + 0x83000000, 0x2c000000, 0x1a000000, 0x1b000000, 0x6e000000, + 0x5a000000, 0xa0000000, 0x52000000, 0x3b000000, 0xd6000000, + 0xb3000000, 0x29000000, 0xe3000000, 0x2f000000, 0x84000000, + 0x53000000, 0xd1000000, 0x00000000, 0xed000000, 0x20000000, + 0xfc000000, 0xb1000000, 0x5b000000, 0x6a000000, 0xcb000000, + 0xbe000000, 0x39000000, 0x4a000000, 0x4c000000, 0x58000000, + 0xcf000000, 0xd0000000, 0xef000000, 0xaa000000, 0xfb000000, + 0x43000000, 0x4d000000, 0x33000000, 0x85000000, 0x45000000, + 0xf9000000, 0x02000000, 0x7f000000, 0x50000000, 0x3c000000, + 0x9f000000, 0xa8000000, 0x51000000, 0xa3000000, 0x40000000, + 0x8f000000, 0x92000000, 0x9d000000, 0x38000000, 0xf5000000, + 0xbc000000, 0xb6000000, 0xda000000, 0x21000000, 0x10000000, + 0xff000000, 0xf3000000, 0xd2000000, 0xcd000000, 0x0c000000, + 0x13000000, 0xec000000, 0x5f000000, 0x97000000, 0x44000000, + 0x17000000, 0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000, + 0x64000000, 0x5d000000, 0x19000000, 0x73000000, 0x60000000, + 0x81000000, 0x4f000000, 0xdc000000, 0x22000000, 0x2a000000, + 0x90000000, 0x88000000, 0x46000000, 0xee000000, 0xb8000000, + 0x14000000, 0xde000000, 0x5e000000, 0x0b000000, 0xdb000000, + 0xe0000000, 0x32000000, 0x3a000000, 0x0a000000, 0x49000000, + 0x06000000, 0x24000000, 0x5c000000, 0xc2000000, 0xd3000000, + 0xac000000, 0x62000000, 0x91000000, 0x95000000, 0xe4000000, + 0x79000000, 0xe7000000, 0xc8000000, 0x37000000, 0x6d000000, + 0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000, 0x6c000000, + 0x56000000, 0xf4000000, 0xea000000, 0x65000000, 0x7a000000, + 0xae000000, 0x08000000, 0xba000000, 0x78000000, 0x25000000, + 0x2e000000, 0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000, + 0xe8000000, 0xdd000000, 0x74000000, 0x1f000000, 0x4b000000, + 0xbd000000, 0x8b000000, 0x8a000000, 0x70000000, 0x3e000000, + 0xb5000000, 0x66000000, 0x48000000, 0x03000000, 0xf6000000, + 0x0e000000, 0x61000000, 0x35000000, 0x57000000, 0xb9000000, + 0x86000000, 0xc1000000, 0x1d000000, 0x9e000000, 0xe1000000, + 0xf8000000, 0x98000000, 0x11000000, 0x69000000, 0xd9000000, + 0x8e000000, 0x94000000, 0x9b000000, 0x1e000000, 0x87000000, + 0xe9000000, 0xce000000, 0x55000000, 0x28000000, 0xdf000000, + 0x8c000000, 0xa1000000, 0x89000000, 0x0d000000, 0xbf000000, + 0xe6000000, 0x42000000, 0x68000000, 0x41000000, 0x99000000, + 0x2d000000, 0x0f000000, 0xb0000000, 0x54000000, 0xbb000000, + 0x16000000, + }, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline uint8_t get_byte(const uint32_t x, const unsigned n) +{ + return x >> (n << 3); +} + +/* initialise the key schedule from the user supplied key */ + +static uint32_t aes_ror32(uint32_t word, unsigned int shift) +{ + return (word >> shift) | (word << (32 - shift)); +} + +/* aes_expand_key - Expands the AES key as described in FIPS-197 + * @ctx: The location where the computed key will be stored. + * @in_key: The supplied key. + * @key_len: The length of the supplied key. + * + * Returns 0 on success. The function fails only if an invalid key size (or + * pointer) is supplied. + * + * The expanded key size is 240 bytes (max of 14 rounds with a unique 16 + * bytes key schedule plus a 16 bytes key which is used before the first + * round). The decryption key is prepared for the "Equivalent Inverse Cipher" + * as described in FIPS-197. The first slot (16 bytes) of each key (enc or + * dec) is for the initial combination, the second slot for the first round + * and so on. + */ + +static int aes_expand_key(aes_context_t *ctx, const uint8_t *in_key, + unsigned int key_len) +{ + const uint32_t *key = (const uint32_t *)in_key; + uint32_t i; + uint32_t t; + uint32_t u; + uint32_t v; + uint32_t w; + uint32_t j; + + if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) + { + return -1; + } + + ctx->key_length = key_len; + + ctx->key_dec[key_len + 24] = ctx->key_enc[0] = le32_to_cpu(key[0]); + ctx->key_dec[key_len + 25] = ctx->key_enc[1] = le32_to_cpu(key[1]); + ctx->key_dec[key_len + 26] = ctx->key_enc[2] = le32_to_cpu(key[2]); + ctx->key_dec[key_len + 27] = ctx->key_enc[3] = le32_to_cpu(key[3]); + + switch (key_len) + { + case AES_KEYSIZE_128: + t = ctx->key_enc[3]; + for (i = 0; i < 10; ++i) + loop4(i); + break; + + case AES_KEYSIZE_192: + ctx->key_enc[4] = le32_to_cpu(key[4]); + t = ctx->key_enc[5] = le32_to_cpu(key[5]); + for (i = 0; i < 8; ++i) + loop6(i); + break; + + case AES_KEYSIZE_256: + ctx->key_enc[4] = le32_to_cpu(key[4]); + ctx->key_enc[5] = le32_to_cpu(key[5]); + ctx->key_enc[6] = le32_to_cpu(key[6]); + t = ctx->key_enc[7] = le32_to_cpu(key[7]); + for (i = 0; i < 6; ++i) + loop8(i); + loop8tophalf(i); + break; + } + + ctx->key_dec[0] = ctx->key_enc[key_len + 24]; + ctx->key_dec[1] = ctx->key_enc[key_len + 25]; + ctx->key_dec[2] = ctx->key_enc[key_len + 26]; + ctx->key_dec[3] = ctx->key_enc[key_len + 27]; + + for (i = 4; i < key_len + 24; ++i) + { + j = key_len + 24 - (i & ~3) + (i & 3); + imix_col(ctx->key_dec[j], ctx->key_enc[i]); + } + + return 0; +} + +/* aes_set_key - Set the AES key. + * @ctx: AES context struct. + * @in_key: The input key. + * @key_len: The size of the key. + * + * Returns 0 on success, on failure -1 is returned. + * The function uses aes_expand_key() to expand the key. + */ + +static int aes_set_key(aes_context_t *ctx, const uint8_t *in_key, + unsigned int key_len) +{ + int ret; + + ret = aes_expand_key(ctx, in_key, key_len); + if (!ret) return 0; + + return -1; +} + +static void aes_encrypt(aes_context_t *ctx, uint8_t *out, const uint8_t *in) +{ + const uint32_t *src = (const uint32_t *)in; + uint32_t *dst = (uint32_t *)out; + uint32_t b0[4]; + uint32_t b1[4]; + const uint32_t *kp = ctx->key_enc + 4; + const int key_len = ctx->key_length; + + b0[0] = le32_to_cpu(src[0]) ^ ctx->key_enc[0]; + b0[1] = le32_to_cpu(src[1]) ^ ctx->key_enc[1]; + b0[2] = le32_to_cpu(src[2]) ^ ctx->key_enc[2]; + b0[3] = le32_to_cpu(src[3]) ^ ctx->key_enc[3]; + + if (key_len > 24) + { + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + } + + if (key_len > 16) + { + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + } + + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_nround(b0, b1, kp); + f_nround(b1, b0, kp); + f_lround(b0, b1, kp); + + dst[0] = cpu_to_le32(b0[0]); + dst[1] = cpu_to_le32(b0[1]); + dst[2] = cpu_to_le32(b0[2]); + dst[3] = cpu_to_le32(b0[3]); +} + +/* Generate a set of new PRNG values by encrypting a new block. */ + +static void prng_generate(void) +{ + byte input[16]; + byte output[16]; + unsigned int i; + + /* Input for the cipher is a consecutively increasing 32-bit counter. */ + + for (i = 0; i < 4; ++i) + { + input[4 * i] = prng_input_counter & 0xff; + input[4 * i + 1] = (prng_input_counter >> 8) & 0xff; + input[4 * i + 2] = (prng_input_counter >> 16) & 0xff; + input[4 * i + 3] = (prng_input_counter >> 24) & 0xff; + ++prng_input_counter; + } + + aes_encrypt(&prng_context, output, input); + + for (i = 0; i < 4; ++i) + { + prng_values[i] = output[4 * i] | (output[4 * i + 1] << 8) | + (output[4 * i + 2] << 16) | (output[4 * i + 3] << 24); + } + + prng_value_index = 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize Pseudo-RNG using the specified 128-bit key. */ + +void prng_start(prng_seed_t key) +{ + aes_set_key(&prng_context, key, sizeof(prng_seed_t)); + prng_value_index = 4; + prng_input_counter = 0; + prng_enabled = true; +} + +void prng_stop(void) +{ + prng_enabled = false; +} + +/* Read a random 32-bit integer from the PRNG. */ + +unsigned int prng_random(void) +{ + unsigned int result; + + if (!prng_enabled) + { + return 0; + } + + if (prng_value_index >= 4) + { + prng_generate(); + } + + result = prng_values[prng_value_index]; + ++prng_value_index; + + return result; +} diff --git a/games/NXDoom/src/aes_prng.h b/games/NXDoom/src/aes_prng.h new file mode 100644 index 000000000..eed8dd78c --- /dev/null +++ b/games/NXDoom/src/aes_prng.h @@ -0,0 +1,44 @@ +/**************************************************************************** + * apps/games/NXDoom/src/aes_prng.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Pseudo-random number generator for secure demos. + * + ****************************************************************************/ + +#ifndef __AES_PRNG_H__ +#define __AES_PRNG_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/* Nonce value used as random seed for secure demos. */ + +typedef byte prng_seed_t[16]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void prng_start(prng_seed_t seed); +void prng_stop(void); +unsigned int prng_random(void); + +#endif /* __AES_PRNG_H__ */ diff --git a/games/NXDoom/src/config.h b/games/NXDoom/src/config.h new file mode 100644 index 000000000..b64275300 --- /dev/null +++ b/games/NXDoom/src/config.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * apps/games/NXDoom/src/config.h + * + * SPDX-License-Identifer: GPLv2 + * + * NuttX compatible config.h file. + * + * TODO: can we take a cleaner approach later by baking this into logic? + * + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HAVE_DECL_STRCASECMP 1 +#define HAVE_DECL_STRNCASECMP 1 + +#define PACKAGE_TARNAME "nxdoom" +#define PACKAGE_NAME "NXDoom" +#define PACKAGE_STRING "NXDoom v0.0.0" +#define PROGRAM_PREFIX "" + +#define DISABLE_SDL2MIXER +#define DISABLE_SDL2NET diff --git a/games/NXDoom/src/d_dedicated.c b/games/NXDoom/src/d_dedicated.c new file mode 100644 index 000000000..307010f34 --- /dev/null +++ b/games/NXDoom/src/d_dedicated.c @@ -0,0 +1,60 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_dedicated.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Code specific to the standalone dedicated server. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" + +#include "m_argv.h" +#include "net_defs.h" + +#include "net_dedicated.h" +#include "net_server.h" +#include "z_zone.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_cl_run(void) +{ + /* No client present :-) + * + * This is here because the server code sometimes runs this + * to let the client do some processing if it needs to. + * In a standalone dedicated server, we don't have a client. + */ +} + +void d_doom_main(void) +{ + printf(PACKAGE_NAME " standalone dedicated server\n"); + + z_init(); + + net_dedicated_server(); +} diff --git a/games/NXDoom/src/d_event.c b/games/NXDoom/src/d_event.c new file mode 100644 index 000000000..0b2a29e6c --- /dev/null +++ b/games/NXDoom/src/d_event.c @@ -0,0 +1,81 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_event.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: Event handling. + * + * Events are asynchronous inputs generally generated by the game user. + * Events can be discarded if no responder claims them + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXEVENTS 64 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static event_t events[MAXEVENTS]; +static int eventhead; +static int eventtail; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* d_post_event + * Called by the I/O functions when input is detected + */ + +void d_post_event(event_t *ev) +{ + events[eventhead] = *ev; + eventhead = (eventhead + 1) % MAXEVENTS; +} + +/* Read an event from the queue. */ + +event_t *d_pop_event(void) +{ + event_t *result; + + /* No more events waiting. */ + + if (eventtail == eventhead) + { + return NULL; + } + + result = &events[eventtail]; + + /* Advance to the next event in the queue. */ + + eventtail = (eventtail + 1) % MAXEVENTS; + + return result; +} diff --git a/games/NXDoom/src/d_event.h b/games/NXDoom/src/d_event.h new file mode 100644 index 000000000..f1f43133f --- /dev/null +++ b/games/NXDoom/src/d_event.h @@ -0,0 +1,192 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_event.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * + ****************************************************************************/ + +#ifndef __D_EVENT__ +#define __D_EVENT__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Event handling. */ + +/* Input event types. */ + +typedef enum +{ + /* Key press/release events. + * data1: Key code (from doomkeys.h) of the key that was + * pressed or released. This is the key as it appears + * on a US keyboard layout, and does not change with + * layout. + * For ev_keydown only: + * data2: ASCII representation of the key that was pressed that + * changes with the keyboard layout; eg. if 'Z' is + * pressed on a German keyboard, data1='y',data2='z'. + * Not affected by modifier keys. + * data3: ASCII input, fully modified according to keyboard + * layout and any modifier keys that are held down. + * Only set if i_start_text_input() has been called. + */ + + ev_keydown, + ev_keyup, + + /* Mouse movement event. + * data1: Bitfield of buttons currently held down. + * (bit 0 = left; bit 1 = right; bit 2 = middle). + * data2: X axis mouse movement (turn). + * data3: Y axis mouse movement (forward/backward). + */ + + ev_mouse, + + /* Joystick state. + * data1: Bitfield of buttons currently pressed. + * data2: X axis mouse movement (turn). + * data3: Y axis mouse movement (forward/backward). + * data4: Third axis mouse movement (strafe). + * data5: Fourth axis mouse movement (look) + * data6: Dpad and analog stick direction. + */ + + ev_joystick, + + /* Quit event. Triggered when the user clicks the "close" button + * to terminate the application. + */ + + ev_quit +} evtype_t; + +/* Event structure. */ + +typedef struct +{ + evtype_t type; + + /* Event-specific data; see the descriptions given above. */ + + int data1; + int data2; + int data3; + int data4; + int data5; + int data6; +} event_t; + +/* Button/action code definitions. */ + +typedef enum +{ + /* Press "Fire". */ + + BT_ATTACK = 1, + + /* Use button, to open doors, activate switches. */ + + BT_USE = 2, + + /* Flag: game events, not really buttons. */ + + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + /* Flag, weapon change pending. + * If true, the next 3 bits hold weapon num. + */ + + BT_CHANGE = 4, + + /* The 3bit weapon mask and shift, convenience. */ + + BT_WEAPONMASK = (8 + 16 + 32), + BT_WEAPONSHIFT = 3, + + /* Pause the game. */ + + BTS_PAUSE = 1, + + /* Save the game at each console. */ + + BTS_SAVEGAME = 2, + + /* Savegame slot numbers occupy the second byte of buttons. */ + + BTS_SAVEMASK = (4 + 8 + 16), + BTS_SAVESHIFT = 2, +} buttoncode_t; + +/* villsa [STRIFE] Strife specific buttons + * TODO - not finished + */ + +typedef enum +{ + /* Player view look up */ + + BT2_LOOKUP = 1, + + /* Player view look down */ + + BT2_LOOKDOWN = 2, + + /* Center player's view */ + + BT2_CENTERVIEW = 4, + + /* Use inventory item */ + + BT2_INVUSE = 8, + + /* Drop inventory item */ + + BT2_INVDROP = 16, + + /* Jump up and down */ + + BT2_JUMP = 32, + + /* Use medkit */ + + BT2_HEALTH = 128, +} buttoncode2_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by IO functions when input is detected. */ + +void d_post_event(event_t *ev); + +/* Read an event from the event queue */ + +event_t *d_pop_event(void); + +#endif /* __D_EVENT__ */ diff --git a/games/NXDoom/src/d_iwad.c b/games/NXDoom/src/d_iwad.c new file mode 100644 index 000000000..a6f1cd945 --- /dev/null +++ b/games/NXDoom/src/d_iwad.c @@ -0,0 +1,539 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_iwad.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Search for and locate an IWAD file, and initialize according + * to the IWAD type. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "deh_str.h" +#include "doomkeys.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Array of locations to search for IWAD files + * + * "128 IWAD search directories should be enough for anybody". + */ + +#define MAX_IWAD_DIRS 128 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const iwad_t g_iwads[] = +{ + {"doom2.wad", doom2, commercial, "Doom II"}, + {"plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment"}, + {"tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution"}, + {"doom.wad", doom, retail, "Doom"}, + {"doom1.wad", doom, shareware, "Doom Shareware"}, + {"doom2f.wad", doom2, commercial, "Doom II: L'Enfer sur Terre"}, + {"chex.wad", pack_chex, retail, "Chex Quest"}, + {"hacx.wad", pack_hacx, commercial, "Hacx"}, + {"freedoom2.wad", doom2, commercial, "Freedoom: Phase 2"}, + {"freedoom1.wad", doom, retail, "Freedoom: Phase 1"}, + {"freedm.wad", doom2, commercial, "FreeDM"}, + {"heretic.wad", heretic, retail, "Heretic"}, + {"heretic1.wad", heretic, shareware, "Heretic Shareware"}, + {"hexen.wad", hexen, commercial, "Hexen"}, + {"strife1.wad", strife, commercial, "Strife"}, +}; + +static boolean iwad_dirs_built = false; +static const char *iwad_dirs[MAX_IWAD_DIRS]; +static int num_iwad_dirs = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void add_iwad_dir(const char *dir) +{ + if (num_iwad_dirs < MAX_IWAD_DIRS) + { + iwad_dirs[num_iwad_dirs] = dir; + ++num_iwad_dirs; + } +} + +/* Returns true if the specified path is a path to a file + * of the specified name. + */ + +static boolean dir_is_file(const char *path, const char *filename) +{ + return strchr(path, DIR_SEPARATOR) != NULL && + !strcasecmp(m_base_name(path), filename); +} + +/* Check if the specified directory contains the specified IWAD + * file, returning the full path to the IWAD if found, or NULL + * if not found. + */ + +static char *check_dir_has_iwad(const char *dir, const char *iwadname) +{ + char *filename; + char *probe; + + /* As a special case, the "directory" may refer directly to an + * IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH. + */ + + probe = m_file_case_exists(dir); + if (dir_is_file(dir, iwadname) && probe != NULL) + { + return probe; + } + + /* Construct the full path to the IWAD if it is located in + * this directory, and check if it exists. + */ + + if (!strcmp(dir, ".")) + { + filename = m_string_duplicate(iwadname); + } + else + { + filename = m_string_join(dir, DIR_SEPARATOR_S, iwadname, NULL); + } + + free(probe); + probe = m_file_case_exists(filename); + free(filename); + + if (probe != NULL) + { + return probe; + } + + return NULL; +} + +/* Search a directory to try to find an IWAD + * Returns the location of the IWAD if found, otherwise NULL. + */ + +static char *search_dir_for_iwad(const char *dir, int mask, + gamemission_t *mission) +{ + char *filename; + size_t i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (((1 << g_iwads[i].mission) & mask) == 0) + { + continue; + } + + filename = check_dir_has_iwad(dir, (g_iwads[i].name)); + + if (filename != NULL) + { + *mission = g_iwads[i].mission; + + return filename; + } + } + + return NULL; +} + +/* When given an IWAD with the '-iwad' parameter, + * attempt to identify it by its name. + */ + +static gamemission_t identify_iwad_by_name(const char *name, int mask) +{ + size_t i; + gamemission_t mission; + + name = m_base_name(name); + mission = none; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + /* Check if the filename is this IWAD name. + * Only use supported missions: + */ + + if (((1 << g_iwads[i].mission) & mask) == 0) continue; + + /* Check if it ends in this IWAD name. */ + + if (!strcasecmp(name, g_iwads[i].name)) + { + mission = g_iwads[i].mission; + break; + } + } + + return mission; +} + +/* Add IWAD directories parsed from splitting a path string containing + * paths separated by PATH_SEPARATOR. 'suffix' is a string to concatenate + * to the end of the paths before adding them. + */ + +static void add_iwad_path(const char *path, const char *suffix) +{ + char *left; + char *p; + char *dup_path; + + dup_path = m_string_duplicate(path); + + /* Split into individual dirs within the list. */ + + left = dup_path; + + for (; ; ) + { + p = strchr(left, PATH_SEPARATOR); + if (p != NULL) + { + /* Break at the separator and use the left hand side + * as another iwad dir + */ + + *p = '\0'; + + add_iwad_dir(m_string_join(left, suffix, NULL)); + left = p + 1; + } + else + { + break; + } + } + + add_iwad_dir(m_string_join(left, suffix, NULL)); + + free(dup_path); +} + +/* Build a list of IWAD files */ + +static void buld_iwad_dir_list(void) +{ + char *env; + + if (iwad_dirs_built) + { + return; + } + + /* Look in the current directory. Doom always does this. */ + + add_iwad_dir("."); + + /* Next check the directory where the executable is located. This might + * be different from the current directory. + */ + + add_iwad_dir(m_dir_name(myargv[0])); + + /* Add DOOMWADDIR if it is in the environment */ + + env = getenv("DOOMWADDIR"); + if (env != NULL) + { + add_iwad_dir(env); + } + + /* Add dirs from DOOMWADPATH: */ + + env = getenv("DOOMWADPATH"); + if (env != NULL) + { + add_iwad_path(env, ""); + } + + /* Don't run this function again. */ + + iwad_dirs_built = true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Searches WAD search paths for an WAD with a specific filename. */ + +char *d_find_wad_by_name(const char *name) +{ + char *path; + char *probe; + int i; + + /* Absolute path? */ + + probe = m_file_case_exists(name); + if (probe != NULL) + { + return probe; + } + + buld_iwad_dir_list(); + + /* Search through all IWAD paths for a file with the given name. */ + + for (i = 0; i < num_iwad_dirs; ++i) + { + /* As a special case, if this is in DOOMWADDIR or DOOMWADPATH, + * the "directory" may actually refer directly to an IWAD + * file. + */ + + probe = m_file_case_exists(iwad_dirs[i]); + if (dir_is_file(iwad_dirs[i], name) && probe != NULL) + { + return probe; + } + + free(probe); + + /* Construct a string for the full path */ + + path = m_string_join(iwad_dirs[i], DIR_SEPARATOR_S, name, NULL); + + probe = m_file_case_exists(path); + if (probe != NULL) + { + return probe; + } + + free(path); + } + + /* File not found */ + + return NULL; +} + +/* d_try_find_wad_by_name + * + * Searches for a WAD by its filename, or returns a copy of the filename + * if not found. + */ + +char *d_try_find_wad_by_name(const char *filename) +{ + char *result; + + result = d_find_wad_by_name(filename); + + if (result != NULL) + { + return result; + } + else + { + return m_string_duplicate(filename); + } +} + +/* FindIWAD + * Checks availability of IWAD files by name, + * to determine whether registered/commercial features + * should be executed (notably loading PWADs). + */ + +char *d_find_iwad(int mask, gamemission_t *mission) +{ + char *result; + const char *iwadfile; + int iwadparm; + int i; + + /* Check for the -iwad parameter + * + * Specify an IWAD file to use. + * + * @arg + * + */ + + iwadparm = m_check_parm_with_args("-iwad", 1); + + if (iwadparm) + { + /* Search through IWAD dirs for an IWAD with the given name. */ + + iwadfile = myargv[iwadparm + 1]; + + result = d_find_wad_by_name(iwadfile); + + if (result == NULL) + { + i_error("IWAD file '%s' not found!", iwadfile); + } + + *mission = identify_iwad_by_name(result, mask); + } + else + { + /* Search through the list and look for an IWAD */ + + result = NULL; + + buld_iwad_dir_list(); + + for (i = 0; result == NULL && i < num_iwad_dirs; ++i) + { + result = search_dir_for_iwad(iwad_dirs[i], mask, mission); + } + } + + return result; +} + +/* Find all IWADs in the IWAD search path matching the given mask. */ + +const iwad_t **d_find_all_iwads(int mask) +{ + const iwad_t **result; + int result_len; + char *filename; + int i; + + result = malloc(sizeof(iwad_t *) * (arrlen(g_iwads) + 1)); + result_len = 0; + + /* Try to find all IWADs */ + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (((1 << g_iwads[i].mission) & mask) == 0) + { + continue; + } + + filename = d_find_wad_by_name(g_iwads[i].name); + + if (filename != NULL) + { + result[result_len] = &g_iwads[i]; + ++result_len; + } + } + + /* End of list */ + + result[result_len] = NULL; + + return result; +} + +/* Get the IWAD name used for savegames. */ + +const char *d_save_game_iwad_name(gamemission_t gamemission, + game_variant_t gamevariant) +{ + size_t i; + + /* Determine the IWAD name to use for savegames. + * This determines the directory the savegame files get put into. + * + * Note that we match on gamemission rather than on IWAD name. + * This ensures that doom1.wad and doom.wad saves are stored + * in the same place. + */ + + if (gamevariant == freedoom) + { + if (gamemission == doom) + { + return "freedoom1.wad"; + } + else if (gamemission == doom2) + { + return "freedoom2.wad"; + } + } + else if (gamevariant == freedm && gamemission == doom2) + { + return "freedm.wad"; + } + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (gamemission == g_iwads[i].mission) + { + return g_iwads[i].name; + } + } + + /* Default fallback: */ + + return "unknown.wad"; +} + +const char *d_suggest_iwad_name(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (g_iwads[i].mission == mission && g_iwads[i].mode == mode) + { + return g_iwads[i].name; + } + } + + return "unknown.wad"; +} + +const char *d_suggest_game_name(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_iwads); ++i) + { + if (g_iwads[i].mission == mission && + (mode == indetermined || g_iwads[i].mode == mode)) + { + return g_iwads[i].description; + } + } + + return "Unknown game?"; +} diff --git a/games/NXDoom/src/d_iwad.h b/games/NXDoom/src/d_iwad.h new file mode 100644 index 000000000..bb2681c8b --- /dev/null +++ b/games/NXDoom/src/d_iwad.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_iwad.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Find IWAD and initialize according to IWAD type. + * + ****************************************************************************/ + +#ifndef __D_IWAD__ +#define __D_IWAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IWAD_MASK_DOOM \ + ((1 << doom) | (1 << doom2) | (1 << pack_tnt) | (1 << pack_plut) | \ + (1 << pack_chex) | (1 << pack_hacx)) +#define IWAD_MASK_HERETIC (1 << heretic) +#define IWAD_MASK_HEXEN (1 << hexen) +#define IWAD_MASK_STRIFE (1 << strife) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + gamemission_t mission; + game_mode_t mode; + const char *description; +} iwad_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +char *d_find_wad_by_name(const char *filename); +char *d_try_find_wad_by_name(const char *filename); +char *d_find_iwad(int mask, gamemission_t *mission); +const iwad_t **d_find_all_iwads(int mask); +const char *d_save_game_iwad_name(gamemission_t gamemission, + game_variant_t gamevariant); +const char *d_suggest_iwad_name(gamemission_t mission, game_mode_t mode); +const char *d_suggest_game_name(gamemission_t mission, game_mode_t mode); + +#endif /* __D_IWAD__ */ diff --git a/games/NXDoom/src/d_loop.c b/games/NXDoom/src/d_loop.c new file mode 100644 index 000000000..28b00dfaa --- /dev/null +++ b/games/NXDoom/src/d_loop.c @@ -0,0 +1,955 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_loop.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Main loop code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_event.h" +#include "d_loop.h" +#include "d_ticcmd.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" + +#include "m_argv.h" +#include "m_fixed.h" + +#ifdef CONFIG_GAMES_NXDOOM_NET +#include "net_client.h" +#include "net_gui.h" +#include "net_io.h" +#include "net_loop.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_server.h" +#endif + +/* TODO: Move nonvanilla demo functions into a dedicated file. */ + +#include "m_misc.h" +#include "w_wad.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maximum time that we wait in try_run_tics() for netgame data to be + * received before we bail out and render a frame anyway. + * Vanilla Doom used 20 for this value, but we use a smaller value + * instead for better responsiveness of the menu when we're stuck. + */ + +#define MAX_NETGAME_STALL_TICS 5 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The complete set of data for a particular tic. */ + +typedef struct +{ + ticcmd_t cmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + boolean ingame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} ticcmd_set_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* gametic is the tic about to (or currently being) run + * maketic is the tic that hasn't had control made for it yet + * recvtic is the latest tic received from the server. + * + * a gametic cannot be run until ticcmds are received for it + * from all players. + */ + +static ticcmd_set_t ticdata[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* The index of the next tic to be made (with a call to build_ticcmd). */ + +static int maketic; + +/* The number of complete tics received from the server so far. */ + +static int recvtic; + +/* Index of the local player. */ + +static int localplayer; + +/* Used for original sync code. */ + +static int skiptics = 0; + +/* Use new client synchronization code */ + +static boolean new_sync = true; + +/* Callback functions for loop code. */ + +static loop_interface_t *loop_interface = NULL; + +/* Current players in the multiplayer game. + * This is distinct from playeringame[] used by the game code, which may + * modify playeringame[] when playing back multiplayer demos. + */ + +static boolean local_playeringame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + +/* Requested player class "sent" to the server on connect. + * If we are only doing a single player game then this needs to be remembered + * and saved in the game settings. + */ + +static int player_class; + +#ifndef CONFIG_GAMES_NXDOOM_NET +static boolean net_client_connected = false; +static boolean drone = false; +#endif + +static int frameon; +static int frameskip[4]; +static int oldnettics; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* The number of tics that have been run (using run_tic) so far. */ + +int gametic; + +/* When set to true, a single tic is run each time try_run_tics() is called. + * This is used for -timedemo mode. + */ + +boolean singletics = false; + +/* Reduce the bandwidth needed by sampling game input less and transmitting + * less. If ticdup is 2, sample half normal, 3 = one third normal, etc. + */ + +int ticdup; + +/* Amount to offset the timer for game sync. */ + +fixed_t offsetms; + +int lasttime; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* 35 fps clock adjusted by offsetms milliseconds */ + +static int get_adjusted_time(void) +{ + int time_ms; + + time_ms = i_get_time_ms(); + + if (new_sync) + { + /* Use the adjustments from net_client.c only if we are + * using the new sync mode. + */ + + time_ms += (offsetms / FRACUNIT); + } + + return (time_ms * TICRATE) / 1000; +} + +static boolean build_new_tic(void) +{ + int gameticdiv; + ticcmd_t cmd; + + gameticdiv = gametic / ticdup; + + i_start_tic(); + loop_interface->process_events(); + + /* Always run the menu */ + + loop_interface->run_menu(); + + if (drone) + { + /* In drone mode, do not generate any ticcmds. */ + + return false; + } + + if (new_sync) + { + /* If playing single player, do not allow tics to buffer + * up very far + */ + + if (!net_client_connected && maketic - gameticdiv > 2) return false; + + /* Never go more than ~200ms ahead */ + + if (maketic - gameticdiv > 8) return false; + } + else + { + if (maketic - gameticdiv >= 5) return false; + } + + /* printf ("mk:%i ",maketic); */ + + memset(&cmd, 0, sizeof(ticcmd_t)); + loop_interface->build_ticcmd(&cmd, maketic); + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (net_client_connected) + { + net_cl_send_ticcmd(&cmd, maketic); + } +#endif + + ticdata[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].cmds[localplayer] = + cmd; + ticdata[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].ingame[localplayer] = + true; + + ++maketic; + + return true; +} + +static boolean strict_demos(void) +{ + /* @category demo + * + * When recording or playing back demos, disable any extensions + * of the vanilla demo format - record demos as vanilla would do, + * and play back demos as vanilla would do. + * + */ + + return m_parm_exists("-strictdemos"); +} + +static void d_disconnected(void) +{ + /* In drone mode, the game cannot continue once disconnected. */ + + if (drone) + { + i_error("Disconnected from server in drone mode."); + } + + /* disconnected from server */ + + printf("Disconnected from server.\n"); +} + +static int get_low_tic(void) +{ + int lowtic; + + lowtic = maketic; + + if (net_client_connected) + { + if (drone || recvtic < lowtic) + { + lowtic = recvtic; + } + } + + return lowtic; +} + +static void old_net_sync(void) +{ + unsigned int i; + int keyplayer = -1; + + frameon++; + + /* ideally maketic should be 1 - 3 tics above lowtic + * if we are consistently slower, speed up time + */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; i++) + { + if (local_playeringame[i]) + { + keyplayer = i; + break; + } + } + + if (keyplayer < 0) + { + /* If there are no players, we can never advance anyway */ + + return; + } + + if (localplayer == keyplayer) + { + /* the key player does not adapt */ + } + else + { + if (maketic <= recvtic) + { + lasttime--; + } + + frameskip[frameon & 3] = oldnettics > recvtic; + oldnettics = maketic; + + if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) + { + skiptics = 1; + } + } +} + +/* Returns true if there are players in the game: */ + +static boolean players_in_game(void) +{ + boolean result = false; + unsigned int i; + + /* If we are connected to a server, check if there are any players + * in the game. + */ + + if (net_client_connected) + { + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + result = result || local_playeringame[i]; + } + } + + /* Whether single or multi-player, unless we are running as a drone, + * we are in the game. + */ + + if (!drone) + { + result = true; + } + + return result; +} + +/* When using ticdup, certain values must be cleared out when running + * the duplicate ticcmds. + */ + +static void ticdup_squash(ticcmd_set_t *set) +{ + ticcmd_t *cmd; + unsigned int i; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + cmd = &set->cmds[i]; + cmd->chatchar = 0; + if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; + } +} + +/* When running in single player mode, clear all the ingame[] array + * except the local player. + */ + +static void single_player_clear(ticcmd_set_t *set) +{ + unsigned int i; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (i != localplayer) + { + set->ingame[i] = false; + } + } +} + +/* Returns true if the given lump number corresponds to data from a .lmp + * file, as opposed to a WAD. + */ + +static boolean is_demo_file(int lumpnum) +{ + char *lower; + boolean result; + + lower = m_string_duplicate(lumpinfo[lumpnum]->wad_file->path); + m_force_lowercase(lower); + result = m_string_ends_with(lower, ".lmp"); + free(lower); + + return result; +} + +/* Block until the game start message is received from the server. */ + +#ifdef CONFIG_GAMES_NXDOOM_NET +static void block_until_start(net_gamesettings_t *settings, + netgame_startup_callback_t callback) +{ + while (!net_cl_get_settings(settings)) + { + net_cl_run(); + net_sv_run(); + + if (!net_client_connected) + { + i_error("Lost connection to server"); + } + + if (callback != NULL && !callback(net_client_wait_data.ready_players, + net_client_wait_data.num_players)) + { + i_error("Netgame startup aborted."); + } + + usleep(100000); + } +} + +/* d_quit_net_game + * + * Called before quitting to leave a net game without hanging the other + * players + * + * Broadcasts special packets to other players to notify of game exit + */ + +static void d_quit_net_game(void) +{ + net_sv_shutdown(); + net_cl_disconnect(); +} +#endif /* CONFIG_GAMES_NXDOOM_NET */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* net_update + * Builds ticcmds for console player, sends out a packet + */ + +void net_update(void) +{ + int nowtime; + int newtics; + int i; + + /* If we are running with singletics (timing a demo), this + * is all done separately. + */ + + if (singletics) return; + + /* Run network subsystems */ + +#ifdef CONFIG_GAMES_NXDOOM_NET + net_cl_run(); + net_sv_run(); +#endif + + /* check time */ + + nowtime = get_adjusted_time() / ticdup; + newtics = nowtime - lasttime; + + lasttime = nowtime; + + if (skiptics <= newtics) + { + newtics -= skiptics; + skiptics = 0; + } + else + { + skiptics -= newtics; + newtics = 0; + } + + /* build new ticcmds for console player */ + + for (i = 0; i < newtics; i++) + { + if (!build_new_tic()) + { + break; + } + } +} + +/* Invoked by the network engine when a complete set of ticcmds is + * available. + */ + +void d_receive_tic(ticcmd_t *ticcmds, boolean *players_mask) +{ + int i; + + /* Disconnected from server? */ + + if (ticcmds == NULL && players_mask == NULL) + { + d_disconnected(); + return; + } + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (!drone && i == localplayer) + { + /* This is us. Don't overwrite it. */ + } + else + { + ticdata[recvtic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].cmds[i] = + ticcmds[i]; + ticdata[recvtic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].ingame[i] = + players_mask[i]; + } + } + + ++recvtic; +} + +/**************************************************************************** + * Name: d_start_game_loop + * + * Description: + * Called after the screen is set but before the game starts running. + * + ****************************************************************************/ + +void d_start_game_loop(void) +{ + lasttime = get_adjusted_time() / ticdup; +} + +void d_start_net_game(net_gamesettings_t *settings, + netgame_startup_callback_t callback) +{ + int i; + + offsetms = 0; + recvtic = 0; + + settings->consoleplayer = 0; + settings->num_players = 1; + settings->player_classes[0] = player_class; + + /* @category net + * + * Use original network client sync code rather than the improved + * sync code. + */ + + settings->new_sync = !m_parm_exists("-oldsync"); + + /* @category net + * @arg + * + * Send n extra tics in every packet as insurance against dropped + * packets. + */ + + i = m_check_parm_with_args("-extratics", 1); + + if (i > 0) + settings->extratics = atoi(myargv[i + 1]); + else + settings->extratics = 1; + + /* @category net + * @arg + * + * Reduce the resolution of the game by a factor of n, reducing + * the amount of network bandwidth needed. + */ + + i = m_check_parm_with_args("-dup", 1); + + if (i > 0) + settings->ticdup = atoi(myargv[i + 1]); + else + settings->ticdup = 1; + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (net_client_connected) + { + /* Send our game settings and block until game start is received + * from the server. + */ + + net_cl_start_game(settings); + block_until_start(settings, callback); + + /* Read the game settings that were received. */ + + net_cl_get_settings(settings); + } + + if (drone) + { + settings->consoleplayer = 0; + } +#endif + + /* Set the local player and playeringame[] values. */ + + localplayer = settings->consoleplayer; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + local_playeringame[i] = i < settings->num_players; + } + + /* Copy settings to global variables. */ + + ticdup = settings->ticdup; + new_sync = settings->new_sync; + + if (ticdup < 1) + { + i_error("d_start_net_game: invalid ticdup value (%d)", ticdup); + } + + /* TODO: Message disabled until we fix new_sync. + * + * if (!new_sync) + * { + * printf("Syncing netgames like Vanilla Doom.\n"); + * } + */ +} + +boolean d_init_net_game(net_connect_data_t *connect_data) +{ +#ifdef CONFIG_GAMES_NXDOOM_NET + boolean result = false; + net_addr_t *addr = NULL; + int i; + + /* Call d_quit_net_game on exit: */ + + i_at_exit(d_quit_net_game, true); +#endif + + player_class = connect_data->player_class; + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start a multiplayer server, listening for connections. + */ + + if (m_check_parm("-server") > 0 || m_check_parm("-privateserver") > 0) + { + net_sv_init(); + net_sv_add_module(&net_loop_server_module); + net_sv_add_module(&net_sdl_module); + net_sv_register_with_master(); + + net_loop_client_module.init_client(); + addr = net_loop_client_module.resolve_address(NULL); + net_reference_address(addr); + } + else + { + /* @category net + * + * Automatically search the local LAN for a multiplayer + * server and join it. + */ + + i = m_check_parm("-autojoin"); + + if (i > 0) + { + addr = net_find_lan_server(); + + if (addr == NULL) + { + i_error("No server found on local LAN"); + } + } + + /* @arg
+ * @category net + * + * Connect to a multiplayer server running on the given + * address. + */ + + i = m_check_parm_with_args("-connect", 1); + + if (i > 0) + { + net_sdl_module.init_client(); + addr = net_sdl_module.resolve_address(myargv[i + 1]); + net_reference_address(addr); + + if (addr == NULL) + { + i_error("Unable to resolve '%s'\n", myargv[i + 1]); + } + } + } + + if (addr != NULL) + { + if (m_check_parm("-drone") > 0) + { + connect_data->drone = true; + } + + if (!net_cl_connect(addr, connect_data)) + { + i_error("d_init_net_game: Failed to connect to %s:\n%s\n", + net_addr_to_string(addr), net_client_reject_reason); + } + + printf("d_init_net_game: Connected to %s\n", net_addr_to_string(addr)); + net_release_address(addr); + + /* Wait for launch message received from server. */ + + net_wait_for_launch(); + + result = true; + } + + return result; +#endif + return false; +} + +/* try_run_tics */ + +void try_run_tics(void) +{ + int i; + int lowtic; + int entertic; + static int oldentertics; + int realtics; + int availabletics; + int counts; + + /* get real tics */ + + entertic = i_get_time() / ticdup; + realtics = entertic - oldentertics; + oldentertics = entertic; + + /* in singletics mode, run a single tic every time this function + * is called. + */ + + if (singletics) + { + build_new_tic(); + } + else + { + net_update(); + } + + lowtic = get_low_tic(); + + availabletics = lowtic - gametic / ticdup; + + /* decide how many tics to run */ + + if (new_sync) + { + counts = availabletics; + } + else + { + /* decide how many tics to run */ + + if (realtics < availabletics - 1) + counts = realtics + 1; + else if (realtics < availabletics) + counts = realtics; + else + counts = availabletics; + + if (counts < 1) counts = 1; + + if (net_client_connected) + { + old_net_sync(); + } + } + + if (counts < 1) counts = 1; + + /* wait for new tics if needed */ + + while (!players_in_game() || lowtic < gametic / ticdup + counts) + { + net_update(); + + lowtic = get_low_tic(); + + if (lowtic < gametic / ticdup) + i_error("try_run_tics: lowtic < gametic"); + + /* Still no tics to run? Sleep until some are available. */ + + if (lowtic < gametic / ticdup + counts) + { + /* If we're in a netgame, we might spin forever waiting for + * new network data to be received. So don't stay in here + * forever - give the menu a chance to work. + */ + + if (i_get_time() / ticdup - entertic >= MAX_NETGAME_STALL_TICS) + { + return; + } + + usleep(1000); + } + } + + /* run the count * ticdup dics */ + + while (counts--) + { + ticcmd_set_t *set; + + if (!players_in_game()) + { + return; + } + + set = &ticdata[(gametic / ticdup) % + CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (!net_client_connected) + { + single_player_clear(set); + } + + for (i = 0; i < ticdup; i++) + { + if (gametic / ticdup > lowtic) i_error("gametic>lowtic"); + + memcpy(local_playeringame, set->ingame, + sizeof(local_playeringame)); + + loop_interface->run_tic(set->cmds, set->ingame); + gametic++; + + /* modify command for duplicated tics */ + + ticdup_squash(set); + } + + net_update(); /* check for new console commands */ + } +} + +void d_register_loop_callbacks(loop_interface_t *i) +{ + loop_interface = i; +} + +/* If the provided conditional value is true, we're trying to record + * a demo file that will include a non-vanilla extension. The function + * will return true if the conditional is true and it's allowed to use + * this extension (no extensions are allowed if -strictdemos is given + * on the command line). A warning is shown on the console using the + * provided string describing the non-vanilla expansion. + */ + +boolean d_non_vanilla_record(boolean conditional, const char *feature) +{ + if (!conditional || strict_demos()) + { + return false; + } + + printf("Warning: Recording a demo file with a non-vanilla extension " + "(%s). Use -strictdemos to disable this extension.\n", + feature); + + return true; +} + +/* If the provided conditional value is true, we're trying to play back + * a demo that includes a non-vanilla extension. We return true if the + * conditional is true and it's allowed to use this extension, checking + * that: + * - The -strictdemos command line argument is not provided. + * - The given lumpnum identifying the demo to play back identifies a + * demo that comes from a .lmp file, not a .wad file. + * - Before proceeding, a warning is shown to the user on the console. + */ + +boolean d_non_vanilla_playback(boolean conditional, int lumpnum, + const char *feature) +{ + if (!conditional || strict_demos()) + { + return false; + } + + if (!is_demo_file(lumpnum)) + { + printf("Warning: WAD contains demo with a non-vanilla extension " + "(%s)\n", + feature); + return false; + } + + printf("Warning: Playing back a demo file with a non-vanilla extension " + "(%s). Use -strictdemos to disable this extension.\n", + feature); + + return true; +} diff --git a/games/NXDoom/src/d_loop.h b/games/NXDoom/src/d_loop.h new file mode 100644 index 000000000..95c2f7a3c --- /dev/null +++ b/games/NXDoom/src/d_loop.h @@ -0,0 +1,118 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_loop.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Main loop stuff. + * + ****************************************************************************/ + +#ifndef __D_LOOP__ +#define __D_LOOP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_fixed.h" +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Callback function invoked while waiting for the netgame to start. + * The callback is invoked when new players are ready. The callback + * should return true, or return false to abort startup. + */ + +typedef boolean (*netgame_startup_callback_t)(int ready_players, + int num_players); + +typedef struct +{ + /* Read events from the event queue, and process them. */ + + void (*process_events)(void); + + /* Given the current input state, fill in the fields of the specified + * ticcmd_t structure with data for a new tic. + */ + + void (*build_ticcmd)(ticcmd_t *cmd, int maketic); + + /* Advance the game forward one tic, using the specified player input. */ + + void (*run_tic)(ticcmd_t *cmds, boolean *ingame); + + /* Run the menu (runs independently of the game). */ + + void (*run_menu)(void); +} loop_interface_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean singletics; +extern int gametic; +extern int ticdup; +extern fixed_t offsetms; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Register callback functions for the main loop code to use. */ + +void d_register_loop_callbacks(loop_interface_t *i); + +/* Create any new ticcmds and broadcast to other players. */ + +void net_update(void); + +/* ? how many ticks to run? */ + +void try_run_tics(void); + +/* Called at start of game loop to initialize timers */ + +void d_start_game_loop(void); + +/* Initialize networking code and connect to server. */ + +boolean d_init_net_game(net_connect_data_t *connect_data); + +/* Start game with specified settings. The structure will be updated + * with the actual settings for the game. + */ + +void d_start_net_game(net_gamesettings_t *settings, + netgame_startup_callback_t callback); + +/* Check if it is permitted to record a demo with a non-vanilla feature. */ + +boolean d_non_vanilla_record(boolean conditional, const char *feature); + +/* Check if it is permitted to play back a demo with a non-vanilla feature. */ + +boolean d_non_vanilla_playback(boolean conditional, int lumpnum, + const char *feature); + +void d_receive_tic(ticcmd_t *ticcmds, boolean *playeringame); + +#endif /* __D_LOOP__ */ diff --git a/games/NXDoom/src/d_mode.c b/games/NXDoom/src/d_mode.c new file mode 100644 index 000000000..6c4891cc1 --- /dev/null +++ b/games/NXDoom/src/d_mode.c @@ -0,0 +1,247 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_mode.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Functions and definitions relating to the game type and operational + * mode. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" +#include "doomtype.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Valid game mode/mission combinations, with the number of + * episodes/maps for each. + */ + +struct modemission +{ + gamemission_t mission; + game_mode_t mode; + int episode; + int map; +}; + +struct gameversion +{ + gamemission_t mission; + game_version_t version; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct modemission g_valid_modes[] = +{ + {.mission = doom, .mode = shareware, .episode = 1, .map = 9}, + {.mission = doom, .mode = registered, .episode = 3, .map = 9}, + {.mission = doom, .mode = retail, .episode = 4, .map = 9}, + {.mission = doom2, .mode = commercial, .episode = 1, .map = 32}, +}; + +/* Table of valid versions */ + +static struct gameversion g_valid_versions[] = +{ + {.mission = doom, .version = exe_doom_1_2}, + {.mission = doom, .version = exe_doom_1_5}, + {.mission = doom, .version = exe_doom_1_666}, + {.mission = doom, .version = exe_doom_1_7}, + {.mission = doom, .version = exe_doom_1_8}, + {.mission = doom, .version = exe_doom_1_9}, + {.mission = doom, .version = exe_hacx}, + {.mission = doom, .version = exe_ultimate}, + {.mission = doom, .version = exe_final}, + {.mission = doom, .version = exe_final2}, + {.mission = doom, .version = exe_chex}, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Check that a gamemode + gamemission received over the network is valid. */ + +boolean d_valid_game_mode(gamemission_t mission, game_mode_t mode) +{ + int i; + + for (i = 0; i < arrlen(g_valid_modes); ++i) + { + if (g_valid_modes[i].mode == mode && + g_valid_modes[i].mission == mission) + { + return true; + } + } + + return false; +} + +boolean d_valid_episode_map(gamemission_t mission, game_mode_t mode, + int episode, int map) +{ + int i; + + /* Hacks for Heretic secret episodes */ + + if (mission == heretic) + { + if (mode == retail && episode == 6) + { + return map >= 1 && map <= 3; + } + else if (mode == registered && episode == 4) + { + return map == 1; + } + } + + /* Find the table entry for this mission/mode combination. */ + + for (i = 0; i < arrlen(g_valid_modes); ++i) + { + if (mission == g_valid_modes[i].mission && + mode == g_valid_modes[i].mode) + { + return episode >= 1 && episode <= g_valid_modes[i].episode && + map >= 1 && map <= g_valid_modes[i].map; + } + } + + /* Unknown mode/mission combination */ + + return false; +} + +/* Get the number of valid episodes for the specified mission/mode. */ + +int d_get_num_episodes(gamemission_t mission, game_mode_t mode) +{ + int episode; + + episode = 1; + + while (d_valid_episode_map(mission, mode, episode, 1)) + { + ++episode; + } + + return episode - 1; +} + +boolean d_valid_game_version(gamemission_t mission, game_version_t version) +{ + int i; + + /* All Doom variants can use the Doom versions. */ + + if (mission == doom2 || mission == pack_plut || mission == pack_tnt || + mission == pack_hacx || mission == pack_chex) + { + mission = doom; + } + + for (i = 0; i < arrlen(g_valid_versions); ++i) + { + if (g_valid_versions[i].mission == mission && + g_valid_versions[i].version == version) + { + return true; + } + } + + return false; +} + +/* Does this mission type use ExMy form, rather than MAPxy form? */ + +boolean d_is_episode_map(gamemission_t mission) +{ + switch (mission) + { + case doom: + case heretic: + case pack_chex: + return true; + + case none: + case hexen: + case doom2: + case pack_hacx: + case pack_tnt: + case pack_plut: + case strife: + default: + return false; + } +} + +const char *d_game_mission_string(gamemission_t mission) +{ + switch (mission) + { + case none: + default: + return "none"; + case doom: + return "doom"; + case doom2: + return "doom2"; + case pack_tnt: + return "tnt"; + case pack_plut: + return "plutonia"; + case pack_hacx: + return "hacx"; + case pack_chex: + return "chex"; + case heretic: + return "heretic"; + case hexen: + return "hexen"; + case strife: + return "strife"; + } +} + +const char *d_game_mode_string(game_mode_t mode) +{ + switch (mode) + { + case shareware: + return "shareware"; + case registered: + return "registered"; + case commercial: + return "commercial"; + case retail: + return "retail"; + case indetermined: + default: + return "unknown"; + } +} diff --git a/games/NXDoom/src/d_mode.h b/games/NXDoom/src/d_mode.h new file mode 100644 index 000000000..26c282753 --- /dev/null +++ b/games/NXDoom/src/d_mode.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_mode.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Functions and definitions relating to the game type and operational + * mode. + * + ****************************************************************************/ + +#ifndef __D_MODE__ +#define __D_MODE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The "mission" controls what game we are playing. */ + +typedef enum +{ + doom, /* Doom 1 */ + doom2, /* Doom 2 */ + pack_tnt, /* Final Doom: TNT: Evilution */ + pack_plut, /* Final Doom: The Plutonia Experiment */ + pack_chex, /* Chex Quest (modded doom) */ + pack_hacx, /* Hacx (modded doom2) */ + heretic, /* Heretic */ + hexen, /* Hexen */ + strife, /* Strife */ + doom2f, /* Doom 2: L'Enfer sur Terre */ + none +} gamemission_t; + +/* The "mode" allows more accurate specification of the game mode we are + * in: eg. shareware vs. registered. So doom1.wad and doom.wad are the + * same mission, but a different mode. + */ + +typedef enum +{ + shareware, /* Doom/Heretic shareware */ + registered, /* Doom/Heretic registered */ + commercial, /* Doom II/Hexen */ + retail, /* Ultimate Doom */ + indetermined /* Unknown. */ +} game_mode_t; + +/* What version are we emulating? */ + +typedef enum +{ + exe_doom_1_2, /* Doom 1.2: shareware and registered */ + exe_doom_1_5, /* Doom 1.5: " */ + exe_doom_1_666, /* Doom 1.666: for shareware, registered and commercial */ + exe_doom_1_7, /* Doom 1.7/1.7a: " */ + exe_doom_1_8, /* Doom 1.8: " */ + exe_doom_1_9, /* Doom 1.9: " */ + exe_hacx, /* Hacx */ + exe_ultimate, /* Ultimate Doom (retail) */ + exe_final, /* Final Doom */ + exe_final2, /* Final Doom (alternate exe) */ + exe_chex, /* Chex Quest executable (based on Final Doom) */ + exe_heretic_1_3, /* Heretic 1.3 */ + exe_hexen_1_1, /* Hexen 1.1 */ + exe_hexen_1_1r2, /* Hexen 1.1 (alternate exe) */ + exe_strife_1_2, /* Strife v1.2 */ + exe_strife_1_31 /* Strife v1.31 */ +} game_version_t; + +/* What IWAD variant are we using? */ + +typedef enum +{ + vanilla, /* Vanilla Doom */ + freedoom, /* FreeDoom: Phase 1 + 2 */ + freedm, /* FreeDM */ + bfgedition, /* Doom Classic (Doom 3: BFG Edition) */ +} game_variant_t; + +/* Skill level. */ + +typedef enum +{ + sk_noitems = -1, /* the "-skill 0" hack */ + sk_baby = 0, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean d_valid_game_mode(gamemission_t mission, game_mode_t mode); +boolean d_valid_game_version(gamemission_t mission, game_version_t version); +boolean d_valid_episode_map(gamemission_t mission, game_mode_t mode, + int episode, int map); +int d_get_num_episodes(gamemission_t mission, game_mode_t mode); +boolean d_is_episode_map(gamemission_t mission); +const char *d_game_mission_string(gamemission_t mission); +const char *d_game_mode_string(game_mode_t mode); + +#endif /* __D_MODE__ */ diff --git a/games/NXDoom/src/d_ticcmd.h b/games/NXDoom/src/d_ticcmd.h new file mode 100644 index 000000000..c67340727 --- /dev/null +++ b/games/NXDoom/src/d_ticcmd.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/d_ticcmd.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __D_TICCMD__ +#define __D_TICCMD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The data sampled per tick (single player) + * and transmitted to other peers (multiplayer). + * Mainly movements/button commands per game tick, + * plus a checksum for internal state consistency. + */ + +typedef struct +{ + signed char forwardmove; /* *2048 for move */ + signed char sidemove; /* *2048 for move */ + short angleturn; /* <<16 for angle delta */ + byte chatchar; + byte buttons; + + /* villsa [STRIFE] according to the asm, + * consistency is a short, not a byte + */ + + byte consistency; /* checks for net game */ + + /* villsa - Strife specific: */ + + byte buttons2; + int inventory; + + /* Heretic/Hexen specific: */ + + byte lookfly; /* look/fly up/down/centering */ + byte arti; /* artitype_t to use */ +} ticcmd_t; + +#endif /* __D_TICCMD__ */ diff --git a/games/NXDoom/src/deh_defs.h b/games/NXDoom/src/deh_defs.h new file mode 100644 index 000000000..f4f5c1ea4 --- /dev/null +++ b/games/NXDoom/src/deh_defs.h @@ -0,0 +1,120 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Definitions for use in the dehacked code + * + ****************************************************************************/ + +#ifndef DEH_DEFS_H +#define DEH_DEFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "sha1.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct deh_context_s deh_context_t; +typedef struct deh_section_s deh_section_t; +typedef void (*deh_section_init_t)(void); +typedef void *(*deh_section_start_t)(deh_context_t *context, char *line); +typedef void (*deh_section_end_t)(deh_context_t *context, void *tag); +typedef void (*deh_line_parser_t)(deh_context_t *context, char *line, + void *tag); +typedef void (*deh_sha1_hash_t)(SHA1_CTX *context); + +struct deh_section_s +{ + const char *name; + + /* Called on startup to initialize code */ + + deh_section_init_t init; + + /* This is called when a new section is started. The pointer + * returned is used as a tag for the following calls. + */ + + deh_section_start_t start; + + /* This is called for each line in the section */ + + deh_line_parser_t line_parser; + + /* This is called at the end of the section for any cleanup */ + + deh_section_end_t end; + + /* Called when generating an SHA1 sum of the dehacked state */ + + deh_sha1_hash_t sha1_hash; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern deh_section_t *deh_section_types[]; +extern const char *deh_signatures[]; + +/* deh_ammo.c: */ + +extern deh_section_t deh_section_ammo; + +/* deh_cheat.c: */ + +extern deh_section_t deh_section_cheat; + +/* deh_frame.c: */ + +extern deh_section_t deh_section_frame; + +/* deh_misc.c: */ + +extern deh_section_t deh_section_misc; + +/* deh_ptr.c: */ + +extern deh_section_t deh_section_pointer; + +/* deh_sound.c */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +extern deh_section_t deh_section_sound; +#endif + +/* deh_text.c: */ + +extern deh_section_t deh_section_text; + +/* deh_thing.c: */ + +extern deh_section_t deh_section_thing; + +/* deh_weapon.c: */ + +extern deh_section_t deh_section_weapon; + +/* deh_bexstr.c: */ + +extern deh_section_t deh_section_bexstr; + +#endif /* DEH_DEFS_H */ diff --git a/games/NXDoom/src/deh_io.c b/games/NXDoom/src/deh_io.c new file mode 100644 index 000000000..fa1102bb5 --- /dev/null +++ b/games/NXDoom/src/deh_io.c @@ -0,0 +1,392 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Dehacked I/O code (does all reads from dehacked files) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "deh_defs.h" +#include "deh_io.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + DEH_INPUT_FILE, + DEH_INPUT_LUMP +} deh_input_type_t; + +struct deh_context_s +{ + deh_input_type_t type; + char *filename; + + /* If the input comes from a memory buffer, pointer to the memory + * buffer. + */ + + unsigned char *input_buffer; + size_t input_buffer_len; + unsigned int input_buffer_pos; + int lumpnum; + + /* If the input comes from a file, the file stream for reading + * data. + */ + + FILE *stream; + + /* Current line number that we have reached: */ + + int linenum; + + /* Used by deh_read_line: */ + + boolean last_was_newline; + char *readbuffer; + int readbuffer_size; + + /* Error handling. */ + + boolean had_error; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_context_t *deh_new_context(void) +{ + deh_context_t *context; + + context = z_malloc(sizeof(*context), PU_STATIC, NULL); + + /* Initial read buffer size of 128 bytes */ + + context->readbuffer_size = 128; + context->readbuffer = z_malloc(context->readbuffer_size, PU_STATIC, NULL); + context->linenum = 0; + context->last_was_newline = true; + + context->had_error = false; + + return context; +} + +/* Increase the read buffer size */ + +static void increase_read_buffer(deh_context_t *context) +{ + char *newbuffer; + int newbuffer_size; + + newbuffer_size = context->readbuffer_size * 2; + newbuffer = z_malloc(newbuffer_size, PU_STATIC, NULL); + + memcpy(newbuffer, context->readbuffer, context->readbuffer_size); + + z_free(context->readbuffer); + + context->readbuffer = newbuffer; + context->readbuffer_size = newbuffer_size; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Open a dehacked file for reading + * Returns NULL if open failed + */ + +deh_context_t *deh_open_file(const char *filename) +{ + FILE *fstream; + deh_context_t *context; + + fstream = fopen(filename, "r"); + + if (fstream == NULL) return NULL; + + context = deh_new_context(); + + context->type = DEH_INPUT_FILE; + context->stream = fstream; + context->filename = m_string_duplicate(filename); + + return context; +} + +/* Open a WAD lump for reading. */ + +deh_context_t *deh_open_lump(int lumpnum) +{ + deh_context_t *context; + void *lump; + + lump = w_cache_lump_num(lumpnum, PU_STATIC); + + context = deh_new_context(); + + context->type = DEH_INPUT_LUMP; + context->lumpnum = lumpnum; + context->input_buffer = lump; + context->input_buffer_len = w_lump_length(lumpnum); + context->input_buffer_pos = 0; + + context->filename = malloc(9); + m_str_copy(context->filename, lumpinfo[lumpnum]->name, 9); + + return context; +} + +/* Close dehacked file */ + +void deh_close_file(deh_context_t *context) +{ + if (context->type == DEH_INPUT_FILE) + { + fclose(context->stream); + } + else if (context->type == DEH_INPUT_LUMP) + { + w_release_lump_num(context->lumpnum); + } + + free(context->filename); + z_free(context->readbuffer); + z_free(context); +} + +int deh_get_char_file(deh_context_t *context) +{ + if (feof(context->stream)) + { + /* end of file */ + + return -1; + } + + return fgetc(context->stream); +} + +int deh_get_char_lump(deh_context_t *context) +{ + int result; + + if (context->input_buffer_pos >= context->input_buffer_len) + { + return -1; + } + + result = context->input_buffer[context->input_buffer_pos]; + ++context->input_buffer_pos; + + return result; +} + +/* Reads a single character from a dehacked file */ + +int deh_get_char(deh_context_t *context) +{ + int result = 0; + boolean last_was_cr = false; + + /* Track the current line number */ + + if (context->last_was_newline) + { + ++context->linenum; + } + + /* Read characters, converting CRLF to LF */ + + do + { + switch (context->type) + { + case DEH_INPUT_FILE: + result = deh_get_char_file(context); + break; + + case DEH_INPUT_LUMP: + result = deh_get_char_lump(context); + break; + } + + /* Handle \r characters not paired with \n */ + + if (last_was_cr && result != '\n') + { + switch (context->type) + { + case DEH_INPUT_FILE: + ungetc(result, context->stream); + break; + + case DEH_INPUT_LUMP: + --context->input_buffer_pos; + break; + } + + return '\r'; + } + + last_was_cr = result == '\r'; + } + while (last_was_cr); + + context->last_was_newline = result == '\n'; + + return result; +} + +/* Read a whole line */ + +char *deh_read_line(deh_context_t *context, boolean extended) +{ + int c; + int pos; + boolean escaped = false; + + for (pos = 0; ; ) + { + c = deh_get_char(context); + + if (c < 0 && pos == 0) + { + /* end of file */ + + return NULL; + } + + /* cope with lines of any length: increase the buffer size */ + + if (pos >= context->readbuffer_size) + { + increase_read_buffer(context); + } + + /* extended string support */ + + if (extended && c == '\\') + { + c = deh_get_char(context); + + /* "\n" in the middle of a string indicates an internal linefeed */ + + if (c == 'n') + { + context->readbuffer[pos] = '\n'; + ++pos; + continue; + } + + /* values to be assigned may be split onto multiple lines by ending + * each line that is to be continued with a backslash + */ + + if (c == '\n') + { + escaped = true; + continue; + } + } + + /* blanks before the backslash are included in the string + * but indentation after the linefeed is not + */ + + if (escaped && c >= 0 && isspace(c) && c != '\n') + { + continue; + } + else + { + escaped = false; + } + + if (c == '\n' || c < 0) + { + /* end of line: a full line has been read */ + + context->readbuffer[pos] = '\0'; + break; + } + else if (c != '\0') + { + /* normal character; don't allow NUL characters to be + * added. + */ + + context->readbuffer[pos] = (char)c; + ++pos; + } + } + + return context->readbuffer; +} + +void deh_warning(deh_context_t *context, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + + fprintf(stderr, "%s:%i: warning: ", context->filename, context->linenum); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + + va_end(args); +} + +void deh_error(deh_context_t *context, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + + fprintf(stderr, "%s:%i: ", context->filename, context->linenum); + vfprintf(stderr, msg, args); + fprintf(stderr, "\n"); + + va_end(args); + + context->had_error = true; +} + +boolean deh_had_error(deh_context_t *context) +{ + return context->had_error; +} diff --git a/games/NXDoom/src/deh_io.h b/games/NXDoom/src/deh_io.h new file mode 100644 index 000000000..8ee12b5c0 --- /dev/null +++ b/games/NXDoom/src/deh_io.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dehacked I/O code (does all reads from dehacked files) + * + ****************************************************************************/ + +#ifndef DEH_IO_H +#define DEH_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_defs.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +deh_context_t *deh_open_file(const char *filename); +deh_context_t *deh_open_lump(int lumpnum); +void deh_close_file(deh_context_t *context); +int deh_get_char(deh_context_t *context); +char *deh_read_line(deh_context_t *context, boolean extended); +void deh_error(deh_context_t *context, const char *msg, ...) + PRINTF_ATTR(2, 3); +void deh_warning(deh_context_t *context, const char *msg, ...) + PRINTF_ATTR(2, 3); +boolean deh_had_error(deh_context_t *context); + +#endif /* DEH_IO_H */ diff --git a/games/NXDoom/src/deh_main.c b/games/NXDoom/src/deh_main.c new file mode 100644 index 000000000..f6eb667ac --- /dev/null +++ b/games/NXDoom/src/deh_main.c @@ -0,0 +1,554 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_main.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Main dehacked code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "doomtype.h" +#include "i_glob.h" +#include "i_system.h" +#include "m_argv.h" +#include "w_wad.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean deh_initialized = false; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* If true, we can parse [STRINGS] sections in BEX format. */ + +boolean deh_allow_extended_strings = false; + +/* If true, we can do long string replacements. */ + +boolean deh_allow_long_strings = false; + +/* If true, we can do cheat replacements longer than the originals. */ + +boolean deh_allow_long_cheats = false; + +/* If false, dehacked cheat replacements are ignored. */ + +boolean deh_apply_cheats = true; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Called on startup to call the Init functions */ + +static void initialize_sections(void) +{ + unsigned int i; + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (deh_section_types[i]->init != NULL) + { + deh_section_types[i]->init(); + } + } +} + +static void deh_init(void) +{ + /* @category mod + * + * Ignore cheats in dehacked files. + */ + + if (m_check_parm("-nocheats") > 0) + { + deh_apply_cheats = false; + } + + /* Call init functions for all the section definitions. */ + + initialize_sections(); + + deh_initialized = true; +} + +/* Given a section name, get the section structure which corresponds */ + +static deh_section_t *get_section_by_name(char *name) +{ + unsigned int i; + + /* we explicitly do not recognize [STRINGS] sections at all + * if extended strings are not allowed + */ + + if (!deh_allow_extended_strings && !strncasecmp("[STRINGS]", name, 9)) + { + return NULL; + } + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (!strcasecmp(deh_section_types[i]->name, name)) + { + return deh_section_types[i]; + } + } + + return NULL; +} + +/* Is the string passed just whitespace? */ + +static boolean is_whitespace(char *s) +{ + for (; *s; ++s) + { + if (!isspace(*s)) return false; + } + + return true; +} + +/* Strip whitespace from the start and end of a string */ + +static char *clean_string(char *s) +{ + char *strending; + + /* Leading whitespace */ + + while (*s && isspace(*s)) + ++s; + + /* Trailing whitespace */ + + strending = s + strlen(s) - 1; + + while (strlen(s) > 0 && isspace(*strending)) + { + *strending = '\0'; + --strending; + } + + return s; +} + +static boolean check_signatures(deh_context_t *context) +{ + size_t i; + char *line; + + /* Read the first line */ + + line = deh_read_line(context, false); + + if (line == NULL) + { + return false; + } + + /* Check all signatures to see if one matches */ + + for (i = 0; deh_signatures[i] != NULL; ++i) + { + if (!strcmp(deh_signatures[i], line)) + { + return true; + } + } + + return false; +} + +/* Parses a comment string in a dehacked file. */ + +static void deh_parse_comment(char *comment) +{ + /* Welcome, to the super-secret Chocolate Doom-specific Dehacked + * overrides function. + * + * Putting these magic comments into your Dehacked lumps will + * allow you to go beyond the normal limits of Vanilla Dehacked. + * Because of this, these comments are deliberately undocumented, + * and if you're using them you should be aware that your mod + * is not compatible with Vanilla Doom and you're probably a + * very naughty person. + * + + * Allow comments containing this special value to allow string + * replacements longer than those permitted by DOS dehacked. + * This allows us to use a dehacked patch for doing string + * replacements for emulating Chex Quest. + * + * If you use this, your dehacked patch may not work in Vanilla + * Doom. + */ + + if (strstr(comment, "*allow-long-strings*") != NULL) + { + deh_allow_long_strings = true; + } + + /* Allow magic comments to allow longer cheat replacements than + * those permitted by DOS dehacked. This is also for Chex + * Quest. + */ + + if (strstr(comment, "*allow-long-cheats*") != NULL) + { + deh_allow_long_cheats = true; + } + + /* Allow magic comments to allow parsing [STRINGS] section + * that are usually only found in BEX format files. This allows + * for substitution of map and episode names when loading + * Freedoom/FreeDM IWADs. + */ + + if (strstr(comment, "*allow-extended-strings*") != NULL) + { + deh_allow_extended_strings = true; + } +} + +/* Parses a dehacked file by reading from the context */ + +static void deh_parse_context(deh_context_t *context) +{ + deh_section_t *current_section = NULL; + char section_name[20]; + void *tag = NULL; + boolean extended; + char *line; + + /* Read the header and check it matches the signature */ + + if (!check_signatures(context)) + { + deh_error(context, "This is not a valid dehacked patch file!"); + } + + /* Read the file */ + + while (!deh_had_error(context)) + { + /* Read the next line. We only allow the special extended parsing + * for the BEX [STRINGS] section. + */ + + extended = current_section != NULL && + !strcasecmp(current_section->name, "[STRINGS]"); + line = deh_read_line(context, extended); + + /* end of file? */ + + if (line == NULL) + { + return; + } + + while (line[0] != '\0' && isspace(line[0])) + ++line; + + if (line[0] == '#') + { + /* comment */ + + deh_parse_comment(line); + continue; + } + + if (is_whitespace(line)) + { + if (current_section != NULL) + { + /* end of section */ + + if (current_section->end != NULL) + { + current_section->end(context, tag); + } + + current_section = NULL; + } + } + else + { + if (current_section != NULL) + { + /* parse this line */ + + current_section->line_parser(context, line, tag); + } + else + { + /* possibly the start of a new section */ + + sscanf(line, "%19s", section_name); + + current_section = get_section_by_name(section_name); + + if (current_section != NULL) + { + tag = current_section->start(context, line); + } + else + { + /* printf("unknown section name %s\n", section_name); */ + } + } + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void deh_checksum(sha1_digest_t digest) +{ + SHA1_CTX sha1_context; + unsigned int i; + + sha1init(&sha1_context); + + for (i = 0; deh_section_types[i] != NULL; ++i) + { + if (deh_section_types[i]->sha1_hash != NULL) + { + deh_section_types[i]->sha1_hash(&sha1_context); + } + } + + sha1final(digest, &sha1_context); +} + +/* This pattern is used a lot of times in different sections, + * an assignment is essentially just a statement of the form: + * + * Variable Name = Value + * + * The variable name can include spaces or any other characters. + * The string is split on the '=', essentially. + * + * Returns true if read correctly + */ + +boolean deh_parse_assignment(char *line, char **variable_name, char **value) +{ + char *p; + + /* find the equals */ + + p = strchr(line, '='); + + if (p == NULL) + { + return false; + } + + /* variable name at the start + * turn the '=' into a \0 to terminate the string here + */ + + *p = '\0'; + *variable_name = clean_string(line); + + /* value immediately follows the '=' */ + + *value = clean_string(p + 1); + + return true; +} + +/* Parses a dehacked file */ + +int deh_loadfile(const char *filename) +{ + deh_context_t *context; + boolean had_error; + + if (!deh_initialized) + { + deh_init(); + } + + /* Before parsing a new file, reset special override flags to false. + * Magic comments should only apply to the file in which they were + * defined, and shouldn't carry over to subsequent files as well. + */ + + deh_allow_long_strings = false; + deh_allow_long_cheats = false; + deh_allow_extended_strings = false; + + printf(" loading %s\n", filename); + + context = deh_open_file(filename); + + if (context == NULL) + { + fprintf(stderr, "deh_loadfile: Unable to open %s\n", filename); + return 0; + } + + deh_parse_context(context); + + had_error = deh_had_error(context); + + deh_close_file(context); + + if (had_error) + { + i_error("Error parsing dehacked file"); + } + + return 1; +} + +/* Load all dehacked patches from the given directory. */ + +void deh_auto_load_patches(const char *path) +{ + const char *filename; + glob_t *glob; + + glob = i_start_multi_glob(path, GLOB_FLAG_NOCASE | GLOB_FLAG_SORTED, + "*.deh", "*.hhe", "*.seh", NULL); + for (; ; ) + { + filename = i_next_glob(glob); + if (filename == NULL) + { + break; + } + + printf(" [autoload]"); + deh_loadfile(filename); + } + + i_end_glob(glob); +} + +/* Load dehacked file from WAD lump. + * If allow_long is set, allow long strings and cheats just for this lump. + */ + +int deh_load_lump(int lumpnum, boolean allow_long, boolean allow_error) +{ + deh_context_t *context; + boolean had_error; + + if (!deh_initialized) + { + deh_init(); + } + + /* Reset all special flags to defaults. */ + + deh_allow_long_strings = allow_long; + deh_allow_long_cheats = allow_long; + deh_allow_extended_strings = false; + + context = deh_open_lump(lumpnum); + + if (context == NULL) + { + fprintf(stderr, "deh_loadfile: Unable to open lump %i\n", lumpnum); + return 0; + } + + deh_parse_context(context); + + had_error = deh_had_error(context); + + deh_close_file(context); + + /* If there was an error while parsing, abort with an error, but allow + * errors to just be ignored if allow_error=true. + */ + + if (!allow_error && had_error) + { + i_error("Error parsing dehacked lump"); + } + + return 1; +} + +int deh_load_lump_by_name(const char *name, boolean allow_long, + boolean allow_error) +{ + int lumpnum; + + lumpnum = w_check_num_for_name(name); + + if (lumpnum == -1) + { + fprintf(stderr, "deh_load_lump_by_name: '%s' lump not found\n", name); + return 0; + } + + return deh_load_lump(lumpnum, allow_long, allow_error); +} + +/* Check the command line for -deh argument, and others. */ + +void deh_parse_command_line(void) +{ + char *filename; + int p; + + /* @arg + * @category mod + * + * Load the given dehacked patch(es) + */ + + p = m_check_parm("-deh"); + + if (p > 0) + { + ++p; + + while (p < myargc && myargv[p][0] != '-') + { + filename = d_try_find_wad_by_name(myargv[p]); + deh_loadfile(filename); + free(filename); + ++p; + } + } +} diff --git a/games/NXDoom/src/deh_main.h b/games/NXDoom/src/deh_main.h new file mode 100644 index 000000000..8ae728243 --- /dev/null +++ b/games/NXDoom/src/deh_main.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dehacked entrypoint and common code + * + ****************************************************************************/ + +#ifndef DEH_MAIN_H +#define DEH_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_str.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* These are the limits that dehacked uses (from dheinit.h in the dehacked + * source). If these limits are exceeded, it does not generate an error, but + * a warning is displayed. + */ + +#define DEH_VANILLA_NUMSTATES 966 +#define DEH_VANILLA_NUMSFX 107 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean deh_allow_extended_strings; +extern boolean deh_allow_long_strings; +extern boolean deh_allow_long_cheats; +extern boolean deh_apply_cheats; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void deh_parse_command_line(void); +int deh_loadfile(const char *filename); +void deh_auto_load_patches(const char *path); +int deh_load_lump(int lumpnum, boolean allow_long, boolean allow_error); +int deh_load_lump_by_name(const char *name, boolean allow_long, + boolean allow_error); + +boolean deh_parse_assignment(char *line, char **variable_name, char **value); + +void deh_checksum(sha1_digest_t digest); + +#endif /* DEH_MAIN_H */ diff --git a/games/NXDoom/src/deh_mapping.c b/games/NXDoom/src/deh_mapping.c new file mode 100644 index 000000000..17bed2074 --- /dev/null +++ b/games/NXDoom/src/deh_mapping.c @@ -0,0 +1,206 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_mapping.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dehacked "mapping" code + * Allows the fields in structures to be mapped out and accessed by + * name + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" + +#include "deh_mapping.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_mapping_entry_t *get_mapping_entry_by_name(deh_context_t *context, + deh_mapping_t *mapping, + char *name) +{ + int i; + + for (i = 0; mapping->entries[i].name != NULL; ++i) + { + deh_mapping_entry_t *entry = &mapping->entries[i]; + + if (!strcasecmp(entry->name, name)) + { + if (entry->location == NULL) + { + deh_warning(context, "Field '%s' is unsupported", name); + return NULL; + } + + return entry; + } + } + + /* Not found. */ + + deh_warning(context, "Field named '%s' not found", name); + + return NULL; +} + +/* Get the location of the specified field in the specified structure. */ + +static void *get_struct_field(void *structptr, deh_mapping_t *mapping, + deh_mapping_entry_t *entry) +{ + unsigned int offset; + + offset = (uint8_t *)entry->location - (uint8_t *)mapping->base; + + return (uint8_t *)structptr + offset; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Set the value of a particular field in a structure by name */ + +boolean deh_set_mapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, int value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = get_mapping_entry_by_name(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + /* Sanity check: */ + + if (entry->is_string) + { + deh_error(context, "Tried to set '%s' as integer (BUG)", name); + return false; + } + + location = get_struct_field(structptr, mapping, entry); + + /* Set field content based on its type: */ + + switch (entry->size) + { + case 1: + *((uint8_t *)location) = value; + break; + case 2: + *((uint16_t *)location) = value; + break; + case 4: + *((uint32_t *)location) = value; + break; + default: + deh_error(context, "Unknown field type for '%s' (BUG)", name); + return false; + } + + return true; +} + +/* Set the value of a string field in a structure by name */ + +boolean deh_set_string_mapping(deh_context_t *context, + deh_mapping_t *mapping, void *structptr, char *name, char *value) +{ + deh_mapping_entry_t *entry; + void *location; + + entry = get_mapping_entry_by_name(context, mapping, name); + + if (entry == NULL) + { + return false; + } + + /* Sanity check: */ + + if (!entry->is_string) + { + deh_error(context, "Tried to set '%s' as string (BUG)", name); + return false; + } + + location = get_struct_field(structptr, mapping, entry); + + /* Copy value into field: */ + + m_str_copy(location, value, entry->size); + + return true; +} + +void deh_struct_sha1_sum(SHA1_CTX *context, deh_mapping_t *mapping, + void *structptr) +{ + int i; + + /* Go through each mapping */ + + for (i = 0; mapping->entries[i].name != NULL; ++i) + { + deh_mapping_entry_t *entry = &mapping->entries[i]; + void *location; + + if (entry->location == NULL) + { + /* Unsupported field */ + + continue; + } + + /* Add in data for this field */ + + location = (uint8_t *)structptr + + ((uint8_t *)entry->location - (uint8_t *)mapping->base); + + switch (entry->size) + { + case 1: + sha1_updateint32(context, *((uint8_t *)location)); + break; + case 2: + sha1_updateint32(context, *((uint16_t *)location)); + break; + case 4: + sha1_updateint32(context, *((uint32_t *)location)); + break; + default: + i_error("Unknown dehacked mapping field type for '%s' (BUG)", + entry->name); + break; + } + } +} diff --git a/games/NXDoom/src/deh_mapping.h b/games/NXDoom/src/deh_mapping.h new file mode 100644 index 000000000..cd5cf5bf6 --- /dev/null +++ b/games/NXDoom/src/deh_mapping.h @@ -0,0 +1,106 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_mapping.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dehacked "mapping" code + * Allows the fields in structures to be mapped out and accessed by + * name + * + ****************************************************************************/ + +#ifndef DEH_MAPPING_H +#define DEH_MAPPING_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_io.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEH_BEGIN_MAPPING(mapping_name, structname) \ + static structname deh_mapping_base; \ + static deh_mapping_t mapping_name = {&deh_mapping_base, { + +#define DEH_MAPPING(deh_name, fieldname) \ + {deh_name, &deh_mapping_base.fieldname, \ + sizeof(deh_mapping_base.fieldname), false}, + +#define DEH_MAPPING_STRING(deh_name, fieldname) \ + {deh_name, &deh_mapping_base.fieldname, \ + sizeof(deh_mapping_base.fieldname), true}, + +#define DEH_UNSUPPORTED_MAPPING(deh_name) {deh_name, NULL, -1, false}, + +#define DEH_END_MAPPING \ + {NULL, NULL, -1} \ + } \ + } \ + ; + +#define MAX_MAPPING_ENTRIES 32 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct deh_mapping_s deh_mapping_t; +typedef struct deh_mapping_entry_s deh_mapping_entry_t; + +struct deh_mapping_entry_s +{ + /* field name */ + + const char *name; + + /* location relative to the base in the deh_mapping_t struct + * If this is NULL, it is an unsupported mapping + */ + + void *location; + + /* field size */ + + int size; + + /* if true, this is a string value. */ + + boolean is_string; +}; + +struct deh_mapping_s +{ + void *base; + deh_mapping_entry_t entries[MAX_MAPPING_ENTRIES]; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean deh_set_mapping(deh_context_t *context, deh_mapping_t *mapping, + void *structptr, char *name, int value); +boolean deh_set_string_mapping(deh_context_t *context, + deh_mapping_t *mapping, void *structptr, char *name, char *value); +void deh_struct_sha1_sum(SHA1_CTX *context, deh_mapping_t *mapping, + void *structptr); + +#endif /* DEH_MAPPING_H */ diff --git a/games/NXDoom/src/deh_str.c b/games/NXDoom/src/deh_str.c new file mode 100644 index 000000000..9c3f8b8b4 --- /dev/null +++ b/games/NXDoom/src/deh_str.c @@ -0,0 +1,228 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_str.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Parses Text substitution sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "deh_str.h" +#include "doomtype.h" +#include "m_misc.h" + +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *from_text; + char *to_text; +} deh_substitution_t; + +typedef enum +{ + FORMAT_ARG_INVALID, + FORMAT_ARG_INT, + FORMAT_ARG_FLOAT, + FORMAT_ARG_CHAR, + FORMAT_ARG_STRING, + FORMAT_ARG_PTR, + FORMAT_ARG_SAVE_POS +} format_arg_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static deh_substitution_t **hash_table = NULL; +static int hash_table_entries; +static int hash_table_length = -1; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* This is the algorithm used by glib */ + +static unsigned int strhash(const char *s) +{ + const char *p = s; + unsigned int h = *p; + + if (h) + { + for (p += 1; *p; p++) + h = (h << 5) - h + *p; + } + + return h; +} + +static deh_substitution_t *substitution_for_string(const char *s) +{ + int entry; + + /* Fallback if we have not initialized the hash table yet */ + + if (hash_table_length < 0) return NULL; + + entry = strhash(s) % hash_table_length; + + while (hash_table[entry] != NULL) + { + if (!strcmp(hash_table[entry]->from_text, s)) + { + return hash_table[entry]; /* substitution found! */ + } + + entry = (entry + 1) % hash_table_length; + } + + /* no substitution found */ + + return NULL; +} + +static void init_hash_table(void) +{ + /* init hash table */ + + hash_table_entries = 0; + hash_table_length = 16; + hash_table = z_malloc(sizeof(deh_substitution_t *) * hash_table_length, + PU_STATIC, NULL); + memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); +} + +static void deh_add_to_hashtable(deh_substitution_t *sub); + +static void increase_hashtable(void) +{ + deh_substitution_t **old_table; + int old_table_length; + int i; + + /* save the old table */ + + old_table = hash_table; + old_table_length = hash_table_length; + + /* double the size */ + + hash_table_length *= 2; + hash_table = z_malloc(sizeof(deh_substitution_t *) * hash_table_length, + PU_STATIC, NULL); + memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length); + + /* go through the old table and insert all the old entries */ + + for (i = 0; i < old_table_length; ++i) + { + if (old_table[i] != NULL) + { + deh_add_to_hashtable(old_table[i]); + } + } + + /* free the old table */ + + z_free(old_table); +} + +static void deh_add_to_hashtable(deh_substitution_t *sub) +{ + int entry; + + /* if the hash table is more than 60% full, increase its size */ + + if ((hash_table_entries * 10) / hash_table_length > 6) + { + increase_hashtable(); + } + + /* find where to insert it */ + + entry = strhash(sub->from_text) % hash_table_length; + + while (hash_table[entry] != NULL) + { + entry = (entry + 1) % hash_table_length; + } + + hash_table[entry] = sub; + ++hash_table_entries; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void deh_add_string_replacement(const char *from_text, const char *to_text) +{ + deh_substitution_t *sub; + size_t len; + + /* Initialize the hash table if this is the first time */ + + if (hash_table_length < 0) + { + init_hash_table(); + } + + /* Check to see if there is an existing substitution already in place. */ + + sub = substitution_for_string(from_text); + + if (sub != NULL) + { + z_free(sub->to_text); + + len = strlen(to_text) + 1; + sub->to_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->to_text, to_text, len); + } + else + { + /* We need to allocate a new substitution. */ + + sub = z_malloc(sizeof(*sub), PU_STATIC, 0); + + /* We need to create our own duplicates of the provided strings. */ + + len = strlen(from_text) + 1; + sub->from_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->from_text, from_text, len); + + len = strlen(to_text) + 1; + sub->to_text = z_malloc(len, PU_STATIC, NULL); + memcpy(sub->to_text, to_text, len); + + deh_add_to_hashtable(sub); + } +} diff --git a/games/NXDoom/src/deh_str.h b/games/NXDoom/src/deh_str.h new file mode 100644 index 000000000..3b2e6fefe --- /dev/null +++ b/games/NXDoom/src/deh_str.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_str.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dehacked string replacements + * + ****************************************************************************/ + +#ifndef DEH_STR_H +#define DEH_STR_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Used to do dehacked text substitutions throughout the program */ + +void deh_add_string_replacement(const char *from_text, const char *to_text); + +#endif /* DEH_STR_H */ diff --git a/games/NXDoom/src/deh_text.c b/games/NXDoom/src/deh_text.c new file mode 100644 index 000000000..927a3e7c6 --- /dev/null +++ b/games/NXDoom/src/deh_text.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * apps/games/NXDoom/src/deh_text.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses Text substitution sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +#include "z_zone.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void deh_text_parse_line(deh_context_t *context, char *line, + void *tag); +static void *deh_text_start(deh_context_t *context, char *line); +static void *deh_text_start(deh_context_t *context, char *line); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_text = +{ + "Text", NULL, deh_text_start, deh_text_parse_line, NULL, NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Given a string length, find the maximum length of a string that can + * replace it. + */ + +static int txt_max_string_length(int len) +{ + /* Enough bytes for the string and the NUL terminator */ + + len += 1; + + /* All strings in doom.exe are on 4-byte boundaries, so we may be able + * to support a slightly longer string. + * Extend up to the next 4-byte boundary + */ + + len += (4 - (len % 4)) % 4; + + /* Less one for the NUL terminator. */ + + return len - 1; +} + +static void *deh_text_start(deh_context_t *context, char *line) +{ + char *from_text; + char *to_text; + int fromlen; + int to_len; + int i; + + if (sscanf(line, "Text %i %i", &fromlen, &to_len) != 2) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + /* Only allow string replacements that are possible in Vanilla Doom. + * Chocolate Doom is unforgiving! + */ + + if (!deh_allow_long_strings && to_len > txt_max_string_length(fromlen)) + { + deh_error(context, "Replacement string is longer than the maximum " + "possible in doom.exe"); + return NULL; + } + + from_text = malloc(fromlen + 1); + to_text = malloc(to_len + 1); + + /* read in the "from" text */ + + for (i = 0; i < fromlen; ++i) + { + from_text[i] = deh_get_char(context); + } + + from_text[fromlen] = '\0'; + + /* read in the "to" text */ + + for (i = 0; i < to_len; ++i) + { + to_text[i] = deh_get_char(context); + } + + to_text[to_len] = '\0'; + + deh_add_string_replacement(from_text, to_text); + + free(from_text); + free(to_text); + + return NULL; +} + +static void deh_text_parse_line(deh_context_t *context, char *line, + void *tag) +{ + /* not used */ +} diff --git a/games/NXDoom/src/doom/.gitignore b/games/NXDoom/src/doom/.gitignore new file mode 100644 index 000000000..d4e88e5a2 --- /dev/null +++ b/games/NXDoom/src/doom/.gitignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +tags +TAGS diff --git a/games/NXDoom/src/doom/am_map.c b/games/NXDoom/src/doom/am_map.c new file mode 100644 index 000000000..31daeb30b --- /dev/null +++ b/games/NXDoom/src/doom/am_map.c @@ -0,0 +1,1817 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/am_map.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: the automap code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" + +#include "doomdef.h" +#include "doomkeys.h" +#include "p_local.h" +#include "st_stuff.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_cheat.h" +#include "m_controls.h" +#include "m_misc.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "doomstat.h" +#include "r_state.h" + +#include "dstrings.h" + +#include "am_map.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* For use if I do walls with outsides/insides */ + +#define REDS (256 - 5 * 16) +#define REDRANGE 16 +#define BLUES (256 - 4 * 16 + 8) +#define BLUERANGE 8 +#define GREENS (7 * 16) +#define GREENRANGE 16 +#define GRAYS (6 * 16) +#define GRAYSRANGE 16 +#define BROWNS (4 * 16) +#define BROWNRANGE 16 +#define YELLOWS (256 - 32 + 7) +#define YELLOWRANGE 1 +#define BLACK 0 +#define WHITE (256 - 47) + +/* Automap colors */ + +#define BACKGROUND BLACK +#define YOURCOLORS WHITE +#define YOURRANGE 0 +#define WALLCOLORS REDS +#define WALLRANGE REDRANGE +#define TSWALLCOLORS GRAYS +#define TSWALLRANGE GRAYSRANGE +#define FDWALLCOLORS BROWNS +#define FDWALLRANGE BROWNRANGE +#define CDWALLCOLORS YELLOWS +#define CDWALLRANGE YELLOWRANGE +#define THINGCOLORS GREENS +#define THINGRANGE GREENRANGE +#define SECRETWALLCOLORS WALLCOLORS +#define SECRETWALLRANGE WALLRANGE +#define GRIDCOLORS (GRAYS + GRAYSRANGE / 2) +#define GRIDRANGE 0 +#define XHAIRCOLORS GRAYS + +/* drawing stuff */ + +#define AM_NUMMARKPOINTS 10 + +/* scale on entry */ + +#define INITSCALEMTOF (.2 * FRACUNIT) + +/* how much the automap moves window per tic in frame-buffer coordinates + * moves 140 pixels in 1 second + */ + +#define F_PANINC 4 + +/* how much zoom-in per tic goes to 2x in 1 second */ + +#define M_ZOOMIN ((int)(1.02 * FRACUNIT)) + +/* how much zoom-out per tic pulls out to 0.5x in 1 second */ + +#define M_ZOOMOUT ((int)(FRACUNIT / 1.02)) + +/* translates between frame-buffer and map distances */ + +#define FTOM(x) fixed_mul(((x) << FRACBITS), scale_ftom) +#define MTOF(x) (fixed_mul((x), scale_mtof) >> FRACBITS) + +/* translates between frame-buffer and map coordinates */ + +#define CXMTOF(x) (f_x + MTOF((x) - m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y) - m_y))) + +/* the following is crap */ + +#define LINE_NEVERSEE ML_DONTDRAW + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int x; + int y; +} fpoint_t; + +typedef struct +{ + fpoint_t a; + fpoint_t b; +} fline_t; + +typedef struct +{ + fixed_t x; + fixed_t y; +} mpoint_t; + +typedef struct +{ + mpoint_t a; + mpoint_t b; +} mline_t; + +typedef struct +{ + fixed_t slp; + fixed_t islp; +} islope_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* The vector graphics for the automap. A line drawing of the player pointing + * right, starting from the middle. + */ + +#define R ((8 * PLAYERRADIUS) / 7) +mline_t player_arrow[] = +{ + { + { + -R + R / 8, + 0, + }, + { + R, + 0, + }, + }, /* ----- */ + { + { + R, + 0, + }, + { + R - R / 2, + R / 4, + }, + }, /* -----> */ + { + { + R, + 0, + }, + { + R - R / 2, + -R / 4, + }, + }, + { + { + -R + R / 8, + 0, + }, + { + -R - R / 8, + R / 4, + }, + }, /* >----> */ + { + { + -R + R / 8, + 0, + }, + { + -R - R / 8, + -R / 4, + }, + }, + { + { + -R + 3 * R / 8, + 0, + }, + { + -R + R / 8, + R / 4, + }, + }, /* >>---> */ + { + { + -R + 3 * R / 8, + 0, + }, + { + -R + R / 8, + -R / 4, + }, + }, +}; +#undef R + +#define R ((8 * PLAYERRADIUS) / 7) +mline_t cheat_player_arrow[] = +{ + { + { + -R + R / 8, + 0, + }, + { + R, + 0, + }, + }, /* ----- */ + { + { + R, + 0, + }, + { + R - R / 2, + R / 6, + }, + }, /* -----> */ + { + { + R, + 0, + }, + { + R - R / 2, + -R / 6, + }, + }, + { + { + -R + R / 8, + 0, + }, + { + -R - R / 8, + R / 6, + }, + }, /* >-----> */ + { + { + -R + R / 8, + 0, + }, + { + -R - R / 8, + -R / 6, + }, + }, + { + { + -R + 3 * R / 8, + 0, + }, + { + -R + R / 8, + R / 6, + }, + }, /* >>-----> */ + { + { + -R + 3 * R / 8, + 0, + }, + { + -R + R / 8, + -R / 6, + }, + }, + { + { + -R / 2, + 0, + }, + { + -R / 2, + -R / 6, + }, + }, /* >>-d---> */ + { + { + -R / 2, + -R / 6, + }, + { + -R / 2 + R / 6, + -R / 6, + }, + }, + { + { + -R / 2 + R / 6, + -R / 6, + }, + { + -R / 2 + R / 6, + R / 4, + }, + }, + { + { + -R / 6, + 0, + }, + { + -R / 6, + -R / 6, + }, + }, /* >>-dd--> */ + { + { + -R / 6, + -R / 6, + }, + { + 0, + -R / 6, + }, + }, + { + { + 0, + -R / 6, + }, + { + 0, + R / 4, + }, + }, + { + { + R / 6, + R / 4, + }, + { + R / 6, + -R / 7, + }, + }, /* >>-ddt-> */ + { + { + R / 6, + -R / 7, + }, + { + R / 6 + R / 32, + -R / 7 - R / 32, + }, + }, + { + { + R / 6 + R / 32, + -R / 7 - R / 32, + }, + { + R / 6 + R / 10, + -R / 7, + }, + }, +}; +#undef R + +#define R (FRACUNIT) +mline_t triangle_guy[] = +{ + { + { + (fixed_t)(-.867 * R), + (fixed_t)(-.5 * R), + }, + { + (fixed_t)(.867 * R), + (fixed_t)(-.5 * R), + }, + }, + { + { + (fixed_t)(.867 * R), + (fixed_t)(-.5 * R), + }, + { + (fixed_t)(0), + (fixed_t)(R), + }, + }, + { + { + (fixed_t)(0), + (fixed_t)(R), + }, + { + (fixed_t)(-.867 * R), + (fixed_t)(-.5 * R), + }, + }, +}; +#undef R + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = +{ + { + { + (fixed_t)(-.5 * R), + (fixed_t)(-.7 * R), + }, + { + (fixed_t)(R), + (fixed_t)(0), + }, + }, + { + { + (fixed_t)(R), + (fixed_t)(0), + }, + { + (fixed_t)(-.5 * R), + (fixed_t)(.7 * R), + }, + }, + { + { + (fixed_t)(-.5 * R), + (fixed_t)(.7 * R), + }, + { + (fixed_t)(-.5 * R), + (fixed_t)(-.7 * R), + }, + }, +}; +#undef R + +boolean automapactive = false; + +cheatseq_t cheat_amap = CHEAT("iddt", 0); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int cheating = 0; +static int grid = 0; + +static int finit_width = SCREENWIDTH; +static int finit_height = SCREENHEIGHT - ST_HEIGHT; + +/* location of window on screen */ + +static int f_x; +static int f_y; + +/* size of window on screen */ + +static int f_w; +static int f_h; + +static int lightlev; /* used for funky strobing effect */ +static pixel_t *fb; /* pseudo-frame buffer */ +static int amclock; + +/* how far the window pans each tic (map coords) */ + +static mpoint_t m_paninc; + +/* how far the window zooms in each tic (map coords) */ + +static fixed_t mtof_zoommul; + +/* how far the window zooms in each tic (fb coords) */ + +static fixed_t ftom_zoommul; + +/* LL x,y where the window is on the map (map coords) */ + +static fixed_t m_x; +static fixed_t m_y; + +/* UR x,y where the window is on the map (map coords) */ + +static fixed_t m_x2; +static fixed_t m_y2; + +/* width/height of window on map (map coords) */ + +static fixed_t m_w; +static fixed_t m_h; + +/* based on level size */ + +static fixed_t min_x; +static fixed_t min_y; +static fixed_t max_x; +static fixed_t max_y; + +static fixed_t max_w; /* max_x-min_x, */ +static fixed_t max_h; /* max_y-min_y */ + +/* based on player size */ + +static fixed_t min_w; +static fixed_t min_h; + +static fixed_t min_scale_mtof; /* used to tell when to stop zooming out */ +static fixed_t max_scale_mtof; /* used to tell when to stop zooming in */ + +/* old stuff for recovery later */ + +static fixed_t old_m_w; +static fixed_t old_m_h; +static fixed_t old_m_x; +static fixed_t old_m_y; + +/* old location used by the Follower routine */ + +static mpoint_t f_oldloc; + +/* used by MTOF to scale from map-to-frame-buffer coords */ + +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; + +/* used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) */ + +static fixed_t scale_ftom; + +static player_t *plr; /* the player represented by an arrow */ + +static patch_t *marknums[10]; /* numbers used for marking by the automap */ + +static mpoint_t markpoints[AM_NUMMARKPOINTS]; /* where the points are */ + +static int markpointnum = 0; /* next point to be assigned */ + +/* specifies whether to follow the player around */ + +static int followplayer = 1; + +static boolean stopped = true; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am_get_i_slope + * + * Description: + * Calculates the slope and slope according to the x-axis of a line + * segment in map coordinates (with the upright y-axis n' all) so + * that it can be used with the brain-dead drawing stuff. + * + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void am_get_i_slope(mline_t *ml, islope_t *is) +{ + int dx; + int dy; + + dy = ml->a.y - ml->b.y; + dx = ml->b.x - ml->a.x; + if (!dy) + is->islp = (dx < 0 ? -INT_MAX : INT_MAX); + else + is->islp = fixed_div(dx, dy); + if (!dx) + is->slp = (dy < 0 ? -INT_MAX : INT_MAX); + else + is->slp = fixed_div(dy, dx); +} +#endif + +/**************************************************************************** + * Name: am_activate_new_scale + ****************************************************************************/ + +static void am_activate_new_scale(void) +{ + m_x += m_w / 2; + m_y += m_h / 2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w / 2; + m_y -= m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +/**************************************************************************** + * Name: am_save_scale_and_loc + ****************************************************************************/ + +static void am_save_scale_and_loc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +/**************************************************************************** + * Name: am_restore_scale_and_loc + ****************************************************************************/ + +static void am_restore_scale_and_loc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + + if (!followplayer) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + } + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + /* Change the scaling multipliers */ + + scale_mtof = fixed_div(f_w << FRACBITS, m_w); + scale_ftom = fixed_div(FRACUNIT, scale_mtof); +} + +/**************************************************************************** + * Name: am_add_mark + * + * Description: + * adds a marker at the current location + * + ****************************************************************************/ + +static void am_add_mark(void) +{ + markpoints[markpointnum].x = m_x + m_w / 2; + markpoints[markpointnum].y = m_y + m_h / 2; + markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS; +} + +/**************************************************************************** + * Name: am_find_minmax_boundaries + * + * Description: + * Determines bounding box of all vertices, sets global variables + * controlling zoom range. + * + ****************************************************************************/ + +static void am_find_minmax_boundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = INT_MAX; + max_x = max_y = -INT_MAX; + + for (i = 0; i < numvertices; i++) + { + if (vertices[i].x < min_x) + min_x = vertices[i].x; + else if (vertices[i].x > max_x) + max_x = vertices[i].x; + + if (vertices[i].y < min_y) + min_y = vertices[i].y; + else if (vertices[i].y > max_y) + max_y = vertices[i].y; + } + + max_w = max_x - min_x; + max_h = max_y - min_y; + + min_w = 2 * PLAYERRADIUS; /* const? never changed? */ + min_h = 2 * PLAYERRADIUS; + + a = fixed_div(f_w << FRACBITS, max_w); + b = fixed_div(f_h << FRACBITS, max_h); + + min_scale_mtof = a < b ? a : b; + max_scale_mtof = fixed_div(f_h << FRACBITS, 2 * PLAYERRADIUS); +} + +/**************************************************************************** + * Name: am_change_window_loc + ****************************************************************************/ + +static void am_change_window_loc(void) +{ + if (m_paninc.x || m_paninc.y) + { + followplayer = 0; + f_oldloc.x = INT_MAX; + } + + m_x += m_paninc.x; + m_y += m_paninc.y; + + if (m_x + m_w / 2 > max_x) + m_x = max_x - m_w / 2; + else if (m_x + m_w / 2 < min_x) + m_x = min_x - m_w / 2; + + if (m_y + m_h / 2 > max_y) + m_y = max_y - m_h / 2; + else if (m_y + m_h / 2 < min_y) + m_y = min_y - m_h / 2; + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +/**************************************************************************** + * Name: am_init_variables + ****************************************************************************/ + +static void am_init_variables(void) +{ + int pnum; + static event_t st_notify = + { + ev_keyup, AM_MSGENTERED, 0, 0 + }; + + automapactive = true; + fb = i_video_buffer; + + f_oldloc.x = INT_MAX; + amclock = 0; + lightlev = 0; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + /* find player to center on initially */ + + if (playeringame[consoleplayer]) + { + plr = &players[consoleplayer]; + } + else + { + plr = &players[0]; + + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + { + if (playeringame[pnum]) + { + plr = &players[pnum]; + break; + } + } + } + + m_x = plr->mo->x - m_w / 2; + m_y = plr->mo->y - m_h / 2; + am_change_window_loc(); + + /* for saving & restoring */ + + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + /* inform the status bar of the change */ + + st_responder(&st_notify); +} + +/**************************************************************************** + * Name: am_load_pics + ****************************************************************************/ + +static void am_load_pics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "AMMNUM%d", i); + marknums[i] = w_cache_lump_name(namebuf, PU_STATIC); + } +} + +/**************************************************************************** + * Name: am_unload_pics + ****************************************************************************/ + +static void am_unload_pics(void) +{ + int i; + char namebuf[9]; + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "AMMNUM%d", i); + w_release_lump_name(namebuf); + } +} + +/**************************************************************************** + * Name: am_clear_marks + ****************************************************************************/ + +static void am_clear_marks(void) +{ + int i; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + { + markpoints[i].x = -1; /* means empty */ + } + + markpointnum = 0; +} + +/**************************************************************************** + * Name: am_level_init + * + * Description: + * should be called at the start of every level right now, i figure it out + * myself + * + ****************************************************************************/ + +static void am_level_init(void) +{ + f_x = f_y = 0; + f_w = finit_width; + f_h = finit_height; + + am_clear_marks(); + + am_find_minmax_boundaries(); + scale_mtof = fixed_div(min_scale_mtof, (int)(0.7 * FRACUNIT)); + if (scale_mtof > max_scale_mtof) scale_mtof = min_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); +} + +/**************************************************************************** + * Name: am_start + ****************************************************************************/ + +static void am_start(void) +{ + static int lastlevel = -1; + static int lastepisode = -1; + + if (!stopped) am_stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + am_level_init(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + + am_init_variables(); + am_load_pics(); +} + +/**************************************************************************** + * Name: am_min_out_window_scale + * + * Description: + * set the window scale to the maximum size + * + ****************************************************************************/ + +static void am_min_out_window_scale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_max_out_window_scale + * + * Description: + * set the window scale to the minimum size + * + ****************************************************************************/ + +static void am_max_out_window_scale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_change_window_scale + * + * Description: + * Zooming + * + ****************************************************************************/ + +static void am_change_window_scale(void) +{ + /* Change the scaling multipliers */ + + scale_mtof = fixed_mul(scale_mtof, mtof_zoommul); + scale_ftom = fixed_div(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + am_min_out_window_scale(); + else if (scale_mtof > max_scale_mtof) + am_max_out_window_scale(); + else + am_activate_new_scale(); +} + +/**************************************************************************** + * Name: am_do_follow_player + ****************************************************************************/ + +static void am_do_follow_player(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { + m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2; + m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + + /* m_x = FTOM(MTOF(plr->mo->x - m_w/2)); + * m_y = FTOM(MTOF(plr->mo->y - m_h/2)); + * m_x = plr->mo->x - m_w/2; + * m_y = plr->mo->y - m_h/2; + */ + } +} + +/**************************************************************************** + * Name: am_update_light_lev + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void am_update_light_lev(void) +{ + static int nexttic = 0; + + /* static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 }; */ + + static int litelevels[] = + { + 0, 4, 7, 10, 12, 14, 15, 15 + }; + + static int litelevelscnt = 0; + + /* Change light level */ + + if (amclock > nexttic) + { + lightlev = litelevels[litelevelscnt++]; + if (litelevelscnt == arrlen(litelevels)) litelevelscnt = 0; + nexttic = amclock + 6 - (amclock % 6); + } +} +#endif + +/**************************************************************************** + * Name: am_clear_fb + * + * Description: + * Clear automap frame buffer. + * + ****************************************************************************/ + +static void am_clear_fb(int color) +{ + memset(fb, color, f_w * f_h * sizeof(*fb)); +} + +/**************************************************************************** + * Name: am_clip_m_line + * + * Description: + * Automap clipping of lines. + * Based on Cohen-Sutherland clipping algorithm but with a slightly + * faster reject and precalculated slopes. If the speed is needed, + * use a hash algorithm to handle the common cases. + * + ****************************************************************************/ + +static boolean am_clip_m_line(mline_t *ml, fline_t *fl) +{ + enum + { + LEFT = 1, + RIGHT = 2, + BOTTOM = 4, + TOP = 8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) \ + (oc) |= TOP; \ + else if ((my) >= f_h) \ + (oc) |= BOTTOM; \ + if ((mx) < 0) \ + (oc) |= LEFT; \ + else if ((mx) >= f_w) \ + (oc) |= RIGHT; + + /* do trivial rejects and outcodes */ + + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) return false; /* trivially outside */ + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) return false; /* trivially outside */ + + /* transform to frame-buffer coordinates. */ + + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) return false; + + while (outcode1 | outcode2) + { + /* may be partially inside box find an outside point */ + + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + /* clip to each side */ + + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y)) / dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx * (fl->a.y - f_h)) / dy; + tmp.y = f_h - 1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (f_w - 1 - fl->a.x)) / dx; + tmp.x = f_w - 1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy * (-fl->a.x)) / dx; + tmp.x = 0; + } + else + { + tmp.x = 0; + tmp.y = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) return false; /* trivially outside */ + } + + return true; +} +#undef DOOUTCODE + +/**************************************************************************** + * Name: am_draw_f_line + * + * Description: + * Classic Bresenham w/ whatever optimizations needed for speed + * + ****************************************************************************/ + +static void am_draw_f_line(fline_t *fl, int color) +{ + register int x; + register int y; + register int dx; + register int dy; + register int sx; + register int sy; + register int ax; + register int ay; + register int d; + + static int fuck = 0; + + /* For debugging only */ + + if (fl->a.x < 0 || fl->a.x >= f_w || fl->a.y < 0 || fl->a.y >= f_h || + fl->b.x < 0 || fl->b.x >= f_w || fl->b.y < 0 || fl->b.y >= f_h) + { + fprintf(stderr, "fuck %d \r", fuck++); + return; + } + +#define PUTDOT(xx, yy, cc) fb[(yy) * f_w + (xx)] = (cc) + + dx = fl->b.x - fl->a.x; + ax = 2 * (dx < 0 ? -dx : dx); + sx = dx < 0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy < 0 ? -dy : dy); + sy = dy < 0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax / 2; + while (1) + { + PUTDOT(x, y, color); + if (x == fl->b.x) return; + if (d >= 0) + { + y += sy; + d -= ax; + } + + x += sx; + d += ay; + } + } + else + { + d = ax - ay / 2; + while (1) + { + PUTDOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + + y += sy; + d += ax; + } + } +} + +/**************************************************************************** + * Name: am_draw_m_line + * + * Description: + * Clip lines, draw visible part sof lines. + * + ****************************************************************************/ + +void am_draw_m_line(mline_t *ml, int color) +{ + static fline_t fl; + + if (am_clip_m_line(ml, &fl)) + { + /* draws it on frame buffer using fb coords */ + + am_draw_f_line(&fl, color); + } +} + +/**************************************************************************** + * Name: am_draw_grid + * + * Description: + * Draws flat (floor/ceiling tile) aligned grid lines. + * + ****************************************************************************/ + +static void am_draw_grid(int color) +{ + fixed_t x, y; + fixed_t start; + fixed_t end; + mline_t ml; + + /* Figure out start of vertical gridlines */ + + start = m_x; + if ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) - + ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS)); + end = m_x + m_w; + + /* draw vertical gridlines */ + + ml.a.y = m_y; + ml.b.y = m_y + m_h; + for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.x = x; + ml.b.x = x; + am_draw_m_line(&ml, color); + } + + /* Figure out start of horizontal gridlines */ + + start = m_y; + if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)) + start += (MAPBLOCKUNITS << FRACBITS) - + ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS)); + end = m_y + m_h; + + /* draw horizontal gridlines */ + + ml.a.x = m_x; + ml.b.x = m_x + m_w; + for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS)) + { + ml.a.y = y; + ml.b.y = y; + am_draw_m_line(&ml, color); + } +} + +/**************************************************************************** + * Name: am_draw_walls + * + * Description: + * Determines visible lines, draws them. This is LineDef based, not LineSeg + * based. + * + ****************************************************************************/ + +static void am_draw_walls(void) +{ + int i; + static mline_t l; + + for (i = 0; i < numlines; i++) + { + l.a.x = lines[i].v1->x; + l.a.y = lines[i].v1->y; + l.b.x = lines[i].v2->x; + l.b.y = lines[i].v2->y; + if (cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & LINE_NEVERSEE) && !cheating) continue; + if (!lines[i].backsector) + { + am_draw_m_line(&l, WALLCOLORS + lightlev); + } + else + { + if (lines[i].special == 39) + { + /* teleporters */ + + am_draw_m_line(&l, WALLCOLORS + WALLRANGE / 2); + } + else if (lines[i].flags & ML_SECRET) /* secret door */ + { + if (cheating) + am_draw_m_line(&l, SECRETWALLCOLORS + lightlev); + else + am_draw_m_line(&l, WALLCOLORS + lightlev); + } + else if (lines[i].backsector->floorheight != + lines[i].frontsector->floorheight) + { + /* floor level change */ + + am_draw_m_line(&l, FDWALLCOLORS + lightlev); + } + else if (lines[i].backsector->ceilingheight != + lines[i].frontsector->ceilingheight) + { + /* ceiling level change */ + + am_draw_m_line(&l, CDWALLCOLORS + lightlev); + } + else if (cheating) + { + am_draw_m_line(&l, TSWALLCOLORS + lightlev); + } + } + } + else if (plr->powers[pw_allmap]) + { + if (!(lines[i].flags & LINE_NEVERSEE)) + { + am_draw_m_line(&l, GRAYS + 3); + } + } + } +} + +/**************************************************************************** + * Name: am_rotate + * + * Description: + * Rotation in 2D. + * Used to rotate player arrow line character. + * + ****************************************************************************/ + +static void am_rotate(fixed_t *x, fixed_t *y, angle_t a) +{ + fixed_t tmpx; + + tmpx = fixed_mul(*x, finecosine[a >> ANGLETOFINESHIFT]) - + fixed_mul(*y, finesine[a >> ANGLETOFINESHIFT]); + + *y = fixed_mul(*x, finesine[a >> ANGLETOFINESHIFT]) + + fixed_mul(*y, finecosine[a >> ANGLETOFINESHIFT]); + + *x = tmpx; +} + +static void am_draw_line_character(mline_t *lineguy, int lineguylines, + fixed_t scale, angle_t angle, int color, + fixed_t x, fixed_t y) +{ + int i; + mline_t l; + + for (i = 0; i < lineguylines; i++) + { + l.a.x = lineguy[i].a.x; + l.a.y = lineguy[i].a.y; + + if (scale) + { + l.a.x = fixed_mul(scale, l.a.x); + l.a.y = fixed_mul(scale, l.a.y); + } + + if (angle) am_rotate(&l.a.x, &l.a.y, angle); + + l.a.x += x; + l.a.y += y; + + l.b.x = lineguy[i].b.x; + l.b.y = lineguy[i].b.y; + + if (scale) + { + l.b.x = fixed_mul(scale, l.b.x); + l.b.y = fixed_mul(scale, l.b.y); + } + + if (angle) am_rotate(&l.b.x, &l.b.y, angle); + + l.b.x += x; + l.b.y += y; + + am_draw_m_line(&l, color); + } +} + +static void am_draw_players(void) +{ + int i; + player_t *p; + int their_color = -1; + int color; + + static int their_colors[] = + { + GREENS, GRAYS, BROWNS, REDS + }; + + if (!netgame) + { + if (cheating) + { + am_draw_line_character( + cheat_player_arrow, arrlen(cheat_player_arrow), 0, + plr->mo->angle, WHITE, plr->mo->x, plr->mo->y); + } + else + { + am_draw_line_character(player_arrow, arrlen(player_arrow), 0, + plr->mo->angle, WHITE, plr->mo->x, + plr->mo->y); + } + + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + their_color++; + p = &players[i]; + + if ((deathmatch && !singledemo) && p != plr) continue; + + if (!playeringame[i]) continue; + + if (p->powers[pw_invisibility]) + color = 246; /* *close* to black */ + else + color = their_colors[their_color]; + + am_draw_line_character(player_arrow, arrlen(player_arrow), 0, + p->mo->angle, color, p->mo->x, p->mo->y); + } +} + +static void am_draw_things(int colors, int colorrange) +{ + int i; + mobj_t *t; + + for (i = 0; i < numsectors; i++) + { + t = sectors[i].thinglist; + while (t) + { + am_draw_line_character(thintriangle_guy, arrlen(thintriangle_guy), + 16 << FRACBITS, t->angle, colors + lightlev, + t->x, t->y); + t = t->snext; + } + } +} + +static void am_draw_marks(void) +{ + int i; + int fx; + int fy; + int w; + int h; + + for (i = 0; i < AM_NUMMARKPOINTS; i++) + { + if (markpoints[i].x != -1) + { + /* w = SHORT(marknums[i]->width); + * h = SHORT(marknums[i]->height); + */ + + w = 5; /* because something's wrong with the wad, i guess */ + h = 6; /* because something's wrong with the wad, i guess */ + fx = CXMTOF(markpoints[i].x); + fy = CYMTOF(markpoints[i].y); + if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h) + v_draw_patch(fx, fy, marknums[i]); + } + } +} + +static void am_draw_crosshair(int color) +{ + fb[(f_w * (f_h + 1)) / 2] = color; /* single point for now */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: am_stop + ****************************************************************************/ + +void am_stop(void) +{ + static event_t st_notify = + { + 0, ev_keyup, AM_MSGEXITED, 0 + }; + + am_unload_pics(); + automapactive = false; + st_responder(&st_notify); + stopped = true; +} + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Updates on Game Tick + * + ****************************************************************************/ + +void am_ticker(void) +{ + if (!automapactive) return; + + amclock++; + + if (followplayer) am_do_follow_player(); + + /* Change the zoom if necessary */ + + if (ftom_zoommul != FRACUNIT) am_change_window_scale(); + + /* Change x,y location */ + + if (m_paninc.x || m_paninc.y) am_change_window_loc(); + + /* Update light level */ + + /* am_update_light_lev(); */ +} + +/**************************************************************************** + * Name: am_responder + * + * Description: + * Handle events (user inputs) in automap mode + * + ****************************************************************************/ + +boolean am_responder(event_t *ev) +{ + int rc; + static int bigstate = 0; + static char buffer[20]; + int key; + + rc = false; + + if (ev->type == ev_joystick && joybautomap >= 0 && + (ev->data1 & (1 << joybautomap)) != 0) + { + joywait = i_get_time() + 5; + + if (!automapactive) + { + am_start(); + viewactive = false; + } + else + { + bigstate = 0; + viewactive = true; + am_stop(); + } + + return true; + } + + if (!automapactive) + { + if (ev->type == ev_keydown && ev->data1 == key_map_toggle) + { + am_start(); + viewactive = false; + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + key = ev->data1; + + if (key == key_map_east) /* pan right */ + { + if (!followplayer) + m_paninc.x = FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_west) /* pan left */ + { + if (!followplayer) + m_paninc.x = -FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_north) /* pan up */ + { + if (!followplayer) + m_paninc.y = FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_south) /* pan down */ + { + if (!followplayer) + m_paninc.y = -FTOM(F_PANINC); + else + rc = false; + } + else if (key == key_map_zoomout) /* zoom out */ + { + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + } + else if (key == key_map_zoomin) /* zoom in */ + { + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + } + else if (key == key_map_toggle) + { + bigstate = 0; + viewactive = true; + am_stop(); + } + else if (key == key_map_maxzoom) + { + bigstate = !bigstate; + if (bigstate) + { + am_save_scale_and_loc(); + am_min_out_window_scale(); + } + else + am_restore_scale_and_loc(); + } + else if (key == key_map_follow) + { + followplayer = !followplayer; + f_oldloc.x = INT_MAX; + if (followplayer) + plr->message = (AMSTR_FOLLOWON); + else + plr->message = (AMSTR_FOLLOWOFF); + } + else if (key == key_map_grid) + { + grid = !grid; + if (grid) + plr->message = (AMSTR_GRIDON); + else + plr->message = (AMSTR_GRIDOFF); + } + else if (key == key_map_mark) + { + snprintf(buffer, sizeof(buffer), "%s %d", (AMSTR_MARKEDSPOT), + markpointnum); + plr->message = buffer; + am_add_mark(); + } + else if (key == key_map_clearmark) + { + am_clear_marks(); + plr->message = (AMSTR_MARKSCLEARED); + } + else + { + rc = false; + } + + if ((!deathmatch || gameversion <= exe_doom_1_8) && + cht_check_cheat(&cheat_amap, ev->data2)) + { + rc = false; + cheating = (cheating + 1) % 3; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + key = ev->data1; + + if (key == key_map_east) + { + if (!followplayer) m_paninc.x = 0; + } + else if (key == key_map_west) + { + if (!followplayer) m_paninc.x = 0; + } + else if (key == key_map_north) + { + if (!followplayer) m_paninc.y = 0; + } + else if (key == key_map_south) + { + if (!followplayer) m_paninc.y = 0; + } + else if (key == key_map_zoomout || key == key_map_zoomin) + { + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + } + } + + return rc; +} + +void am_drawer(void) +{ + if (!automapactive) return; + + am_clear_fb(BACKGROUND); + if (grid) am_draw_grid(GRIDCOLORS); + am_draw_walls(); + am_draw_players(); + if (cheating == 2) am_draw_things(THINGCOLORS, THINGRANGE); + am_draw_crosshair(XHAIRCOLORS); + + am_draw_marks(); + + v_mark_rect(f_x, f_y, f_w, f_h); +} diff --git a/games/NXDoom/src/doom/am_map.h b/games/NXDoom/src/doom/am_map.h new file mode 100644 index 000000000..38b8e4ab1 --- /dev/null +++ b/games/NXDoom/src/doom/am_map.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/am_map.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * AutoMap module. + * + ****************************************************************************/ + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "m_cheat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Used by ST StatusBar stuff. */ + +#define AM_MSGHEADER (('a' << 24) + ('m' << 16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e' << 8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x' << 8)) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern cheatseq_t cheat_amap; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: am_responder + * + * Description: + * Called by main loop. + * + ****************************************************************************/ + +boolean am_responder(event_t *ev); + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Called by main loop. + * + ****************************************************************************/ + +void am_ticker(void); + +/**************************************************************************** + * Name: am_ticker + * + * Description: + * Called by main loop. Called instead of view drawer if automap active. + * + ****************************************************************************/ + +void am_drawer(void); + +/**************************************************************************** + * Name: am_stop + * + * Description: + * Called to force the automap to quit if the level is completed while it is + * up. + * + ****************************************************************************/ + +void am_stop(void); + +#endif /* __AMMAP_H__ */ diff --git a/games/NXDoom/src/doom/d_englsh.h b/games/NXDoom/src/doom/d_englsh.h new file mode 100644 index 000000000..3f0945fc9 --- /dev/null +++ b/games/NXDoom/src/doom/d_englsh.h @@ -0,0 +1,695 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_englsh.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Printed strings for translation. + * English language support (default). + * + ****************************************************************************/ + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + +/* Printed strings for translation */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* D_Main.C */ + +#define D_DEVSTR "Development mode ON.\n" +#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" + +/* M_Menu.C */ + +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n" PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n" PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n" PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n" PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n" PRESSYN +#define QLPROMPT \ + "do you want to quickload the game named\n\n'%s'?\n\n" PRESSYN + +#define NEWGAME \ + "you can't start a new game\n" \ + "while in a network game.\n\n" PRESSKEY + +#define NIGHTMARE \ + "are you sure? this skill level\n" \ + "isn't even remotely fair.\n\n" PRESSYN + +#define SWSTRING \ + "this is the shareware version of doom.\n\n" \ + "you need to order the entire trilogy.\n\n" PRESSKEY + +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define NETEND "you can't end a netgame!\n\n" PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n" PRESSYN + +#define DOSY "(press y to quit to dos.)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +/* P_inter.C */ + +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +/* P_Doors.C */ + +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" + +/* G_game.C */ + +#define GGSAVED "game saved." + +/* HU_stuff.C */ + +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "level 1: entryway" +#define HUSTR_2 "level 2: underhalls" +#define HUSTR_3 "level 3: the gantlet" +#define HUSTR_4 "level 4: the focus" +#define HUSTR_5 "level 5: the waste tunnels" +#define HUSTR_6 "level 6: the crusher" +#define HUSTR_7 "level 7: dead simple" +#define HUSTR_8 "level 8: tricks and traps" +#define HUSTR_9 "level 9: the pit" +#define HUSTR_10 "level 10: refueling base" +#define HUSTR_11 "level 11: 'o' of destruction!" + +#define HUSTR_12 "level 12: the factory" +#define HUSTR_13 "level 13: downtown" +#define HUSTR_14 "level 14: the inmost dens" +#define HUSTR_15 "level 15: industrial zone" +#define HUSTR_16 "level 16: suburbs" +#define HUSTR_17 "level 17: tenements" +#define HUSTR_18 "level 18: the courtyard" +#define HUSTR_19 "level 19: the citadel" +#define HUSTR_20 "level 20: gotcha!" + +#define HUSTR_21 "level 21: nirvana" +#define HUSTR_22 "level 22: the catacombs" +#define HUSTR_23 "level 23: barrels o' fun" +#define HUSTR_24 "level 24: the chasm" +#define HUSTR_25 "level 25: bloodfalls" +#define HUSTR_26 "level 26: the abandoned mines" +#define HUSTR_27 "level 27: monster condo" +#define HUSTR_28 "level 28: the spirit world" +#define HUSTR_29 "level 29: the living end" +#define HUSTR_30 "level 30: icon of sin" + +#define HUSTR_31 "level 31: wolfenstein" +#define HUSTR_32 "level 32: grosse" + +#define PHUSTR_1 "level 1: congo" +#define PHUSTR_2 "level 2: well of souls" +#define PHUSTR_3 "level 3: aztec" +#define PHUSTR_4 "level 4: caged" +#define PHUSTR_5 "level 5: ghost town" +#define PHUSTR_6 "level 6: baron's lair" +#define PHUSTR_7 "level 7: caughtyard" +#define PHUSTR_8 "level 8: realm" +#define PHUSTR_9 "level 9: abattoire" +#define PHUSTR_10 "level 10: onslaught" +#define PHUSTR_11 "level 11: hunted" + +#define PHUSTR_12 "level 12: speed" +#define PHUSTR_13 "level 13: the crypt" +#define PHUSTR_14 "level 14: genesis" +#define PHUSTR_15 "level 15: the twilight" +#define PHUSTR_16 "level 16: the omen" +#define PHUSTR_17 "level 17: compound" +#define PHUSTR_18 "level 18: neurosphere" +#define PHUSTR_19 "level 19: nme" +#define PHUSTR_20 "level 20: the death domain" + +#define PHUSTR_21 "level 21: slayer" +#define PHUSTR_22 "level 22: impossible mission" +#define PHUSTR_23 "level 23: tombstone" +#define PHUSTR_24 "level 24: the final frontier" +#define PHUSTR_25 "level 25: the temple of darkness" +#define PHUSTR_26 "level 26: bunker" +#define PHUSTR_27 "level 27: anti-christ" +#define PHUSTR_28 "level 28: the sewers" +#define PHUSTR_29 "level 29: odyssey of noises" +#define PHUSTR_30 "level 30: the gateway of hell" + +#define PHUSTR_31 "level 31: cyberden" +#define PHUSTR_32 "level 32: go 2 it" + +#define THUSTR_1 "level 1: system control" +#define THUSTR_2 "level 2: human bbq" +#define THUSTR_3 "level 3: power control" +#define THUSTR_4 "level 4: wormhole" +#define THUSTR_5 "level 5: hanger" +#define THUSTR_6 "level 6: open season" +#define THUSTR_7 "level 7: prison" +#define THUSTR_8 "level 8: metal" +#define THUSTR_9 "level 9: stronghold" +#define THUSTR_10 "level 10: redemption" +#define THUSTR_11 "level 11: storage facility" + +#define THUSTR_12 "level 12: crater" +#define THUSTR_13 "level 13: nukage processing" +#define THUSTR_14 "level 14: steel works" +#define THUSTR_15 "level 15: dead zone" +#define THUSTR_16 "level 16: deepest reaches" +#define THUSTR_17 "level 17: processing area" +#define THUSTR_18 "level 18: mill" +#define THUSTR_19 "level 19: shipping/respawning" +#define THUSTR_20 "level 20: central processing" + +#define THUSTR_21 "level 21: administration center" +#define THUSTR_22 "level 22: habitat" +#define THUSTR_23 "level 23: lunar mining project" +#define THUSTR_24 "level 24: quarry" +#define THUSTR_25 "level 25: baron's den" +#define THUSTR_26 "level 26: ballistyx" +#define THUSTR_27 "level 27: mount pain" +#define THUSTR_28 "level 28: heck" +#define THUSTR_29 "level 29: river styx" +#define THUSTR_30 "level 30: last call" + +#define THUSTR_31 "level 31: pharaoh" +#define THUSTR_32 "level 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +/* The following should NOT be changed unless it seems just AWFULLY + * necessary + */ + +#define HUSTR_PLRGREEN "Green: " +#define HUSTR_PLRINDIGO "Indigo: " +#define HUSTR_PLRBROWN "Brown: " +#define HUSTR_PLRRED "Red: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +/* AM_map.C */ + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +/* ST_stuff.C */ + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +/* F_Finale.C */ + +#define E1TEXT \ + "Once you beat the big badasses and\n" \ + "clean out the moon base you're supposed\n" \ + "to win, aren't you? Aren't you? Where's\n" \ + "your fat reward and ticket home? What\n" \ + "the hell is this? It's not supposed to\n" \ + "end this way!\n" \ + "\n" \ + "It stinks like rotten meat, but looks\n" \ + "like the lost Deimos base. Looks like\n" \ + "you're stuck on The Shores of Hell.\n" \ + "The only way out is through.\n" \ + "\n" \ + "To continue the DOOM experience, play\n" \ + "The Shores of Hell and its amazing\n" \ + "sequel, Inferno!\n" + +#define E2TEXT \ + "You've done it! The hideous cyber-\n" \ + "demon lord that ruled the lost Deimos\n" \ + "moon base has been slain and you\n" \ + "are triumphant! But ... where are\n" \ + "you? You clamber to the edge of the\n" \ + "moon and look down to see the awful\n" \ + "truth.\n" \ + "\n" \ + "Deimos floats above Hell itself!\n" \ + "You've never heard of anyone escaping\n" \ + "from Hell, but you'll make the bastards\n" \ + "sorry they ever heard of you! Quickly,\n" \ + "you rappel down to the surface of\n" \ + "Hell.\n" \ + "\n" \ + "Now, it's on to the final chapter of\n" \ + "DOOM! -- Inferno." + +#define E3TEXT \ + "The loathsome spiderdemon that\n" \ + "masterminded the invasion of the moon\n" \ + "bases and caused so much death has had\n" \ + "its ass kicked for all time.\n" \ + "\n" \ + "A hidden doorway opens and you enter.\n" \ + "You've proven too tough for Hell to\n" \ + "contain, and now Hell at last plays\n" \ + "fair -- for you emerge from the door\n" \ + "to see the green fields of Earth!\n" \ + "Home at last.\n" \ + "\n" \ + "You wonder what's been happening on\n" \ + "Earth while you were battling evil\n" \ + "unleashed. It's good that no Hell-\n" \ + "spawn could have come through that\n" \ + "door with you ..." + +#define E4TEXT \ + "the spider mastermind must have sent forth\n" \ + "its legions of hellspawn before your\n" \ + "final confrontation with that terrible\n" \ + "beast from hell. but you stepped forward\n" \ + "and brought forth eternal damnation and\n" \ + "suffering upon the horde as a true hero\n" \ + "would in the face of something so evil.\n" \ + "\n" \ + "besides, someone was gonna pay for what\n" \ + "happened to daisy, your pet rabbit.\n" \ + "\n" \ + "but now, you see spread before you more\n" \ + "potential pain and gibbitude as a nation\n" \ + "of demons run amok among our cities.\n" \ + "\n" \ + "next stop, hell on earth!" + +/* after level 6, put this: */ + +#define C1TEXT \ + "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ + "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ + "\n" \ + "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ + "OF THE STARBASE AND FIND THE CONTROLLING\n" \ + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ + "HOSTAGE." + +/* After level 11, put this: */ + +#define C2TEXT \ + "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n" \ + "THE NIGHTMARE. NOW YOU ARE THE ONLY\n" \ + "HUMAN LEFT ON THE FACE OF THE PLANET.\n" \ + "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n" \ + "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n" \ + "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n" \ + "THAT YOU HAVE SAVED YOUR SPECIES.\n" \ + "\n" \ + "BUT THEN, EARTH CONTROL BEAMS DOWN A\n" \ + "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n" \ + "THE SOURCE OF THE ALIEN INVASION. IF YOU\n" \ + "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n" \ + "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n" \ + "YOUR OWN HOME CITY, NOT FAR FROM THE\n" \ + "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n" \ + "UP AND RETURN TO THE FRAY." + +/* After level 20, put this: */ + +#define C3TEXT \ + "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n" \ + "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n" \ + "YOU SEE NO WAY TO DESTROY THE CREATURES'\n" \ + "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n" \ + "TEETH AND PLUNGE THROUGH IT.\n" \ + "\n" \ + "THERE MUST BE A WAY TO CLOSE IT ON THE\n" \ + "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n" \ + "GOT TO GO THROUGH HELL TO GET TO IT?" + +/* After level 29, put this: */ + +#define C4TEXT \ + "THE HORRENDOUS VISAGE OF THE BIGGEST\n" \ + "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n" \ + "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n" \ + "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n" \ + "UP AND DIES, ITS THRASHING LIMBS\n" \ + "DEVASTATING UNTOLD MILES OF HELL'S\n" \ + "SURFACE.\n" \ + "\n" \ + "YOU'VE DONE IT. THE INVASION IS OVER.\n" \ + "EARTH IS SAVED. HELL IS A WRECK. YOU\n" \ + "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n" \ + "DIE, NOW. WIPING THE SWEAT FROM YOUR\n" \ + "FOREHEAD YOU BEGIN THE LONG TREK BACK\n" \ + "HOME. REBUILDING EARTH OUGHT TO BE A\n" \ + "LOT MORE FUN THAN RUINING IT WAS.\n" + +/* Before level 31, put this: */ + +#define C5TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n" \ + "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n" \ + "HUMANS, RATHER THAN DEMONS. YOU WONDER\n" \ + "WHO THE INMATES OF THIS CORNER OF HELL\n" \ + "WILL BE." + +/* Before level 32, put this: */ + +#define C6TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE\n" \ + "SUPER SECRET LEVEL! YOU'D BETTER\n" \ + "BLAZE THROUGH THIS ONE!\n" + +/* after map 06 */ + +#define P1TEXT \ + "You gloat over the steaming carcass of the\n" \ + "Guardian. With its death, you've wrested\n" \ + "the Accelerator from the stinking claws\n" \ + "of Hell. You relax and glance around the\n" \ + "room. Damn! There was supposed to be at\n" \ + "least one working prototype, but you can't\n" \ + "see it. The demons must have taken it.\n" \ + "\n" \ + "You must find the prototype, or all your\n" \ + "struggles will have been wasted. Keep\n" \ + "moving, keep fighting, keep killing.\n" \ + "Oh yes, keep living, too." + +/* after map 11 */ + +#define P2TEXT \ + "Even the deadly Arch-Vile labyrinth could\n" \ + "not stop you, and you've gotten to the\n" \ + "prototype Accelerator which is soon\n" \ + "efficiently and permanently deactivated.\n" \ + "\n" \ + "You're good at that kind of thing." + +/* after map 20 */ + +#define P3TEXT \ + "You've bashed and battered your way into\n" \ + "the heart of the devil-hive. Time for a\n" \ + "Search-and-Destroy mission, aimed at the\n" \ + "Gatekeeper, whose foul offspring is\n" \ + "cascading to Earth. Yeah, he's bad. But\n" \ + "you know who's worse!\n" \ + "\n" \ + "Grinning evilly, you check your gear, and\n" \ + "get ready to give the bastard a little Hell\n" \ + "of your own making!" + +/* after map 30 */ + +#define P4TEXT \ + "The Gatekeeper's evil face is splattered\n" \ + "all over the place. As its tattered corpse\n" \ + "collapses, an inverted Gate forms and\n" \ + "sucks down the shards of the last\n" \ + "prototype Accelerator, not to mention the\n" \ + "few remaining demons. You're done. Hell\n" \ + "has gone back to pounding bad dead folks \n" \ + "instead of good live ones. Remember to\n" \ + "tell your grandkids to put a rocket\n" \ + "launcher in your coffin. If you go to Hell\n" \ + "when you die, you'll need it for some\n" \ + "final cleaning-up ..." + +/* before map 31 */ + +#define P5TEXT \ + "You've found the second-hardest level we\n" \ + "got. Hope you have a saved game a level or\n" \ + "two previous. If not, be prepared to die\n" \ + "aplenty. For master marines only." + +/* before map 32 */ + +#define P6TEXT \ + "Betcha wondered just what WAS the hardest\n" \ + "level we had ready for ya? Now you know.\n" \ + "No one gets out alive." + +#define T1TEXT \ + "You've fought your way out of the infested\n" \ + "experimental labs. It seems that UAC has\n" \ + "once again gulped it down. With their\n" \ + "high turnover, it must be hard for poor\n" \ + "old UAC to buy corporate health insurance\n" \ + "nowadays..\n" \ + "\n" \ + "Ahead lies the military complex, now\n" \ + "swarming with diseased horrors hot to get\n" \ + "their teeth into you. With luck, the\n" \ + "complex still has some warlike ordnance\n" \ + "laying around." + +#define T2TEXT \ + "You hear the grinding of heavy machinery\n" \ + "ahead. You sure hope they're not stamping\n" \ + "out new hellspawn, but you're ready to\n" \ + "ream out a whole herd if you have to.\n" \ + "They might be planning a blood feast, but\n" \ + "you feel about as mean as two thousand\n" \ + "maniacs packed into one mad killer.\n" \ + "\n" \ + "You don't plan to go down easy." + +#define T3TEXT \ + "The vista opening ahead looks real damn\n" \ + "familiar. Smells familiar, too -- like\n" \ + "fried excrement. You didn't like this\n" \ + "place before, and you sure as hell ain't\n" \ + "planning to like it now. The more you\n" \ + "brood on it, the madder you get.\n" \ + "Hefting your gun, an evil grin trickles\n" \ + "onto your face. Time to take some names." + +#define T4TEXT \ + "Suddenly, all is silent, from one horizon\n" \ + "to the other. The agonizing echo of Hell\n" \ + "fades away, the nightmare sky turns to\n" \ + "blue, the heaps of monster corpses start \n" \ + "to evaporate along with the evil stench \n" \ + "that filled the air. Jeeze, maybe you've\n" \ + "done it. Have you really won?\n" \ + "\n" \ + "Something rumbles in the distance.\n" \ + "A blue light begins to glow inside the\n" \ + "ruined skull of the demon-spitter." + +#define T5TEXT \ + "What now? Looks totally different. Kind\n" \ + "of like King Tut's condo. Well,\n" \ + "whatever's here can't be any worse\n" \ + "than usual. Can it? Or maybe it's best\n" \ + "to let sleeping gods lie.." + +#define T6TEXT \ + "Time for a vacation. You've burst the\n" \ + "bowels of hell and by golly you're ready\n" \ + "for a break. You mutter to yourself,\n" \ + "Maybe someone else can kick Hell's ass\n" \ + "next time around. Ahead lies a quiet town,\n" \ + "with peaceful flowing water, quaint\n" \ + "buildings, and presumably no Hellspawn.\n" \ + "\n" \ + "As you step off the transport, you hear\n" \ + "the stomp of a cyberdemon's iron shoe." + +/* Character cast strings F_FINALE.C */ + +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + +/* DOOM end messages */ + +#define DOOM1_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "please don't leave, there's more\ndemons to toast!", \ + "let's beat it -- this is turning\ninto a bloodbath!", \ + "i wouldn't leave if i were you.\ndos is much worse.", \ + "you're trying to say you like dos\nbetter than me, right?", \ + "don't leave yet -- there's a\ndemon around that corner!", \ + "ya know, next time you come in here\ni'm gonna toast ya.", \ + "go ahead and leave. see if i care.", + +/* QuitDOOM II messages */ + +#define DOOM2_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "you want to quit?\nthen, thou hast lost an eighth!", \ + "don't go now, there's a \ndimensional shambler waiting\nat the dos " \ + "prompt!", \ + "get outta here and go back\nto your boring programs.", \ + "if i were your boss, i'd \n deathmatch ya in a minute!", \ + "look, bud. you leave now\nand you forfeit your body count!", \ + "just leave. when you come\nback, i'll be waiting with a bat.", \ + "you're lucky i don't smack\nyou for thinking about leaving.", + +#endif /* __D_ENGLSH__ */ diff --git a/games/NXDoom/src/doom/d_french.h b/games/NXDoom/src/doom/d_french.h new file mode 100644 index 000000000..0252aa747 --- /dev/null +++ b/games/NXDoom/src/doom/d_french.h @@ -0,0 +1,698 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_french.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Printed strings for translation. + * French language support. + * TODO: finish implementation + * WARN: Non-ascii text does not render properly. Find a solution or cope. + * + ****************************************************************************/ + +#ifndef __D_FRENCH__ +#define __D_FRENCH__ + +/* Printed strings for translation */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* D_Main.C */ + +#define D_DEVSTR "Mode développement activé.\n" +#define D_CDROM "Version CD-ROM: default.cfg de c:\\doomdata\n" + +/* M_Menu.C */ + +#define PRESSKEY "appuie une touche." +#define PRESSYN "appuie y ou n." +#define QUITMSG "es-tu certain que tu veux quitter ce super jeu ?" +#define LOADNET "tu ne peux pas charger pendant un jeu en ligne!\n\n" PRESSKEY +#define QLOADNET \ + "tu ne peux pas charger rapidement pendant un jeu en ligne!\n\n" PRESSKEY +#define QSAVESPOT "tu n'as pas choisi un slot sauvegarde rapide\n\n" PRESSKEY +#define SAVEDEAD "tu ne peux pas sauvegarder si tu ne joue pas\n\n" PRESSKEY +#define QSPROMPT "sauvegarde ton jeu nommé\n\n'%s'?\n\n" PRESSYN +#define QLPROMPT \ + "tu veux charger rapidement le jeu nommé\n\n'%s'?\n\n" PRESSYN + +#define NEWGAME \ + "tu ne peux pas commencer un nouveau jeu\n" \ + "durant un jeu en ligne.\n\n" PRESSKEY + +#define NIGHTMARE \ + "es-tu sure? ce niveau\n" \ + "n'est absolument pas juste.\n\n" PRESSYN + +#define SWSTRING \ + "ceci est le version shareware de doom.\n\n" \ + "tu besoin de commander la trilogie entiere.\n\n" PRESSKEY + +#define MSGOFF "Messages DÉSACTIVÉS" +#define MSGON "Messages ACTIVÉS" +#define NETEND "tu ne peux pas quitter un jeu en ligne!\n\n" PRESSKEY +#define ENDGAME "es-tu sure que tu veux quitter le jeu?\n\n" PRESSYN + +#define DOSY "(appuie y pour quitter vers dos.)" + +#define DETAILHI "Haute définition" +#define DETAILLO "Faible définition" +#define GAMMALVL0 "Correction gamma DÉSACTIVÉ" +#define GAMMALVL1 "Correction gamma niveau 1" +#define GAMMALVL2 "Correction gamma niveau 2" +#define GAMMALVL3 "Correction gamma niveau 3" +#define GAMMALVL4 "Correction gamma niveau 4" +#define EMPTYSTRING "slot vide" + +/* P_inter.C */ + +#define GOTARMOR "Ramassé l'armure." +#define GOTMEGA "Ramassé le Mega Armure!" +#define GOTHTHBONUS "Ramassé un bonus de santé." +#define GOTARMBONUS "Ramassé un bonus d'armure." +#define GOTSTIM "Ramassé un stimpack." +#define GOTMEDINEED "Ramassé un medikit que tu as VRAIMENT besoin!" +#define GOTMEDIKIT "Ramassé un medikit." +#define GOTSUPER "Superchargé!" + +#define GOTBLUECARD "Ramassé une carte d'accès bleue." +#define GOTYELWCARD "Ramassé une carte d'accès jaune." +#define GOTREDCARD "Ramassé une carte d'accès rouge." +#define GOTBLUESKUL "Ramassé une clé crâne bleue." +#define GOTYELWSKUL "Ramassé une clé crâne jaune." +#define GOTREDSKULL "Ramassé une clé crâne rouge." + +#define GOTINVUL "Invulnérabilité!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Invisibilité Partielle" +#define GOTSUIT "Radiation Shielding Suit" // TODO? +#define GOTMAP "Carte de la Zone Informatique" +#define GOTVISOR "Light Amplification Visor" // TODO? +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Ramassé un clip." +#define GOTCLIPBOX "Ramassé une boîte de balles." +#define GOTROCKET "Ramassé une fusée." +#define GOTROCKBOX "Ramassé une boîte de fusées." +#define GOTCELL "Ramassé une cellule énergétique." +#define GOTCELLBOX "Ramassé un paquet de cellules énergétiques." +#define GOTSHELLS "Ramassé 4 cartouches de fusil de chasse." +#define GOTSHELLBOX "Ramassé une boîte de cartouches de fusil de chasse." +#define GOTBACKPACK "Ramassé une sac à dos plein d'ammo!" + +#define GOTBFG9000 "Tu as eu le BFG9000! Hohoho, oui!" +#define GOTCHAINGUN "Tu as eu le chaingun!" +#define GOTCHAINSAW "Un scie à chaîne! Trouve de la viande!" +#define GOTLAUNCHER "Tu as eu la lance-roquettes!" +#define GOTPLASMA "Tu as eu le pistolet à plasma!" +#define GOTSHOTGUN "Tu as eu le fusil de chasse!" +#define GOTSHOTGUN2 "Tu as eu le super fusil de chasse!" + +/* P_Doors.C */ + +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" + +/* G_game.C */ + +#define GGSAVED "game saved." + +/* HU_stuff.C */ + +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "niveau 1: entryway" +#define HUSTR_2 "niveau 2: underhalls" +#define HUSTR_3 "niveau 3: the gantlet" +#define HUSTR_4 "niveau 4: the focus" +#define HUSTR_5 "niveau 5: the waste tunnels" +#define HUSTR_6 "niveau 6: the crusher" +#define HUSTR_7 "niveau 7: dead simple" +#define HUSTR_8 "niveau 8: tricks and traps" +#define HUSTR_9 "niveau 9: the pit" +#define HUSTR_10 "niveau 10: refueling base" +#define HUSTR_11 "niveau 11: 'o' of destruction!" + +#define HUSTR_12 "niveau 12: the factory" +#define HUSTR_13 "niveau 13: downtown" +#define HUSTR_14 "niveau 14: the inmost dens" +#define HUSTR_15 "niveau 15: industrial zone" +#define HUSTR_16 "niveau 16: suburbs" +#define HUSTR_17 "niveau 17: tenements" +#define HUSTR_18 "niveau 18: the courtyard" +#define HUSTR_19 "niveau 19: the citadel" +#define HUSTR_20 "niveau 20: gotcha!" + +#define HUSTR_21 "niveau 21: nirvana" +#define HUSTR_22 "niveau 22: the catacombs" +#define HUSTR_23 "niveau 23: barrels o' fun" +#define HUSTR_24 "niveau 24: the chasm" +#define HUSTR_25 "niveau 25: bloodfalls" +#define HUSTR_26 "niveau 26: the abandoned mines" +#define HUSTR_27 "niveau 27: monster condo" +#define HUSTR_28 "niveau 28: the spirit world" +#define HUSTR_29 "niveau 29: the living end" +#define HUSTR_30 "niveau 30: icon of sin" + +#define HUSTR_31 "niveau 31: wolfenstein" +#define HUSTR_32 "niveau 32: grosse" + +#define PHUSTR_1 "niveau 1: congo" +#define PHUSTR_2 "niveau 2: well of souls" +#define PHUSTR_3 "niveau 3: aztec" +#define PHUSTR_4 "niveau 4: caged" +#define PHUSTR_5 "niveau 5: ghost town" +#define PHUSTR_6 "niveau 6: baron's lair" +#define PHUSTR_7 "niveau 7: caughtyard" +#define PHUSTR_8 "niveau 8: realm" +#define PHUSTR_9 "niveau 9: abattoire" +#define PHUSTR_10 "niveau 10: onslaught" +#define PHUSTR_11 "niveau 11: hunted" + +#define PHUSTR_12 "niveau 12: speed" +#define PHUSTR_13 "niveau 13: the crypt" +#define PHUSTR_14 "niveau 14: genesis" +#define PHUSTR_15 "niveau 15: the twilight" +#define PHUSTR_16 "niveau 16: the omen" +#define PHUSTR_17 "niveau 17: compound" +#define PHUSTR_18 "niveau 18: neurosphere" +#define PHUSTR_19 "niveau 19: nme" +#define PHUSTR_20 "niveau 20: the death domain" + +#define PHUSTR_21 "niveau 21: slayer" +#define PHUSTR_22 "niveau 22: impossible mission" +#define PHUSTR_23 "niveau 23: tombstone" +#define PHUSTR_24 "niveau 24: the final frontier" +#define PHUSTR_25 "niveau 25: the temple of darkness" +#define PHUSTR_26 "niveau 26: bunker" +#define PHUSTR_27 "niveau 27: anti-christ" +#define PHUSTR_28 "niveau 28: the sewers" +#define PHUSTR_29 "niveau 29: odyssey of noises" +#define PHUSTR_30 "niveau 30: the gateway of hell" + +#define PHUSTR_31 "niveau 31: cyberden" +#define PHUSTR_32 "niveau 32: go 2 it" + +#define THUSTR_1 "niveau 1: system control" +#define THUSTR_2 "niveau 2: human bbq" +#define THUSTR_3 "niveau 3: power control" +#define THUSTR_4 "niveau 4: wormhole" +#define THUSTR_5 "niveau 5: hanger" +#define THUSTR_6 "niveau 6: open season" +#define THUSTR_7 "niveau 7: prison" +#define THUSTR_8 "niveau 8: metal" +#define THUSTR_9 "niveau 9: stronghold" +#define THUSTR_10 "niveau 10: redemption" +#define THUSTR_11 "niveau 11: storage facility" + +#define THUSTR_12 "niveau 12: crater" +#define THUSTR_13 "niveau 13: nukage processing" +#define THUSTR_14 "niveau 14: steel works" +#define THUSTR_15 "niveau 15: dead zone" +#define THUSTR_16 "niveau 16: deepest reaches" +#define THUSTR_17 "niveau 17: processing area" +#define THUSTR_18 "niveau 18: mill" +#define THUSTR_19 "niveau 19: shipping/respawning" +#define THUSTR_20 "niveau 20: central processing" + +#define THUSTR_21 "niveau 21: administration center" +#define THUSTR_22 "niveau 22: habitat" +#define THUSTR_23 "niveau 23: lunar mining project" +#define THUSTR_24 "niveau 24: quarry" +#define THUSTR_25 "niveau 25: baron's den" +#define THUSTR_26 "niveau 26: ballistyx" +#define THUSTR_27 "niveau 27: mount pain" +#define THUSTR_28 "niveau 28: heck" +#define THUSTR_29 "niveau 29: river styx" +#define THUSTR_30 "niveau 30: last call" + +#define THUSTR_31 "niveau 31: pharaoh" +#define THUSTR_32 "niveau 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Oui" +#define HUSTR_CHATMACRO0 "Non" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +/* The following should NOT be changed unless it seems just AWFULLY + * necessary + */ + +#define HUSTR_PLRGREEN "Vert: " +#define HUSTR_PLRINDIGO "Indigo: " +#define HUSTR_PLRBROWN "Brun: " +#define HUSTR_PLRRED "Rouge: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +/* AM_map.C */ + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +/* ST_stuff.C */ + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +/* F_Finale.C */ + +#define E1TEXT \ + "Once you beat the big badasses and\n" \ + "clean out the moon base you're supposed\n" \ + "to win, aren't you? Aren't you? Where's\n" \ + "your fat reward and ticket home? What\n" \ + "the hell is this? It's not supposed to\n" \ + "end this way!\n" \ + "\n" \ + "It stinks like rotten meat, but looks\n" \ + "like the lost Deimos base. Looks like\n" \ + "you're stuck on The Shores of Hell.\n" \ + "The only way out is through.\n" \ + "\n" \ + "To continue the DOOM experience, play\n" \ + "The Shores of Hell and its amazing\n" \ + "sequel, Inferno!\n" + +#define E2TEXT \ + "You've done it! The hideous cyber-\n" \ + "demon lord that ruled the lost Deimos\n" \ + "moon base has been slain and you\n" \ + "are triumphant! But ... where are\n" \ + "you? You clamber to the edge of the\n" \ + "moon and look down to see the awful\n" \ + "truth.\n" \ + "\n" \ + "Deimos floats above Hell itself!\n" \ + "You've never heard of anyone escaping\n" \ + "from Hell, but you'll make the bastards\n" \ + "sorry they ever heard of you! Quickly,\n" \ + "you rappel down to the surface of\n" \ + "Hell.\n" \ + "\n" \ + "Now, it's on to the final chapter of\n" \ + "DOOM! -- Inferno." + +#define E3TEXT \ + "The loathsome spiderdemon that\n" \ + "masterminded the invasion of the moon\n" \ + "bases and caused so much death has had\n" \ + "its ass kicked for all time.\n" \ + "\n" \ + "A hidden doorway opens and you enter.\n" \ + "You've proven too tough for Hell to\n" \ + "contain, and now Hell at last plays\n" \ + "fair -- for you emerge from the door\n" \ + "to see the green fields of Earth!\n" \ + "Home at last.\n" \ + "\n" \ + "You wonder what's been happening on\n" \ + "Earth while you were battling evil\n" \ + "unleashed. It's good that no Hell-\n" \ + "spawn could have come through that\n" \ + "door with you ..." + +#define E4TEXT \ + "the spider mastermind must have sent forth\n" \ + "its legions of hellspawn before your\n" \ + "final confrontation with that terrible\n" \ + "beast from hell. but you stepped forward\n" \ + "and brought forth eternal damnation and\n" \ + "suffering upon the horde as a true hero\n" \ + "would in the face of something so evil.\n" \ + "\n" \ + "besides, someone was gonna pay for what\n" \ + "happened to daisy, your pet rabbit.\n" \ + "\n" \ + "but now, you see spread before you more\n" \ + "potential pain and gibbitude as a nation\n" \ + "of demons run amok among our cities.\n" \ + "\n" \ + "next stop, hell on earth!" + +/* after niveau 6, put this: */ + +#define C1TEXT \ + "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ + "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ + "\n" \ + "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ + "OF THE STARBASE AND FIND THE CONTROLLING\n" \ + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ + "HOSTAGE." + +/* After niveau 11, put this: */ + +#define C2TEXT \ + "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n" \ + "THE NIGHTMARE. NOW YOU ARE THE ONLY\n" \ + "HUMAN LEFT ON THE FACE OF THE PLANET.\n" \ + "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n" \ + "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n" \ + "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n" \ + "THAT YOU HAVE SAVED YOUR SPECIES.\n" \ + "\n" \ + "BUT THEN, EARTH CONTROL BEAMS DOWN A\n" \ + "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n" \ + "THE SOURCE OF THE ALIEN INVASION. IF YOU\n" \ + "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n" \ + "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n" \ + "YOUR OWN HOME CITY, NOT FAR FROM THE\n" \ + "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n" \ + "UP AND RETURN TO THE FRAY." + +/* After niveau 20, put this: */ + +#define C3TEXT \ + "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n" \ + "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n" \ + "YOU SEE NO WAY TO DESTROY THE CREATURES'\n" \ + "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n" \ + "TEETH AND PLUNGE THROUGH IT.\n" \ + "\n" \ + "THERE MUST BE A WAY TO CLOSE IT ON THE\n" \ + "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n" \ + "GOT TO GO THROUGH HELL TO GET TO IT?" + +/* After niveau 29, put this: */ + +#define C4TEXT \ + "THE HORRENDOUS VISAGE OF THE BIGGEST\n" \ + "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n" \ + "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n" \ + "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n" \ + "UP AND DIES, ITS THRASHING LIMBS\n" \ + "DEVASTATING UNTOLD MILES OF HELL'S\n" \ + "SURFACE.\n" \ + "\n" \ + "YOU'VE DONE IT. THE INVASION IS OVER.\n" \ + "EARTH IS SAVED. HELL IS A WRECK. YOU\n" \ + "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n" \ + "DIE, NOW. WIPING THE SWEAT FROM YOUR\n" \ + "FOREHEAD YOU BEGIN THE LONG TREK BACK\n" \ + "HOME. REBUILDING EARTH OUGHT TO BE A\n" \ + "LOT MORE FUN THAN RUINING IT WAS.\n" + +/* Before niveau 31, put this: */ + +#define C5TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n" \ + "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n" \ + "HUMANS, RATHER THAN DEMONS. YOU WONDER\n" \ + "WHO THE INMATES OF THIS CORNER OF HELL\n" \ + "WILL BE." + +/* Before niveau 32, put this: */ + +#define C6TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE\n" \ + "SUPER SECRET LEVEL! YOU'D BETTER\n" \ + "BLAZE THROUGH THIS ONE!\n" + +/* after map 06 */ + +#define P1TEXT \ + "You gloat over the steaming carcass of the\n" \ + "Guardian. With its death, you've wrested\n" \ + "the Accelerator from the stinking claws\n" \ + "of Hell. You relax and glance around the\n" \ + "room. Damn! There was supposed to be at\n" \ + "least one working prototype, but you can't\n" \ + "see it. The demons must have taken it.\n" \ + "\n" \ + "You must find the prototype, or all your\n" \ + "struggles will have been wasted. Keep\n" \ + "moving, keep fighting, keep killing.\n" \ + "Oh yes, keep living, too." + +/* after map 11 */ + +#define P2TEXT \ + "Even the deadly Arch-Vile labyrinth could\n" \ + "not stop you, and you've gotten to the\n" \ + "prototype Accelerator which is soon\n" \ + "efficiently and permanently deactivated.\n" \ + "\n" \ + "You're good at that kind of thing." + +/* after map 20 */ + +#define P3TEXT \ + "You've bashed and battered your way into\n" \ + "the heart of the devil-hive. Time for a\n" \ + "Search-and-Destroy mission, aimed at the\n" \ + "Gatekeeper, whose foul offspring is\n" \ + "cascading to Earth. Yeah, he's bad. But\n" \ + "you know who's worse!\n" \ + "\n" \ + "Grinning evilly, you check your gear, and\n" \ + "get ready to give the bastard a little Hell\n" \ + "of your own making!" + +/* after map 30 */ + +#define P4TEXT \ + "The Gatekeeper's evil face is splattered\n" \ + "all over the place. As its tattered corpse\n" \ + "collapses, an inverted Gate forms and\n" \ + "sucks down the shards of the last\n" \ + "prototype Accelerator, not to mention the\n" \ + "few remaining demons. You're done. Hell\n" \ + "has gone back to pounding bad dead folks \n" \ + "instead of good live ones. Remember to\n" \ + "tell your grandkids to put a rocket\n" \ + "launcher in your coffin. If you go to Hell\n" \ + "when you die, you'll need it for some\n" \ + "final cleaning-up ..." + +/* before map 31 */ + +#define P5TEXT \ + "You've found the second-hardest niveau we\n" \ + "got. Hope you have a saved game a niveau or\n" \ + "two previous. If not, be prepared to die\n" \ + "aplenty. For master marines only." + +/* before map 32 */ + +#define P6TEXT \ + "Betcha wondered just what WAS the hardest\n" \ + "niveau we had ready for ya? Now you know.\n" \ + "No one gets out alive." + +#define T1TEXT \ + "You've fought your way out of the infested\n" \ + "experimental labs. It seems that UAC has\n" \ + "once again gulped it down. With their\n" \ + "high turnover, it must be hard for poor\n" \ + "old UAC to buy corporate health insurance\n" \ + "nowadays..\n" \ + "\n" \ + "Ahead lies the military complex, now\n" \ + "swarming with diseased horrors hot to get\n" \ + "their teeth into you. With luck, the\n" \ + "complex still has some warlike ordnance\n" \ + "laying around." + +#define T2TEXT \ + "You hear the grinding of heavy machinery\n" \ + "ahead. You sure hope they're not stamping\n" \ + "out new hellspawn, but you're ready to\n" \ + "ream out a whole herd if you have to.\n" \ + "They might be planning a blood feast, but\n" \ + "you feel about as mean as two thousand\n" \ + "maniacs packed into one mad killer.\n" \ + "\n" \ + "You don't plan to go down easy." + +#define T3TEXT \ + "The vista opening ahead looks real damn\n" \ + "familiar. Smells familiar, too -- like\n" \ + "fried excrement. You didn't like this\n" \ + "place before, and you sure as hell ain't\n" \ + "planning to like it now. The more you\n" \ + "brood on it, the madder you get.\n" \ + "Hefting your gun, an evil grin trickles\n" \ + "onto your face. Time to take some names." + +#define T4TEXT \ + "Suddenly, all is silent, from one horizon\n" \ + "to the other. The agonizing echo of Hell\n" \ + "fades away, the nightmare sky turns to\n" \ + "blue, the heaps of monster corpses start \n" \ + "to evaporate along with the evil stench \n" \ + "that filled the air. Jeeze, maybe you've\n" \ + "done it. Have you really won?\n" \ + "\n" \ + "Something rumbles in the distance.\n" \ + "A blue light begins to glow inside the\n" \ + "ruined skull of the demon-spitter." + +#define T5TEXT \ + "What now? Looks totally different. Kind\n" \ + "of like King Tut's condo. Well,\n" \ + "whatever's here can't be any worse\n" \ + "than usual. Can it? Or maybe it's best\n" \ + "to let sleeping gods lie.." + +#define T6TEXT \ + "Time for a vacation. You've burst the\n" \ + "bowels of hell and by golly you're ready\n" \ + "for a break. You mutter to yourself,\n" \ + "Maybe someone else can kick Hell's ass\n" \ + "next time around. Ahead lies a quiet town,\n" \ + "with peaceful flowing water, quaint\n" \ + "buildings, and presumably no Hellspawn.\n" \ + "\n" \ + "As you step off the transport, you hear\n" \ + "the stomp of a cyberdemon's iron shoe." + +/* Character cast strings F_FINALE.C */ + +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + +/* DOOM end messages */ + +#define DOOM1_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "please don't leave, there's more\ndemons to toast!", \ + "let's beat it -- this is turning\ninto a bloodbath!", \ + "i wouldn't leave if i were you.\ndos is much worse.", \ + "you're trying to say you like dos\nbetter than me, right?", \ + "don't leave yet -- there's a\ndemon around that corner!", \ + "ya know, next time you come in here\ni'm gonna toast ya.", \ + "go ahead and leave. see if i care.", + +/* QuitDOOM II messages */ + +#define DOOM2_ENDMSG \ + "are you sure you want to\nquit this great game?", \ + "you want to quit?\nthen, thou hast lost an eighth!", \ + "don't go now, there's a \ndimensional shambler waiting\nat the dos " \ + "prompt!", \ + "get outta here and go back\nto your boring programs.", \ + "if i were your boss, i'd \n deathmatch ya in a minute!", \ + "look, bud. you leave now\nand you forfeit your body count!", \ + "just leave. when you come\nback, i'll be waiting with a bat.", \ + "you're lucky i don't smack\nyou for thinking about leaving.", + +#endif /* __D_FRENCH__ */ diff --git a/games/NXDoom/src/doom/d_items.c b/games/NXDoom/src/doom/d_items.c new file mode 100644 index 000000000..8fc606b6c --- /dev/null +++ b/games/NXDoom/src/doom/d_items.c @@ -0,0 +1,143 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_items.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to sprite numbers. */ + +#include "info.h" + +#include "d_items.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* PSPRITE ACTIONS for weapons. + * This struct controls the weapon animations. + * + * Each entry is: + * ammo/ammunition type + * upstate + * downstate + * readystate + * atkstate, i.e. attack/fire/hit frame + * flashstate, muzzle flash + */ + +weaponinfo_t weaponinfo[NUMWEAPONS] = +{ + { + /* fist */ + + am_noammo, + S_PUNCHUP, + S_PUNCHDOWN, + S_PUNCH, + S_PUNCH1, + S_NULL, + }, + { + /* pistol */ + + am_clip, + S_PISTOLUP, + S_PISTOLDOWN, + S_PISTOL, + S_PISTOL1, + S_PISTOLFLASH, + }, + { + /* shotgun */ + + am_shell, + S_SGUNUP, + S_SGUNDOWN, + S_SGUN, + S_SGUN1, + S_SGUNFLASH1, + }, + { + /* chaingun */ + + am_clip, + S_CHAINUP, + S_CHAINDOWN, + S_CHAIN, + S_CHAIN1, + S_CHAINFLASH1, + }, + { + /* missile launcher */ + + am_misl, + S_MISSILEUP, + S_MISSILEDOWN, + S_MISSILE, + S_MISSILE1, + S_MISSILEFLASH1, + }, + { + /* plasma rifle */ + + am_cell, + S_PLASMAUP, + S_PLASMADOWN, + S_PLASMA, + S_PLASMA1, + S_PLASMAFLASH1, + }, + { + /* bfg 9000 */ + + am_cell, + S_BFGUP, + S_BFGDOWN, + S_BFG, + S_BFG1, + S_BFGFLASH1, + }, + { + /* chainsaw */ + + am_noammo, + S_SAWUP, + S_SAWDOWN, + S_SAW, + S_SAW1, + S_NULL, + }, + { + /* super shotgun */ + + am_shell, + S_DSGUNUP, + S_DSGUNDOWN, + S_DSGUN, + S_DSGUN1, + S_DSGUNFLASH1, + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/d_items.h b/games/NXDoom/src/doom/d_items.h new file mode 100644 index 000000000..bcc50b9af --- /dev/null +++ b/games/NXDoom/src/doom/d_items.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_items.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Items: key cards, artifacts, weapon, ammunition. + * + ****************************************************************************/ + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Weapon info: sprite frames, ammunition use. */ + +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; +} weaponinfo_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + +#endif /* __D_ITEMS__ */ diff --git a/games/NXDoom/src/doom/d_main.c b/games/NXDoom/src/doom/d_main.c new file mode 100644 index 000000000..52df6eda1 --- /dev/null +++ b/games/NXDoom/src/doom/d_main.c @@ -0,0 +1,2184 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DOOM main program (d_doom_main) and game loop (d_doomloop), + * plus functions to determine game mode (shareware, registered), + * parse command line parameters, configure game parameters (turbo), + * and call the startup functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "deh_main.h" +#include "doomdef.h" +#include "doomstat.h" + +#include "dstrings.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "d_iwad.h" + +#include "v_diskicon.h" +#include "v_video.h" +#include "w_main.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "f_finale.h" +#include "f_wipe.h" + +#include "m_argv.h" +#include "m_config.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "p_saveg.h" + +#include "i_endoom.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" + +#include "g_game.h" + +#include "am_map.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "wi_stuff.h" + +#ifdef CONFIG_GAMES_NXDOOM_NET +#include "net_client.h" +#include "net_dedicated.h" +#include "net_query.h" +#endif + +#include "p_setup.h" +#include "r_local.h" +#include "statdump.h" + +#include "d_main.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pack +{ + const char *name; + int mission; +}; + +struct gameversion +{ + const char *description; + const char *cmdline; + game_version_t version; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* D-DoomLoop() + * + * Not a globally visible function, just included for source reference, + * called by d_doom_main, never exits. + * + * Manages timing and IO, calls all ?_Responder, ?_Ticker, and ?_Drawer, + * calls i_get_time, i_start_frame, and i_start_tic + */ + +void d_doomloop(void); + +void d_connect_net_game(void); +void d_check_net_game(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Location where savegames are stored */ + +char *savegamedir; + +/* location of IWAD and WAD files */ + +char *iwadfile; + +boolean devparm; /* started game with -devparm */ +boolean nomonsters; /* checkparm of -nomonsters */ +boolean respawnparm; /* checkparm of -respawn */ +boolean fastparm; /* checkparm of -fast */ + +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; +int startloadgame; + +boolean advancedemo; + +/* Store demo, do not accept any inputs */ + +boolean storedemo; + +/* If true, the main game loop has started. */ + +boolean main_loop_started = false; + +int show_endoom = 1; +int show_diskicon = 1; + +/* wipegamestate can be set to -1 to force a wipe on the next draw */ + +gamestate_t wipegamestate = GS_DEMOSCREEN; + +/* DEMO LOOP */ + +int demosequence; +int pagetic; +const char *pagename; + +/* print title for every printed line */ + +char title[128]; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Copyright message banners + * Some dehacked mods replace these. These are only displayed if they are + * replaced by dehacked. + */ + +static const char *g_copyright_banners[] = +{ + "========================================================================" + "===\n" + "ATTENTION: This version of DOOM has been modified. If you would like " + "to\n" + "get a copy of the original game, call 1-800-IDGAMES or see the readme " + "file.\n" + " You will not receive technical support for modified games.\n" + " press enter to continue\n" + "========================================================================" + "===\n", + + "========================================================================" + "===\n" + " Commercial product - do not distribute!\n" + " Please report software piracy to the SPA: 1-800-388-PIR8\n" + "========================================================================" + "===\n", + + "========================================================================" + "===\n" + " Shareware!\n" + "========================================================================" + "===\n", +}; + +/* These are the lumps that will be checked in IWAD, + * if any one is not present, execution will be aborted. + */ + +static char g_name[23][8] = +{ + "e2m1", "e2m2", "e2m3", "e2m4", "e2m5", "e2m6", + "e2m7", "e2m8", "e2m9", "e3m1", "e3m3", "e3m3", + "e3m4", "e3m5", "e3m6", "e3m7", "e3m8", "e3m9", + "dphoof", "bfgga0", "heada1", "cybra1", "spida1d1", +}; + +static char *g_gamedescription; + +/* Add configuration file variable bindings. */ + +static const char *const g_chat_macro_defaults[10] = +{ + HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2, HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, HUSTR_CHATMACRO5, HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, HUSTR_CHATMACRO9, +}; + +/* Strings for dehacked replacements of the startup banner + * + * These are from the original source: some of them are perhaps + * not used in any dehacked patches + */ + +static const char *g_banners[] = +{ + /* doom2.wad */ + + " " + "DOOM 2: Hell on Earth v%i.%i" + " ", + + /* doom2.wad v1.666 */ + + " " + "DOOM 2: Hell on Earth v%i.%i66" + " ", + + /* doom1.wad */ + + " " + "DOOM Shareware Startup v%i.%i" + " ", + + /* doom.wad */ + + " " + "DOOM Registered Startup v%i.%i" + " ", + + /* Registered DOOM uses this */ + + " " + "DOOM System Startup v%i.%i" + " ", + + /* Doom v1.666 */ + + " " + "DOOM System Startup v%i.%i66" + " " + + /* doom.wad (Ultimate DOOM) */ + + " " + "The Ultimate DOOM Startup v%i.%i" + " ", + + /* tnt.wad */ + + " " + "DOOM 2: TNT - Evilution v%i.%i" + " ", + + /* plutonia.wad */ + + " " + "DOOM 2: Plutonia Experiment v%i.%i" + " ", +}; + +static const struct gameversion g_gameversions[] = +{ + {"Doom 1.2", "1.2", exe_doom_1_2}, + {"Doom 1.5", "1.5", exe_doom_1_5}, + {"Doom 1.666", "1.666", exe_doom_1_666}, + {"Doom 1.7/1.7a", "1.7", exe_doom_1_7}, + {"Doom 1.8", "1.8", exe_doom_1_8}, + {"Doom 1.9", "1.9", exe_doom_1_9}, + {"Hacx", "hacx", exe_hacx}, + {"Ultimate Doom", "ultimate", exe_ultimate}, + {"Final Doom", "final", exe_final}, + {"Final Doom (alt)", "final2", exe_final2}, + {"Chex Quest", "chex", exe_chex}, + {NULL, NULL, 0}, +}; + +static const struct pack g_packs[] = +{ + {"doom2", doom2}, + {"tnt", pack_tnt}, + {"plutonia", pack_plut}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void enable_loading_disk(void) +{ + const char *disk_lump_name; + + if (show_diskicon) + { + if (m_check_parm("-cdrom") > 0) + { + disk_lump_name = ("STCDROM"); + } + else + { + disk_lump_name = ("STDISK"); + } + + v_enable_loading_disk(disk_lump_name, SCREENWIDTH - LOADING_DISK_W, + SCREENHEIGHT - LOADING_DISK_H); + } +} + +static void d_bind_variables(void) +{ + int i; + + m_apply_platform_defaults(); + + i_bind_input_variables(); + i_bind_video_variables(); + i_bind_joystick_variables(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_bind_sound_variables(); +#endif + + m_bind_base_controls(); + m_bind_weapon_controls(); + m_bind_map_controls(); + m_bind_menu_controls(); + m_bind_chat_controls(MAXPLAYERS); + + key_multi_msgplayer[0] = HUSTR_KEYGREEN; + key_multi_msgplayer[1] = HUSTR_KEYINDIGO; + key_multi_msgplayer[2] = HUSTR_KEYBROWN; + key_multi_msgplayer[3] = HUSTR_KEYRED; + +#ifdef CONFIG_GAMES_NXDOOM_NET + net_bind_variables(); +#endif + + m_bind_int_variable("mouse_sensitivity", &g_mouse_sensitivity); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + m_bind_int_variable("sfx_volume", &g_sfx_volume); + m_bind_int_variable("music_volume", &g_music_volume); + m_bind_int_variable("snd_channels", &snd_channels); +#endif + m_bind_int_variable("show_messages", &g_show_messages); + m_bind_int_variable("screenblocks", &screenblocks); + m_bind_int_variable("detaillevel", &g_detail_level); + m_bind_int_variable("vanilla_savegame_limit", &vanilla_savegame_limit); + m_bind_int_variable("vanilla_demo_limit", &vanilla_demo_limit); + m_bind_int_variable("show_endoom", &show_endoom); + m_bind_int_variable("show_diskicon", &show_diskicon); + + /* Multiplayer chat macros */ + + for (i = 0; i < 10; ++i) + { + char buf[12]; + + chat_macros[i] = m_string_duplicate(g_chat_macro_defaults[i]); + snprintf(buf, sizeof(buf), "chatmacro%i", i); + m_bind_string_variable(buf, &chat_macros[i]); + } +} + +/* Function called at exit to display the ENDOOM screen */ + +static void d_endoom(void) +{ +#ifdef CONFIG_GAMES_NXDOOM_ENDOOM + byte *endoom; + + /* Don't show ENDOOM if we have it disabled, or we're running + * in screensaver or control test mode. Only show it once the + * game has actually started. + */ + + if (!show_endoom || !main_loop_started || screensaver_mode || + m_check_parm("-testcontrols") > 0) + { + return; + } + + endoom = w_cache_lump_name(("ENDOOM"), PU_STATIC); + + i_endoom(endoom); +#else + return; +#endif +} + +static boolean is_french_iwad(void) +{ + return (gamemission == doom2 && w_check_num_for_name("M_RDTHIS") < 0 && + w_check_num_for_name("M_EPISOD") < 0 && + w_check_num_for_name("M_EPI1") < 0 && + w_check_num_for_name("M_EPI2") < 0 && + w_check_num_for_name("M_EPI3") < 0 && + w_check_num_for_name("WIOSTF") < 0 && + w_check_num_for_name("WIOBJ") >= 0); +} + +/* Load dehacked patches needed for certain IWADs. */ + +static void load_iwad_deh(void) +{ + /* The Freedoom IWADs have DEHACKED lumps that must be loaded. */ + + if (gamevariant == freedoom || gamevariant == freedm) + { + /* Old versions of Freedoom (before 2014-09) did not have technically + * valid DEHACKED lumps, so ignore errors and just continue if this + * is an old IWAD. + */ + + deh_load_lump_by_name("DEHACKED", false, true); + } + + /* If this is the HACX IWAD, we need to load the DEHACKED lump. */ + + if (gameversion == exe_hacx) + { + if (!deh_load_lump_by_name("DEHACKED", true, false)) + { + i_error("DEHACKED lump not found. Please check that this is the " + "Hacx v1.2 IWAD."); + } + } + + /* Chex Quest needs a separate Dehacked patch which must be downloaded + * and installed next to the IWAD. + */ + + if (gameversion == exe_chex) + { + char *chex_deh = NULL; + char *dirname; + + /* Look for chex.deh in the same directory as the IWAD file. */ + + dirname = m_dir_name(iwadfile); + chex_deh = m_string_join(dirname, DIR_SEPARATOR_S, "chex.deh", NULL); + free(dirname); + + /* If the dehacked patch isn't found, try searching the WAD + * search path instead. We might find it... + */ + + if (!m_file_exists(chex_deh)) + { + free(chex_deh); + chex_deh = d_find_wad_by_name("chex.deh"); + } + + /* Still not found? */ + + if (chex_deh == NULL) + { + i_error("Unable to find Chex Quest dehacked file (chex.deh).\n" + "The dehacked file is required in order to emulate\n" + "chex.exe correctly. It can be found in your nearest\n" + "/idgames repository mirror at:\n\n" + " themes/chex/chexdeh.zip"); + } + + if (!deh_loadfile(chex_deh)) + { + i_error("Failed to load chex.deh needed for emulating chex.exe."); + } + } + + if (is_french_iwad()) + { + char *french_deh = NULL; + char *dirname; + + /* Look for french.deh in the same directory as the IWAD file. */ + + dirname = m_dir_name(iwadfile); + french_deh = + m_string_join(dirname, DIR_SEPARATOR_S, "french.deh", NULL); + printf("French version\n"); + free(dirname); + + /* If the dehacked patch isn't found, try searching the WAD + * search path instead. We might find it... + */ + + if (!m_file_exists(french_deh)) + { + free(french_deh); + french_deh = d_find_wad_by_name("french.deh"); + } + + /* Still not found? */ + + if (french_deh == NULL) + { + i_error( + "Unable to find French Doom II dehacked file\n" + "(french.deh). The dehacked file is required in order to\n" + "emulate French doom2.exe correctly. It can be found in\n" + "your nearest /idgames repository mirror at:\n\n" + " utils/exe_edit/patches/french.zip" + ); + } + + if (!deh_loadfile(french_deh)) + { + i_error("Failed to load french.deh needed for emulating French\n" + "doom2.exe."); + } + } +} + +static void g_check_demo_status_at_exit(void) +{ + g_check_demo_status(); +} + +/* draw current display, possibly wiping it from the previous */ + +static boolean d_display(void) +{ + static boolean viewactivestate = false; + static boolean g_menuactivestate = false; + static boolean inhelpscreensstate = false; + static boolean d_fullscreen = false; + static gamestate_t oldgamestate = -1; + static int borderdrawcount; + int y; + boolean wipe; + boolean redrawsbar; + + redrawsbar = false; + + /* change the view size if needed */ + + if (setsizeneeded) + { + r_execute_set_view_size(); + oldgamestate = -1; /* force background redraw */ + borderdrawcount = 3; + } + + /* save the current screen if about to wipe */ + + if (gamestate != wipegamestate) + { + wipe = true; + wipe_start_screen(0, 0, SCREENWIDTH, SCREENHEIGHT); + } + else + { + wipe = false; + } + + if (gamestate == GS_LEVEL && gametic) + { + hu_erase(); + } + + /* do buffered drawing */ + + switch (gamestate) + { + case GS_LEVEL: + + if (!gametic) + { + break; + } + + if (automapactive) + { + am_drawer(); + } + + if (wipe || (viewheight != SCREENHEIGHT && d_fullscreen)) + { + redrawsbar = true; + } + + if (inhelpscreensstate && !inhelpscreens) + { + redrawsbar = true; /* just put away the help screen */ + } + + st_drawer(viewheight == SCREENHEIGHT, redrawsbar); + d_fullscreen = viewheight == SCREENHEIGHT; + break; + + case GS_INTERMISSION: + wi_drawer(); + break; + + case GS_FINALE: + f_drawer(); + break; + + case GS_DEMOSCREEN: + d_page_drawer(); + break; + } + + /* draw buffered stuff to screen */ + + i_update_no_blit(); + + /* draw the view directly */ + + if (gamestate == GS_LEVEL && !automapactive && gametic) + { + r_render_player_view(&players[displayplayer]); + } + + if (gamestate == GS_LEVEL && gametic) + { + hu_drawer(); + } + + /* clean up border stuff */ + + if (gamestate != oldgamestate && gamestate != GS_LEVEL) + i_set_palette(w_cache_lump_name(("PLAYPAL"), PU_CACHE)); + + /* see if the border needs to be initially drawn */ + + if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL) + { + viewactivestate = false; /* view was not active */ + r_fill_back_screen(); /* draw the pattern into the back screen */ + } + + /* see if the border needs to be updated to the screen */ + + if (gamestate == GS_LEVEL && !automapactive && + scaledviewwidth != SCREENWIDTH) + { + if (g_menuactive || g_menuactivestate || !viewactivestate) + borderdrawcount = 3; + if (borderdrawcount) + { + r_draw_view_border(); /* erase old menu stuff */ + borderdrawcount--; + } + } + + if (testcontrols) + { + /* Box showing current mouse speed */ + + v_draw_mouse_speed_box(testcontrols_mousespeed); + } + + g_menuactivestate = g_menuactive; + viewactivestate = viewactive; + inhelpscreensstate = inhelpscreens; + oldgamestate = wipegamestate = gamestate; + + /* draw pause pic */ + + if (paused) + { + if (automapactive) + y = 4; + else + y = viewwindowy + 4; + v_draw_patch_direct(viewwindowx + (scaledviewwidth - 68) / 2, y, + w_cache_lump_name(("M_PAUSE"), PU_CACHE)); + } + + /* menus go directly to the screen */ + + m_drawer(); /* menu is drawn even on top of everything */ + net_update(); /* send out any new accumulation */ + + return wipe; +} + +/**************************************************************************** + * Name: d_grab_mouse_callback + * + * Description: + * Called to determine whether to grab the mouse pointer + * + ****************************************************************************/ + +static boolean d_grab_mouse_callback(void) +{ + /* Drone players don't need mouse focus */ + +#ifdef CONFIG_GAMES_NXDOOM_NET + if (drone) return false; +#endif + + /* when menu is active or game is paused, release the mouse */ + + if (g_menuactive || paused) return false; + + /* only grab mouse when playing levels (but not demos) */ + + return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo; +} + +/**************************************************************************** + * Name: d_run_frame + ****************************************************************************/ + +static void d_run_frame(void) +{ + int nowtime; + int tics; + static int wipestart; + static boolean wipe; + + if (wipe) + { + do + { + nowtime = i_get_time(); + tics = nowtime - wipestart; + usleep(1000); + } + while (tics <= 0); + + wipestart = nowtime; + wipe = !wipe_screen_wipe(WIPE_MELT, 0, 0, + SCREENWIDTH, SCREENHEIGHT, tics); + i_update_no_blit(); + m_drawer(); /* menu is drawn even on top of wipes */ + i_finish_update(); /* page flip or blit buffer */ + return; + } + + /* frame synchronous IO operations */ + + i_start_frame(); + + try_run_tics(); /* will run at least one tic */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_update_sounds(players[consoleplayer].mo); /* move positional sounds */ +#endif + + /* Update display, next frame, with current state if no profiling is on */ + + if (screenvisible && !nodrawers) + { + if ((wipe = d_display())) + { + /* start wipe on this frame */ + + wipe_endscreen(0, 0, SCREENWIDTH, SCREENHEIGHT); + + wipestart = i_get_time() - 1; + } + else + { + /* normal update */ + + i_finish_update(); /* page flip or blit buffer */ + } + } +} + +/* Get game name: if the startup banner has been replaced, use that. + * Otherwise, use the name given + */ + +static char *get_game_name(const char *gamename) +{ + size_t i; + + for (i = 0; i < arrlen(g_banners); ++i) + { + const char *deh_sub; + + /* Has the banner been replaced? */ + + deh_sub = (g_banners[i]); + + if (deh_sub != g_banners[i]) + { + size_t gamename_size; + int version; + char *deh_gamename; + + /* Has been replaced. + * We need to expand via printf to include the Doom version number + * We also need to cut off spaces to get the basic name + */ + + gamename_size = strlen(deh_sub) + 10; + deh_gamename = malloc(gamename_size); + if (deh_gamename == NULL) + { + i_error("GetGameName: Failed to allocate new string"); + } + + version = g_vanilla_version_code(); + snprintf(deh_gamename, gamename_size, g_banners[i], version / 100, + version % 100); + + while (deh_gamename[0] != '\0' && isspace(deh_gamename[0])) + { + memmove(deh_gamename, deh_gamename + 1, gamename_size - 1); + } + + while (deh_gamename[0] != '\0' && + isspace(deh_gamename[strlen(deh_gamename) - 1])) + { + deh_gamename[strlen(deh_gamename) - 1] = '\0'; + } + + return deh_gamename; + } + } + + return m_string_duplicate(gamename); +} + +static void set_mission_for_pack_name(const char *pack_name) +{ + int i; + + for (i = 0; i < arrlen(g_packs); ++i) + { + if (!strcasecmp(pack_name, g_packs[i].name)) + { + gamemission = g_packs[i].mission; + return; + } + } + + printf("Valid mission packs are:\n"); + + for (i = 0; i < arrlen(g_packs); ++i) + { + printf("\t%s\n", g_packs[i].name); + } + + i_error("Unknown mission pack name: %s", pack_name); +} + +/* Find out what version of Doom is playing. */ + +static void d_identify_version(void) +{ + /* gamemission is set up by the d_find_iwad function. But if + * we specify '-iwad', we have to identify using + * IdentifyIWADByName. However, if the iwad does not match + * any known IWAD name, we may have a dilemma. Try to + * identify by its contents. + */ + + if (gamemission == none) + { + unsigned int i; + + for (i = 0; i < numlumps; ++i) + { + if (!strncasecmp(lumpinfo[i]->name, "MAP01", 8)) + { + gamemission = doom2; + break; + } + else if (!strncasecmp(lumpinfo[i]->name, "E1M1", 8)) + { + gamemission = doom; + break; + } + } + + if (gamemission == none) + { + /* Still no idea. I don't think this is going to work. */ + + i_error("Unknown or invalid IWAD file."); + } + } + + /* Make sure gamemode is set up correctly */ + + if (logical_gamemission == doom) + { + /* Doom 1. But which version? */ + + if (w_check_num_for_name("E4M1") > 0) + { + /* Ultimate Doom */ + + gamemode = retail; + } + else if (w_check_num_for_name("E3M1") > 0) + { + gamemode = registered; + } + else + { + gamemode = shareware; + } + } + else + { + int p; + + /* Doom 2 of some kind. */ + + gamemode = commercial; + + /* We can manually override the gamemission that we got from the + * IWAD detection code. This allows us to eg. play Plutonia 2 + * with Freedoom and get the right level names. + */ + + /* @category compat + * @arg + * + * Explicitly specify a Doom II "mission pack" to run as, instead of + * detecting it based on the filename. Valid values are: "doom2", + * "tnt" and "plutonia". + * + */ + + p = m_check_parm_with_args("-pack", 1); + if (p > 0) + { + set_mission_for_pack_name(myargv[p + 1]); + } + } +} + +/* Set the gamedescription string */ + +static void d_set_game_description(void) +{ + if (logical_gamemission == doom) + { + /* Doom 1. But which version? */ + + if (gamevariant == freedoom) + { + g_gamedescription = get_game_name("Freedoom: Phase 1"); + } + else if (gamemode == retail) + { + /* Ultimate Doom */ + + g_gamedescription = get_game_name("The Ultimate DOOM"); + } + else if (gamemode == registered) + { + g_gamedescription = get_game_name("DOOM Registered"); + } + else if (gamemode == shareware) + { + g_gamedescription = get_game_name("DOOM Shareware"); + } + } + else + { + /* Doom 2 of some kind. But which mission? */ + + if (gamevariant == freedm) + { + g_gamedescription = get_game_name("FreeDM"); + } + else if (gamevariant == freedoom) + { + g_gamedescription = get_game_name("Freedoom: Phase 2"); + } + else if (logical_gamemission == doom2) + { + g_gamedescription = get_game_name("DOOM 2: Hell on Earth"); + } + else if (logical_gamemission == pack_plut) + { + g_gamedescription = get_game_name("DOOM 2: Plutonia Experiment"); + } + else if (logical_gamemission == pack_tnt) + { + g_gamedescription = get_game_name("DOOM 2: TNT - Evilution"); + } + } + + if (g_gamedescription == NULL) + { + g_gamedescription = m_string_duplicate("Unknown"); + } +} + +static boolean d_addfile(char *filename) +{ + wad_file_t *handle; + + printf(" adding %s\n", filename); + handle = w_add_file(filename); + + return handle != NULL; +} + +/* Prints a message only if it has been modified by dehacked. */ + +static void print_dehacked_banners(void) +{ + size_t i; + + for (i = 0; i < arrlen(g_copyright_banners); ++i) + { + const char *deh_s; + + deh_s = (g_copyright_banners[i]); + + if (deh_s != g_copyright_banners[i]) + { + printf("%s", deh_s); + + /* Make sure the modified banner always ends in a newline + * character. If it doesn't, add a newline. This fixes av.wad. + */ + + if (deh_s[strlen(deh_s) - 1] != '\n') + { + printf("\n"); + } + } + } +} + +/* Initialize the game version */ + +static void init_game_version(void) +{ + byte *demolump; + char demolumpname[6]; + int demoversion; + int p; + int i; + boolean status; + + /* @arg + * @category compat + * + * Emulate a specific version of Doom. Valid values are "1.2", + * "1.5", "1.666", "1.7", "1.8", "1.9", "ultimate", "final", + * "final2", "hacx" and "chex". + * + */ + + p = m_check_parm_with_args("-gameversion", 1); + + if (p) + { + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + if (!strcmp(myargv[p + 1], g_gameversions[i].cmdline)) + { + gameversion = g_gameversions[i].version; + break; + } + } + + if (g_gameversions[i].description == NULL) + { + printf("Supported game versions:\n"); + + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + printf("\t%s (%s)\n", g_gameversions[i].cmdline, + g_gameversions[i].description); + } + + i_error("Unknown game version '%s'", myargv[p + 1]); + } + } + else + { + /* Determine automatically */ + + if (gamemission == pack_chex) + { + /* chex.exe - identified by iwad filename */ + + gameversion = exe_chex; + } + else if (gamemission == pack_hacx) + { + /* hacx.exe: identified by iwad filename */ + + gameversion = exe_hacx; + } + else if (gamemode == shareware || gamemode == registered || + (gamemode == commercial && gamemission == doom2)) + { + /* original */ + + gameversion = exe_doom_1_9; + + /* Detect version from demo lump */ + + for (i = 1; i <= 3; ++i) + { + snprintf(demolumpname, 6, "demo%i", i); + if (w_check_num_for_name(demolumpname) > 0) + { + demolump = w_cache_lump_name(demolumpname, PU_STATIC); + demoversion = demolump[0]; + w_release_lump_name(demolumpname); + status = true; + switch (demoversion) + { + case 0: + case 1: + case 2: + case 3: + case 4: + gameversion = exe_doom_1_2; + break; + case 106: + gameversion = exe_doom_1_666; + break; + case 107: + gameversion = exe_doom_1_7; + break; + case 108: + gameversion = exe_doom_1_8; + break; + case 109: + gameversion = exe_doom_1_9; + break; + default: + status = false; + break; + } + + if (status) + { + break; + } + } + } + } + else if (gamemode == retail) + { + gameversion = exe_ultimate; + } + else if (gamemode == commercial) + { + /* Final Doom: tnt or plutonia + * Defaults to emulating the first Final Doom executable, + * which has the crash in the demo loop; however, having + * this as the default should mean that it plays back + * most demos correctly. + */ + + gameversion = exe_final; + } + } + + /* Deathmatch 2.0 did not exist until Doom v1.4 */ + + if (gameversion <= exe_doom_1_2 && deathmatch == 2) + { + deathmatch = 1; + } + + /* The original exe does not support retail - 4th episode not supported */ + + if (gameversion < exe_ultimate && gamemode == retail) + { + gamemode = registered; + } + + /* EXEs prior to the Final Doom exes do not support Final Doom. */ + + if (gameversion < exe_final && gamemode == commercial && + (gamemission == pack_tnt || gamemission == pack_plut)) + { + gamemission = doom2; + } +} + +static void print_game_version(void) +{ + int i; + + for (i = 0; g_gameversions[i].description != NULL; ++i) + { + if (g_gameversions[i].version == gameversion) + { + printf("Emulating the behavior of the " + "'%s' executable.\n", + g_gameversions[i].description); + break; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* d_process_events + * Send all the events of the given timestamp down the responder chain + */ + +void d_process_events(void) +{ + event_t *ev; + + /* IF STORE DEMO, DO NOT ACCEPT INPUT */ + + if (storedemo) return; + + while ((ev = d_pop_event()) != NULL) + { + if (m_responder(ev)) continue; /* menu ate the event */ + g_responder(ev); + } +} + +/* d_doomloop */ + +void d_doomloop(void) +{ + if (gamevariant == bfgedition && + (demorecording || (gameaction == ga_playdemo) || netgame)) + { + printf(" WARNING: You are playing using one of the Doom Classic\n" + " IWAD files shipped with the Doom 3: BFG Edition. These are\n" + " known to be incompatible with the regular IWAD files and\n" + " may cause demos and network games to get out of sync.\n"); + } + + if (demorecording) + { + g_begin_recording(); + } + + main_loop_started = true; + + i_set_window_title(g_gamedescription); + i_graphics_check_commandline(); + i_set_grab_mouse_callback(d_grab_mouse_callback); + i_init_graphics(); + enable_loading_disk(); + + try_run_tics(); + + v_restore_buffer(); + r_execute_set_view_size(); + + d_start_game_loop(); + + if (testcontrols) + { + wipegamestate = gamestate; + } + + while (1) + { + d_run_frame(); + } +} + +/* d_page_ticker + * Handles timing for warped projection + */ + +void d_page_ticker(void) +{ + if (--pagetic < 0) d_advance_demo(); +} + +/**************************************************************************** + * Name: d_page_drawer + ****************************************************************************/ + +void d_page_drawer(void) +{ + v_draw_patch(0, 0, w_cache_lump_name(pagename, PU_CACHE)); +} + +/**************************************************************************** + * Name: d_advance_demo + * + * Description: + * Called after each demo or intro demosequence finishes + * + ****************************************************************************/ + +void d_advance_demo(void) +{ + advancedemo = true; +} + +/**************************************************************************** + * Name: d_do_advance_demo + * + * Description: + * This cycles through the demo sequences. + * FIXME - version dependent demo numbers? + * + ****************************************************************************/ + +void d_do_advance_demo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; /* not reborn */ + advancedemo = false; + usergame = false; /* no save / end game here */ + paused = false; + gameaction = ga_nothing; + + /* The Ultimate Doom executable changed the demo sequence to add + * a DEMO4 demo. Final Doom was based on Ultimate, so also + * includes this change; however, the Final Doom IWADs do not + * include a DEMO4 lump, so the game bombs out with an error + * when it reaches this point in the demo sequence. + * + * However! There is an alternate version of Final Doom that + * includes a fixed executable. + */ + + if (gameversion == exe_ultimate || gameversion == exe_final) + demosequence = (demosequence + 1) % 7; + else + demosequence = (demosequence + 1) % 6; + + switch (demosequence) + { + case 0: + if (gamemode == commercial) + pagetic = TICRATE * 11; + else + pagetic = 170; + gamestate = GS_DEMOSCREEN; + pagename = ("TITLEPIC"); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + { + s_start_music(MUS_DM2TTL); + } + else + { + s_start_music(MUS_INTRO); + } +#endif + + break; + case 1: + g_defered_play_demo(("demo1")); + break; + case 2: + pagetic = 200; + gamestate = GS_DEMOSCREEN; + pagename = ("CREDIT"); + break; + case 3: + g_defered_play_demo(("demo2")); + break; + case 4: + gamestate = GS_DEMOSCREEN; + if (gamemode == commercial) + { + pagetic = TICRATE * 11; + pagename = ("TITLEPIC"); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_music(MUS_DM2TTL); +#endif + } + else + { + pagetic = 200; + + if (gameversion >= exe_ultimate) + pagename = ("CREDIT"); + else + pagename = ("HELP2"); + } + break; + case 5: + g_defered_play_demo(("demo3")); + break; + + /* THE DEFINITIVE DOOM Special Edition demo */ + + case 6: + g_defered_play_demo(("demo4")); + break; + } + + /* The Doom 3: BFG Edition version of doom2.wad does not have a + * TITLETPIC lump. Use INTERPIC instead as a workaround. + */ + + if (gamevariant == bfgedition && !strcasecmp(pagename, "TITLEPIC") && + w_check_num_for_name("titlepic") < 0) + { + pagename = ("INTERPIC"); + } +} + +/* d_start_title */ + +void d_start_title(void) +{ + gameaction = ga_nothing; + demosequence = -1; + d_advance_demo(); +} + +/* d_doom_main */ + +void d_doom_main(void) +{ + int p; + char file[256]; + char demolumpname[9]; + + i_at_exit(d_endoom, false); + + /* print banner */ + + i_print_banner(PACKAGE_STRING); + + printf("z_init: Init zone memory allocation daemon. \n"); + z_init(); + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start a dedicated server, routing packets but not participating + * in the game itself. + * + */ + + if (m_check_parm("-dedicated") > 0) + { + printf("Dedicated server mode.\n"); + net_dedicated_server(); + + /* Never returns */ + } + + /* @category net + * + * Query the Internet master server for a global list of active + * servers. + * + */ + + if (m_check_parm("-search")) + { + net_master_query(); + exit(0); + } + + /* @arg
+ * @category net + * + * Query the status of the server running on the given IP + * address. + * + */ + + p = m_check_parm_with_args("-query", 1); + + if (p) + { + net_query_address(myargv[p + 1]); + exit(0); + } + + /* @category net + * + * Search the local LAN for running servers. + * + */ + + if (m_check_parm("-localsearch")) + { + net_lan_query(); + exit(0); + } +#endif + + /* @category game + * @vanilla + * + * Disable monsters. + * + */ + + nomonsters = m_check_parm("-nomonsters"); + + /* @category game + * @vanilla + * + * Monsters respawn after being killed. + * + */ + + respawnparm = m_check_parm("-respawn"); + + /* @category game + * @vanilla + * + * Monsters move faster. + * + */ + + fastparm = m_check_parm("-fast"); + + /* @vanilla + * + * Developer mode. F1 saves a screenshot in the current working + * directory. + * + */ + + devparm = m_check_parm("-devparm"); + + i_display_fps_dots(devparm); + + /* @category net + * @vanilla + * + * Start a deathmatch game. + * + */ + + if (m_check_parm("-deathmatch")) deathmatch = 1; + + /* @category net + * @vanilla + * + * Start a deathmatch 2.0 game. Weapons do not stay in place and + * all items respawn after 30 seconds. + * + */ + + if (m_check_parm("-altdeath")) deathmatch = 2; + + if (devparm) + { + printf(D_DEVSTR); + } + + /* find which dir to use for config files */ + + /* Auto-detect the configuration dir. */ + + m_set_config_dir(NULL); + + /* @category game + * @arg + * @vanilla + * + * Turbo mode. The player's speed is multiplied by x%. If unspecified, + * x defaults to 200. Values are rounded up to 10 and down to 400. + * + */ + + if ((p = m_check_parm("-turbo"))) + { + int scale = 200; + + if (p < myargc - 1) scale = atoi(myargv[p + 1]); + if (scale < 10) scale = 10; + if (scale > 400) scale = 400; + printf("turbo scale: %i%%\n", scale); + forwardmove[0] = forwardmove[0] * scale / 100; + forwardmove[1] = forwardmove[1] * scale / 100; + sidemove[0] = sidemove[0] * scale / 100; + sidemove[1] = sidemove[1] * scale / 100; + } + + /* init subsystems */ + + printf("v_init: allocate screens.\n"); + v_init(); + + /* Load configuration files before initialising other subsystems. */ + + printf("m_load_defaults: Load system defaults.\n"); + m_set_config_filenames("default.cfg", PROGRAM_PREFIX "doom.cfg"); + d_bind_variables(); + m_load_defaults(); + + /* Save configuration at exit. */ + + i_at_exit(m_save_defaults, false); + + /* Find main IWAD file and load it. */ + + iwadfile = d_find_iwad(IWAD_MASK_DOOM, &gamemission); + + /* None found? */ + + if (iwadfile == NULL) + { + i_error("Game mode indeterminate. No IWAD file was found. Try\n" + "specifying one with the '-iwad' command line parameter.\n"); + } + + modifiedgame = false; + + printf("W_Init: Init WADfiles.\n"); + d_addfile(iwadfile); + + w_check_correct_iwad(doom); + + /* Now that we've loaded the IWAD, we can figure out what gamemission + * we're playing and which version of Vanilla Doom we need to emulate. + */ + + d_identify_version(); + init_game_version(); + + /* Check which IWAD variant we are using. */ + + if (w_check_num_for_name("FREEDOOM") >= 0) + { + if (w_check_num_for_name("FREEDM") >= 0) + { + gamevariant = freedm; + } + else + { + gamevariant = freedoom; + } + } + else if (w_check_num_for_name("DMENUPIC") >= 0) + { + gamevariant = bfgedition; + } + + /* @category mod + * + * Disable automatic loading of Dehacked patches for certain + * IWAD files. + * + */ + + if (!m_parm_exists("-nodeh")) + { + /* Some IWADs have dehacked patches that need to be loaded for + * them to be played properly. + */ + + load_iwad_deh(); + } + + /* Doom 3: BFG Edition includes modified versions of the classic + * IWADs which can be identified by an additional DMENUPIC lump. + * Furthermore, the M_GDHIGH lumps have been modified in a way that + * makes them incompatible to Vanilla Doom and the modified version + * of doom2.wad is missing the TITLEPIC lump. + * We specifically check for DMENUPIC here, before PWADs have been + * loaded which could probably include a lump of that name. + */ + + if (gamevariant == bfgedition) + { + printf("BFG Edition: Using workarounds as needed.\n"); + + /* BFG Edition changes the names of the secret levels to + * censor the Wolfenstein references. It also has an extra + * secret level (MAP33). In Vanilla Doom (meaning the DOS + * version), MAP33 overflows into the Plutonia level names + * array, so HUSTR_33 is actually PHUSTR_1. + */ + + deh_add_string_replacement(HUSTR_31, "level 31: idkfa"); + deh_add_string_replacement(HUSTR_32, "level 32: keen"); + deh_add_string_replacement(PHUSTR_1, "level 33: betray"); + + /* The BFG edition doesn't have the "low detail" menu option (fair + * enough). But bizarrely, it reuses the M_GDHIGH patch as a label + * for the options menu (says "Fullscreen:"). Why the perpetrators + * couldn't just add a new graphic lump and had to reuse this one, + * I don't know. + * + * The end result is that M_GDHIGH is too wide and causes the game + * to crash. As a workaround to get a minimum level of support for + * the BFG edition IWADs, use the "ON"/"OFF" graphics instead. + */ + + deh_add_string_replacement("M_GDHIGH", "M_MSGON"); + deh_add_string_replacement("M_GDLOW", "M_MSGOFF"); + + /* The BFG edition's "Screen Size:" graphic has also been changed + * to say "Gamepad:". Fortunately, it (along with the original + * Doom IWADs) has an unused graphic that says "Display". So we + * can swap this in instead, and it kind of makes sense. + */ + + deh_add_string_replacement("M_SCRNSZ", "M_DISP"); + } + + /* @category mod + * + * Disable auto-loading of .wad and .deh files. + * + */ + + if (!m_parm_exists("-noautoload") && gamemode != shareware) + { + char *autoload_dir; + + /* common auto-loaded files for all Doom flavors */ + + if (gamemission < pack_chex) + { + autoload_dir = m_get_autoload_dir("doom-all"); + if (autoload_dir != NULL) + { + deh_auto_load_patches(autoload_dir); + w_auto_load_wads(autoload_dir); + free(autoload_dir); + } + } + + /* auto-loaded files per IWAD */ + + autoload_dir = m_get_autoload_dir( + d_save_game_iwad_name(gamemission, gamevariant)); + + if (autoload_dir != NULL) + { + deh_auto_load_patches(autoload_dir); + w_auto_load_wads(autoload_dir); + free(autoload_dir); + } + } + + /* Load Dehacked patches specified on the command line with -deh. + * Note that there's a very careful and deliberate ordering to how + * Dehacked patches are loaded. The order we use is: + * 1. IWAD dehacked patches. + * 2. Command line dehacked patches specified with -deh. + * 3. PWAD dehacked patches in DEHACKED lumps. + */ + + deh_parse_command_line(); + + /* Load PWAD files. */ + + modifiedgame = w_parse_command_line(); + + /* Debug: + * w_print_directory(); + */ + + /* @arg + * @category demo + * @vanilla + * + * Play back the demo named demo.lmp. + * + */ + + p = m_check_parm_with_args("-playdemo", 1); + + if (!p) + { + /* @arg + * @category demo + * @vanilla + * + * Play back the demo named demo.lmp, determining the framerate + * of the screen. + * + */ + + p = m_check_parm_with_args("-timedemo", 1); + } + + if (p) + { + char *uc_filename = strdup(myargv[p + 1]); + m_force_uppercase(uc_filename); + + /* With Vanilla you have to specify the file without extension, + * but make that optional. + */ + + if (m_string_ends_with(uc_filename, ".LMP")) + { + m_str_copy(file, myargv[p + 1], sizeof(file)); + } + else + { + snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]); + } + + free(uc_filename); + + if (d_addfile(file)) + { + m_str_copy(demolumpname, lumpinfo[numlumps - 1]->name, + sizeof(demolumpname)); + } + else + { + /* If file failed to load, still continue trying to play + * the demo in the same way as Vanilla Doom. This makes + * tricks like "-playdemo demo1" possible. + */ + + m_str_copy(demolumpname, myargv[p + 1], sizeof(demolumpname)); + } + + printf("Playing demo %s.\n", file); + } + + i_at_exit(g_check_demo_status_at_exit, true); + + /* Generate the WAD hash table. Speed things up a bit. */ + + w_generate_hash_table(); + + /* Load DEHACKED lumps from WAD files - but only if we give the right + * command line parameter. + */ + + /* @category mod + * + * Load Dehacked patches from DEHACKED lumps contained in one of the + * loaded PWAD files. + * + */ + + if (m_parm_exists("-dehlump")) + { + int i; + int loaded = 0; + int numiwadlumps = numlumps; + + while (!w_is_iwad_lump(lumpinfo[numiwadlumps - 1])) + { + numiwadlumps--; + } + + for (i = numiwadlumps; i < numlumps; ++i) + { + if (!strncmp(lumpinfo[i]->name, "DEHACKED", 8)) + { + deh_load_lump(i, false, false); + loaded++; + } + } + + printf(" loaded %i DEHACKED lumps from PWAD files.\n", loaded); + } + + /* Set the gamedescription string. This is only possible now that + * we've finished loading Dehacked patches. + */ + + d_set_game_description(); + + savegamedir = + m_get_save_game_dir(d_save_game_iwad_name(gamemission, gamevariant)); + + /* Check for -file in shareware */ + + if (modifiedgame && (gamevariant != freedoom)) + { + int i; + + if (gamemode == shareware) + i_error(("\nYou cannot -file with the shareware " + "version. Register!")); + + /* Check for fake IWAD with right name, + * but w/o all the lumps of the registered version. + */ + + if (gamemode == registered) + { + for (i = 0; i < 23; i++) + { + if (w_check_num_for_name(g_name[i]) < 0) + { + i_error(("\nThis is not the registered version.")); + } + } + } + } + + if (w_check_num_for_name("SS_START") >= 0 || + w_check_num_for_name("FF_END") >= 0) + { + i_print_divider(); + printf(" WARNING: The loaded WAD file contains modified sprites or\n" + " floor textures. You may want to use the '-merge' command\n" + " line option instead of '-file'.\n"); + } + + i_print_startup_banner(g_gamedescription); + print_dehacked_banners(); + + printf("i_init: Setting up machine state.\n"); + i_check_is_screensaver(); + i_init_timer(); + i_init_joystick(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_init_sound(doom); + i_init_music(); +#endif + +#ifdef CONFIG_GAMES_NXDOOM_NET + printf("net_init: Init network subsystem.\n"); + net_init(); +#endif + + /* Initial netgame startup. Connect to server etc. */ + + d_connect_net_game(); + + /* get skill / episode / map from params */ + + startskill = sk_medium; + startepisode = 1; + startmap = 1; + autostart = false; + + /* @category game + * @arg + * @vanilla + * + * Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of + * 0 disables all monsters. + * + */ + + p = m_check_parm_with_args("-skill", 1); + + if (p) + { + startskill = myargv[p + 1][0] - '1'; + autostart = true; + } + + /* @category game + * @arg + * @vanilla + * + * Start playing episode n (1-4). + * + */ + + p = m_check_parm_with_args("-episode", 1); + + if (p) + { + startepisode = myargv[p + 1][0] - '0'; + startmap = 1; + autostart = true; + } + + timelimit = 0; + + /* @arg + * @category net + * @vanilla + * + * For multiplayer games: exit each level after n minutes. + * + */ + + p = m_check_parm_with_args("-timer", 1); + + if (p) + { + timelimit = atoi(myargv[p + 1]); + } + + /* @category net + * @vanilla + * + * Austin Virtual Gaming: end levels after 20 minutes. + * + */ + + p = m_check_parm("-avg"); + + if (p) + { + timelimit = 20; + } + + /* @category game + * @arg [ | ] + * @vanilla + * + * Start a game immediately, warping to ExMy (Doom 1) or MAPxy + * (Doom 2). + * + */ + + p = m_check_parm_with_args("-warp", 1); + + if (p) + { + if (gamemode == commercial) + startmap = atoi(myargv[p + 1]); + else + { + startepisode = myargv[p + 1][0] - '0'; + + if (p + 2 < myargc) + { + startmap = myargv[p + 2][0] - '0'; + } + else + { + startmap = 1; + } + } + + autostart = true; + } + + /* Undocumented: + * Invoked by setup to test the controls. + */ + + p = m_check_parm("-testcontrols"); + + if (p > 0) + { + startepisode = 1; + startmap = 1; + autostart = true; + testcontrols = true; + } + + /* Check for load game parameter + * We do this here and save the slot number, so that the network code + * can override it or send the load slot to other players. + */ + + /* @category game + * @arg + * @vanilla + * + * Load the game in slot s. + * + */ + + p = m_check_parm_with_args("-loadgame", 1); + + if (p) + { + startloadgame = atoi(myargv[p + 1]); + } + else + { + startloadgame = -1; /* Not loading a game */ + } + + printf("m_init: Init miscellaneous info.\n"); + m_init(); + + printf("r_init: Init DOOM refresh daemon - "); + r_init(); + + printf("\np_init: Init Playloop state.\n"); + p_init(); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + printf("s_init: Setting up sound.\n"); + s_init(g_sfx_volume * 8, g_music_volume * 8); +#endif + + printf("d_check_net_game: Checking network game status.\n"); + d_check_net_game(); + + print_game_version(); + + printf("hu_init: Setting up heads up display.\n"); + hu_init(); + + printf("st_init: Init status bar.\n"); + st_init(); + + /* If Doom II without a MAP01 lump, this is a store demo. + * Moved this here so that MAP01 isn't constantly looked up + * in the main loop. + */ + + if (gamemode == commercial && w_check_num_for_name("map01") < 0) + storedemo = true; + + if (m_check_parm_with_args("-statdump", 1)) + { + i_at_exit(stat_dump, true); + printf("External statistics registered.\n"); + } + + /* @arg + * @category demo + * @vanilla + * + * Record a demo named x.lmp. + */ + + p = m_check_parm_with_args("-record", 1); + + if (p) + { + g_record_demo(myargv[p + 1]); + autostart = true; + } + + p = m_check_parm_with_args("-playdemo", 1); + if (p) + { + singledemo = true; /* quit after one demo */ + g_defered_play_demo(demolumpname); + d_doomloop(); /* never returns */ + } + + p = m_check_parm_with_args("-timedemo", 1); + if (p) + { + g_time_demo(demolumpname); + d_doomloop(); /* never returns */ + } + + if (startloadgame >= 0) + { + m_str_copy(file, p_save_game_file(startloadgame), sizeof(file)); + g_load_game(file); + } + + if (gameaction != ga_loadgame) + { + if (autostart || netgame) + g_init_new(startskill, startepisode, startmap); + else + d_start_title(); /* start up intro loop */ + } + + d_doomloop(); /* never returns */ +} diff --git a/games/NXDoom/src/doom/d_main.h b/games/NXDoom/src/doom/d_main.h new file mode 100644 index 000000000..f97422597 --- /dev/null +++ b/games/NXDoom/src/doom/d_main.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __D_MAIN__ +#define __D_MAIN__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern gameaction_t gameaction; +extern boolean advancedemo; + +extern const char *pagename; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Read events from all input devices */ + +void d_process_events(void); + +/* BASE LEVEL */ + +void d_page_ticker(void); +void d_page_drawer(void); +void d_advance_demo(void); +void d_do_advance_demo(void); +void d_start_title(void); + +#endif /* __D_MAIN__ */ diff --git a/games/NXDoom/src/doom/d_net.c b/games/NXDoom/src/doom/d_net.c new file mode 100644 index 000000000..3be74240c --- /dev/null +++ b/games/NXDoom/src/doom/d_net.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_net.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DOOM Network game communication and protocol, all OS independent parts. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_main.h" +#include "doomdef.h" +#include "doomstat.h" +#include "g_game.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_menu.h" +#include "m_misc.h" +#include "w_checksum.h" +#include "w_wad.h" + +#include "deh_main.h" + +#include "d_loop.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void run_tic(ticcmd_t *cmds, boolean *ingame); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static loop_interface_t g_doom_loop_interface = +{ + d_process_events, + g_build_ticcmd, + run_tic, + m_ticker, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +ticcmd_t *netcmds; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: player_quit_game + * + * Description: + * Called when a player leaves the game + * + ****************************************************************************/ + +static void player_quit_game(player_t *player) +{ + static char exitmsg[80]; + unsigned int player_num; + + player_num = player - players; + + /* Do this the same way as Vanilla Doom does, to allow dehacked + * replacements of this message + */ + + m_str_copy(exitmsg, ("Player 1 left the game"), sizeof(exitmsg)); + + exitmsg[7] += player_num; + + playeringame[player_num] = false; + players[consoleplayer].message = exitmsg; + + /* TODO: check if it is sensible to do this: */ + + if (demorecording) + { + g_check_demo_status(); + } +} + +static void run_tic(ticcmd_t *cmds, boolean *ingame) +{ + unsigned int i; + + /* Check for player quits. */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (!demoplayback && playeringame[i] && !ingame[i]) + { + player_quit_game(&players[i]); + } + } + + netcmds = cmds; + + /* check that there are players in the game. if not, we cannot + * run a tic. + */ + + if (advancedemo) d_do_advance_demo(); + + g_ticker(); +} + +/**************************************************************************** + * Name: Load_Game_Settings + * + * Description: + * Load game settings from the specified structure and + * set global variables. + * + ****************************************************************************/ + +static void load_game_settings(net_gamesettings_t *settings) +{ + unsigned int i; + + deathmatch = settings->deathmatch; + startepisode = settings->episode; + startmap = settings->map; + startskill = settings->skill; + startloadgame = settings->loadgame; + lowres_turn = settings->lowres_turn; + nomonsters = settings->nomonsters; + fastparm = settings->fast_monsters; + respawnparm = settings->respawn_monsters; + timelimit = settings->timelimit; + consoleplayer = settings->consoleplayer; + + if (lowres_turn) + { + printf("NOTE: Turning resolution is reduced; this is probably " + "because there is a client recording a Vanilla demo.\n"); + } + + for (i = 0; i < MAXPLAYERS; ++i) + { + playeringame[i] = i < settings->num_players; + } +} + +/**************************************************************************** + * Name: save_game_settings + * + * Description: + * Save the game settings from global variables to the specified + * game settings structure. + * + ****************************************************************************/ + +static void save_game_settings(net_gamesettings_t *settings) +{ + /* Fill in game settings structure with appropriate parameters + * for the new game + */ + + settings->deathmatch = deathmatch; + settings->episode = startepisode; + settings->map = startmap; + settings->skill = startskill; + settings->loadgame = startloadgame; + settings->gameversion = gameversion; + settings->nomonsters = nomonsters; + settings->fast_monsters = fastparm; + settings->respawn_monsters = respawnparm; + settings->timelimit = timelimit; + + settings->lowres_turn = + (m_parm_exists("-record") && !m_parm_exists("-longtics")) || + m_parm_exists("-shorttics"); +} + +static void init_connect_data(net_connect_data_t *connect_data) +{ + boolean shorttics; + + connect_data->max_players = MAXPLAYERS; + connect_data->drone = false; + + /* @category net + * + * Run as the left screen in three screen mode. + */ + + if (m_check_parm("-left") > 0) + { + viewangleoffset = ANG90; + connect_data->drone = true; + } + + /* @category net + * + * Run as the right screen in three screen mode. + */ + + if (m_check_parm("-right") > 0) + { + viewangleoffset = ANG270; + connect_data->drone = true; + } + + /* Connect data */ + + /* Game type fields: */ + + connect_data->gamemode = gamemode; + connect_data->gamemission = gamemission; + + /* @category demo + * + * Play with low turning resolution to emulate demo recording. + */ + + shorttics = m_parm_exists("-shorttics"); + + /* Are we recording a demo? Possibly set lowres turn mode */ + + connect_data->lowres_turn = + (m_parm_exists("-record") && !m_parm_exists("-longtics")) || shorttics; + + /* Read checksums of our WAD directory and dehacked information */ + + w_checksum(connect_data->wad_sha1sum); + deh_checksum(connect_data->deh_sha1sum); + + /* Are we playing with the Freedoom IWAD? */ + + connect_data->is_freedoom = w_check_num_for_name("FREEDOOM") >= 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void d_connect_net_game(void) +{ + net_connect_data_t connect_data; + + init_connect_data(&connect_data); + netgame = d_init_net_game(&connect_data); + +#ifdef CONFIG_GAMES_NXDOOM_NET + /* @category net + * + * Start the game playing as though in a netgame with a single + * player. This can also be used to play back single player netgame + * demos. + */ + + if (m_check_parm("-solo-net") > 0) + { + netgame = true; + } +#endif +} + +/**************************************************************************** + * Name: d_check_net_game + * + * Description: + * Works out player numbers among the net participants + * + ****************************************************************************/ + +void d_check_net_game(void) +{ + net_gamesettings_t settings; + + if (netgame) + { + autostart = true; + } + + d_register_loop_callbacks(&g_doom_loop_interface); + + save_game_settings(&settings); + d_start_net_game(&settings, NULL); + load_game_settings(&settings); + + printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", + startskill, deathmatch, startmap, startepisode); + + printf("player %i of %i (%i nodes)\n", consoleplayer + 1, + settings.num_players, settings.num_players); + + /* Show players here; the server might have specified a time limit */ + + if (timelimit > 0 && deathmatch) + { + /* Gross hack to work like Vanilla: */ + + if (timelimit == 20 && m_check_parm("-avg")) + { + printf("Austin Virtual Gaming: Levels will end " + "after 20 minutes\n"); + } + else + { + printf("Levels will end after %d minute", timelimit); + if (timelimit > 1) printf("s"); + printf(".\n"); + } + } +} diff --git a/games/NXDoom/src/doom/d_player.h b/games/NXDoom/src/doom/d_player.h new file mode 100644 index 000000000..54defc586 --- /dev/null +++ b/games/NXDoom/src/doom/d_player.h @@ -0,0 +1,236 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_player.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * + ****************************************************************************/ + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* The player data structure depends on a number + * of other structs: items (internal inventory), + * animation states (closely tied to the sprites + * used to represent them, unfortunately). + */ + +#include "d_items.h" +#include "p_pspr.h" + +/* In addition, the player is just a special + * case of the generic moving object/actor. + */ + +#include "p_mobj.h" + +/* Finally, for odd reasons, the player input + * is buffered within the player data struct, + * as commands per game tick. + */ + +#include "d_ticcmd.h" + +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Player states. */ + +typedef enum +{ + PST_LIVE, /* Playing or camping. */ + PST_DEAD, /* Dead on the ground, view follows killer. */ + PST_REBORN /* Ready to restart/respawn??? */ +} playerstate_t; + +/* Player internal flags, for cheats and debug. */ + +typedef enum +{ + CF_NOCLIP = 1, /* No clipping, walk through barriers. */ + CF_GODMODE = 2, /* No damage, no health loss. */ + CF_NOMOMENTUM = 4 /* Not really a cheat, just a debug aid. */ +} cheat_t; + +/* Extended player object info: player_t */ + +typedef struct player_s +{ + mobj_t *mo; + playerstate_t playerstate; + ticcmd_t cmd; + + /* Determine POV, including viewpoint bobbing during movement. + * Focal origin above r.z + */ + + fixed_t viewz; + + /* Base height above floor for viewz. */ + + fixed_t viewheight; + + /* Bob/squat speed. */ + + fixed_t deltaviewheight; + + /* bounded/scaled total momentum. */ + + fixed_t bob; + + /* This is only used between levels, + * mo->health is used during levels. + */ + + int health; + int armorpoints; + + /* Armor type is 0-2. */ + + int armortype; + + /* Power ups. invinc and invis are tic counters. */ + + int powers[NUMPOWERS]; + boolean cards[NUMCARDS]; + boolean backpack; + + /* Frags, kills of other players. */ + + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + /* Is wp_nochange if not changing. */ + + weapontype_t pendingweapon; + + int weaponowned[NUMWEAPONS]; + int ammo[NUMAMMO]; + int maxammo[NUMAMMO]; + + /* True if button down last tic. */ + + int attackdown; + int usedown; + + /* Bit flags, for cheats and debug. + * See cheat_t, above. + */ + + int cheats; + + /* Refired shots are less accurate. */ + + int refire; + + /* For intermission stats. */ + + int killcount; + int itemcount; + int secretcount; + + /* Hint messages. */ + + const char *message; + + /* For screen flashing (red or bright). */ + + int damagecount; + int bonuscount; + + /* Who did damage (NULL for floors/ceilings). */ + + mobj_t *attacker; + + /* So gun flashes light up areas. */ + + int extralight; + + /* Current PLAYPAL, ??? can be set to REDCOLORMAP for pain, etc. + */ + + int fixedcolormap; + + /* Player skin colorshift, 0-3 for which color to draw player. + */ + + int colormap; + + /* Overlay view sprites (gun, etc). */ + + pspdef_t psprites[NUMPSPRITES]; + + /* True if secret level has been done. */ + + boolean didsecret; +} player_t; + +/* INTERMISSION + * Structure passed e.g. to wi_start(wb) + */ + +typedef struct +{ + boolean in; /* whether the player is in game */ + + /* Player stats, kills, collected items etc. */ + + int skills; + int sitems; + int ssecret; + int stime; + int frags[4]; + int score; /* current score on entry, modified on return */ +} wbplayerstruct_t; + +typedef struct +{ + int epsd; /* episode # (0-2) */ + + /* if true, splash the secret level */ + + boolean didsecret; + + /* previous and next levels, origin 0 */ + + int last; + int next; + + int maxkills; + int maxitems; + int maxsecret; + int maxfrags; + + /* the par time */ + + int partime; + + /* index of this player in game */ + + int pnum; + + wbplayerstruct_t plyr[MAXPLAYERS]; +} wbstartstruct_t; + +#endif /* __D_PLAYER__ */ diff --git a/games/NXDoom/src/doom/d_textur.h b/games/NXDoom/src/doom/d_textur.h new file mode 100644 index 000000000..cf77f38b4 --- /dev/null +++ b/games/NXDoom/src/doom/d_textur.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_textur.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Typedefs related to to textures etc., + * isolated here to make it easier separating modules. + * + ****************************************************************************/ + +#ifndef __D_TEXTUR__ +#define __D_TEXTUR__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Flats? + * + * a pic is an unmasked block of pixels + */ + +typedef struct +{ + byte width; + byte height; + byte data; +} pic_t; + +#endif /* __D_TEXTUR__ */ diff --git a/games/NXDoom/src/doom/d_think.h b/games/NXDoom/src/doom/d_think.h new file mode 100644 index 000000000..8ec8ada7f --- /dev/null +++ b/games/NXDoom/src/doom/d_think.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/d_think.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * MapObj data. Map Objects or mobjs are actors, entities, + * thinker, take-your-pick... anything that moves, acts, or + * suffers state changes of more or less violent nature. + * + ****************************************************************************/ + +#ifndef __D_THINK__ +#define __D_THINK__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Experimental stuff. + * To compile this as "ANSI C with classes" we will need to handle the + * various action functions cleanly. + */ + +typedef void (*actionf_v)(void); +typedef void (*actionf_p1)(void *); +typedef void (*actionf_p2)(void *, void *); + +typedef union +{ + actionf_v acv; + actionf_p1 acp1; + actionf_p2 acp2; +} actionf_t; + +/* Historically, "think_t" is yet another function pointer to a routine to + * handle an actor. + */ + +typedef actionf_t think_t; + +/* Doubly linked list of actors. */ + +typedef struct thinker_s +{ + struct thinker_s *prev; + struct thinker_s *next; + think_t function; +} thinker_t; + +#endif /* __D_THINK__ */ diff --git a/games/NXDoom/src/doom/deh_ammo.c b/games/NXDoom/src/doom/deh_ammo.c new file mode 100644 index 000000000..83aabf00e --- /dev/null +++ b/games/NXDoom/src/doom/deh_ammo.c @@ -0,0 +1,128 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_ammo.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Ammo" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "doomdef.h" +#include "doomtype.h" +#include "p_local.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_ammo_start(deh_context_t *context, char *line); +static void deh_ammo_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_ammo_sha1_hash(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_ammo = +{ + "Ammo", + NULL, + deh_ammo_start, + deh_ammo_parse_line, + NULL, + deh_ammo_sha1_hash, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_ammo_start(deh_context_t *context, char *line) +{ + int ammo_number = 0; + + if (sscanf(line, "Ammo %i", &ammo_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (ammo_number < 0 || ammo_number >= NUMAMMO) + { + deh_warning(context, "Invalid ammo number: %i", ammo_number); + return NULL; + } + + return &maxammo[ammo_number]; +} + +static void deh_ammo_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int ivalue; + int ammo_number; + + if (tag == NULL) return; + + ammo_number = ((int *)tag) - maxammo; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + /* maxammo */ + + if (!strcasecmp(variable_name, "Per ammo")) + clipammo[ammo_number] = ivalue; + else if (!strcasecmp(variable_name, "Max ammo")) + maxammo[ammo_number] = ivalue; + else + { + deh_warning(context, "Field named '%s' not found", variable_name); + } +} + +static void deh_ammo_sha1_hash(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMAMMO; ++i) + { + sha1_updateint32(context, clipammo[i]); + sha1_updateint32(context, maxammo[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_bexstr.c b/games/NXDoom/src/doom/deh_bexstr.c new file mode 100644 index 000000000..c1ff9c83d --- /dev/null +++ b/games/NXDoom/src/doom/deh_bexstr.c @@ -0,0 +1,406 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_bexstr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2014 Fabian Greffrath + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses [STRINGS] sections in BEX files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +#include "dstrings.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + const char *macro; + const char *string; +} bex_string_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_bex_str_start(deh_context_t *context, char *line); +static void deh_bex_str_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* mnemonic keys table */ + +static const bex_string_t g_bex_stringtable[] = +{ + {"D_DEVSTR", D_DEVSTR}, /* part 1 - general initialization and prompts */ + {"D_CDROM", D_CDROM}, + {"QUITMSG", QUITMSG}, + {"LOADNET", LOADNET}, + {"QLOADNET", QLOADNET}, + {"QSAVESPOT", QSAVESPOT}, + {"SAVEDEAD", SAVEDEAD}, + {"QSPROMPT", QSPROMPT}, + {"QLPROMPT", QLPROMPT}, + {"NEWGAME", NEWGAME}, + {"NIGHTMARE", NIGHTMARE}, + {"SWSTRING", SWSTRING}, + {"MSGOFF", MSGOFF}, + {"MSGON", MSGON}, + {"NETEND", NETEND}, + {"ENDGAME", ENDGAME}, + {"DETAILHI", DETAILHI}, + {"DETAILLO", DETAILLO}, + {"GAMMALVL0", GAMMALVL0}, + {"GAMMALVL1", GAMMALVL1}, + {"GAMMALVL2", GAMMALVL2}, + {"GAMMALVL3", GAMMALVL3}, + {"GAMMALVL4", GAMMALVL4}, + {"EMPTYSTRING", EMPTYSTRING}, + {"GGSAVED", GGSAVED}, + {"SAVEGAMENAME", SAVEGAMENAME}, + {"GOTARMOR", + GOTARMOR}, /* part 2 - messages when the player gets things */ + {"GOTMEGA", GOTMEGA}, + {"GOTHTHBONUS", GOTHTHBONUS}, + {"GOTARMBONUS", GOTARMBONUS}, + {"GOTSTIM", GOTSTIM}, + {"GOTMEDINEED", GOTMEDINEED}, + {"GOTMEDIKIT", GOTMEDIKIT}, + {"GOTSUPER", GOTSUPER}, + {"GOTBLUECARD", GOTBLUECARD}, + {"GOTYELWCARD", GOTYELWCARD}, + {"GOTREDCARD", GOTREDCARD}, + {"GOTBLUESKUL", GOTBLUESKUL}, + {"GOTYELWSKUL", GOTYELWSKUL}, + {"GOTREDSKULL", GOTREDSKULL}, + {"GOTINVUL", GOTINVUL}, + {"GOTBERSERK", GOTBERSERK}, + {"GOTINVIS", GOTINVIS}, + {"GOTSUIT", GOTSUIT}, + {"GOTMAP", GOTMAP}, + {"GOTVISOR", GOTVISOR}, + {"GOTMSPHERE", GOTMSPHERE}, + {"GOTCLIP", GOTCLIP}, + {"GOTCLIPBOX", GOTCLIPBOX}, + {"GOTROCKET", GOTROCKET}, + {"GOTROCKBOX", GOTROCKBOX}, + {"GOTCELL", GOTCELL}, + {"GOTCELLBOX", GOTCELLBOX}, + {"GOTSHELLS", GOTSHELLS}, + {"GOTSHELLBOX", GOTSHELLBOX}, + {"GOTBACKPACK", GOTBACKPACK}, + {"GOTBFG9000", GOTBFG9000}, + {"GOTCHAINGUN", GOTCHAINGUN}, + {"GOTCHAINSAW", GOTCHAINSAW}, + {"GOTLAUNCHER", GOTLAUNCHER}, + {"GOTPLASMA", GOTPLASMA}, + {"GOTSHOTGUN", GOTSHOTGUN}, + {"GOTSHOTGUN2", GOTSHOTGUN2}, + {"PD_BLUEO", PD_BLUEO}, /* part 3 - messages when keys are needed */ + {"PD_REDO", PD_REDO}, + {"PD_YELLOWO", PD_YELLOWO}, + {"PD_BLUEK", PD_BLUEK}, + {"PD_REDK", PD_REDK}, + {"PD_YELLOWK", PD_YELLOWK}, + {"HUSTR_MSGU", HUSTR_MSGU}, /* part 4 - multiplayer messaging */ + {"HUSTR_MESSAGESENT", HUSTR_MESSAGESENT}, + {"HUSTR_CHATMACRO0", HUSTR_CHATMACRO0}, + {"HUSTR_CHATMACRO1", HUSTR_CHATMACRO1}, + {"HUSTR_CHATMACRO2", HUSTR_CHATMACRO2}, + {"HUSTR_CHATMACRO3", HUSTR_CHATMACRO3}, + {"HUSTR_CHATMACRO4", HUSTR_CHATMACRO4}, + {"HUSTR_CHATMACRO5", HUSTR_CHATMACRO5}, + {"HUSTR_CHATMACRO6", HUSTR_CHATMACRO6}, + {"HUSTR_CHATMACRO7", HUSTR_CHATMACRO7}, + {"HUSTR_CHATMACRO8", HUSTR_CHATMACRO8}, + {"HUSTR_CHATMACRO9", HUSTR_CHATMACRO9}, + {"HUSTR_TALKTOSELF1", HUSTR_TALKTOSELF1}, + {"HUSTR_TALKTOSELF2", HUSTR_TALKTOSELF2}, + {"HUSTR_TALKTOSELF3", HUSTR_TALKTOSELF3}, + {"HUSTR_TALKTOSELF4", HUSTR_TALKTOSELF4}, + {"HUSTR_TALKTOSELF5", HUSTR_TALKTOSELF5}, + {"HUSTR_PLRGREEN", HUSTR_PLRGREEN}, + {"HUSTR_PLRINDIGO", HUSTR_PLRINDIGO}, + {"HUSTR_PLRBROWN", HUSTR_PLRBROWN}, + {"HUSTR_PLRRED", HUSTR_PLRRED}, + {"HUSTR_E1M1", HUSTR_E1M1}, /* part 5 - level names in the automap */ + {"HUSTR_E1M2", HUSTR_E1M2}, + {"HUSTR_E1M3", HUSTR_E1M3}, + {"HUSTR_E1M4", HUSTR_E1M4}, + {"HUSTR_E1M5", HUSTR_E1M5}, + {"HUSTR_E1M6", HUSTR_E1M6}, + {"HUSTR_E1M7", HUSTR_E1M7}, + {"HUSTR_E1M8", HUSTR_E1M8}, + {"HUSTR_E1M9", HUSTR_E1M9}, + {"HUSTR_E2M1", HUSTR_E2M1}, + {"HUSTR_E2M2", HUSTR_E2M2}, + {"HUSTR_E2M3", HUSTR_E2M3}, + {"HUSTR_E2M4", HUSTR_E2M4}, + {"HUSTR_E2M5", HUSTR_E2M5}, + {"HUSTR_E2M6", HUSTR_E2M6}, + {"HUSTR_E2M7", HUSTR_E2M7}, + {"HUSTR_E2M8", HUSTR_E2M8}, + {"HUSTR_E2M9", HUSTR_E2M9}, + {"HUSTR_E3M1", HUSTR_E3M1}, + {"HUSTR_E3M2", HUSTR_E3M2}, + {"HUSTR_E3M3", HUSTR_E3M3}, + {"HUSTR_E3M4", HUSTR_E3M4}, + {"HUSTR_E3M5", HUSTR_E3M5}, + {"HUSTR_E3M6", HUSTR_E3M6}, + {"HUSTR_E3M7", HUSTR_E3M7}, + {"HUSTR_E3M8", HUSTR_E3M8}, + {"HUSTR_E3M9", HUSTR_E3M9}, + {"HUSTR_E4M1", HUSTR_E4M1}, + {"HUSTR_E4M2", HUSTR_E4M2}, + {"HUSTR_E4M3", HUSTR_E4M3}, + {"HUSTR_E4M4", HUSTR_E4M4}, + {"HUSTR_E4M5", HUSTR_E4M5}, + {"HUSTR_E4M6", HUSTR_E4M6}, + {"HUSTR_E4M7", HUSTR_E4M7}, + {"HUSTR_E4M8", HUSTR_E4M8}, + {"HUSTR_E4M9", HUSTR_E4M9}, + {"HUSTR_1", HUSTR_1}, + {"HUSTR_2", HUSTR_2}, + {"HUSTR_3", HUSTR_3}, + {"HUSTR_4", HUSTR_4}, + {"HUSTR_5", HUSTR_5}, + {"HUSTR_6", HUSTR_6}, + {"HUSTR_7", HUSTR_7}, + {"HUSTR_8", HUSTR_8}, + {"HUSTR_9", HUSTR_9}, + {"HUSTR_10", HUSTR_10}, + {"HUSTR_11", HUSTR_11}, + {"HUSTR_12", HUSTR_12}, + {"HUSTR_13", HUSTR_13}, + {"HUSTR_14", HUSTR_14}, + {"HUSTR_15", HUSTR_15}, + {"HUSTR_16", HUSTR_16}, + {"HUSTR_17", HUSTR_17}, + {"HUSTR_18", HUSTR_18}, + {"HUSTR_19", HUSTR_19}, + {"HUSTR_20", HUSTR_20}, + {"HUSTR_21", HUSTR_21}, + {"HUSTR_22", HUSTR_22}, + {"HUSTR_23", HUSTR_23}, + {"HUSTR_24", HUSTR_24}, + {"HUSTR_25", HUSTR_25}, + {"HUSTR_26", HUSTR_26}, + {"HUSTR_27", HUSTR_27}, + {"HUSTR_28", HUSTR_28}, + {"HUSTR_29", HUSTR_29}, + {"HUSTR_30", HUSTR_30}, + {"HUSTR_31", HUSTR_31}, + {"HUSTR_32", HUSTR_32}, + {"PHUSTR_1", PHUSTR_1}, + {"PHUSTR_2", PHUSTR_2}, + {"PHUSTR_3", PHUSTR_3}, + {"PHUSTR_4", PHUSTR_4}, + {"PHUSTR_5", PHUSTR_5}, + {"PHUSTR_6", PHUSTR_6}, + {"PHUSTR_7", PHUSTR_7}, + {"PHUSTR_8", PHUSTR_8}, + {"PHUSTR_9", PHUSTR_9}, + {"PHUSTR_10", PHUSTR_10}, + {"PHUSTR_11", PHUSTR_11}, + {"PHUSTR_12", PHUSTR_12}, + {"PHUSTR_13", PHUSTR_13}, + {"PHUSTR_14", PHUSTR_14}, + {"PHUSTR_15", PHUSTR_15}, + {"PHUSTR_16", PHUSTR_16}, + {"PHUSTR_17", PHUSTR_17}, + {"PHUSTR_18", PHUSTR_18}, + {"PHUSTR_19", PHUSTR_19}, + {"PHUSTR_20", PHUSTR_20}, + {"PHUSTR_21", PHUSTR_21}, + {"PHUSTR_22", PHUSTR_22}, + {"PHUSTR_23", PHUSTR_23}, + {"PHUSTR_24", PHUSTR_24}, + {"PHUSTR_25", PHUSTR_25}, + {"PHUSTR_26", PHUSTR_26}, + {"PHUSTR_27", PHUSTR_27}, + {"PHUSTR_28", PHUSTR_28}, + {"PHUSTR_29", PHUSTR_29}, + {"PHUSTR_30", PHUSTR_30}, + {"PHUSTR_31", PHUSTR_31}, + {"PHUSTR_32", PHUSTR_32}, + {"THUSTR_1", THUSTR_1}, + {"THUSTR_2", THUSTR_2}, + {"THUSTR_3", THUSTR_3}, + {"THUSTR_4", THUSTR_4}, + {"THUSTR_5", THUSTR_5}, + {"THUSTR_6", THUSTR_6}, + {"THUSTR_7", THUSTR_7}, + {"THUSTR_8", THUSTR_8}, + {"THUSTR_9", THUSTR_9}, + {"THUSTR_10", THUSTR_10}, + {"THUSTR_11", THUSTR_11}, + {"THUSTR_12", THUSTR_12}, + {"THUSTR_13", THUSTR_13}, + {"THUSTR_14", THUSTR_14}, + {"THUSTR_15", THUSTR_15}, + {"THUSTR_16", THUSTR_16}, + {"THUSTR_17", THUSTR_17}, + {"THUSTR_18", THUSTR_18}, + {"THUSTR_19", THUSTR_19}, + {"THUSTR_20", THUSTR_20}, + {"THUSTR_21", THUSTR_21}, + {"THUSTR_22", THUSTR_22}, + {"THUSTR_23", THUSTR_23}, + {"THUSTR_24", THUSTR_24}, + {"THUSTR_25", THUSTR_25}, + {"THUSTR_26", THUSTR_26}, + {"THUSTR_27", THUSTR_27}, + {"THUSTR_28", THUSTR_28}, + {"THUSTR_29", THUSTR_29}, + {"THUSTR_30", THUSTR_30}, + {"THUSTR_31", THUSTR_31}, + {"THUSTR_32", THUSTR_32}, + {"AMSTR_FOLLOWON", + AMSTR_FOLLOWON}, /* part 6 - messages as a result of toggling states */ + {"AMSTR_FOLLOWOFF", AMSTR_FOLLOWOFF}, + {"AMSTR_GRIDON", AMSTR_GRIDON}, + {"AMSTR_GRIDOFF", AMSTR_GRIDOFF}, + {"AMSTR_MARKEDSPOT", AMSTR_MARKEDSPOT}, + {"AMSTR_MARKSCLEARED", AMSTR_MARKSCLEARED}, + {"STSTR_MUS", STSTR_MUS}, + {"STSTR_NOMUS", STSTR_NOMUS}, + {"STSTR_DQDON", STSTR_DQDON}, + {"STSTR_DQDOFF", STSTR_DQDOFF}, + {"STSTR_KFAADDED", STSTR_KFAADDED}, + {"STSTR_FAADDED", STSTR_FAADDED}, + {"STSTR_NCON", STSTR_NCON}, + {"STSTR_NCOFF", STSTR_NCOFF}, + {"STSTR_BEHOLD", STSTR_BEHOLD}, + {"STSTR_BEHOLDX", STSTR_BEHOLDX}, + {"STSTR_CHOPPERS", STSTR_CHOPPERS}, + {"STSTR_CLEV", STSTR_CLEV}, + {"E1TEXT", E1TEXT}, /* part 7 - episode intermission texts */ + {"E2TEXT", E2TEXT}, + {"E3TEXT", E3TEXT}, + {"E4TEXT", E4TEXT}, + {"C1TEXT", C1TEXT}, + {"C2TEXT", C2TEXT}, + {"C3TEXT", C3TEXT}, + {"C4TEXT", C4TEXT}, + {"C5TEXT", C5TEXT}, + {"C6TEXT", C6TEXT}, + {"P1TEXT", P1TEXT}, + {"P2TEXT", P2TEXT}, + {"P3TEXT", P3TEXT}, + {"P4TEXT", P4TEXT}, + {"P5TEXT", P5TEXT}, + {"P6TEXT", P6TEXT}, + {"T1TEXT", T1TEXT}, + {"T2TEXT", T2TEXT}, + {"T3TEXT", T3TEXT}, + {"T4TEXT", T4TEXT}, + {"T5TEXT", T5TEXT}, + {"T6TEXT", T6TEXT}, + {"CC_ZOMBIE", CC_ZOMBIE}, /* part 8 - creature names for the finale */ + {"CC_SHOTGUN", CC_SHOTGUN}, + {"CC_HEAVY", CC_HEAVY}, + {"CC_IMP", CC_IMP}, + {"CC_DEMON", CC_DEMON}, + {"CC_LOST", CC_LOST}, + {"CC_CACO", CC_CACO}, + {"CC_HELL", CC_HELL}, + {"CC_BARON", CC_BARON}, + {"CC_ARACH", CC_ARACH}, + {"CC_PAIN", CC_PAIN}, + {"CC_REVEN", CC_REVEN}, + {"CC_MANCU", CC_MANCU}, + {"CC_ARCH", CC_ARCH}, + {"CC_SPIDER", CC_SPIDER}, + {"CC_CYBER", CC_CYBER}, + {"CC_HERO", CC_HERO}, + {"BGFLATE1", "FLOOR4_8"}, /* part 9 - intermission tiled backgrounds */ + {"BGFLATE2", "SFLR6_1"}, + {"BGFLATE3", "MFLR8_4"}, + {"BGFLATE4", "MFLR8_3"}, + {"BGFLAT06", "SLIME16"}, + {"BGFLAT11", "RROCK14"}, + {"BGFLAT20", "RROCK07"}, + {"BGFLAT30", "RROCK17"}, + {"BGFLAT15", "RROCK13"}, + {"BGFLAT31", "RROCK19"}, + {"BGCASTCALL", "BOSSBACK"}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_bexstr = +{ + "[STRINGS]", + NULL, + deh_bex_str_start, + deh_bex_str_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_bex_str_start(deh_context_t *context, char *line) +{ + char s[10]; + + if (sscanf(line, "%9s", s) == 0 || strncmp("[STRINGS]", s, sizeof(s))) + { + deh_warning(context, "Parse error on section start"); + } + + return NULL; +} + +static void deh_bex_str_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + deh_warning(context, "Failed to parse assignment"); + return; + } + + for (i = 0; i < arrlen(g_bex_stringtable); i++) + { + if (!strcmp(g_bex_stringtable[i].macro, variable_name)) + { + deh_add_string_replacement(g_bex_stringtable[i].string, value); + } + } +} diff --git a/games/NXDoom/src/doom/deh_cheat.c b/games/NXDoom/src/doom/deh_cheat.c new file mode 100644 index 000000000..f01c08976 --- /dev/null +++ b/games/NXDoom/src/doom/deh_cheat.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_cheat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Cheat" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "am_map.h" +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "st_stuff.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + cheatseq_t *seq; +} deh_cheat_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_cheat_start(deh_context_t *context, char *line); +static void deh_cheat_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static deh_cheat_t g_allcheats[] = +{ + {"Change music", &cheat_mus}, + {"Chainsaw", &cheat_choppers}, + {"God mode", &cheat_god}, + {"Ammo & Keys", &cheat_ammo}, + {"Ammo", &cheat_ammonokey}, + {"No Clipping 1", &cheat_noclip}, + {"No Clipping 2", &cheat_commercial_noclip}, + {"Invincibility", &cheat_powerup[0]}, + {"Berserk", &cheat_powerup[1]}, + {"Invisibility", &cheat_powerup[2]}, + {"Radiation Suit", &cheat_powerup[3]}, + {"Auto-map", &cheat_powerup[4]}, + {"Lite-Amp Goggles", &cheat_powerup[5]}, + {"BEHOLD menu", &cheat_powerup[6]}, + {"Level Warp", &cheat_clev}, + {"Player Position", &cheat_mypos}, + {"Map cheat", &cheat_amap}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_cheat = +{ + "Cheat", + NULL, + deh_cheat_start, + deh_cheat_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static deh_cheat_t *find_cheat_by_name(char *name) +{ + size_t i; + + for (i = 0; i < arrlen(g_allcheats); ++i) + { + if (!strcasecmp(g_allcheats[i].name, name)) return &g_allcheats[i]; + } + + return NULL; +} + +static void *deh_cheat_start(deh_context_t *context, char *line) +{ + return NULL; +} + +static void deh_cheat_parse_line(deh_context_t *context, char *line, + void *tag) +{ + deh_cheat_t *cheat; + char *variable_name; + char *value; + unsigned char *unsvalue; + unsigned int i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + unsvalue = (unsigned char *)value; + + cheat = find_cheat_by_name(variable_name); + + if (cheat == NULL) + { + deh_warning(context, "Unknown cheat '%s'", variable_name); + return; + } + + /* write the value into the cheat sequence */ + + i = 0; + + while (unsvalue[i] != 0 && unsvalue[i] != 0xff) + { + /* If the cheat length exceeds the Vanilla limit, stop. This + * does not apply if we have the limit turned off. + */ + + if (!deh_allow_long_cheats && i >= cheat->seq->sequence_len) + { + deh_warning(context, "Cheat sequence longer than supported by " + "Vanilla dehacked"); + break; + } + + if (deh_apply_cheats) + { + cheat->seq->sequence[i] = unsvalue[i]; + } + + ++i; + + /* Absolute limit - don't exceed */ + + if (i >= MAX_CHEAT_LEN - cheat->seq->parameter_chars) + { + deh_error(context, "Cheat sequence too long!"); + return; + } + } + + if (deh_apply_cheats) + { + cheat->seq->sequence[i] = '\0'; + } +} diff --git a/games/NXDoom/src/doom/deh_doom.c b/games/NXDoom/src/doom/deh_doom.c new file mode 100644 index 000000000..419caffa8 --- /dev/null +++ b/games/NXDoom/src/doom/deh_doom.c @@ -0,0 +1,65 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_doom.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Top-level dehacked definitions for Doom dehacked. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_defs.h" +#include "deh_main.h" +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *deh_signatures[] = +{ + "Patch File for DeHackEd v2.3", + "Patch File for DeHackEd v3.0", + NULL, +}; + +/* List of section types: */ + +deh_section_t *deh_section_types[] = +{ + &deh_section_ammo, + &deh_section_cheat, + &deh_section_frame, + &deh_section_misc, + &deh_section_pointer, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + &deh_section_sound, +#else + NULL, +#endif + &deh_section_text, + &deh_section_thing, + &deh_section_weapon, + &deh_section_bexstr, + NULL, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/deh_frame.c b/games/NXDoom/src/doom/deh_frame.c new file mode 100644 index 000000000..d5ec55216 --- /dev/null +++ b/games/NXDoom/src/doom/deh_frame.c @@ -0,0 +1,198 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_frame.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Frame" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_items.h" +#include "doomtype.h" +#include "info.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "deh_mapping.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(state_mapping, state_t) +DEH_MAPPING("Sprite number", sprite) +DEH_MAPPING("Sprite subnumber", frame) +DEH_MAPPING("Duration", tics) +DEH_MAPPING("Next frame", nextstate) +#if 0 +DEH_MAPPING("Unknown 1", misc1) +DEH_MAPPING("Unknown 2", misc2) +#endif +DEH_UNSUPPORTED_MAPPING("Codep frame") +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_frame_start(deh_context_t *context, char *line); +static void deh_frame_overflow(deh_context_t *context, char *varname, + int value); +static void deh_frame_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_frame_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_frame = +{ + "Frame", + NULL, + deh_frame_start, + deh_frame_parse_line, + NULL, + deh_frame_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_frame_start(deh_context_t *context, char *line) +{ + int frame_number = 0; + state_t *state; + + if (sscanf(line, "Frame %i", &frame_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (frame_number < 0 || frame_number >= NUMSTATES) + { + deh_warning(context, "Invalid frame number: %i", frame_number); + return NULL; + } + + if (frame_number >= DEH_VANILLA_NUMSTATES) + { + deh_warning(context, + "Attempt to modify frame %i: this will cause " + "problems in Vanilla dehacked.", + frame_number); + } + + state = &states[frame_number]; + + return state; +} + +/* Simulate a frame overflow: Doom has 967 frames in the states[] array, but + * DOS dehacked internally only allocates memory for 966. As a result, + * attempts to set frame 966 (the last frame) will overflow the dehacked + * array and overwrite the weaponinfo[] array instead. + * + * This is noticeable in Batman Doom where it is impossible to switch weapons + * away from the fist once selected. + */ + +static void deh_frame_overflow(deh_context_t *context, char *varname, + int value) +{ + if (!strcasecmp(varname, "Duration")) + { + weaponinfo[0].ammo = value; + } + else if (!strcasecmp(varname, "Codep frame")) + { + weaponinfo[0].upstate = value; + } + else if (!strcasecmp(varname, "Next frame")) + { + weaponinfo[0].downstate = value; + } + else if (!strcasecmp(varname, "Unknown 1")) + { + weaponinfo[0].readystate = value; + } + else if (!strcasecmp(varname, "Unknown 2")) + { + weaponinfo[0].atkstate = value; + } + else + { + deh_error(context, "Unable to simulate frame overflow: field '%s'", + varname); + } +} + +static void deh_frame_parse_line(deh_context_t *context, char *line, + void *tag) +{ + state_t *state; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + state = (state_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + if (state == &states[NUMSTATES - 1]) + { + deh_frame_overflow(context, variable_name, ivalue); + } + else + { + /* set the appropriate field */ + + deh_set_mapping(context, &state_mapping, state, variable_name, ivalue); + } +} + +static void deh_frame_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + deh_struct_sha1_sum(context, &state_mapping, &states[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_misc.c b/games/NXDoom/src/doom/deh_misc.c new file mode 100644 index 000000000..e932ce3df --- /dev/null +++ b/games/NXDoom/src/doom/deh_misc.c @@ -0,0 +1,281 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_misc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Misc" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" +#include "deh_misc.h" +#include "doomtype.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct misc_settings +{ + const char *deh_name; + int *value; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_misc_start(deh_context_t *context, char *line); +static void deh_misc_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_misc_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Dehacked: "Initial Health" + * This is the initial health a player has when starting anew. + * See g_player_reborn in g_game.c + */ + +int deh_initial_health = DEH_DEFAULT_INITIAL_HEALTH; + +/* Dehacked: "Initial bullets" + * This is the number of bullets the player has when starting anew. + * See g_player_reborn in g_game.c + */ + +int deh_initial_bullets = DEH_DEFAULT_INITIAL_BULLETS; + +/* Dehacked: "Max Health" + * This is the maximum health that can be reached using health + * potions. See p_touch_special_thing in p_inter.c + */ + +int deh_max_health = DEH_DEFAULT_MAX_HEALTH; + +/* Dehacked: "Max Armor" + * This is the maximum armor which can be reached by picking up + * armor helmets. See p_touch_special_thing in p_inter.c + */ + +int deh_max_armor = DEH_DEFAULT_MAX_ARMOR; + +/* Dehacked: "Green Armor Class" + * This is the armor class that is given when picking up the green + * armor or an armor helmet. See p_touch_special_thing in p_inter.c + * + * DOS dehacked only modifies the behavior of the green armor shirt, + * the armor class set by armor helmets is not affected. + */ + +int deh_green_armor_class = DEH_DEFAULT_GREEN_ARMOR_CLASS; + +/* Dehacked: "Blue Armor Class" + * This is the armor class that is given when picking up the blue + * armor or a megasphere. See p_touch_special_thing in p_inter.c + * + * DOS dehacked only modifies the MegaArmor behavior and not + * the MegaSphere, which always gives armor type 2. + */ + +int deh_blue_armor_class = DEH_DEFAULT_BLUE_ARMOR_CLASS; + +/* Dehacked: "Max soulsphere" + * The maximum health which can be reached by picking up the + * soulsphere. See p_touch_special_thing in p_inter.c + */ + +int deh_max_soulsphere = DEH_DEFAULT_MAX_SOULSPHERE; + +/* Dehacked: "Soulsphere health" + * The amount of health bonus that picking up a soulsphere + * gives. See p_touch_special_thing in p_inter.c + */ + +int deh_soulsphere_health = DEH_DEFAULT_SOULSPHERE_HEALTH; + +/* Dehacked: "Megasphere health" + * This is what the health is set to after picking up a + * megasphere. See p_touch_special_thing in p_inter.c + */ + +int deh_megasphere_health = DEH_DEFAULT_MEGASPHERE_HEALTH; + +/* Dehacked: "God mode health" + * This is what the health value is set to when cheating using + * the IDDQD god mode cheat. See st_responder in st_stuff.c + */ + +int deh_god_mode_health = DEH_DEFAULT_GOD_MODE_HEALTH; + +/* Dehacked: "IDFA Armor" + * This is what the armor is set to when using the IDFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idfa_armor = DEH_DEFAULT_IDFA_ARMOR; + +/* Dehacked: "IDFA Armor Class" + * This is what the armor class is set to when using the IDFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idfa_armor_class = DEH_DEFAULT_IDFA_ARMOR_CLASS; + +/* Dehacked: "IDKFA Armor" + * This is what the armor is set to when using the IDKFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idkfa_armor = DEH_DEFAULT_IDKFA_ARMOR; + +/* Dehacked: "IDKFA Armor Class" + * This is what the armor class is set to when using the IDKFA cheat. + * See st_responder in st_stuff.c + */ + +int deh_idkfa_armor_class = DEH_DEFAULT_IDKFA_ARMOR_CLASS; + +/* Dehacked: "BFG Cells/Shot" + * This is the number of CELLs firing the BFG uses up. + * See p_check_ammo and a_fire_bfg in p_pspr.c + */ + +int deh_bfg_cells_per_shot = DEH_DEFAULT_BFG_CELLS_PER_SHOT; + +/* Dehacked: "Monsters infight" + * This controls whether monsters can harm other monsters of the same + * species. For example, whether an imp fireball will damage other + * imps. The value of this in dehacked patches is weird - '202' means + * off, while '221' means on. + * + * See pit_check_thing in p_map.c + */ + +int deh_species_infighting = DEH_DEFAULT_SPECIES_INFIGHTING; + +deh_section_t deh_section_misc = +{ + "Misc", + NULL, + deh_misc_start, + deh_misc_parse_line, + NULL, + deh_misc_sha1_sum, +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct misc_settings misc_settings[] = +{ + {"Initial Health", &deh_initial_health}, + {"Initial Bullets", &deh_initial_bullets}, + {"Max Health", &deh_max_health}, + {"Max Armor", &deh_max_armor}, + {"Green Armor Class", &deh_green_armor_class}, + {"Blue Armor Class", &deh_blue_armor_class}, + {"Max Soulsphere", &deh_max_soulsphere}, + {"Soulsphere Health", &deh_soulsphere_health}, + {"Megasphere Health", &deh_megasphere_health}, + {"God Mode Health", &deh_god_mode_health}, + {"IDFA Armor", &deh_idfa_armor}, + {"IDFA Armor Class", &deh_idfa_armor_class}, + {"IDKFA Armor", &deh_idkfa_armor}, + {"IDKFA Armor Class", &deh_idkfa_armor_class}, + {"BFG Cells/Shot", &deh_bfg_cells_per_shot}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_misc_start(deh_context_t *context, char *line) +{ + return NULL; +} + +static void deh_misc_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + int ivalue; + size_t i; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + if (!strcasecmp(variable_name, "Monsters Infight")) + { + /* See notes above. */ + + if (ivalue == 202) + { + deh_species_infighting = 0; + } + else if (ivalue == 221) + { + deh_species_infighting = 1; + } + else + { + deh_warning(context, "Invalid value for 'Monsters Infight': %i", + ivalue); + } + + return; + } + + for (i = 0; i < arrlen(misc_settings); ++i) + { + if (!strcasecmp(variable_name, misc_settings[i].deh_name)) + { + *misc_settings[i].value = ivalue; + return; + } + } + + deh_warning(context, "Unknown Misc variable '%s'", variable_name); +} + +static void deh_misc_sha1_sum(SHA1_CTX *context) +{ + unsigned int i; + + for (i = 0; i < arrlen(misc_settings); ++i) + { + sha1_updateint32(context, *misc_settings[i].value); + } +} diff --git a/games/NXDoom/src/doom/deh_misc.h b/games/NXDoom/src/doom/deh_misc.h new file mode 100644 index 000000000..a55eb6452 --- /dev/null +++ b/games/NXDoom/src/doom/deh_misc.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_misc.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Misc" sections in dehacked files + * + ****************************************************************************/ + +#ifndef DEH_MISC_H +#define DEH_MISC_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEH_DEFAULT_INITIAL_HEALTH 100 +#define DEH_DEFAULT_INITIAL_BULLETS 50 +#define DEH_DEFAULT_MAX_HEALTH 200 +#define DEH_DEFAULT_MAX_ARMOR 200 +#define DEH_DEFAULT_GREEN_ARMOR_CLASS 1 +#define DEH_DEFAULT_BLUE_ARMOR_CLASS 2 +#define DEH_DEFAULT_MAX_SOULSPHERE 200 +#define DEH_DEFAULT_SOULSPHERE_HEALTH 100 +#define DEH_DEFAULT_MEGASPHERE_HEALTH 200 +#define DEH_DEFAULT_GOD_MODE_HEALTH 100 +#define DEH_DEFAULT_IDFA_ARMOR 200 +#define DEH_DEFAULT_IDFA_ARMOR_CLASS 2 +#define DEH_DEFAULT_IDKFA_ARMOR 200 +#define DEH_DEFAULT_IDKFA_ARMOR_CLASS 2 +#define DEH_DEFAULT_BFG_CELLS_PER_SHOT 40 +#define DEH_DEFAULT_SPECIES_INFIGHTING 0 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int deh_initial_health; +extern int deh_initial_bullets; +extern int deh_max_health; +extern int deh_max_armor; +extern int deh_green_armor_class; +extern int deh_blue_armor_class; +extern int deh_max_soulsphere; +extern int deh_soulsphere_health; +extern int deh_megasphere_health; +extern int deh_god_mode_health; +extern int deh_idfa_armor; +extern int deh_idfa_armor_class; +extern int deh_idkfa_armor; +extern int deh_idkfa_armor_class; +extern int deh_bfg_cells_per_shot; +extern int deh_species_infighting; + +#if 0 + +/* To compile without dehacked, it's possible to use these: */ + +#define deh_initial_health DEH_DEFAULT_INITIAL_HEALTH +#define deh_initial_bullets DEH_DEFAULT_INITIAL_BULLETS +#define deh_max_health DEH_DEFAULT_MAX_HEALTH +#define deh_max_armor DEH_DEFAULT_MAX_ARMOR +#define deh_green_armor_class DEH_DEFAULT_GREEN_ARMOR_CLASS +#define deh_blue_armor_class DEH_DEFAULT_BLUE_ARMOR_CLASS +#define deh_max_soulsphere DEH_DEFAULT_MAX_SOULSPHERE +#define deh_soulsphere_health DEH_DEFAULT_SOULSPHERE_HEALTH +#define deh_megasphere_health DEH_DEFAULT_MEGASPHERE_HEALTH +#define deh_god_mode_health DEH_DEFAULT_GOD_MODE_HEALTH +#define deh_idfa_armor DEH_DEFAULT_IDFA_ARMOR +#define deh_idfa_armor_class DEH_DEFAULT_IDFA_ARMOR_CLASS +#define deh_idkfa_armor DEH_DEFAULT_IDKFA_ARMOR +#define deh_idkfa_armor_class DEH_DEFAULT_IDKFA_ARMOR_CLASS +#define deh_bfg_cells_per_shot DEH_DEFAULT_BFG_CELLS_PER_SHOT +#define deh_species_infighting DEH_DEFAULT_SPECIES_INFIGHTING + +#endif + +#endif /* DEH_MISC_H */ diff --git a/games/NXDoom/src/doom/deh_ptr.c b/games/NXDoom/src/doom/deh_ptr.c new file mode 100644 index 000000000..78c14c75e --- /dev/null +++ b/games/NXDoom/src/doom/deh_ptr.c @@ -0,0 +1,172 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_ptr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses Action Pointer entries in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "info.h" + +#include "deh_defs.h" +#include "deh_io.h" +#include "deh_main.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void deh_pointer_init(void); +static void *deh_pointer_start(deh_context_t *context, char *line); +static void deh_pointer_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_pointer_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static actionf_t codeptrs[NUMSTATES]; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_pointer = +{ + "Pointer", + deh_pointer_init, + deh_pointer_start, + deh_pointer_parse_line, + NULL, + deh_pointer_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int code_pointer_index(actionf_t *ptr) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + if (!memcmp(&codeptrs[i], ptr, sizeof(actionf_t))) + { + return i; + } + } + + return -1; +} + +static void deh_pointer_init(void) +{ + int i; + + /* Initialize list of dehacked pointers */ + + for (i = 0; i < NUMSTATES; ++i) + codeptrs[i] = states[i].action; +} + +static void *deh_pointer_start(deh_context_t *context, char *line) +{ + int frame_number = 0; + + /* FIXME: can the third argument here be something other than "Frame" + * or are we ok? + */ + + if (sscanf(line, "Pointer %*i (%*s %i)", &frame_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (frame_number < 0 || frame_number >= NUMSTATES) + { + deh_warning(context, "Invalid frame number: %i", frame_number); + return NULL; + } + + return &states[frame_number]; +} + +static void deh_pointer_parse_line(deh_context_t *context, char *line, + void *tag) +{ + state_t *state; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + state = (state_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* set the appropriate field */ + + if (!strcasecmp(variable_name, "Codep frame")) + { + if (ivalue < 0 || ivalue >= NUMSTATES) + { + deh_warning(context, "Invalid state '%i'", ivalue); + } + else + { + state->action = codeptrs[ivalue]; + } + } + else + { + deh_warning(context, "Unknown variable name '%s'", variable_name); + } +} + +static void deh_pointer_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMSTATES; ++i) + { + sha1_updateint32(context, code_pointer_index(&states[i].action)); + } +} diff --git a/games/NXDoom/src/doom/deh_sound.c b/games/NXDoom/src/doom/deh_sound.c new file mode 100644 index 000000000..e3a8d12c7 --- /dev/null +++ b/games/NXDoom/src/doom/deh_sound.c @@ -0,0 +1,133 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Sound" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" +#include "doomtype.h" +#include "sounds.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t) +DEH_UNSUPPORTED_MAPPING("Offset") +DEH_UNSUPPORTED_MAPPING("Zero/One") +DEH_MAPPING("Value", priority) +DEH_MAPPING("Zero 1", link) +DEH_MAPPING("Zero 2", pitch) +DEH_MAPPING("Zero 3", volume) +DEH_UNSUPPORTED_MAPPING("Zero 4") +DEH_MAPPING("Neg. One 1", usefulness) +DEH_MAPPING("Neg. One 2", lumpnum) +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_sound_start(deh_context_t *context, char *line); +static void deh_sound_parse_line(deh_context_t *context, char *line, + void *tag); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_sound = +{ + "Sound", + NULL, + deh_sound_start, + deh_sound_parse_line, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_sound_start(deh_context_t *context, char *line) +{ + int sound_number = 0; + + if (sscanf(line, "Sound %i", &sound_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (sound_number < 0 || sound_number >= SFX_NUMSFX) + { + deh_warning(context, "Invalid sound number: %i", sound_number); + return NULL; + } + + if (sound_number >= DEH_VANILLA_NUMSFX) + { + deh_warning(context, + "Attempt to modify SFX %i. This will cause " + "problems in Vanilla dehacked.", + sound_number); + } + + return &s_sfx[sound_number]; +} + +static void deh_sound_parse_line(deh_context_t *context, char *line, + void *tag) +{ + sfxinfo_t *sfx; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + sfx = (sfxinfo_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* Set the field value */ + + deh_set_mapping(context, &sound_mapping, sfx, variable_name, ivalue); +} diff --git a/games/NXDoom/src/doom/deh_thing.c b/games/NXDoom/src/doom/deh_thing.c new file mode 100644 index 000000000..964b423b2 --- /dev/null +++ b/games/NXDoom/src/doom/deh_thing.c @@ -0,0 +1,161 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_thing.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Thing" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" + +#include "info.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t) +DEH_MAPPING("Bits", flags) +DEH_MAPPING("Width", radius) +DEH_MAPPING("Height", height) +DEH_MAPPING("Mass", mass) +DEH_MAPPING("Speed", speed) +DEH_MAPPING("ID #", doomednum) +DEH_MAPPING("Hit points", spawnhealth) +DEH_MAPPING("Initial frame", spawnstate) +DEH_MAPPING("First moving frame", seestate) +DEH_MAPPING("Injury frame", painstate) +DEH_MAPPING("Pain chance", painchance) +DEH_MAPPING("Close attack frame", meleestate) +DEH_MAPPING("Far attack frame", missilestate) +DEH_MAPPING("Death frame", deathstate) +DEH_MAPPING("Exploding frame", xdeathstate) +DEH_MAPPING("Respawn frame", raisestate) +DEH_MAPPING("Missile damage", damage) +#ifdef CONFIG_GAMES_NXDOOM_SOUND +DEH_MAPPING("Reaction time", reactiontime) +DEH_MAPPING("Alert sound", seesound) +DEH_MAPPING("Attack sound", attacksound) +DEH_MAPPING("Pain sound", painsound) +DEH_MAPPING("Death sound", deathsound) +DEH_MAPPING("Action sound", activesound) +#endif +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_thing_start(deh_context_t *context, char *line); +static void deh_thing_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_thing_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_thing = +{ + "Thing", + NULL, + deh_thing_start, + deh_thing_parse_line, + NULL, + deh_thing_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_thing_start(deh_context_t *context, char *line) +{ + int thing_number = 0; + mobjinfo_t *mobj; + + if (sscanf(line, "Thing %i", &thing_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + /* dehacked files are indexed from 1 */ + + --thing_number; + + if (thing_number < 0 || thing_number >= NUMMOBJTYPES) + { + deh_warning(context, "Invalid thing number: %i", thing_number); + return NULL; + } + + mobj = &mobjinfo[thing_number]; + + return mobj; +} + +static void deh_thing_parse_line(deh_context_t *context, char *line, + void *tag) +{ + mobjinfo_t *mobj; + char *variable_name; + char *value; + int ivalue; + + if (tag == NULL) return; + + mobj = (mobjinfo_t *)tag; + + /* Parse the assignment */ + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + /* all values are integers */ + + ivalue = atoi(value); + + /* Set the field value */ + + deh_set_mapping(context, &thing_mapping, mobj, variable_name, ivalue); +} + +static void deh_thing_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMMOBJTYPES; ++i) + { + deh_struct_sha1_sum(context, &thing_mapping, &mobjinfo[i]); + } +} diff --git a/games/NXDoom/src/doom/deh_weapon.c b/games/NXDoom/src/doom/deh_weapon.c new file mode 100644 index 000000000..7c2ea08c3 --- /dev/null +++ b/games/NXDoom/src/doom/deh_weapon.c @@ -0,0 +1,131 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/deh_weapon.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Parses "Weapon" sections in dehacked files + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +#include "d_items.h" + +#include "deh_defs.h" +#include "deh_main.h" +#include "deh_mapping.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t) +DEH_MAPPING("Ammo type", ammo) +DEH_MAPPING("Deselect frame", upstate) +DEH_MAPPING("Select frame", downstate) +DEH_MAPPING("Bobbing frame", readystate) +DEH_MAPPING("Shooting frame", atkstate) +DEH_MAPPING("Firing frame", flashstate) +DEH_END_MAPPING + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void *deh_weapon_start(deh_context_t *context, char *line); +static void deh_weapon_parse_line(deh_context_t *context, char *line, + void *tag); +static void deh_weapon_sha1_sum(SHA1_CTX *context); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +deh_section_t deh_section_weapon = +{ + "Weapon", + NULL, + deh_weapon_start, + deh_weapon_parse_line, + NULL, + deh_weapon_sha1_sum, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void *deh_weapon_start(deh_context_t *context, char *line) +{ + int weapon_number = 0; + + if (sscanf(line, "Weapon %i", &weapon_number) != 1) + { + deh_warning(context, "Parse error on section start"); + return NULL; + } + + if (weapon_number < 0 || weapon_number >= NUMWEAPONS) + { + deh_warning(context, "Invalid weapon number: %i", weapon_number); + return NULL; + } + + return &weaponinfo[weapon_number]; +} + +static void deh_weapon_parse_line(deh_context_t *context, char *line, + void *tag) +{ + char *variable_name; + char *value; + weaponinfo_t *weapon; + int ivalue; + + if (tag == NULL) return; + + weapon = (weaponinfo_t *)tag; + + if (!deh_parse_assignment(line, &variable_name, &value)) + { + /* Failed to parse */ + + deh_warning(context, "Failed to parse assignment"); + return; + } + + ivalue = atoi(value); + + deh_set_mapping(context, &weapon_mapping, weapon, variable_name, ivalue); +} + +static void deh_weapon_sha1_sum(SHA1_CTX *context) +{ + int i; + + for (i = 0; i < NUMWEAPONS; ++i) + { + deh_struct_sha1_sum(context, &weapon_mapping, &weaponinfo[i]); + } +} + diff --git a/games/NXDoom/src/doom/doomdata.h b/games/NXDoom/src/doom/doomdata.h new file mode 100644 index 000000000..ede23cf5e --- /dev/null +++ b/games/NXDoom/src/doom/doomdata.h @@ -0,0 +1,248 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdata.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * all external data is defined here + * most of the data is loaded into different structures at run time + * some internal structures shared by many modules are here + * + ****************************************************************************/ + +#ifndef __DOOMDATA__ +#define __DOOMDATA__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* The most basic types we use, portability. */ + +#include "doomtype.h" + +/* Some global defines, that configure the game. */ + +#include "doomdef.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* LineDef attributes. */ + +/* Solid, is an obstacle. */ + +#define ML_BLOCKING 1 + +/* Blocks monsters only. */ + +#define ML_BLOCKMONSTERS 2 + +/* Backside will not be present at all if not two sided. */ + +#define ML_TWOSIDED 4 + +/* If a texture is pegged, the texture will have the end exposed to air held + * constant at the top or bottom of the texture (stairs or pulled down + * things) and will move with a height change of one of the neighbor sectors. + * + * Unpegged textures always have the first row of the texture at the top + * pixel the line for both top and bottom textures (use next to windows). + */ + +/* upper texture unpegged */ + +#define ML_DONTPEGTOP 8 + +/* lower texture unpegged */ + +#define ML_DONTPEGBOTTOM 16 + +/* In AutoMap: don't map as two sided: IT'S A SECRET! */ + +#define ML_SECRET 32 + +/* Sound rendering: don't let sound cross two of these. */ + +#define ML_SOUNDBLOCK 64 + +/* Don't draw on the automap at all. */ + +#define ML_DONTDRAW 128 + +/* Set if already seen, thus drawn in automap. */ + +#define ML_MAPPED 256 + +/* Indicate a leaf. */ + +#define NF_SUBSECTOR 0x8000 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Map level types. + * The following data structures define the persistent format + * used in the lumps of the WAD files. + */ + +/* Lump order in a map WAD: each map needs a couple of lumps + * to provide a complete scene geometry description. + */ + +enum +{ + ML_LABEL, /* A separator, name, ExMx or MAPxx */ + ML_THINGS, /* Monsters, items.. */ + ML_LINEDEFS, /* LineDefs, from editing */ + ML_SIDEDEFS, /* SideDefs, from editing */ + ML_VERTICES, /* Vertices, edited and BSP splits generated */ + ML_SEGS, /* LineSegs, from LineDefs split by BSP */ + ML_SSECTORS, /* SubSectors, list of LineSegs */ + ML_NODES, /* BSP nodes */ + ML_SECTORS, /* Sectors, from editing */ + ML_REJECT, /* LUT, sector-sector visibility */ + ML_BLOCKMAP /* LUT, motion clipping, walls/grid element */ +}; + +/* A single Vertex. */ + +begin_packed_struct struct mapvertex_t +{ + short x; + short y; +} end_packed_struct; + +typedef struct mapvertex_t mapvertex_t; + +/* A SideDef, defining the visual appearance of a wall, by setting textures + * and offsets. + */ + +begin_packed_struct struct mapsidedef_t +{ + short textureoffset; + short rowoffset; + char toptexture[8]; + char bottomtexture[8]; + char midtexture[8]; + short sector; +} end_packed_struct; + +typedef struct mapsidedef_t mapsidedef_t; + +/* A LineDef, as used for editing, and as input to the BSP builder. + */ + +begin_packed_struct struct maplinedef_t +{ + short v1; + short v2; + short flags; + short special; + short tag; + + /* sidenum[1] will be -1 if one sided */ + + short sidenum[2]; +} end_packed_struct; + +typedef struct maplinedef_t maplinedef_t; + +/* Sector definition, from editing. */ + +begin_packed_struct struct mapsector_t +{ + short floorheight; + short ceilingheight; + char floorpic[8]; + char ceilingpic[8]; + short lightlevel; + short special; + short tag; +} end_packed_struct; + +typedef struct mapsector_t mapsector_t; + +/* SubSector, as generated by BSP. */ + +begin_packed_struct struct mapsubsector_t +{ + short numsegs; + + /* Index of first one, segs are stored sequentially. */ + + short firstseg; +} end_packed_struct; + +typedef struct mapsubsector_t mapsubsector_t; + +/* LineSeg, generated by splitting LineDefs using partition lines selected by + * BSP builder. + */ + +begin_packed_struct struct mapseg_t +{ + short v1; + short v2; + short angle; + short linedef; + short side; + short offset; +} end_packed_struct; + +typedef struct mapseg_t mapseg_t; + +/* BSP node structure. */ + +begin_packed_struct struct mapnode_t +{ + /* Partition line from (x,y) to x+dx,y+dy */ + + short x; + short y; + short dx; + short dy; + + /* Bounding box for each child, clip against view frustum. */ + + short bbox[2][4]; + + /* If NF_SUBSECTOR its a subsector, else it's a node of another subtree. */ + + unsigned short children[2]; +} end_packed_struct; + +typedef struct mapnode_t mapnode_t; + +/* Thing definition, position, orientation and type, plus skill/visibility + * flags and attributes. + */ + +begin_packed_struct struct mapthing_t +{ + short x; + short y; + short angle; + short type; + short options; +} end_packed_struct; + +typedef struct mapthing_t mapthing_t; + +#endif /* __DOOMDATA__ */ diff --git a/games/NXDoom/src/doom/doomdef.c b/games/NXDoom/src/doom/doomdef.c new file mode 100644 index 000000000..5cc227884 --- /dev/null +++ b/games/NXDoom/src/doom/doomdef.c @@ -0,0 +1,33 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdef.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DoomDef - basic defines for DOOM, e.g. Version, game mode + * and skill level, and display parameters. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/doomdef.h b/games/NXDoom/src/doom/doomdef.h new file mode 100644 index 000000000..cf00f12ee --- /dev/null +++ b/games/NXDoom/src/doom/doomdef.h @@ -0,0 +1,171 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomdef.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Internally used data structures for virtually everything, lots of other + * stuff. + * + ****************************************************************************/ + +#ifndef __DOOMDEF__ +#define __DOOMDEF__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_mode.h" +#include "doomtype.h" +#include "i_timer.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* DOOM version */ + +#define DOOM_VERSION 109 + +/* Version code for cph's longtics hack ("v1.91") */ + +#define DOOM_191_VERSION 111 + +/* The maximum number of players, multiplayer/networking. */ + +#define MAXPLAYERS 4 + +/* Difficulty/skill settings/filters. */ + +/* Skill flags. */ + +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 + +/* Deaf monsters/do not react to sound. */ + +#define MTF_AMBUSH 8 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The current state of the game: whether we are playing, gazing at the + * intermission screen, the game final animation, or a demo. + */ + +typedef enum +{ + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN, +} gamestate_t; + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, + ga_screenshot +} gameaction_t; + +/* Key cards. */ + +typedef enum +{ + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + + NUMCARDS +} card_t; + +/* The defined weapons, including a marker indicating user has not changed + * weapon. + */ + +typedef enum +{ + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + + NUMWEAPONS, + + /* No pending weapon change. */ + + wp_nochange +} weapontype_t; + +/* Ammunition types defined. */ + +typedef enum +{ + am_clip, /* Pistol / chaingun ammo. */ + am_shell, /* Shotgun / double barreled shotgun. */ + am_cell, /* Plasma rifle, BFG. */ + am_misl, /* Missile launcher. */ + NUMAMMO, + am_noammo /* Unlimited for chainsaw / fist. */ +} ammotype_t; + +/* Power up artifacts. */ + +typedef enum +{ + pw_invulnerability, + pw_strength, + pw_invisibility, + pw_ironfeet, + pw_allmap, + pw_infrared, + NUMPOWERS +} powertype_t; + +/* Power up durations, how many seconds till expiration, assuming TICRATE is + * 35 ticks/second. + */ + +typedef enum +{ + INVULNTICS = (30 * TICRATE), + INVISTICS = (60 * TICRATE), + INFRATICS = (120 * TICRATE), + IRONTICS = (60 * TICRATE) +} powerduration_t; + +#endif /* __DOOMDEF__ */ diff --git a/games/NXDoom/src/doom/doomstat.c b/games/NXDoom/src/doom/doomstat.c new file mode 100644 index 000000000..5f38dd84c --- /dev/null +++ b/games/NXDoom/src/doom/doomstat.c @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomstat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Put all global state variables here. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomstat.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Game Mode - identify IWAD as shareware, retail etc. */ + +game_mode_t gamemode = indetermined; +gamemission_t gamemission = doom; +game_version_t gameversion = exe_final2; +game_variant_t gamevariant = vanilla; + +/* Set if homebrew PWAD stuff has been added. */ + +boolean modifiedgame; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/doomstat.h b/games/NXDoom/src/doom/doomstat.h new file mode 100644 index 000000000..55f31f130 --- /dev/null +++ b/games/NXDoom/src/doom/doomstat.h @@ -0,0 +1,273 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/doomstat.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * All the global variables that store the internal state. + * Theoretically speaking, the internal state of the engine + * should be found by looking at the variables collected + * here, and every relevant module will have to include + * this header file. + * In practice, things are a bit messy. + * + ****************************************************************************/ + +#ifndef __D_STATE__ +#define __D_STATE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We need globally shared data structures, for defining the global state + * variables. + */ + +#include "d_loop.h" +#include "doomdata.h" + +/* We need the player data structure as well. */ + +#include "d_player.h" + +/* Game mode/mission */ + +#include "d_mode.h" + +#include "net_defs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Convenience macro. + * 'gamemission' can be equal to pack_chex or pack_hacx, but these are + * just modified versions of doom and doom2, and should be interpreted + * as the same most of the time. + */ + +#define logical_gamemission \ + (gamemission == pack_chex ? doom \ + : gamemission == pack_hacx ? doom2 \ + : gamemission) + +#define MAX_DM_STARTS 10 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Command line parameters. */ + +extern boolean nomonsters; /* checkparm of -nomonsters */ +extern boolean respawnparm; /* checkparm of -respawn */ +extern boolean fastparm; /* checkparm of -fast */ + +extern boolean devparm; /* DEBUG: launched with -devparm */ + +/* Game Mode - identify IWAD as shareware, retail etc. */ + +extern game_mode_t gamemode; +extern gamemission_t gamemission; +extern game_version_t gameversion; +extern game_variant_t gamevariant; + +/* Set if homebrew PWAD stuff has been added. */ + +extern boolean modifiedgame; + +/* Selected skill type, map etc. */ + +/* Defaults for menu, methinks. */ + +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +/* Savegame slot to load on startup. This is the value provided to + * the -loadgame option. If this has not been provided, this is -1. + */ + +extern int startloadgame; + +extern boolean autostart; + +/* Selected by user. */ + +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +/* If non-zero, exit the level after this number of minutes */ + +extern int timelimit; + +/* Nightmare mode flag, single player. */ + +extern boolean respawnmonsters; + +/* Netgame? Only true if >1 player. */ + +extern boolean netgame; + +/* 0=Cooperative; 1=Deathmatch; 2=Altdeath */ + +extern int deathmatch; + +/* Internal parameters for sound rendering. + * These have been taken from the DOS version, + * but are not (yet) supported with Linux + * (e.g. no sound volume adjustment with menu. + * From m_menu.c: + * Sound FX volume has default, 0 - 15 + * Music volume has default, 0 - 15 + * These are multiplied by 8. + */ + +extern int g_sfx_volume; +extern int g_music_volume; + +/* Status flags for refresh. */ + +/* Depending on view size - no status bar? + * Note that there is no way to disable the status bar explicitly. + */ + +extern boolean statusbaractive; + +extern boolean automapactive; /* In AutoMap mode? */ +extern boolean g_menuactive; /* Menu over-layed? */ +extern boolean paused; /* Game Pause? */ + +extern boolean viewactive; + +extern boolean nodrawers; + +extern boolean testcontrols; +extern int testcontrols_mousespeed; + +/* This one is related to the 3-screen display mode. + * ANG90 = left side, ANG270 = right + */ + +extern int viewangleoffset; + +/* Player taking events, and displaying. */ + +extern int consoleplayer; +extern int displayplayer; + +/* Scores, rating. + * Statistics on a given map, for intermission. + */ + +extern int totalkills; +extern int totalitems; +extern int totalsecret; + +/* Timer, for scores. */ + +extern int levelstarttic; /* gametic at level start */ +extern int leveltime; /* tics in game play for par */ + +/* DEMO playback/recording related stuff. + * No demo, there is a human player in charge? + * Disable save/end game? + */ + +extern boolean usergame; + +/* ? */ + +extern boolean demoplayback; +extern boolean demorecording; + +/* Round angleturn in ticcmds to the nearest 256. This is used when + * recording Vanilla demos in netgames. + */ + +extern boolean lowres_turn; + +/* Quit after playing a demo from cmdline. */ + +extern boolean singledemo; + +/* ? */ + +extern gamestate_t gamestate; + +/* Internal parameters, fixed. + * These are set by the engine, and not changed + * according to user inputs. Partly load from + * WAD, partly set at startup time. + */ + +/* Bookkeeping on players - state. */ + +extern player_t players[MAXPLAYERS]; + +/* Alive? Disconnected? */ + +extern boolean playeringame[MAXPLAYERS]; + +/* Player spawn spots for deathmatch. */ + +extern mapthing_t deathmatchstarts[MAX_DM_STARTS]; +extern mapthing_t *deathmatch_p; + +/* Player spawn spots. */ + +extern mapthing_t playerstarts[MAXPLAYERS]; +extern boolean playerstartsingame[MAXPLAYERS]; + +/* Intermission stats. + * Parameters for world map / intermission. + */ + +extern wbstartstruct_t wminfo; + +/* Internal parameters, used for engine. */ + +/* File handling stuff. */ + +extern char *savegamedir; + +/* if true, load all graphics at level load */ + +extern boolean precache; + +/* wipegamestate can be set to -1 to force a wipe on the next draw */ + +extern gamestate_t wipegamestate; + +extern int g_mouse_sensitivity; + +extern int bodyqueslot; + +/* Needed to store the number of the dummy sky flat. + * Used for rendering, as well as tracking projectiles etc. + */ + +extern int skyflatnum; + +/* Netgame stuff (buffers and pointers, i.e. indices). */ + +extern int rndindex; + +extern ticcmd_t *netcmds; + +#endif /* __D_STATE__ */ diff --git a/games/NXDoom/src/doom/dstrings.c b/games/NXDoom/src/doom/dstrings.c new file mode 100644 index 000000000..514e8c408 --- /dev/null +++ b/games/NXDoom/src/doom/dstrings.c @@ -0,0 +1,71 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/dstrings.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Globally defined strings. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "dstrings.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *doom1_endmsg[] = +{ + DOOM1_ENDMSG +}; + +const char *doom2_endmsg[] = +{ + DOOM2_ENDMSG +}; + +/* UNUSED messages included in the source release */ + +#if 0 +char *endmsg[] = +{ + /* DOOM1 */ + + QUITMSG, + + /* FinalDOOM? */ + + "fuck you, pussy!\nget the fuck out!", + "you quit and i'll jizz\nin your cystholes!", + "if you leave, i'll make\nthe lord drink my jizz.", + "hey, ron! can we say\n'fuck' in the game?", + "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", + "suck it down, asshole!\nyou're a fucking wimp!", + "don't quit now! we're \nstill spending your money!", + + /* Internal debug. Different style, too. */ + + "THIS IS NO MESSAGE!\nPage intentionally left blank.", +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/dstrings.h b/games/NXDoom/src/doom/dstrings.h new file mode 100644 index 000000000..b1330f66d --- /dev/null +++ b/games/NXDoom/src/doom/dstrings.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/dstrings.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DOOM strings, by language. + * + ****************************************************************************/ + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* All important printed strings. */ + +#if defined(CONFIG_GAMES_NXDOOM_LANG_EN) +#include "d_englsh.h" +#elif defined(CONFIG_GAMES_NXDOOM_LANG_FR) +#include "d_french.h" +#else +#error "Unsupported language choice." +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Misc. other strings. */ + +#define SAVEGAMENAME "doomsav" + +/* QuitDOOM messages; 8 per each game type */ + +#define NUM_QUITMESSAGES (8) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *doom1_endmsg[]; +extern const char *doom2_endmsg[]; + +#endif /* __DSTRINGS__ */ diff --git a/games/NXDoom/src/doom/f_finale.c b/games/NXDoom/src/doom/f_finale.c new file mode 100644 index 000000000..a9d964aff --- /dev/null +++ b/games/NXDoom/src/doom/f_finale.c @@ -0,0 +1,769 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_finale.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Game completion, final screen animation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +/* Functions. */ + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Data. */ + +#include "d_main.h" +#include "dstrings.h" + +#include "doomstat.h" +#include "r_state.h" + +#include "hu_stuff.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TEXTSPEED 3 +#define TEXTWAIT 250 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + F_STAGE_TEXT, + F_STAGE_ARTSCREEN, + F_STAGE_CAST, +} finalestage_t; + +/* ? + * #include "doomstat.h" + * #include "r_local.h" + * #include "f_finale.h" + */ + +typedef struct +{ + gamemission_t mission; + int episode; + int level; + const char *background; + const char *text; +} textscreen_t; + +typedef struct +{ + const char *name; + mobjtype_t type; +} castinfo_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static textscreen_t g_textscreens[] = +{ + {doom, 1, 8, "FLOOR4_8", E1TEXT}, + {doom, 2, 8, "SFLR6_1", E2TEXT}, + {doom, 3, 8, "MFLR8_4", E3TEXT}, + {doom, 4, 8, "MFLR8_3", E4TEXT}, + {doom2, 1, 6, "SLIME16", C1TEXT}, + {doom2, 1, 11, "RROCK14", C2TEXT}, + {doom2, 1, 20, "RROCK07", C3TEXT}, + {doom2, 1, 30, "RROCK17", C4TEXT}, + {doom2, 1, 15, "RROCK13", C5TEXT}, + {doom2, 1, 31, "RROCK19", C6TEXT}, + {pack_tnt, 1, 6, "SLIME16", T1TEXT}, + {pack_tnt, 1, 11, "RROCK14", T2TEXT}, + {pack_tnt, 1, 20, "RROCK07", T3TEXT}, + {pack_tnt, 1, 30, "RROCK17", T4TEXT}, + {pack_tnt, 1, 15, "RROCK13", T5TEXT}, + {pack_tnt, 1, 31, "RROCK19", T6TEXT}, + {pack_plut, 1, 6, "SLIME16", P1TEXT}, + {pack_plut, 1, 11, "RROCK14", P2TEXT}, + {pack_plut, 1, 20, "RROCK07", P3TEXT}, + {pack_plut, 1, 30, "RROCK17", P4TEXT}, + {pack_plut, 1, 15, "RROCK13", P5TEXT}, + {pack_plut, 1, 31, "RROCK19", P6TEXT}, +}; + +/* Final DOOM 2 animation + * Casting by id Software. in order of appearance + */ + +static castinfo_t g_castorder[] = +{ + {CC_ZOMBIE, MT_POSSESSED}, + {CC_SHOTGUN, MT_SHOTGUY}, + {CC_HEAVY, MT_CHAINGUY}, + {CC_IMP, MT_TROOP}, + {CC_DEMON, MT_SERGEANT}, + {CC_LOST, MT_SKULL}, + {CC_CACO, MT_HEAD}, + {CC_HELL, MT_KNIGHT}, + {CC_BARON, MT_BRUISER}, + {CC_ARACH, MT_BABY}, + {CC_PAIN, MT_PAIN}, + {CC_REVEN, MT_UNDEAD}, + {CC_MANCU, MT_FATSO}, + {CC_ARCH, MT_VILE}, + {CC_SPIDER, MT_SPIDER}, + {CC_CYBER, MT_CYBORG}, + {CC_HERO, MT_PLAYER}, + {NULL, 0}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Stage of animation: */ + +finalestage_t finalestage; +unsigned int finalecount; + +const char *finaletext; +const char *finaleflat; + +int castnum; +int casttics; +state_t *caststate; +boolean castdeath; +int castframes; +int castonmelee; +boolean castattacking; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void f_start_cast(void) +{ + wipegamestate = -1; /* force a screen wipe */ + castnum = 0; + caststate = &states[mobjinfo[g_castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = F_STAGE_CAST; + castframes = 0; + castonmelee = 0; + castattacking = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(MUS_EVIL, true); +#endif +} + +static void f_text_write(void) +{ + byte *src; + pixel_t *dest; + int x; + int y; + int w; + signed int count; + const char *ch; + int c; + int cx; + int cy; + + /* erase the entire screen to a tiled background */ + + src = w_cache_lump_name(finaleflat, PU_CACHE); + dest = i_video_buffer; + + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + /* draw some of the text onto the screen */ + + cx = 10; + cy = 10; + ch = finaletext; + + count = ((signed int)finalecount - 10) / TEXTSPEED; + if (count < 0) count = 0; + for (; count; count--) + { + c = *ch++; + if (!c) break; + if (c == '\n') + { + cx = 10; + cy += 11; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) break; + v_draw_patch(cx, cy, hu_font[c]); + cx += w; + } +} + +static void f_cast_ticker(void) +{ + int st; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sfx; +#endif + + if (--casttics > 0) return; /* not time to change state yet */ + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + /* switch from deathstate to next monster */ + + castnum++; + castdeath = false; + if (g_castorder[castnum].name == NULL) castnum = 0; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mobjinfo[g_castorder[castnum].type].seesound) + { + s_start_sound(NULL, mobjinfo[g_castorder[castnum].type].seesound); + } + +#endif + + caststate = &states[mobjinfo[g_castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + /* just advance to next state in animation */ + + if (caststate == &states[S_PLAY_ATK1]) + { + goto stopattack; /* Oh, gross hack! */ + } + + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + /* sound hacks.... */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + switch (st) + { + case S_PLAY_ATK1: + sfx = SFX_DSHTGN; + break; + case S_POSS_ATK2: + sfx = SFX_PISTOL; + break; + case S_SPOS_ATK2: + sfx = SFX_SHOTGN; + break; + case S_VILE_ATK2: + sfx = SFX_VILATK; + break; + case S_SKEL_FIST2: + sfx = SFX_SKESWG; + break; + case S_SKEL_FIST4: + sfx = SFX_SKEPCH; + break; + case S_SKEL_MISS2: + sfx = SFX_SKEATK; + break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: + sfx = SFX_FIRSHT; + break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: + sfx = SFX_SHOTGN; + break; + case S_TROO_ATK3: + sfx = SFX_CLAW; + break; + case S_SARG_ATK2: + sfx = SFX_SGTATK; + break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: + sfx = SFX_FIRSHT; + break; + case S_SKULL_ATK2: + sfx = SFX_SKLATK; + break; + case S_SPID_ATK2: + case S_SPID_ATK3: + sfx = SFX_SHOTGN; + break; + case S_BSPI_ATK2: + sfx = SFX_PLASMA; + break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: + sfx = SFX_RLAUNC; + break; + case S_PAIN_ATK3: + sfx = SFX_SKLATK; + break; + default: + sfx = 0; + break; + } + + if (sfx) s_start_sound(NULL, sfx); +#endif + } + + if (castframes == 12) + { + /* go into attack frame */ + + castattacking = true; + + if (castonmelee) + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].meleestate]; + } + else + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].missilestate]; + } + + castonmelee ^= 1; + + if (caststate == &states[S_NULL]) + { + if (castonmelee) + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].meleestate]; + } + else + { + caststate = + &states[mobjinfo[g_castorder[castnum].type].missilestate]; + } + } + } + + if (castattacking) + { + if (castframes == 24 || + caststate == &states[mobjinfo[g_castorder[castnum].type].seestate]) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[g_castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) casttics = 15; +} + +static boolean f_cast_responder(event_t *ev) +{ + if (ev->type != ev_keydown) return false; + + if (castdeath) return true; /* already in dying frames */ + + /* go into death frame */ + + castdeath = true; + caststate = &states[mobjinfo[g_castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mobjinfo[g_castorder[castnum].type].deathsound) + { + s_start_sound(NULL, mobjinfo[g_castorder[castnum].type].deathsound); + } +#endif + + return true; +} + +static void f_cast_print(const char *text) +{ + const char *ch; + int c; + int cx; + int w; + int width; + + /* find width */ + + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + width += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + width += w; + } + + /* draw it */ + + cx = SCREENWIDTH / 2 - width / 2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + v_draw_patch(cx, 180, hu_font[c]); + cx += w; + } +} + +static void f_cast_drawer(void) +{ + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + patch_t *patch; + + /* erase the entire screen to a background */ + + v_draw_patch(0, 0, w_cache_lump_name(("BOSSBACK"), PU_CACHE)); + + f_cast_print((g_castorder[castnum].name)); + + /* draw the current frame in the middle of the screen */ + + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + + patch = w_cache_lump_num(lump + firstspritelump, PU_CACHE); + if (flip) + v_draw_patch_flipped(SCREENWIDTH / 2, 170, patch); + else + v_draw_patch(SCREENWIDTH / 2, 170, patch); +} + +static void f_draw_patch_col(int x, patch_t *patch, int col) +{ + column_t *column; + byte *source; + pixel_t *dest; + pixel_t *desttop; + int count; + + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + desttop = i_video_buffer + x; + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } +} + +static void f_bunny_scroll(void) +{ + signed int scrolled; + int x; + patch_t *p1; + patch_t *p2; + char name[10]; + int stage; + static int laststage; + + p1 = w_cache_lump_name(("PFUB2"), PU_LEVEL); + p2 = w_cache_lump_name(("PFUB1"), PU_LEVEL); + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT); + + scrolled = (SCREENWIDTH - ((signed int)finalecount - 230) / 2); + if (scrolled > SCREENWIDTH) scrolled = SCREENWIDTH; + if (scrolled < 0) scrolled = 0; + + for (x = 0; x < SCREENWIDTH; x++) + { + if (x + scrolled < SCREENWIDTH) + f_draw_patch_col(x, p1, x + scrolled); + else + f_draw_patch_col(x, p2, x + scrolled - SCREENWIDTH); + } + + if (finalecount < 1130) return; + if (finalecount < 1180) + { + v_draw_patch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, + w_cache_lump_name(("END0"), PU_CACHE)); + laststage = 0; + return; + } + + stage = (finalecount - 1180) / 5; + if (stage > 6) stage = 6; + if (stage > laststage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PISTOL); +#endif + laststage = stage; + } + + snprintf(name, 10, "END%i", stage); + v_draw_patch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, + w_cache_lump_name(name, PU_CACHE)); +} + +static void f_art_screen_drawer(void) +{ + const char *lumpname; + + if (gameepisode == 3) + { + f_bunny_scroll(); + } + else + { + switch (gameepisode) + { + case 1: + if (gameversion >= exe_ultimate) + { + lumpname = "CREDIT"; + } + else + { + lumpname = "HELP2"; + } + break; + case 2: + lumpname = "VICTORY2"; + break; + case 4: + lumpname = "ENDPIC"; + break; + default: + return; + } + + lumpname = (lumpname); + + v_draw_patch(0, 0, w_cache_lump_name(lumpname, PU_CACHE)); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* f_start_finale */ + +void f_start_finale(void) +{ + size_t i; + + gameaction = ga_nothing; + gamestate = GS_FINALE; + viewactive = false; + automapactive = false; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (logical_gamemission == doom) + { + s_change_music(MUS_VICTOR, true); + } + else + { + s_change_music(MUS_READ_M, true); + } +#endif + + /* Find the right screen and set the text and background */ + + for (i = 0; i < arrlen(g_textscreens); ++i) + { + textscreen_t *screen = &g_textscreens[i]; + + /* Hack for Chex Quest */ + + if (gameversion == exe_chex && screen->mission == doom) + { + screen->level = 5; + } + + if (logical_gamemission == screen->mission && + (logical_gamemission != doom || gameepisode == screen->episode) && + gamemap == screen->level) + { + finaletext = screen->text; + finaleflat = screen->background; + } + } + + /* Do dehacked substitutions of strings */ + + finaletext = (finaletext); + finaleflat = (finaleflat); + + finalestage = F_STAGE_TEXT; + finalecount = 0; +} + +boolean f_responder(event_t *event) +{ + if (finalestage == F_STAGE_CAST) return f_cast_responder(event); + + return false; +} + +void f_ticker(void) +{ + size_t i; + + /* check for skipping */ + + if ((gamemode == commercial) && (finalecount > 50)) + { + /* go on to the next level */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].cmd.buttons) break; + } + + if (i < MAXPLAYERS) + { + if (gamemap == 30) + f_start_cast(); + else + gameaction = ga_worlddone; + } + } + + /* advance animation */ + + finalecount++; + + if (finalestage == F_STAGE_CAST) + { + f_cast_ticker(); + return; + } + + if (gamemode == commercial) return; + + if (finalestage == F_STAGE_TEXT && + finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT) + { + finalecount = 0; + finalestage = F_STAGE_ARTSCREEN; + wipegamestate = -1; /* force a wipe */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameepisode == 3) s_start_music(MUS_BUNNY); +#endif + } +} + +void f_drawer(void) +{ + switch (finalestage) + { + case F_STAGE_CAST: + f_cast_drawer(); + break; + case F_STAGE_TEXT: + f_text_write(); + break; + case F_STAGE_ARTSCREEN: + f_art_screen_drawer(); + break; + } +} diff --git a/games/NXDoom/src/doom/f_finale.h b/games/NXDoom/src/doom/f_finale.h new file mode 100644 index 000000000..3e60e15a7 --- /dev/null +++ b/games/NXDoom/src/doom/f_finale.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_finale.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef __F_FINALE__ +#define __F_FINALE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by main loop. */ + +boolean f_responder(event_t *ev); + +/* Called by main loop. */ + +void f_ticker(void); + +/* Called by main loop. */ + +void f_drawer(void); + +void f_start_finale(void); + +#endif /* __F_FINALE__ */ diff --git a/games/NXDoom/src/doom/f_wipe.c b/games/NXDoom/src/doom/f_wipe.c new file mode 100644 index 000000000..e10a0bd21 --- /dev/null +++ b/games/NXDoom/src/doom/f_wipe.c @@ -0,0 +1,296 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_wipe.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Mission begin melt/wipe screen special effect. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_video.h" +#include "m_random.h" +#include "v_video.h" +#include "z_zone.h" + +#include "doomtype.h" + +#include "f_wipe.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void wipe_shitty_col_major_xform(dpixel_t *array, int width, + int height); +static int wip_init_colour_xform(int width, int height, int ticks); +static int wipe_do_colour_xform(int width, int height, int ticks); +static int wip_exit_color_xform(int width, int height, int ticks); +static int wipe_init_melt(int width, int height, int ticks); +static int wipe_do_melt(int width, int height, int ticks); +static int wipe_exit_melt(int width, int height, int ticks); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* when zero, stop the wipe */ + +static boolean go = 0; + +static pixel_t *wipe_scr_start; +static pixel_t *wipe_scr_end; +static pixel_t *wipe_scr; + +static int *g_y; + +static int (*g_wipes[])(int, int, int) = +{ + wip_init_colour_xform, wipe_do_colour_xform, wip_exit_color_xform, + wipe_init_melt, wipe_do_melt, wipe_exit_melt, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void wipe_shitty_col_major_xform(dpixel_t *array, int width, + int height) +{ + int x; + int y; + dpixel_t *dest; + + dest = (dpixel_t *)z_malloc(width * height * sizeof(*dest), PU_STATIC, 0); + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + dest[x * height + y] = array[y * width + x]; + } + } + + memcpy(array, dest, width * height * sizeof(*dest)); + + z_free(dest); +} + +static int wip_init_colour_xform(int width, int height, int ticks) +{ + memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr)); + return 0; +} + +static int wipe_do_colour_xform(int width, int height, int ticks) +{ + boolean changed; + pixel_t *w; + pixel_t *e; + int newval; + + changed = false; + w = wipe_scr; + e = wipe_scr_end; + + while (w != wipe_scr + width * height) + { + if (*w != *e) + { + if (*w > *e) + { + newval = *w - ticks; + if (newval < *e) + *w = *e; + else + *w = newval; + changed = true; + } + else if (*w < *e) + { + newval = *w + ticks; + if (newval > *e) + *w = *e; + else + *w = newval; + changed = true; + } + } + + w++; + e++; + } + + return !changed; +} + +static int wip_exit_color_xform(int width, int height, int ticks) +{ + return 0; +} + +static int wipe_init_melt(int width, int height, int ticks) +{ + int i; + int r; + + /* copy start screen to main screen */ + + memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr)); + + /* makes this wipe faster (in theory) + * to have stuff in column-major format + */ + + wipe_shitty_col_major_xform((dpixel_t *)wipe_scr_start, width / 2, height); + wipe_shitty_col_major_xform((dpixel_t *)wipe_scr_end, width / 2, height); + + /* setup initial column positions + * (y<0 => not ready to scroll yet) + */ + + g_y = (int *)z_malloc(width * sizeof(int), PU_STATIC, 0); + g_y[0] = -(m_random() % 16); + for (i = 1; i < width; i++) + { + r = (m_random() % 3) - 1; + g_y[i] = g_y[i - 1] + r; + if (g_y[i] > 0) + g_y[i] = 0; + else if (g_y[i] == -16) + g_y[i] = -15; + } + + return 0; +} + +static int wipe_do_melt(int width, int height, int ticks) +{ + int i; + int j; + int dy; + int idx; + + dpixel_t *s; + dpixel_t *d; + boolean done = true; + + width /= 2; + + while (ticks--) + { + for (i = 0; i < width; i++) + { + if (g_y[i] < 0) + { + g_y[i]++; + done = false; + } + else if (g_y[i] < height) + { + dy = (g_y[i] < 16) ? g_y[i] + 1 : 8; + if (g_y[i] + dy >= height) dy = height - g_y[i]; + s = &((dpixel_t *)wipe_scr_end)[i * height + g_y[i]]; + d = &((dpixel_t *)wipe_scr)[g_y[i] * width + i]; + idx = 0; + for (j = dy; j; j--) + { + d[idx] = *(s++); + idx += width; + } + + g_y[i] += dy; + s = &((dpixel_t *)wipe_scr_start)[i * height]; + d = &((dpixel_t *)wipe_scr)[g_y[i] * width + i]; + idx = 0; + for (j = height - g_y[i]; j; j--) + { + d[idx] = *(s++); + idx += width; + } + + done = false; + } + } + } + + return done; +} + +static int wipe_exit_melt(int width, int height, int ticks) +{ + z_free(g_y); + z_free(wipe_scr_start); + z_free(wipe_scr_end); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int wipe_start_screen(int x, int y, int width, int height) +{ + wipe_scr_start = z_malloc( + SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, NULL); + i_read_screen(wipe_scr_start); + return 0; +} + +int wipe_endscreen(int x, int y, int width, int height) +{ + wipe_scr_end = z_malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end), + PU_STATIC, NULL); + i_read_screen(wipe_scr_end); + v_draw_block(x, y, width, height, wipe_scr_start); /* restore start scr. */ + return 0; +} + +int wipe_screen_wipe(int wipeno, int x, int y, int width, int height, + int ticks) +{ + int rc; + + /* initial stuff */ + + if (!go) + { + go = 1; + wipe_scr = i_video_buffer; + (*g_wipes[wipeno * 3])(width, height, ticks); + } + + /* do a piece of wipe-in */ + + v_mark_rect(0, 0, width, height); + rc = (*g_wipes[wipeno * 3 + 1])(width, height, ticks); + + /* final stuff */ + + if (rc) + { + go = 0; + (*g_wipes[wipeno * 3 + 2])(width, height, ticks); + } + + return !go; +} diff --git a/games/NXDoom/src/doom/f_wipe.h b/games/NXDoom/src/doom/f_wipe.h new file mode 100644 index 000000000..c421385a5 --- /dev/null +++ b/games/NXDoom/src/doom/f_wipe.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/f_wipe.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Mission start screen wipe/melt, special effects. + * + ****************************************************************************/ + +#ifndef __F_WIPE_H__ +#define __F_WIPE_H__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* SCREEN WIPE PACKAGE */ + +enum +{ + /* simple gradual pixel change for 8-bit only */ + + WIPE_COLORXFORM, + WIPE_MELT, /* weird screen melt */ + WIPE_NUMWIPES +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int wipe_start_screen(int x, int y, int width, int height); + +int wipe_endscreen(int x, int y, int width, int height); + +int wipe_screen_wipe(int wipeno, int x, int y, int width, int height, + int ticks); + +#endif /* __F_WIPE_H__ */ diff --git a/games/NXDoom/src/doom/g_game.c b/games/NXDoom/src/doom/g_game.c new file mode 100644 index 000000000..241ffcfa6 --- /dev/null +++ b/games/NXDoom/src/doom/g_game.c @@ -0,0 +1,2466 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/g_game.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "doomstat.h" + +#include "deh_main.h" +#include "deh_misc.h" + +#include "f_finale.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_swap.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "m_random.h" +#include "z_zone.h" + +#include "p_saveg.h" +#include "p_setup.h" +#include "p_tick.h" + +#include "d_main.h" + +#include "am_map.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "statdump.h" +#include "wi_stuff.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "w_wad.h" + +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Data. */ + +#include "dstrings.h" + +/* SKY handling - still the wrong place. */ + +#include "r_data.h" +#include "r_sky.h" + +#include "g_game.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SAVEGAMESIZE 0x2c000 + +#define MAXPLMOVE (forwardmove[1]) + +#define TURBOTHRESHOLD 0x32 + +#define BODYQUESIZE 32 + +#define SLOWTURNTICS 6 + +#define NUMKEYS 256 +#define MAX_JOY_BUTTONS 20 + +#define DEMOMARKER 0x80 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct weapon_order +{ + weapontype_t weapon; + weapontype_t weapon_num; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_spawn_player(mapthing_t *mthing); +void g_player_reborn(int player); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Gamestate the last time g_ticker was called. */ + +gamestate_t oldgamestate; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +boolean respawnmonsters; +int gameepisode; +int gamemap; + +/* If non-zero, exit the level after this number of minutes. */ + +int timelimit; + +boolean paused; +boolean sendpause; /* send a pause event next tic */ +boolean sendsave; /* send a save event next tic */ +boolean usergame; /* ok to save / end game */ + +boolean timingdemo; /* if true, exit with report on completion */ +boolean nodrawers; /* for comparative timing purposes */ +int starttime; /* for comparative timing purposes */ + +boolean viewactive; + +int deathmatch; /* only if started as net death */ +boolean netgame; /* only true if packets are broadcast */ +boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; + +boolean turbodetected[MAXPLAYERS]; + +int consoleplayer; /* player taking events and displaying */ +int displayplayer; /* view being displayed */ +int levelstarttic; /* gametic at level start */ +int totalkills; +int totalitems; +int totalsecret; /* for intermission */ + +char *demoname; +boolean demorecording; +boolean longtics; /* cph's doom 1.91 longtics hack */ +boolean lowres_turn; /* low resolution turning for longtics */ +boolean demoplayback; +boolean netdemo; +byte *demobuffer; +byte *demo_p; +byte *demoend; +boolean singledemo; /* quit after playing a demo from cmdline */ + +boolean precache = true; /* if true, load all graphics at start */ + +boolean testcontrols = false; /* Invoked by setup to test controls */ +int testcontrols_mousespeed; + +wbstartstruct_t wminfo; /* params for world map / intermission */ + +byte consistency[MAXPLAYERS][CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +fixed_t forwardmove[2] = +{ + 0x19, 0x32 +}; + +fixed_t sidemove[2] = +{ + 0x18, 0x28 +}; + +/* + slow turn */ + +fixed_t angleturn[3] = +{ + 640, 1280, 320 +}; + +/* mouse values are used once */ + +int mousex; +int mousey; + +mobj_t *bodyque[BODYQUESIZE]; +int bodyqueslot; + +int vanilla_savegame_limit = 1; +int vanilla_demo_limit = 1; + +/* G_DoCompleted */ + +boolean secretexit; + +char savename[256]; + +skill_t d_skill; +int d_episode; +int d_map; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int *weapon_keys[] = +{ + &key_weapon1, &key_weapon2, &key_weapon3, &key_weapon4, + &key_weapon5, &key_weapon6, &key_weapon7, &key_weapon8, +}; + +/* Set to -1 or +1 to switch to the previous or next weapon. */ + +static int next_weapon = 0; + +/* Used for prev/next weapon keys. */ + +static const struct weapon_order weapon_order_table[] = +{ + {wp_fist, wp_fist}, + {wp_chainsaw, wp_fist}, + {wp_pistol, wp_pistol}, + {wp_shotgun, wp_shotgun}, + {wp_supershotgun, wp_shotgun}, + {wp_chaingun, wp_chaingun}, + {wp_missile, wp_missile}, + {wp_plasma, wp_plasma}, + {wp_bfg, wp_bfg}, +}; + +static boolean gamekeydown[NUMKEYS]; +static int turnheld; /* for accelerative turning */ + +static boolean mousearray[MAX_MOUSE_BUTTONS + 1]; +static boolean *mousebuttons = &mousearray[1]; /* allow [-1] */ + +static int dclicktime; +static boolean dclickstate; +static int dclicks; +static int dclicktime2; +static boolean dclickstate2; +static int dclicks2; + +/* joystick values are repeated */ + +static int joyxmove; +static int joyymove; +static int joystrafemove; +static boolean joyarray[MAX_JOY_BUTTONS + 1]; +static boolean *joybuttons = &joyarray[1]; /* allow [-1] */ + +static int savegameslot; +static char savedescription[32]; + +/* DOOM Par Times */ + +static const int pars[4][10] = +{ + {0}, + {0, 30, 75, 120, 90, 165, 180, 180, 30, 165}, + {0, 90, 90, 90, 120, 90, 360, 240, 30, 170}, + {0, 90, 45, 90, 150, 90, 90, 165, 30, 135}, +}; + +/* DOOM II Par Times */ + +static const int cpars[32] = +{ + 30, 90, 120, 120, 90, 150, 120, 120, 270, 90, /* 1-10 */ + 210, 150, 150, 150, 210, 150, 420, 150, 210, 150, /* 11-20 */ + 240, 150, 180, 150, 150, 300, 330, 420, 300, 180, /* 21-30 */ + 120, 30, /* 31-32 */ +}; + +/* Chex Quest Par Times */ + +static const int chexpars[6] = +{ + 0, 120, 360, 480, 200, 360, +}; + +static const char *defdemoname; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean weapon_selectable(weapontype_t weapon) +{ + /* Can't select the super shotgun in Doom 1. */ + + if (weapon == wp_supershotgun && logical_gamemission == doom) + { + return false; + } + + /* These weapons aren't available in shareware. */ + + if ((weapon == wp_plasma || weapon == wp_bfg) && gamemission == doom && + gamemode == shareware) + { + return false; + } + + /* Can't select a weapon if we don't own it. */ + + if (!players[consoleplayer].weaponowned[weapon]) + { + return false; + } + + /* Can't select the fist if we have the chainsaw, unless + * we also have the berserk pack. + */ + + if (weapon == wp_fist && players[consoleplayer].weaponowned[wp_chainsaw] && + !players[consoleplayer].powers[pw_strength]) + { + return false; + } + + return true; +} + +static int g_next_weapon(int direction) +{ + weapontype_t weapon; + int start_i; + int i; + + /* Find index in the table. */ + + if (players[consoleplayer].pendingweapon == wp_nochange) + { + weapon = players[consoleplayer].readyweapon; + } + else + { + weapon = players[consoleplayer].pendingweapon; + } + + for (i = 0; i < arrlen(weapon_order_table); ++i) + { + if (weapon_order_table[i].weapon == weapon) + { + break; + } + } + + /* The current weapon must be present in weapon_order_table + * otherwise something has gone terribly wrong + */ + + if (i >= arrlen(weapon_order_table)) + { + i_error("Internal error: weapon %d not present in weapon_order_table", + weapon); + } + + /* Switch weapon. Don't loop forever. */ + + start_i = i; + do + { + i += direction; + i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table); + } + while (i != start_i && !weapon_selectable(weapon_order_table[i].weapon)); + + return weapon_order_table[i].weapon_num; +} + +#if 0 /* UNUSED */ +static int g_cmd_checksum(ticcmd_t *cmd) +{ + size_t i; + int sum = 0; + + for (i = 0; i < sizeof(*cmd) / 4 - 1; i++) + sum += ((int *)cmd)[i]; + + return sum; +} +#endif + +static void g_do_load_level(void) +{ + int i; + + /* Set the sky map. + * First thing, we have a dummy sky texture name, + * a flat. The data is in the WAD only because + * we look for an actual index, instead of simply + * setting one. + */ + + skyflatnum = r_flat_num_for_name((SKYFLATNAME)); + + /* The "Sky never changes in Doom II" bug was fixed in + * the id Anthology version of doom2.exe for Final Doom. + */ + + if ((gamemode == commercial) && + (gameversion == exe_final2 || gameversion == exe_chex)) + { + const char *skytexturename; + + if (gamemap < 12) + { + skytexturename = "SKY1"; + } + else if (gamemap < 21) + { + skytexturename = "SKY2"; + } + else + { + skytexturename = "SKY3"; + } + + skytexturename = (skytexturename); + + skytexture = r_texture_num_for_name(skytexturename); + } + + levelstarttic = gametic; /* for time calculation */ + + if (wipegamestate == GS_LEVEL) wipegamestate = -1; /* force a wipe */ + + gamestate = GS_LEVEL; + + for (i = 0; i < MAXPLAYERS; i++) + { + turbodetected[i] = false; + if (playeringame[i] && players[i].playerstate == PST_DEAD) + players[i].playerstate = PST_REBORN; + memset(players[i].frags, 0, sizeof(players[i].frags)); + } + + p_setup_level(gameepisode, gamemap, 0, gameskill); + displayplayer = consoleplayer; /* view the guy you are playing */ + gameaction = ga_nothing; + z_check_heap(); + + /* clear cmd building stuff */ + + memset(gamekeydown, 0, sizeof(gamekeydown)); + joyxmove = joyymove = joystrafemove = 0; + mousex = mousey = 0; + sendpause = sendsave = paused = false; + memset(mousearray, 0, sizeof(mousearray)); + memset(joyarray, 0, sizeof(joyarray)); + + if (testcontrols) + { + players[consoleplayer].message = "Press escape to quit."; + } +} + +static void set_joy_buttons(unsigned int buttons_mask) +{ + int i; + + for (i = 0; i < MAX_JOY_BUTTONS; ++i) + { + int button_on = (buttons_mask & (1 << i)) != 0; + + /* Detect button press: */ + + if (!joybuttons[i] && button_on) + { + /* Weapon cycling: */ + + if (i == joybprevweapon) + { + next_weapon = -1; + } + else if (i == joybnextweapon) + { + next_weapon = 1; + } + } + + joybuttons[i] = button_on; + } +} + +static void set_mouse_buttons(unsigned int buttons_mask) +{ + int i; + + for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) + { + unsigned int button_on = (buttons_mask & (1 << i)) != 0; + + /* Detect button press: */ + + if (!mousebuttons[i] && button_on) + { + if (i == mousebprevweapon) + { + next_weapon = -1; + } + else if (i == mousebnextweapon) + { + next_weapon = 1; + } + } + + mousebuttons[i] = button_on; + } +} + +/* g_check_spot + * Returns false if the player cannot be respawned + * at the given mapthing_t spot because something is occupying it + */ + +static boolean g_check_spot(int playernum, mapthing_t *mthing) +{ + fixed_t x; + fixed_t y; + subsector_t *ss; + mobj_t *mo; + int i; + + if (!players[playernum].mo) + { + /* first spawn of level, before corpses */ + + for (i = 0; i < playernum; i++) + { + if (players[i].mo->x == mthing->x << FRACBITS && + players[i].mo->y == mthing->y << FRACBITS) + { + return false; + } + } + + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (!p_check_position(players[playernum].mo, x, y)) return false; + + /* flush an old corpse if needed */ + + if (bodyqueslot >= BODYQUESIZE) + { + p_remove_mobj(bodyque[bodyqueslot % BODYQUESIZE]); + } + + bodyque[bodyqueslot % BODYQUESIZE] = players[playernum].mo; + bodyqueslot++; + + /* spawn a teleport fog */ + + ss = r_point_in_subsector(x, y); + + /* The code in the released source looks like this: + * + * an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) + * >> ANGLETOFINESHIFT; + * mo = p_spawn_mobj (x+20*finecosine[an], y+20*finesine[an] + * , ss->sector->floorheight + * , MT_TFOG); + * + * But 'an' can be a signed value in the DOS version. This means that + * we get a negative index and the lookups into finecosine/finesine + * end up dereferencing values in finetangent[]. + * A player spawning on a deathmatch start facing directly west spawns + * "silently" with no spawn fog. Emulate this. + * + * This code is imported from PrBoom+. + */ + + fixed_t xa; + fixed_t ya; + signed int an; + + /* This calculation overflows in Vanilla Doom, but here we deliberately + * avoid integer overflow as it is undefined behavior, so the value of + * 'an' will always be positive. + */ + + an = (ANG45 >> ANGLETOFINESHIFT) * ((signed int)mthing->angle / 45); + + switch (an) + { + case 4096: /* -4096: */ + xa = finetangent[2048]; /* finecosine[-4096] */ + ya = finetangent[0]; /* finesine[-4096] */ + break; + case 5120: /* -3072: */ + xa = finetangent[3072]; /* finecosine[-3072] */ + ya = finetangent[1024]; /* finesine[-3072] */ + break; + case 6144: /* -2048: */ + xa = finesine[0]; /* finecosine[-2048] */ + ya = finetangent[2048]; /* finesine[-2048] */ + break; + case 7168: /* -1024: */ + xa = finesine[1024]; /* finecosine[-1024] */ + ya = finetangent[3072]; /* finesine[-1024] */ + break; + case 0: + case 1024: + case 2048: + case 3072: + xa = finecosine[an]; + ya = finesine[an]; + break; + case 8192: /* 360 deg: */ + xa = tantoangle[0]; /* finecosine[8192] */ + ya = finesine[8192]; /* finesine[8192] */ + break; + default: + i_error("g_check_spot: unexpected angle %d\n", an); + xa = ya = 0; + break; + } + + mo = p_spawn_mobj(x + 20 * xa, y + 20 * ya, ss->sector->floorheight, + MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (players[consoleplayer].viewz != 1) + { + /* don't start sound on first frame */ + + s_start_sound(mo, SFX_TELEPT); + } +#else + UNUSED(mo); +#endif + + return true; +} + +static void g_do_reborn(int playernum) +{ + int i; + + if (!netgame) + { + /* reload the level from scratch */ + + gameaction = ga_loadlevel; + } + else + { + /* respawn at the start first disassociate the corpse */ + + players[playernum].mo->player = NULL; + + /* spawn at random spot if in death match */ + + if (deathmatch) + { + g_death_match_spawn_player(playernum); + return; + } + + if (g_check_spot(playernum, &playerstarts[playernum])) + { + p_spawn_player(&playerstarts[playernum]); + return; + } + + /* try to spawn at one of the other players spots */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (g_check_spot(playernum, &playerstarts[i])) + { + playerstarts[i].type = playernum + 1; /* fake as other player */ + p_spawn_player(&playerstarts[i]); + playerstarts[i].type = i + 1; /* restore */ + return; + } + + /* he's going to be inside something. Too bad. */ + } + + p_spawn_player(&playerstarts[playernum]); + } +} + +static void g_do_new_game(void) +{ + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + g_init_new(d_skill, d_episode, d_map); + gameaction = ga_nothing; +} + +static void g_do_save_game(void) +{ + char *savegame_file; + char *temp_savegame_file; + char *recovery_savegame_file; + + recovery_savegame_file = NULL; + temp_savegame_file = p_temp_save_game_file(); + savegame_file = p_save_game_file(savegameslot); + + /* Open the savegame file for writing. We write to a temporary file + * and then rename it at the end if it was successfully written. + * This prevents an existing savegame from being overwritten by + * a corrupted one, or if a savegame buffer overrun occurs. + */ + + save_stream = fopen(temp_savegame_file, "wb"); + + if (save_stream == NULL) + { + /* Failed to save the game, so we're going to have to abort. But + * to be nice, save to somewhere else before we call i_error(). + */ + + recovery_savegame_file = m_temp_file("recovery.dsg"); + save_stream = fopen(recovery_savegame_file, "wb"); + if (save_stream == NULL) + { + i_error("Failed to open either '%s' or '%s' to write savegame.", + temp_savegame_file, recovery_savegame_file); + } + } + + savegame_error = false; + + p_write_save_game_header(savedescription); + + p_archive_players(); + p_archive_world(); + p_archive_thinkers(); + p_archive_specials(); + + p_write_save_game_eof(); + + /* Enforce the same savegame size limit as in Vanilla Doom, + * except if the vanilla_savegame_limit setting is turned off. + */ + + if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE) + { + i_error("Savegame buffer overrun"); + } + + /* Finish up, close the savegame file. */ + + fclose(save_stream); + + if (recovery_savegame_file != NULL) + { + /* We failed to save to the normal location, but we wrote a + * recovery file to the temp directory. Now we can bomb out + * with an error. + */ + + i_error("Failed to open savegame file '%s' for writing.\n" + "But your game has been saved to '%s' for recovery.", + temp_savegame_file, recovery_savegame_file); + } + + /* Now rename the temporary savegame file to the actual savegame + * file, overwriting the old savegame if there was one there. + */ + + remove(savegame_file); + rename(temp_savegame_file, savegame_file); + + gameaction = ga_nothing; + m_str_copy(savedescription, "", sizeof(savedescription)); + + players[consoleplayer].message = (GGSAVED); + + /* draw the pattern into the back screen */ + + r_fill_back_screen(); +} + +/* Generate a string describing a demo version */ + +static const char *demo_version_description(int version) +{ + static char resultbuf[24]; + + switch (version) + { + case 104: + return "v1.4"; + case 105: + return "v1.5"; + case 106: + return "v1.6/v1.666"; + case 107: + return "v1.7/v1.7a"; + case 108: + return "v1.8"; + case 109: + return "v1.9"; + case 111: + return "v1.91 hack demo?"; + default: + break; + } + + /* Unknown version. Perhaps this is a pre-v1.4 IWAD? If the version + * byte is in the range 0-4 then it can be a v1.0-v1.2 demo. + */ + + if (version >= 0 && version <= 4) + { + return "v1.0/v1.1/v1.2"; + } + else + { + snprintf(resultbuf, sizeof(resultbuf), "%i.%i (unknown)", + version / 100, version % 100); + return resultbuf; + } +} + +static void g_do_play_demo(void) +{ + skill_t skill; + int i; + int lumpnum; + int episode; + int map; + int demoversion; + boolean olddemo = false; + + lumpnum = w_get_num_for_name(defdemoname); + gameaction = ga_nothing; + demobuffer = w_cache_lump_num(lumpnum, PU_STATIC); + demo_p = demobuffer; + + demoversion = *demo_p++; + + if (demoversion >= 0 && demoversion <= 4) + { + olddemo = true; + demo_p--; + } + + longtics = false; + + /* Longtics demos use the modified format that is generated by cph's + * hacked "v1.91" doom exe. This is a non-vanilla extension. + */ + + if (d_non_vanilla_playback(demoversion == DOOM_191_VERSION, lumpnum, + "Doom 1.91 demo format")) + { + longtics = true; + } + else if (demoversion != g_vanilla_version_code() && + !(gameversion <= exe_doom_1_2 && olddemo)) + { + const char *message = "Demo is from a different game version!\n" + "(read %i, should be %i)\n" + "\n" + "*** You may need to upgrade your version " + "of Doom to v1.9. ***\n" + " See: https://www.doomworld.com/classicdoom" + "/info/patches.php\n" + " This appears to be %s."; + + i_error(message, demoversion, g_vanilla_version_code(), + demo_version_description(demoversion)); + } + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + + if (!olddemo) + { + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + } + else + { + deathmatch = 0; + respawnparm = 0; + fastparm = 0; + nomonsters = 0; + consoleplayer = 0; + } + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + + if (playeringame[1] || m_check_parm("-solo-net") > 0 || + m_check_parm("-netdemo") > 0) + { + netgame = true; + netdemo = true; + } + + /* don't spend a lot of time in loadlevel */ + + precache = false; + g_init_new(skill, episode, map); + precache = true; + starttime = i_get_time(); + + usergame = false; + demoplayback = true; +} + +/* g_playerfinishlevel + * Can when a player completes a level. + */ + +static void g_player_finish_level(int player) +{ + player_t *p; + + p = &players[player]; + + memset(p->powers, 0, sizeof(p->powers)); + memset(p->cards, 0, sizeof(p->cards)); + p->mo->flags &= ~MF_SHADOW; /* cancel invisibility */ + p->extralight = 0; /* cancel gun flashes */ + p->fixedcolormap = 0; /* cancel ir gogles */ + p->damagecount = 0; /* no palette changes */ + p->bonuscount = 0; +} + +static void g_do_completed(void) +{ + int i; + + gameaction = ga_nothing; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + g_player_finish_level(i); /* take away cards and stuff */ + } + } + + if (automapactive) am_stop(); + + if (gamemode != commercial) + { + /* Chex Quest ends after 5 levels, rather than 8. */ + + if (gameversion == exe_chex) + { + if (gamemap == 5) + { + gameaction = ga_victory; + return; + } + } + else + { + switch (gamemap) + { + case 8: + gameaction = ga_victory; + return; + case 9: + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + break; + } + } + } + + /* #if 0 Hmmm - why? */ + + if ((gamemap == 8) && (gamemode != commercial)) + { + /* victory */ + + gameaction = ga_victory; + return; + } + + if ((gamemap == 9) && (gamemode != commercial)) + { + /* exit secret level */ + + for (i = 0; i < MAXPLAYERS; i++) + players[i].didsecret = true; + } + + /* #endif */ + + wminfo.didsecret = players[consoleplayer].didsecret; + wminfo.epsd = gameepisode - 1; + wminfo.last = gamemap - 1; + + /* wminfo.next is 0 biased, unlike gamemap */ + + if (gamemode == commercial) + { + if (secretexit) switch (gamemap) + { + case 15: + wminfo.next = 30; + break; + case 31: + wminfo.next = 31; + break; + } + else + switch (gamemap) + { + case 31: + case 32: + wminfo.next = 15; + break; + default: + wminfo.next = gamemap; + } + } + else + { + if (secretexit) + wminfo.next = 8; /* go to secret level */ + else if (gamemap == 9) + { + /* returning from secret level */ + + switch (gameepisode) + { + case 1: + wminfo.next = 3; + break; + case 2: + wminfo.next = 5; + break; + case 3: + wminfo.next = 6; + break; + case 4: + wminfo.next = 2; + break; + } + } + else + wminfo.next = gamemap; /* go to next level */ + } + + wminfo.maxkills = totalkills; + wminfo.maxitems = totalitems; + wminfo.maxsecret = totalsecret; + wminfo.maxfrags = 0; + + /* Set par time. Exceptions are added for purposes of + * statcheck regression testing. + */ + + if (gamemode == commercial) + { + /* map33 reads its par time from beyond the cpars[] array */ + + if (gamemap == 33) + { + int cpars32; + + memcpy(&cpars32, (GAMMALVL0), sizeof(int)); + cpars32 = LONG(cpars32); + + wminfo.partime = TICRATE * cpars32; + } + else + { + wminfo.partime = TICRATE * cpars[gamemap - 1]; + } + } + + /* Doom episode 4 doesn't have a par time, so this + * overflows into the cpars array. + */ + + else if (gameepisode < 4) + { + if (gameversion == exe_chex && gameepisode == 1 && gamemap < 6) + { + wminfo.partime = TICRATE * chexpars[gamemap]; + } + else + { + wminfo.partime = TICRATE * pars[gameepisode][gamemap]; + } + } + else + { + wminfo.partime = TICRATE * cpars[gamemap]; + } + + wminfo.pnum = consoleplayer; + + for (i = 0; i < MAXPLAYERS; i++) + { + wminfo.plyr[i].in = playeringame[i]; + wminfo.plyr[i].skills = players[i].killcount; + wminfo.plyr[i].sitems = players[i].itemcount; + wminfo.plyr[i].ssecret = players[i].secretcount; + wminfo.plyr[i].stime = leveltime; + memcpy(wminfo.plyr[i].frags, players[i].frags, + sizeof(wminfo.plyr[i].frags)); + } + + gamestate = GS_INTERMISSION; + viewactive = false; + automapactive = false; + + stat_copy(&wminfo); + + wi_start(&wminfo); +} + +static void g_do_world_done(void) +{ + gamestate = GS_LEVEL; + gamemap = wminfo.next + 1; + g_do_load_level(); + gameaction = ga_nothing; + viewactive = true; +} + +/* Increase the size of the demo buffer to allow unlimited demos */ + +static void increase_demo_buffer(void) +{ + int current_length; + byte *new_demobuffer; + byte *new_demop; + int new_length; + + /* Find the current size */ + + current_length = demoend - demobuffer; + + /* Generate a new buffer twice the size */ + + new_length = current_length * 2; + + new_demobuffer = z_malloc(new_length, PU_STATIC, 0); + new_demop = new_demobuffer + (demo_p - demobuffer); + + /* Copy over the old data */ + + memcpy(new_demobuffer, demobuffer, current_length); + + /* Free the old buffer and point the demo pointers at the new buffer. */ + + z_free(demobuffer); + + demobuffer = new_demobuffer; + demo_p = new_demop; + demoend = demobuffer + new_length; +} + +/* DEMO RECORDING */ + +static void g_read_demo_ticcmd(ticcmd_t *cmd) +{ + if (*demo_p == DEMOMARKER) + { + /* end of demo data stream */ + + g_check_demo_status(); + return; + } + + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + + /* If this is a longtics demo, read back in higher resolution */ + + if (longtics) + { + cmd->angleturn = *demo_p++; + cmd->angleturn |= (*demo_p++) << 8; + } + else + { + cmd->angleturn = ((unsigned char)*demo_p++) << 8; + } + + cmd->buttons = (unsigned char)*demo_p++; +} + +static void g_write_demo_ticcmd(ticcmd_t *cmd) +{ + byte *demo_start; + + if (gamekeydown[key_demo_quit]) /* press q to end demo recording */ + g_check_demo_status(); + + demo_start = demo_p; + + *demo_p++ = cmd->forwardmove; + *demo_p++ = cmd->sidemove; + + /* If this is a longtics demo, record in higher resolution */ + + if (longtics) + { + *demo_p++ = (cmd->angleturn & 0xff); + *demo_p++ = (cmd->angleturn >> 8) & 0xff; + } + else + { + *demo_p++ = cmd->angleturn >> 8; + } + + *demo_p++ = cmd->buttons; + + /* reset demo pointer back */ + + demo_p = demo_start; + + if (demo_p > demoend - 16) + { + if (vanilla_demo_limit) + { + /* no more space */ + + g_check_demo_status(); + return; + } + else + { + /* Vanilla demo limit disabled: unlimited + * demo lengths! + */ + + increase_demo_buffer(); + } + } + + g_read_demo_ticcmd(cmd); /* make SURE it is exactly the same */ +} + +/* Called at the start. + * Called by the game initialization functions. + */ + +#if 0 /* UNUSED */ +static void g_init_player(int player) +{ + /* clear everything else to defaults */ + + g_player_reborn(player); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* g_build_ticcmd + * Builds a ticcmd from all of the available inputs + * or reads it from the demo buffer. + * If recording a demo, write it out + */ + +void g_build_ticcmd(ticcmd_t *cmd, int maketic) +{ + int i; + boolean strafe; + boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + + memset(cmd, 0, sizeof(ticcmd_t)); + + cmd->consistency = + consistency[consoleplayer] + [maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] || + joybuttons[joybstrafe]; + + /* fraggle: support the old "joyb_speed = 31" hack which + * allowed an autorun effect + */ + + speed = key_speed >= NUMKEYS || joybspeed >= MAX_JOY_BUTTONS || + gamekeydown[key_speed] || joybuttons[joybspeed] || + mousebuttons[mousebspeed]; + + forward = side = 0; + + /* use two stage accelerative turning + * on the keyboard and joystick + */ + + if (joyxmove < 0 || joyxmove > 0 || gamekeydown[key_right] || + gamekeydown[key_left] || mousebuttons[mousebturnright] || + mousebuttons[mousebturnleft]) + { + turnheld += ticdup; + } + else + { + turnheld = 0; + } + + if (turnheld < SLOWTURNTICS) + { + tspeed = 2; /* slow turn */ + } + else + { + tspeed = speed; + } + + /* let movement keys cancel each other out */ + + if (strafe) + { + if (gamekeydown[key_right] || mousebuttons[mousebturnright]) + { + side += sidemove[speed]; + } + + if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) + { + side -= sidemove[speed]; + } + + if (use_analog && joyxmove) + { + joyxmove = joyxmove * joystick_move_sensitivity / 10; + joyxmove = (joyxmove > FRACUNIT) ? FRACUNIT : joyxmove; + joyxmove = (joyxmove < -FRACUNIT) ? -FRACUNIT : joyxmove; + side += fixed_mul(sidemove[speed], joyxmove); + } + else if (joystick_move_sensitivity) + { + if (joyxmove > 0) side += sidemove[speed]; + if (joyxmove < 0) side -= sidemove[speed]; + } + } + else + { + if (gamekeydown[key_right] || mousebuttons[mousebturnright]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left] || mousebuttons[mousebturnleft]) + cmd->angleturn += angleturn[tspeed]; + if (use_analog && joyxmove) + { + /* Cubic response curve allows for finer control when stick + * deflection is small. + */ + + joyxmove = fixed_mul(fixed_mul(joyxmove, joyxmove), joyxmove); + joyxmove = joyxmove * joystick_turn_sensitivity / 10; + cmd->angleturn -= fixed_mul(angleturn[1], joyxmove); + } + else if (joystick_turn_sensitivity) + { + if (joyxmove > 0) cmd->angleturn -= angleturn[tspeed]; + if (joyxmove < 0) cmd->angleturn += angleturn[tspeed]; + } + } + + if (gamekeydown[key_up]) + { + forward += forwardmove[speed]; + } + + if (gamekeydown[key_down]) + { + forward -= forwardmove[speed]; + } + + if (use_analog && joyymove) + { + joyymove = joyymove * joystick_move_sensitivity / 10; + joyymove = (joyymove > FRACUNIT) ? FRACUNIT : joyymove; + joyymove = (joyymove < -FRACUNIT) ? -FRACUNIT : joyymove; + forward -= fixed_mul(forwardmove[speed], joyymove); + } + else if (joystick_move_sensitivity) + { + if (joyymove < 0) forward += forwardmove[speed]; + if (joyymove > 0) forward -= forwardmove[speed]; + } + + if (gamekeydown[key_strafeleft] || joybuttons[joybstrafeleft] || + mousebuttons[mousebstrafeleft]) + { + side -= sidemove[speed]; + } + + if (gamekeydown[key_straferight] || joybuttons[joybstraferight] || + mousebuttons[mousebstraferight]) + { + side += sidemove[speed]; + } + + if (use_analog && joystrafemove) + { + joystrafemove = joystrafemove * joystick_move_sensitivity / 10; + joystrafemove = (joystrafemove > FRACUNIT) ? FRACUNIT : joystrafemove; + joystrafemove = + (joystrafemove < -FRACUNIT) ? -FRACUNIT : joystrafemove; + side += fixed_mul(sidemove[speed], joystrafemove); + } + else if (joystick_move_sensitivity) + { + if (joystrafemove < 0) side -= sidemove[speed]; + if (joystrafemove > 0) side += sidemove[speed]; + } + + /* buttons */ + + cmd->chatchar = hu_dequeue_chat_char(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire] || + joybuttons[joybfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse]) + { + cmd->buttons |= BT_USE; + dclicks = 0; /* clear double clicks if hit use button */ + } + + /* If the previous or next weapon button is pressed, the + * next_weapon variable is set to change weapons when + * we generate a ticcmd. Choose a new weapon. + */ + + if (gamestate == GS_LEVEL && next_weapon != 0) + { + i = g_next_weapon(next_weapon); + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + } + else + { + /* Check weapon keys. */ + + for (i = 0; i < arrlen(weapon_keys); ++i) + { + int key = *weapon_keys[i]; + + if (gamekeydown[key]) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= i << BT_WEAPONSHIFT; + break; + } + } + } + + next_weapon = 0; + + /* mouse */ + + if (mousebuttons[mousebforward]) + { + forward += forwardmove[speed]; + } + + if (mousebuttons[mousebbackward]) + { + forward -= forwardmove[speed]; + } + + if (dclick_use) + { + /* forward double click */ + + if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1) + { + dclickstate = mousebuttons[mousebforward]; + if (dclickstate) dclicks++; + if (dclicks == 2) + { + cmd->buttons |= BT_USE; + dclicks = 0; + } + else + dclicktime = 0; + } + else + { + dclicktime += ticdup; + if (dclicktime > 20) + { + dclicks = 0; + dclickstate = 0; + } + } + + /* strafe double click */ + + bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe]; + if (bstrafe != dclickstate2 && dclicktime2 > 1) + { + dclickstate2 = bstrafe; + if (dclickstate2) dclicks2++; + if (dclicks2 == 2) + { + cmd->buttons |= BT_USE; + dclicks2 = 0; + } + else + dclicktime2 = 0; + } + else + { + dclicktime2 += ticdup; + if (dclicktime2 > 20) + { + dclicks2 = 0; + dclickstate2 = 0; + } + } + } + + forward += mousey; + + if (strafe) + side += mousex * 2; + else + cmd->angleturn -= mousex * 0x8; + + if (mousex == 0) + { + /* No movement in the previous frame */ + + testcontrols_mousespeed = 0; + } + + mousex = mousey = 0; + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + cmd->forwardmove += forward; + cmd->sidemove += side; + + /* special buttons */ + + if (sendpause) + { + sendpause = false; + cmd->buttons = BT_SPECIAL | BTS_PAUSE; + } + + if (sendsave) + { + sendsave = false; + cmd->buttons = + BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT); + } + + /* low-res turning */ + + if (lowres_turn) + { + static signed short carry = 0; + signed short desired_angleturn; + + desired_angleturn = cmd->angleturn + carry; + + /* round angleturn to the nearest 256 unit boundary + * for recording demos with single byte values for turn + */ + + cmd->angleturn = (desired_angleturn + 128) & 0xff00; + + /* Carry forward the error from the reduced resolution to the + * next tic, so that successive small movements can accumulate. + */ + + carry = desired_angleturn - cmd->angleturn; + } +} + +/* g_responder + * Get info needed to make ticcmd_ts for the players. + */ + +boolean g_responder(event_t *ev) +{ + /* allow spy mode changes even during the demo */ + + if (gamestate == GS_LEVEL && ev->type == ev_keydown && + ev->data1 == key_spy && (singledemo || !deathmatch)) + { + /* spy mode */ + + do + { + displayplayer++; + if (displayplayer == MAXPLAYERS) displayplayer = 0; + } + while (!playeringame[displayplayer] && displayplayer != consoleplayer); + return true; + } + + /* any other key pops up menu if in demos */ + + if (gameaction == ga_nothing && !singledemo && + (demoplayback || gamestate == GS_DEMOSCREEN)) + { + if (ev->type == ev_keydown || (ev->type == ev_mouse && ev->data1) || + (ev->type == ev_joystick && ev->data1)) + { + m_start_control_panel(); + joywait = i_get_time() + 5; + return true; + } + + return false; + } + + if (gamestate == GS_LEVEL) + { +#if 0 + if (devparm && ev->type == ev_keydown && ev->data1 == ';') + { + g_death_match_spawn_player(0); + return true; + } + +#endif + if (hu_responder(ev)) return true; /* chat ate the event */ + if (st_responder(ev)) return true; /* status window ate it */ + if (am_responder(ev)) return true; /* automap ate it */ + } + + if (gamestate == GS_FINALE) + { + if (f_responder(ev)) return true; /* finale ate the event */ + } + + if (testcontrols && ev->type == ev_mouse) + { + /* If we are invoked by setup to test the controls, save the + * mouse speed so that we can display it on-screen. + * Perform a low pass filter on this so that the thermometer + * appears to move smoothly. + */ + + testcontrols_mousespeed = abs(ev->data2); + } + + /* If the next/previous weapon keys are pressed, set the next_weapon + * variable to change weapons when the next ticcmd is generated. + */ + + if (ev->type == ev_keydown && ev->data1 == key_prevweapon) + { + next_weapon = -1; + } + else if (ev->type == ev_keydown && ev->data1 == key_nextweapon) + { + next_weapon = 1; + } + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) + { + sendpause = true; + } + else if (ev->data1 < NUMKEYS) + { + gamekeydown[ev->data1] = true; + } + + return true; /* eat key down events */ + + case ev_keyup: + if (ev->data1 < NUMKEYS) gamekeydown[ev->data1] = false; + return false; /* always let key up events filter down */ + + case ev_mouse: + set_mouse_buttons(ev->data1); + mousex = ev->data2 * (g_mouse_sensitivity + 5) / 10; + mousey = ev->data3 * (g_mouse_sensitivity + 5) / 10; + return true; /* eat events */ + + case ev_joystick: + set_joy_buttons(ev->data1); + joyxmove = ev->data2; + joyymove = ev->data3; + joystrafemove = ev->data4; + return true; /* eat events */ + + default: + break; + } + + return false; +} + +/* g_ticker + * Make ticcmd_ts for the players. + */ + +void g_ticker(void) +{ + int i; + int buf; + ticcmd_t *cmd; + + /* do player reborns if needed */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].playerstate == PST_REBORN) + { + g_do_reborn(i); + } + } + + /* do things to change the game state */ + + while (gameaction != ga_nothing) + { + switch (gameaction) + { + case ga_loadlevel: + g_do_load_level(); + break; + case ga_newgame: + g_do_new_game(); + break; + case ga_loadgame: + g_do_load_game(); + break; + case ga_savegame: + g_do_save_game(); + break; + case ga_playdemo: + g_do_play_demo(); + break; + case ga_completed: + g_do_completed(); + break; + case ga_victory: + f_start_finale(); + break; + case ga_worlddone: + g_do_world_done(); + break; + case ga_screenshot: + v_screenshot("DOOM%02i.%s"); + players[consoleplayer].message = ("screen shot"); + gameaction = ga_nothing; + break; + case ga_nothing: + break; + } + } + + /* get commands, check consistency, and build new consistency check */ + + buf = (gametic / ticdup) % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + cmd = &players[i].cmd; + + memcpy(cmd, &netcmds[i], sizeof(ticcmd_t)); + + if (demoplayback) g_read_demo_ticcmd(cmd); + if (demorecording) g_write_demo_ticcmd(cmd); + + /* check for turbo cheats + * check ~ 4 seconds whether to display the turbo message. + * store if the turbo threshold was exceeded in any tics + * over the past 4 seconds. offset the checking period + * for each player so messages are not displayed at the + * same time. + */ + + if (cmd->forwardmove > TURBOTHRESHOLD) + { + turbodetected[i] = true; + } + + if ((gametic & 31) == 0 && ((gametic >> 5) % MAXPLAYERS) == i && + turbodetected[i]) + { + static char turbomessage[80]; + snprintf(turbomessage, sizeof(turbomessage), "%s is turbo!", + player_names[i]); + players[consoleplayer].message = turbomessage; + turbodetected[i] = false; + } + + if (netgame && !netdemo && !(gametic % ticdup)) + { + if (gametic > CONFIG_GAMES_NXDOOM_NET_BACKUPTICS && + consistency[i][buf] != cmd->consistency) + { + i_error("consistency failure (%i should be %i)", + cmd->consistency, consistency[i][buf]); + } + + if (players[i].mo) + consistency[i][buf] = players[i].mo->x; + else + consistency[i][buf] = rndindex; + } + } + } + + /* check for special buttons */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (players[i].cmd.buttons & BT_SPECIAL) + { + switch (players[i].cmd.buttons & BT_SPECIALMASK) + { + case BTS_PAUSE: + paused ^= 1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (paused) + s_pause_sound(); + else + s_resume_sound(); + break; +#endif + + case BTS_SAVEGAME: + if (!savedescription[0]) + { + m_str_copy(savedescription, "NET GAME", + sizeof(savedescription)); + } + + savegameslot = (players[i].cmd.buttons & BTS_SAVEMASK) >> + BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + } + } + } + } + + /* Have we just finished displaying an intermission screen? */ + + if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION) + { + wi_end(); + } + + oldgamestate = gamestate; + + /* do main actions */ + + switch (gamestate) + { + case GS_LEVEL: + p_ticker(); + st_ticker(); + am_ticker(); + hu_ticker(); + break; + + case GS_INTERMISSION: + wi_ticker(); + break; + + case GS_FINALE: + f_ticker(); + break; + + case GS_DEMOSCREEN: + d_page_ticker(); + break; + } +} + +/* PLAYER STRUCTURE FUNCTIONS + * also see p_spawn_player in P_Things + */ + +/* g_player_reborn + * Called after a player dies almost everything is cleared and initialized + */ + +void g_player_reborn(int player) +{ + player_t *p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + memcpy(frags, players[player].frags, sizeof(frags)); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + memset(p, 0, sizeof(*p)); + + memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + + p->usedown = p->attackdown = true; /* don't do anything immediately */ + p->playerstate = PST_LIVE; + p->health = deh_initial_health; /* Use dehacked value */ + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = deh_initial_bullets; + + for (i = 0; i < NUMAMMO; i++) + p->maxammo[i] = maxammo[i]; +} + +/* g_death_match_spawn_player + * Spawns a player at one of the random death match spots + * called at level load and each death + */ + +void g_death_match_spawn_player(int playernum) +{ + int i; + int j; + int selections; + + selections = deathmatch_p - deathmatchstarts; + if (selections < 4) + i_error("Only %i deathmatch spots, 4 required", selections); + + for (j = 0; j < 20; j++) + { + i = p_random() % selections; + if (g_check_spot(playernum, &deathmatchstarts[i])) + { + deathmatchstarts[i].type = playernum + 1; + p_spawn_player(&deathmatchstarts[i]); + return; + } + } + + /* no good spot, so the player will probably get stuck */ + + p_spawn_player(&playerstarts[playernum]); +} + +void g_screenshot(void) +{ + gameaction = ga_screenshot; +} + +void g_exit_level(void) +{ + secretexit = false; + gameaction = ga_completed; +} + +/* Here's for the german edition. */ + +void g_secret_exit_level(void) +{ + /* IF NO WOLF3D LEVELS, NO SECRET EXIT! */ + + if ((gamemode == commercial) && (w_check_num_for_name("map31") < 0)) + secretexit = false; + else + secretexit = true; + gameaction = ga_completed; +} + +void g_world_done(void) +{ + gameaction = ga_worlddone; + + if (secretexit) players[consoleplayer].didsecret = true; + + if (gamemode == commercial) + { + switch (gamemap) + { + case 15: + case 31: + if (!secretexit) break; + case 6: + case 11: + case 20: + case 30: + f_start_finale(); + break; + } + } +} + +/* g_initfromsavegame + * Can be called by the startup code or the menu task. + */ + +void g_load_game(char *name) +{ + m_str_copy(savename, name, sizeof(savename)); + gameaction = ga_loadgame; +} + +void g_do_load_game(void) +{ + int savedleveltime; + + gameaction = ga_nothing; + + save_stream = fopen(savename, "rb"); + + if (save_stream == NULL) + { + i_error("Could not load savegame %s", savename); + } + + savegame_error = false; + + if (!p_read_save_game_header()) + { + fclose(save_stream); + return; + } + + savedleveltime = leveltime; + + /* load a base level */ + + g_init_new(gameskill, gameepisode, gamemap); + + leveltime = savedleveltime; + + /* dearchive all the modifications */ + + p_unarchive_players(); + p_unarchive_world(); + p_unarchive_thinkers(); + p_unarchive_specials(); + + if (!p_read_save_game_eof()) i_error("Bad savegame"); + + fclose(save_stream); + + if (setsizeneeded) r_execute_set_view_size(); + + /* draw the pattern into the back screen */ + + r_fill_back_screen(); +} + +/* g_save_game + * Called by the menu task. + * Description is a 24 byte text string + */ + +void g_save_game(int slot, char *description) +{ + savegameslot = slot; + m_str_copy(savedescription, description, sizeof(savedescription)); + sendsave = true; +} + +/* g_init_new + * Can be called by the startup code or the menu task, + * consoleplayer, displayplayer, playeringame[] should be set. + */ + +void g_deferred_init_new(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + +void g_init_new(skill_t skill, int episode, int map) +{ + const char *skytexturename; + int i; + + if (paused) + { + paused = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_resume_sound(); +#endif + } + + /* Note: This commented-out block of code was added at some point + * between the DOS version(s) and the Doom source release. It isn't + * found in disassemblies of the DOS version and causes IDCLEV and + * the -warp command line parameter to behave differently. + * This is left here for posterity. + * This was quite messy with SPECIAL and commented parts. + * Supposedly hacks to make the latest edition work. + * It might not work properly. + * + * if (episode < 1) + * episode = 1; + * if ( gamemode == retail ) + * { + * if (episode > 4) + * episode = 4; + * } + * else if ( gamemode == shareware ) + * { + * if (episode > 1) + * episode = 1; // only start episode 1 on shareware + * } + * else + * { + * if (episode > 3) + * episode = 3; + * } + */ + + if (skill > sk_nightmare) skill = sk_nightmare; + + if (gameversion >= exe_ultimate) + { + if (episode == 0) + { + episode = 4; + } + } + else + { + if (episode < 1) + { + episode = 1; + } + + if (episode > 3) + { + episode = 3; + } + } + + if (episode > 1 && gamemode == shareware) + { + episode = 1; + } + + if (map < 1) map = 1; + + if ((map > 9) && (gamemode != commercial)) map = 9; + + m_clear_random(); + + if (skill == sk_nightmare || respawnparm) + respawnmonsters = true; + else + respawnmonsters = false; + + if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare)) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics >>= 1; + mobjinfo[MT_BRUISERSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20 * FRACUNIT; + } + else if (skill != sk_nightmare && gameskill == sk_nightmare) + { + for (i = S_SARG_RUN1; i <= S_SARG_PAIN2; i++) + states[i].tics <<= 1; + mobjinfo[MT_BRUISERSHOT].speed = 15 * FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 10 * FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 10 * FRACUNIT; + } + + /* force players to be initialized upon first level load */ + + for (i = 0; i < MAXPLAYERS; i++) + players[i].playerstate = PST_REBORN; + + usergame = true; /* will be set false if a demo */ + paused = false; + demoplayback = false; + automapactive = false; + viewactive = true; + gameepisode = episode; + gamemap = map; + gameskill = skill; + + /* Set the sky to use. + * + * Note: This IS broken, but it is how Vanilla Doom behaves. + * See http: *doomwiki.org/wiki/Sky_never_changes_in_Doom_II. + * + * Because we set the sky here at the start of a game, not at the + * start of a level, the sky texture never changes unless we + * restore from a saved game. This was fixed before the Doom + * source release, but this IS the way Vanilla DOS Doom behaves. + */ + + if (gamemode == commercial) + { + skytexturename = ("SKY3"); + skytexture = r_texture_num_for_name(skytexturename); + if (gamemap < 21) + { + skytexturename = (gamemap < 12 ? "SKY1" : "SKY2"); + skytexture = r_texture_num_for_name(skytexturename); + } + } + else + { + switch (gameepisode) + { + default: + case 1: + skytexturename = "SKY1"; + break; + case 2: + skytexturename = "SKY2"; + break; + case 3: + skytexturename = "SKY3"; + break; + case 4: /* Special Edition sky */ + skytexturename = "SKY4"; + break; + } + + skytexturename = (skytexturename); + skytexture = r_texture_num_for_name(skytexturename); + } + + g_do_load_level(); +} + +void g_record_demo(const char *name) +{ + size_t demoname_size; + int i; + int maxsize; + + usergame = false; + demoname_size = strlen(name) + 5; + demoname = z_malloc(demoname_size, PU_STATIC, NULL); + snprintf(demoname, demoname_size, "%s.lmp", name); + maxsize = 0x20000; + + /* @arg + * @category demo + * @vanilla + * + * Specify the demo buffer size (KiB) + */ + + i = m_check_parm_with_args("-maxdemo", 1); + if (i) maxsize = atoi(myargv[i + 1]) * 1024; + demobuffer = z_malloc(maxsize, PU_STATIC, NULL); + demoend = demobuffer + maxsize; + + demorecording = true; +} + +/* Get the demo version code appropriate for the version set in gameversion. + */ + +int g_vanilla_version_code(void) +{ + switch (gameversion) + { + case exe_doom_1_666: + return 106; + case exe_doom_1_7: + return 107; + case exe_doom_1_8: + return 108; + case exe_doom_1_9: + default: /* All other versions are variants on v1.9: */ + return 109; + } +} + +void g_begin_recording(void) +{ + int i; + + demo_p = demobuffer; + + /* @category demo + * + * Record a high resolution "Doom 1.91" demo. + */ + + longtics = d_non_vanilla_record(m_parm_exists("-longtics"), + "Doom 1.91 demo format"); + + /* If not recording a longtics demo, record in low res */ + + lowres_turn = !longtics; + + if (longtics) + { + *demo_p++ = DOOM_191_VERSION; + } + else if (gameversion > exe_doom_1_2) + { + *demo_p++ = g_vanilla_version_code(); + } + + *demo_p++ = gameskill; + *demo_p++ = gameepisode; + *demo_p++ = gamemap; + if (longtics || gameversion > exe_doom_1_2) + { + *demo_p++ = deathmatch; + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + *demo_p++ = consoleplayer; + } + + for (i = 0; i < MAXPLAYERS; i++) + *demo_p++ = playeringame[i]; +} + +void g_defered_play_demo(const char *name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +void g_time_demo(char *name) +{ + /* @category video + * @vanilla + * + * Disable rendering the screen entirely. + */ + + nodrawers = m_check_parm("-nodraw"); + + timingdemo = true; + singletics = true; + + defdemoname = name; + gameaction = ga_playdemo; +} + +/* g_check_demo_status + * + * Called after a death or level completion to allow demos to be cleaned up + * Returns true if a new demo loop action will take place + */ + +boolean g_check_demo_status(void) +{ + int endtime; + + if (timingdemo) + { + float fps; + int realtics; + + endtime = i_get_time(); + realtics = endtime - starttime; + fps = ((float)gametic * TICRATE) / realtics; + + /* Prevent recursive calls */ + + timingdemo = false; + demoplayback = false; + + i_error("timed %i gametics in %i realtics (%f fps)", gametic, realtics, + fps); + } + + if (demoplayback) + { + w_release_lump_name(defdemoname); + demoplayback = false; + netdemo = false; + netgame = false; + deathmatch = false; + playeringame[1] = playeringame[2] = playeringame[3] = 0; + respawnparm = false; + fastparm = false; + nomonsters = false; + consoleplayer = 0; + + if (singledemo) + i_quit(); + else + d_advance_demo(); + + return true; + } + + if (demorecording) + { + *demo_p++ = DEMOMARKER; + m_write_file(demoname, demobuffer, demo_p - demobuffer); + z_free(demobuffer); + demorecording = false; + i_error("Demo %s recorded", demoname); + } + + return false; +} diff --git a/games/NXDoom/src/doom/g_game.h b/games/NXDoom/src/doom/g_game.h new file mode 100644 index 000000000..80c3fe2ee --- /dev/null +++ b/games/NXDoom/src/doom/g_game.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/g_game.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Duh. + * + ****************************************************************************/ + +#ifndef __G_GAME__ +#define __G_GAME__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "d_ticcmd.h" +#include "doomdef.h" +#include "m_fixed.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int vanilla_savegame_limit; +extern int vanilla_demo_limit; + +extern fixed_t forwardmove[2]; +extern fixed_t sidemove[2]; + +extern boolean sendpause; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* GAME */ + +void g_death_match_spawn_player(int playernum); + +void g_init_new(skill_t skill, int episode, int map); + +/* Can be called by the startup code or m_responder. + * A normal game starts at map 1, + * but a warp test can start elsewhere + */ + +void g_deferred_init_new(skill_t skill, int episode, int map); + +void g_defered_play_demo(const char *demo); + +/* Can be called by the startup code or m_responder, + * calls p_setup_level or W_EnterWorld. + */ + +void g_load_game(char *name); + +void g_do_load_game(void); + +/* Called by m_responder. */ + +void g_save_game(int slot, char *description); + +/* Only called by startup code. */ + +void g_record_demo(const char *name); + +void g_begin_recording(void); + +void g_play_demo(char *name); +void g_time_demo(char *name); +boolean g_check_demo_status(void); + +void g_exit_level(void); +void g_secret_exit_level(void); + +void g_world_done(void); + +/* Read current data from inputs and build a player movement command. */ + +void g_build_ticcmd(ticcmd_t *cmd, int maketic); + +void g_ticker(void); +boolean g_responder(event_t *ev); + +void g_screenshot(void); + +int g_vanilla_version_code(void); + +#endif /* __G_GAME__ */ diff --git a/games/NXDoom/src/doom/hu_lib.c b/games/NXDoom/src/doom/hu_lib.c new file mode 100644 index 000000000..cfa962331 --- /dev/null +++ b/games/NXDoom/src/doom/hu_lib.c @@ -0,0 +1,318 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_lib.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: heads-up text and input code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "doomstat.h" + +#include "i_swap.h" +#include "v_video.h" + +#include "hu_lib.h" +#include "r_draw.h" +#include "r_local.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* boolean : whether the screen is always erased */ + +#define noterased viewwindowx + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void hu_lib_clear_text(hu_textline_t *t) +{ + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + +void hu_lib_init_text_line(hu_textline_t *t, int x, int y, patch_t **f, + int sc) +{ + t->x = x; + t->y = y; + t->f = f; + t->sc = sc; + hu_lib_clear_text(t); +} + +boolean hu_lib_add_char_to_text_line(hu_textline_t *t, char ch) +{ + if (t->len == HU_MAXLINELENGTH) + { + return false; + } + else + { + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +boolean hu_lib_del_char_from_text_line(hu_textline_t *t) +{ + if (!t->len) + { + return false; + } + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +void hu_lib_draw_text_line(hu_textline_t *l, boolean drawcursor) +{ + int i; + int w; + int x; + unsigned char c; + + /* draw the new stuff */ + + x = l->x; + for (i = 0; i < l->len; i++) + { + c = toupper(l->l[i]); + if (c != ' ' && c >= l->sc && c <= '_') + { + w = SHORT(l->f[c - l->sc]->width); + if (x + w > SCREENWIDTH) break; + v_draw_patch_direct(x, l->y, l->f[c - l->sc]); + x += w; + } + else + { + x += 4; + if (x >= SCREENWIDTH) break; + } + } + + /* draw the cursor if requested */ + + if (drawcursor && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) + { + v_draw_patch_direct(x, l->y, l->f['_' - l->sc]); + } +} + +/* sorta called by hu_erase and just better darn get things straight */ + +void hu_lib_erase_text_line(hu_textline_t *l) +{ + int lh; + int y; + int yoffset; + + /* Only erases when NOT in automap and the screen is reduced, and the text + * must either need updating or refreshing (because of a recent change back + * from the automap) + */ + + if (!automapactive && viewwindowx && l->needsupdate) + { + lh = SHORT(l->f[0]->height) + 1; + for (y = l->y, yoffset = y * SCREENWIDTH; y < l->y + lh; + y++, yoffset += SCREENWIDTH) + { + if (y < viewwindowy || y >= viewwindowy + viewheight) + r_video_erase(yoffset, SCREENWIDTH); /* erase entire line */ + else + { + r_video_erase(yoffset, viewwindowx); /* erase left border */ + r_video_erase(yoffset + viewwindowx + viewwidth, viewwindowx); + + /* erase right border */ + } + } + } + + if (l->needsupdate) l->needsupdate--; +} + +void hu_lib_init_stext(hu_stext_t *s, int x, int y, int h, patch_t **font, + int startchar, boolean *on) +{ + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i = 0; i < h; i++) + hu_lib_init_text_line(&s->l[i], x, y - i * (SHORT(font[0]->height) + 1), + font, startchar); +} + +void hu_lib_add_line_to_stext(hu_stext_t *s) +{ + int i; + + /* add a clear line */ + + if (++s->cl == s->h) s->cl = 0; + hu_lib_clear_text(&s->l[s->cl]); + + /* everything needs updating */ + + for (i = 0; i < s->h; i++) + { + s->l[i].needsupdate = 4; + } +} + +void hu_lib_add_messsage_to_stext(hu_stext_t *s, const char *prefix, + const char *msg) +{ + hu_lib_add_line_to_stext(s); + + if (prefix) + { + while (*prefix) + { + hu_lib_add_char_to_text_line(&s->l[s->cl], *(prefix++)); + } + } + + while (*msg) + { + hu_lib_add_char_to_text_line(&s->l[s->cl], *(msg++)); + } +} + +void hu_lib_draw_stext(hu_stext_t *s) +{ + int i; + int idx; + hu_textline_t *l; + + if (!*s->on) return; /* if not on, don't draw */ + + /* draw everything */ + + for (i = 0; i < s->h; i++) + { + idx = s->cl - i; + if (idx < 0) idx += s->h; /* handle queue of lines */ + + l = &s->l[idx]; + + /* need a decision made here on whether to skip the draw */ + + hu_lib_draw_text_line(l, false); /* no cursor, please */ + } +} + +void hu_lib_erase_stext(hu_stext_t *s) +{ + int i; + + for (i = 0; i < s->h; i++) + { + if (s->laston && !*s->on) s->l[i].needsupdate = 4; + hu_lib_erase_text_line(&s->l[i]); + } + + s->laston = *s->on; +} + +void hu_lib_init_itext(hu_itext_t *it, int x, int y, patch_t **font, + int startchar, boolean *on) +{ + it->lm = 0; /* default left margin is start of text */ + it->on = on; + it->laston = true; + hu_lib_init_text_line(&it->l, x, y, font, startchar); +} + +/* The following deletion routines adhere to the left margin restriction */ + +void hu_lib_del_char_from_itext(hu_itext_t *it) +{ + if (it->l.len != it->lm) hu_lib_del_char_from_text_line(&it->l); +} + +/* Resets left margin as well */ + +void hu_lib_reset_itext(hu_itext_t *it) +{ + it->lm = 0; + hu_lib_clear_text(&it->l); +} + +/* wrapper function for handling general keyed input. + * returns true if it ate the key + */ + +boolean hu_lib_key_in_itext(hu_itext_t *it, unsigned char ch) +{ + ch = toupper(ch); + + if (ch >= ' ' && ch <= '_') + { + hu_lib_add_char_to_text_line(&it->l, (char)ch); + } + else if (ch == KEY_BACKSPACE) + { + hu_lib_del_char_from_itext(it); + } + else if (ch != KEY_ENTER) + { + return false; /* did not eat key */ + } + + return true; /* ate the key */ +} + +void hu_lib_draw_itext(hu_itext_t *it) +{ + hu_textline_t *l = &it->l; + + if (!*it->on) return; + hu_lib_draw_text_line(l, true); /* draw the line w/ cursor */ +} + +void hu_lib_erase_itext(hu_itext_t *it) +{ + if (it->laston && !*it->on) it->l.needsupdate = 4; + hu_lib_erase_text_line(&it->l); + it->laston = *it->on; +} diff --git a/games/NXDoom/src/doom/hu_lib.h b/games/NXDoom/src/doom/hu_lib.h new file mode 100644 index 000000000..e9ab12a24 --- /dev/null +++ b/games/NXDoom/src/doom/hu_lib.h @@ -0,0 +1,178 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_lib.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +#ifndef __HULIB__ +#define __HULIB__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to patches. */ + +#include "r_defs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* font stuff */ + +#define HU_CHARERASE KEY_BACKSPACE + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Typedefs of widgets */ + +/* Text Line widget + * (parent of Scrolling Text and Input Text widgets) + */ + +typedef struct +{ + /* left-justified position of scrolling text window */ + + int x; + int y; + + patch_t **f; /* font */ + int sc; /* start character */ + char l[HU_MAXLINELENGTH + 1]; /* line of text */ + int len; /* current line length */ + + /* whether this line needs to be updated */ + + int needsupdate; +} hu_textline_t; + +/* Scrolling Text window widget (child of Text Line widget) */ + +typedef struct +{ + hu_textline_t l[HU_MAXLINES]; /* text lines to draw */ + int h; /* height in lines */ + int cl; /* current line number */ + + /* pointer to boolean stating whether to update window */ + + boolean *on; + boolean laston; /* last value of *->on. */ +} hu_stext_t; + +/* Input Text Line widget (child of Text Line widget) */ + +typedef struct +{ + hu_textline_t l; /* text line to input on */ + + /* left margin past which I am not to delete characters */ + + int lm; + + /* pointer to boolean stating whether to update window */ + + boolean *on; + boolean laston; /* last value of *->on; */ +} hu_itext_t; + +/* Widget creation, access, and update routines */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* textline code */ + +/* clear a line of text */ + +void hu_lib_clear_text(hu_textline_t *t); + +void hu_lib_init_text_line(hu_textline_t *t, int x, int y, patch_t **f, + int sc); + +/* returns success */ + +boolean hu_lib_add_char_to_text_line(hu_textline_t *t, char ch); + +/* returns success */ + +boolean hu_lib_del_char_from_text_line(hu_textline_t *t); + +/* draws tline */ + +void hu_lib_draw_text_line(hu_textline_t *l, boolean drawcursor); + +/* erases text line */ + +void hu_lib_erase_text_line(hu_textline_t *l); + +/* Scrolling Text window widget routines */ + +void hu_lib_init_stext(hu_stext_t *s, int x, int y, int h, patch_t **font, + int startchar, boolean *on); + +/* add a new line */ + +void hu_lib_add_line_to_stext(hu_stext_t *s); + +/* ? */ + +void hu_lib_add_messsage_to_stext(hu_stext_t *s, const char *prefix, + const char *msg); + +/* draws stext */ + +void hu_lib_draw_stext(hu_stext_t *s); + +/* erases all stext lines */ + +void hu_lib_erase_stext(hu_stext_t *s); + +/* Input Text Line widget routines */ + +void hu_lib_init_itext(hu_itext_t *it, int x, int y, patch_t **font, + int startchar, boolean *on); + +/* enforces left margin */ + +void hu_lib_del_char_from_itext(hu_itext_t *it); + +/* resets line and left margin */ + +void hu_lib_reset_itext(hu_itext_t *it); + +/* whether eaten */ + +boolean hu_lib_key_in_itext(hu_itext_t *it, unsigned char ch); + +void hu_lib_draw_itext(hu_itext_t *it); + +/* erases all itext lines */ + +void hu_lib_erase_itext(hu_itext_t *it); + +#endif /* __HULIB__ */ diff --git a/games/NXDoom/src/doom/hu_stuff.c b/games/NXDoom/src/doom/hu_stuff.c new file mode 100644 index 000000000..9dfd61abc --- /dev/null +++ b/games/NXDoom/src/doom/hu_stuff.c @@ -0,0 +1,641 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_stuff.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: Heads-up displays + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" + +#include "z_zone.h" + +#include "deh_main.h" +#include "i_input.h" +#include "i_swap.h" +#include "i_video.h" + +#include "hu_lib.h" +#include "hu_stuff.h" +#include "m_controls.h" +#include "m_menu.h" +#include "m_misc.h" +#include "w_wad.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Data. */ + +#include "dstrings.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Locally used constants, shortcuts. */ + +#define HU_TITLE (mapnames[(gameepisode - 1) * 9 + gamemap - 1]) +#define HU_TITLE2 (mapnames_commercial[gamemap - 1]) +#define HU_TITLEP (mapnames_commercial[gamemap - 1 + 32]) +#define HU_TITLET (mapnames_commercial[gamemap - 1 + 64]) +#define HU_TITLE_CHEX (mapnames_chex[(gameepisode - 1) * 9 + gamemap - 1]) +#define HU_TITLEHEIGHT 1 +#define HU_TITLEX 0 +#define HU_TITLEY (167 - SHORT(hu_font[0]->height)) + +#define HU_INPUTTOGGLE 't' +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT * (SHORT(hu_font[0]->height) + 1)) +#define HU_INPUTWIDTH 64 +#define HU_INPUTHEIGHT 1 + +#define QUEUESIZE 128 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static player_t *plr; +static hu_textline_t w_title; +static hu_itext_t w_chat; +static boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; + +static boolean message_on; +static boolean message_nottobefuckedwith; + +static hu_stext_t w_message; +static int message_counter; + +static boolean headsupactive = false; + +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *chat_macros[10]; + +const char *player_names[] = +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED, +}; + +char chat_char; /* remove later. */ + +patch_t *hu_font[HU_FONTSIZE]; +boolean chat_on; +boolean message_dontfuckwithme; + +/* Builtin map names. + * The actual names can be found in DStrings.h. + */ + +/* DOOM shareware/registered/retail (Ultimate) names. */ + +const char *mapnames[] = +{ + HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, HUSTR_E1M6, + HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9, HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, + HUSTR_E2M4, HUSTR_E2M5, HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9, + HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5, HUSTR_E3M6, + HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9, HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, + HUSTR_E4M4, HUSTR_E4M5, HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9, + "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", + "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", +}; + +/* Chex Quest names. */ + +const char *mapnames_chex[] = +{ + HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, + HUSTR_E1M5, "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", + "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", +}; + +/* List of names for levels in commercial IWADs + * (doom2.wad, plutonia.wad, tnt.wad). These are stored in a + * single large array; WADs like pl2.wad have a MAP33, and rely on + * the layout in the Vanilla executable, where it is possible to + * overflow the end of one array into the next. + */ + +const char *mapnames_commercial[] = +{ + HUSTR_1, /* DOOM 2 map names. */ + HUSTR_2, + HUSTR_3, + HUSTR_4, + HUSTR_5, + HUSTR_6, + HUSTR_7, + HUSTR_8, + HUSTR_9, + HUSTR_10, + HUSTR_11, + HUSTR_12, + HUSTR_13, + HUSTR_14, + HUSTR_15, + HUSTR_16, + HUSTR_17, + HUSTR_18, + HUSTR_19, + HUSTR_20, + HUSTR_21, + HUSTR_22, + HUSTR_23, + HUSTR_24, + HUSTR_25, + HUSTR_26, + HUSTR_27, + HUSTR_28, + HUSTR_29, + HUSTR_30, + HUSTR_31, + HUSTR_32, + PHUSTR_1, /* Plutonia WAD map names. */ + PHUSTR_2, + PHUSTR_3, + PHUSTR_4, + PHUSTR_5, + PHUSTR_6, + PHUSTR_7, + PHUSTR_8, + PHUSTR_9, + PHUSTR_10, + PHUSTR_11, + PHUSTR_12, + PHUSTR_13, + PHUSTR_14, + PHUSTR_15, + PHUSTR_16, + PHUSTR_17, + PHUSTR_18, + PHUSTR_19, + PHUSTR_20, + PHUSTR_21, + PHUSTR_22, + PHUSTR_23, + PHUSTR_24, + PHUSTR_25, + PHUSTR_26, + PHUSTR_27, + PHUSTR_28, + PHUSTR_29, + PHUSTR_30, + PHUSTR_31, + PHUSTR_32, + THUSTR_1, /* TNT WAD map names. */ + THUSTR_2, + THUSTR_3, + THUSTR_4, + THUSTR_5, + THUSTR_6, + THUSTR_7, + THUSTR_8, + THUSTR_9, + THUSTR_10, + THUSTR_11, + THUSTR_12, + THUSTR_13, + THUSTR_14, + THUSTR_15, + THUSTR_16, + THUSTR_17, + THUSTR_18, + THUSTR_19, + THUSTR_20, + THUSTR_21, + THUSTR_22, + THUSTR_23, + THUSTR_24, + THUSTR_25, + THUSTR_26, + THUSTR_27, + THUSTR_28, + THUSTR_29, + THUSTR_30, + THUSTR_31, + THUSTR_32, + + /* Emulation: TNT maps 33-35 can be warped to and played if they exist + * so include blank names instead of spilling over + */ + + "", + "", + "", +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void hu_stop(void) +{ + headsupactive = false; +} + +static void hu_queue_chat_char(char c) +{ + if (((head + 1) & (QUEUESIZE - 1)) == tail) + { + plr->message = (HUSTR_MSGU); + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE - 1); + } +} + +static void start_chat_input(int dest) +{ + chat_on = true; + hu_lib_reset_itext(&w_chat); + hu_queue_chat_char(HU_BROADCAST); + + i_start_text_input(0, 8, SCREENWIDTH, 16); +} + +static void stop_chat_input(void) +{ + chat_on = false; + i_stop_text_input(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void hu_init(void) +{ + uint8_t i; + uint8_t j; + char buffer[9]; + + /* load the heads-up font */ + + j = HU_FONTSTART; + for (i = 0; i < HU_FONTSIZE; i++) + { + snprintf(buffer, 9, "STCFN%.3u", j++); + hu_font[i] = (patch_t *)w_cache_lump_name(buffer, PU_STATIC); + } +} + +void hu_start(void) +{ + int i; + const char *s; + + if (headsupactive) hu_stop(); + + plr = &players[consoleplayer]; + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + /* create the message widget */ + + hu_lib_init_stext(&w_message, HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font, + HU_FONTSTART, &message_on); + + /* create the map title widget */ + + hu_lib_init_text_line(&w_title, HU_TITLEX, HU_TITLEY, hu_font, + HU_FONTSTART); + + switch (logical_gamemission) + { + case doom: + s = HU_TITLE; + break; + case doom2: + s = HU_TITLE2; + + /* Pre-Final Doom compatibility: map33-map35 names don't spill over */ + + if (gameversion <= exe_doom_1_9 && gamemap >= 33) + { + s = ""; + } + + break; + case pack_plut: + s = HU_TITLEP; + break; + case pack_tnt: + s = HU_TITLET; + break; + default: + s = "Unknown level"; + break; + } + + if (logical_gamemission == doom && gameversion == exe_chex) + { + s = HU_TITLE_CHEX; + } + + /* dehacked substitution to get modified level name */ + + s = (s); + while (*s) + { + hu_lib_add_char_to_text_line(&w_title, *(s++)); + } + + /* create the chat widget */ + + hu_lib_init_itext(&w_chat, HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART, + &chat_on); + + /* create the inputbuffer widgets */ + + for (i = 0; i < MAXPLAYERS; i++) + { + hu_lib_init_itext(&w_inputbuffer[i], 0, 0, 0, 0, &always_off); + } + + headsupactive = true; +} + +void hu_drawer(void) +{ + hu_lib_draw_stext(&w_message); + hu_lib_draw_itext(&w_chat); + if (automapactive) hu_lib_draw_text_line(&w_title, false); +} + +void hu_erase(void) +{ + hu_lib_erase_stext(&w_message); + hu_lib_erase_itext(&w_chat); + hu_lib_erase_text_line(&w_title); +} + +void hu_ticker(void) +{ + int i; + int rc; + char c; + + /* tick down message counter if message is up */ + + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + + if (g_show_messages || message_dontfuckwithme) + { + /* display message if necessary */ + + if ((plr->message && !message_nottobefuckedwith) || + (plr->message && message_dontfuckwithme)) + { + hu_lib_add_messsage_to_stext(&w_message, 0, plr->message); + plr->message = 0; + message_on = true; + message_counter = HU_MSGTIMEOUT; + message_nottobefuckedwith = message_dontfuckwithme; + message_dontfuckwithme = 0; + } + } + + /* check for incoming chat characters */ + + if (netgame) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + if (i != consoleplayer && (c = players[i].cmd.chatchar)) + { + if (c <= HU_BROADCAST) + chat_dest[i] = c; + else + { + rc = hu_lib_key_in_itext(&w_inputbuffer[i], c); + if (rc && c == KEY_ENTER) + { + if (w_inputbuffer[i].l.len && + (chat_dest[i] == consoleplayer + 1 || + chat_dest[i] == HU_BROADCAST)) + { + hu_lib_add_messsage_to_stext(&w_message, + (player_names[i]), + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + s_start_sound(0, SFX_RADIO); + else if (gameversion > exe_doom_1_2) + s_start_sound(0, SFX_TINK); +#endif + } + + hu_lib_reset_itext(&w_inputbuffer[i]); + } + } + + players[i].cmd.chatchar = 0; + } + } + } +} + +char hu_dequeue_chat_char(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE - 1); + } + else + { + c = 0; + } + + return c; +} + +boolean hu_responder(event_t *ev) +{ + static char lastmessage[HU_MAXLINELENGTH + 1]; + const char *macromessage; + boolean eatkey = false; + static boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i = 0; i < MAXPLAYERS; i++) + numplayers += playeringame[i]; + + if (ev->data1 == KEY_RSHIFT) + { + return false; + } + else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) + { + altdown = ev->type == ev_keydown; + return false; + } + + if (ev->type != ev_keydown) return false; + + if (!chat_on) + { + if (ev->data1 == key_message_refresh) + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + eatkey = true; + } + else if (netgame && ev->data2 == key_multi_msg) + { + eatkey = true; + start_chat_input(HU_BROADCAST); + } + else if (netgame && numplayers > 2) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (ev->data2 == key_multi_msgplayer[i]) + { + if (playeringame[i] && i != consoleplayer) + { + eatkey = true; + start_chat_input(i + 1); + break; + } + else if (i == consoleplayer) + { + num_nobrainers++; + if (num_nobrainers < 3) + plr->message = (HUSTR_TALKTOSELF1); + else if (num_nobrainers < 6) + plr->message = (HUSTR_TALKTOSELF2); + else if (num_nobrainers < 9) + plr->message = (HUSTR_TALKTOSELF3); + else if (num_nobrainers < 32) + plr->message = (HUSTR_TALKTOSELF4); + else + plr->message = (HUSTR_TALKTOSELF5); + } + } + } + } + } + else + { + /* send a macro */ + + if (altdown) + { + c = ev->data1 - '0'; + if (c > 9) return false; + macromessage = chat_macros[c]; + + /* kill last message with a '\n' */ + + hu_queue_chat_char(KEY_ENTER); /* DEBUG!!! */ + + /* send the macro message */ + + while (*macromessage) + { + hu_queue_chat_char(*macromessage++); + } + + hu_queue_chat_char(KEY_ENTER); + + /* leave chat mode and notify that it was sent */ + + stop_chat_input(); + m_str_copy(lastmessage, chat_macros[c], sizeof(lastmessage)); + plr->message = lastmessage; + eatkey = true; + } + else + { + c = ev->data3; + + eatkey = hu_lib_key_in_itext(&w_chat, c); + if (eatkey) + { + hu_queue_chat_char(c); + } + + if (c == KEY_ENTER) + { + stop_chat_input(); + if (w_chat.l.len) + { + m_str_copy(lastmessage, w_chat.l.l, sizeof(lastmessage)); + plr->message = lastmessage; + } + } + else if (c == KEY_ESCAPE) + { + stop_chat_input(); + } + } + } + + return eatkey; +} diff --git a/games/NXDoom/src/doom/hu_stuff.h b/games/NXDoom/src/doom/hu_stuff.h new file mode 100644 index 000000000..646a916c2 --- /dev/null +++ b/games/NXDoom/src/doom/hu_stuff.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/hu_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: Head up display + * + ****************************************************************************/ + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Globally visible constants. */ + +#define HU_FONTSTART '!' /* the first font characters */ +#define HU_FONTEND '_' /* the last font characters */ + +/* Calculate # of glyphs in font. */ + +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +#define HU_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 /* in characters */ +#define HU_MSGHEIGHT 1 /* in lines */ + +#define HU_MSGTIMEOUT (4 * TICRATE) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *player_names[4]; +extern char *chat_macros[10]; + +extern patch_t *hu_font[HU_FONTSIZE]; + +extern boolean message_dontfuckwithme; + +extern boolean chat_on; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* HEADS UP TEXT */ + +void hu_init(void); +void hu_start(void); + +boolean hu_responder(event_t *ev); + +void hu_ticker(void); +void hu_drawer(void); +char hu_dequeue_chat_char(void); +void hu_erase(void); + +#endif /* __HU_STUFF_H__ */ diff --git a/games/NXDoom/src/doom/info.c b/games/NXDoom/src/doom/info.c new file mode 100644 index 000000000..e1d6e8ac8 --- /dev/null +++ b/games/NXDoom/src/doom/info.c @@ -0,0 +1,5356 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/info.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Thing frame/state LUT, generated by multigen utility. + * This one is the original DOOM version, preserved. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "m_fixed.h" +#include "sounds.h" + +#include "info.h" + +#include "p_mobj.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if 0 + +/* Using misc1 & misc2 */ + +#define STATE_ENTRY(s, f, t, a, ns) \ + { \ + .sprite = (s), \ + .frame = (f), \ + .tics = (t), \ + .action = {(a)}, \ + .nextstate = (ns), \ + .misc1 = 0, \ + .misc2 = 0, \ + } +#else + +/* No misc1 & misc2 members */ + +#define STATE_ENTRY(s, f, t, a, ns) \ + { \ + .sprite = (s), \ + .frame = (f), \ + .tics = (t), \ + .action = {(void *)(a)}, \ + .nextstate = (ns), \ + } +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const char *sprnames[] = +{ + "TROO", "SHTG", "PUNG", "PISG", "PISF", "SHTF", "SHT2", "CHGG", "CHGF", + "MISG", "MISF", "SAWG", "PLSG", "PLSF", "BFGG", "BFGF", "BLUD", "PUFF", + "BAL1", "BAL2", "PLSS", "PLSE", "MISL", "BFS1", "BFE1", "BFE2", "TFOG", + "IFOG", "PLAY", "POSS", "SPOS", "VILE", "FIRE", "FATB", "FBXP", "SKEL", + "MANF", "FATT", "CPOS", "SARG", "HEAD", "BAL7", "BOSS", "BOS2", "SKUL", + "SPID", "BSPI", "APLS", "APBX", "CYBR", "PAIN", "SSWV", "KEEN", "BBRN", + "BOSF", "ARM1", "ARM2", "BAR1", "BEXP", "FCAN", "BON1", "BON2", "BKEY", + "RKEY", "YKEY", "BSKU", "RSKU", "YSKU", "STIM", "MEDI", "SOUL", "PINV", + "PSTR", "PINS", "MEGA", "SUIT", "PMAP", "PVIS", "CLIP", "AMMO", "ROCK", + "BROK", "CELL", "CELP", "SHEL", "SBOX", "BPAK", "BFUG", "MGUN", "CSAW", + "LAUN", "PLAS", "SHOT", "SGN2", "COLU", "SMT2", "GOR1", "POL2", "POL5", + "POL4", "POL3", "POL1", "POL6", "GOR2", "GOR3", "GOR4", "GOR5", "SMIT", + "COL1", "COL2", "COL3", "COL4", "CAND", "CBRA", "COL6", "TRE1", "TRE2", + "ELEC", "CEYE", "FSKU", "COL5", "TBLU", "TGRN", "TRED", "SMBT", "SMGT", + "SMRT", "HDB1", "HDB2", "HDB3", "HDB4", "HDB5", "HDB6", "POB1", "POB2", + "BRS1", "TLMP", "TLP2", NULL, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Doesn't work with g++, needs actionf_p1 */ + +void a_light0(void *, void *); +void a_weapon_ready(void *, void *); +void a_lower(void *, void *); +void a_raise(void *, void *); +void a_punch(void *, void *); +void a_refire(void *, void *); +void a_fire_pistol(void *, void *); +void a_light1(void *, void *); +void a_fire_shotgun(void *, void *); +void a_light2(void *, void *); +void a_fire_shotgun2(void *, void *); +void a_check_reload(void *, void *); +void a_open_shotgun2(void *, void *); +void a_load_shotgun2(void *, void *); +void a_close_shotgun2(void *, void *); +void a_fire_cgun(void *, void *); +void a_gun_flash(void *, void *); +void a_fire_missile(void *, void *); +void a_saw(void *, void *); +void a_fire_plasma(void *, void *); +void a_bfg_sound(void *, void *); +void a_fire_bfg(void *, void *); +void a_bfg_spray(void *); +void a_explode(void *); +void a_pain(void *); +void a_player_screm(void *); +void a_fall(void *); +void a_xscream(void *); +void a_look(void *); +void a_chase(void *); +void a_face_target(void *); +void a_pos_attack(void *); +void a_scream(void *); +void a_s_pos_attack(void *); +void a_vile_chase(void *); +void a_vile_start(void *); +void a_vile_target(void *); +void a_vile_attack(void *); +void a_start_fire(void *); +void a_fire(void *); +void a_fire_crackle(void *); +void a_tracer(void *); +void a_skel_woosh(void *); +void a_skel_fist(void *); +void a_skel_missile(void *); +void a_fat_raise(void *); +void a_fat_attack1(void *); +void a_fat_attack2(void *); +void a_fat_attack3(void *); +void a_boss_death(void *); +void a_c_pos_attack(void *); +void a_c_pos_refire(void *); +void a_troop_attack(void *); +void a_sarg_attack(void *); +void a_head_attack(void *); +void a_bruis_attack(void *); +void a_skull_attack(void *); +void a_metal(void *); +void a_spid_refire(void *); +void a_baby_metal(void *); +void a_bspi_attack(void *); +void a_hoof(void *); +void a_cyber_attack(void *); +void a_pain_attack(void *); +void a_pain_die(void *); +void a_keen_die(void *); +void a_brain_pain(void *); +void a_brain_scream(void *); +void a_brain_die(void *); +void a_brain_awake(void *); +void a_brain_split(void *); +void a_spawn_sound(void *); +void a_spawn_fly(void *); +void a_brain_explode(void *); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +state_t states[NUMSTATES] = +{ + STATE_ENTRY(SPR_TROO, 0, -1, NULL, S_NULL), /* S_NULL */ + STATE_ENTRY(SPR_SHTG, 4, 0, a_light0, S_NULL), /* S_LIGHTDONE */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_weapon_ready, S_PUNCH), /* S_PUNCH */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_lower, S_PUNCHDOWN), /* S_PUNCHDOWN */ + STATE_ENTRY(SPR_PUNG, 0, 1, a_raise, S_PUNCHUP), /* S_PUNCHUP */ + STATE_ENTRY(SPR_PUNG, 1, 4, NULL, S_PUNCH2), /* S_PUNCH1 */ + STATE_ENTRY(SPR_PUNG, 2, 4, a_punch, S_PUNCH3), /* S_PUNCH2 */ + STATE_ENTRY(SPR_PUNG, 3, 5, NULL, S_PUNCH4), /* S_PUNCH3 */ + STATE_ENTRY(SPR_PUNG, 2, 4, NULL, S_PUNCH5), /* S_PUNCH4 */ + STATE_ENTRY(SPR_PUNG, 1, 5, a_refire, S_PUNCH), /* S_PUNCH5 */ + STATE_ENTRY(SPR_PISG, 0, 1, a_weapon_ready, S_PISTOL), /* S_PISTOL */ + STATE_ENTRY(SPR_PISG, 0, 1, a_lower, S_PISTOLDOWN), /* S_PISTOLDOWN */ + STATE_ENTRY(SPR_PISG, 0, 1, a_raise, S_PISTOLUP), /* S_PISTOLUP */ + STATE_ENTRY(SPR_PISG, 0, 4, NULL, S_PISTOL2), /* S_PISTOL1 */ + STATE_ENTRY(SPR_PISG, 1, 6, a_fire_pistol, S_PISTOL3), /* S_PISTOL2 */ + STATE_ENTRY(SPR_PISG, 2, 4, NULL, S_PISTOL4), /* S_PISTOL3 */ + STATE_ENTRY(SPR_PISG, 1, 5, a_refire, S_PISTOL), /* S_PISTOL4 */ + STATE_ENTRY(SPR_PISF, 32768, 7, a_light1, + S_LIGHTDONE), /* S_PISTOLFLASH */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_weapon_ready, S_SGUN), /* S_SGUN */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_lower, S_SGUNDOWN), /* S_SGUNDOWN */ + STATE_ENTRY(SPR_SHTG, 0, 1, a_raise, S_SGUNUP), /* S_SGUNUP */ + STATE_ENTRY(SPR_SHTG, 0, 3, NULL, S_SGUN2), /* S_SGUN1 */ + STATE_ENTRY(SPR_SHTG, 0, 7, a_fire_shotgun, S_SGUN3), /* S_SGUN2 */ + STATE_ENTRY(SPR_SHTG, 1, 5, NULL, S_SGUN4), /* S_SGUN3 */ + STATE_ENTRY(SPR_SHTG, 2, 5, NULL, S_SGUN5), /* S_SGUN4 */ + STATE_ENTRY(SPR_SHTG, 3, 4, NULL, S_SGUN6), /* S_SGUN5 */ + STATE_ENTRY(SPR_SHTG, 2, 5, NULL, S_SGUN7), /* S_SGUN6 */ + STATE_ENTRY(SPR_SHTG, 1, 5, NULL, S_SGUN8), /* S_SGUN7 */ + STATE_ENTRY(SPR_SHTG, 0, 3, NULL, S_SGUN9), /* S_SGUN8 */ + STATE_ENTRY(SPR_SHTG, 0, 7, a_refire, S_SGUN), /* S_SGUN9 */ + STATE_ENTRY(SPR_SHTF, 32768, 4, a_light1, + S_SGUNFLASH2), /* S_SGUNFLASH1 */ + STATE_ENTRY(SPR_SHTF, 32769, 3, a_light2, S_LIGHTDONE), /* S_SGUNFLASH2 */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_weapon_ready, S_DSGUN), /* S_DSGUN */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_lower, S_DSGUNDOWN), /* S_DSGUNDOWN */ + STATE_ENTRY(SPR_SHT2, 0, 1, a_raise, S_DSGUNUP), /* S_DSGUNUP */ + STATE_ENTRY(SPR_SHT2, 0, 3, NULL, S_DSGUN2), /* S_DSGUN1 */ + STATE_ENTRY(SPR_SHT2, 0, 7, a_fire_shotgun2, S_DSGUN3), /* S_DSGUN2 */ + STATE_ENTRY(SPR_SHT2, 1, 7, NULL, S_DSGUN4), /* S_DSGUN3 */ + STATE_ENTRY(SPR_SHT2, 2, 7, a_check_reload, S_DSGUN5), /* S_DSGUN4 */ + STATE_ENTRY(SPR_SHT2, 3, 7, a_open_shotgun2, S_DSGUN6), /* S_DSGUN5 */ + STATE_ENTRY(SPR_SHT2, 4, 7, NULL, S_DSGUN7), /* S_DSGUN6 */ + STATE_ENTRY(SPR_SHT2, 5, 7, a_load_shotgun2, S_DSGUN8), /* S_DSGUN7 */ + STATE_ENTRY(SPR_SHT2, 6, 6, NULL, S_DSGUN9), /* S_DSGUN8 */ + STATE_ENTRY(SPR_SHT2, 7, 6, a_close_shotgun2, S_DSGUN10), /* S_DSGUN9 */ + STATE_ENTRY(SPR_SHT2, 0, 5, a_refire, S_DSGUN), /* S_DSGUN10 */ + STATE_ENTRY(SPR_SHT2, 1, 7, NULL, S_DSNR2), /* S_DSNR1 */ + STATE_ENTRY(SPR_SHT2, 0, 3, NULL, S_DSGUNDOWN), /* S_DSNR2 */ + STATE_ENTRY(SPR_SHT2, 32776, 5, a_light1, + S_DSGUNFLASH2), /* S_DSGUNFLASH1 */ + STATE_ENTRY(SPR_SHT2, 32777, 4, a_light2, + S_LIGHTDONE), /* S_DSGUNFLASH2 */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_weapon_ready, S_CHAIN), /* S_CHAIN */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_lower, S_CHAINDOWN), /* S_CHAINDOWN */ + STATE_ENTRY(SPR_CHGG, 0, 1, a_raise, S_CHAINUP), /* S_CHAINUP */ + STATE_ENTRY(SPR_CHGG, 0, 4, a_fire_cgun, S_CHAIN2), /* S_CHAIN1 */ + STATE_ENTRY(SPR_CHGG, 1, 4, a_fire_cgun, S_CHAIN3), /* S_CHAIN2 */ + STATE_ENTRY(SPR_CHGG, 1, 0, a_refire, S_CHAIN), /* S_CHAIN3 */ + STATE_ENTRY(SPR_CHGF, 32768, 5, a_light1, + S_LIGHTDONE), /* S_CHAINFLASH1 */ + STATE_ENTRY(SPR_CHGF, 32769, 5, a_light2, + S_LIGHTDONE), /* S_CHAINFLASH2 */ + STATE_ENTRY(SPR_MISG, 0, 1, a_weapon_ready, S_MISSILE), /* S_MISSILE */ + STATE_ENTRY(SPR_MISG, 0, 1, a_lower, S_MISSILEDOWN), /* S_MISSILEDOWN */ + STATE_ENTRY(SPR_MISG, 0, 1, a_raise, S_MISSILEUP), /* S_MISSILEUP */ + STATE_ENTRY(SPR_MISG, 1, 8, a_gun_flash, S_MISSILE2), /* S_MISSILE1 */ + STATE_ENTRY(SPR_MISG, 1, 12, a_fire_missile, S_MISSILE3), /* S_MISSILE2 */ + STATE_ENTRY(SPR_MISG, 1, 0, a_refire, S_MISSILE), /* S_MISSILE3 */ + STATE_ENTRY(SPR_MISF, 32768, 3, a_light1, + S_MISSILEFLASH2), /* S_MISSILEFLASH1 */ + STATE_ENTRY(SPR_MISF, 32769, 4, NULL, + S_MISSILEFLASH3), /* S_MISSILEFLASH2 */ + STATE_ENTRY(SPR_MISF, 32770, 4, a_light2, + S_MISSILEFLASH4), /* S_MISSILEFLASH3 */ + STATE_ENTRY(SPR_MISF, 32771, 4, a_light2, + S_LIGHTDONE), /* S_MISSILEFLASH4 */ + STATE_ENTRY(SPR_SAWG, 2, 4, a_weapon_ready, S_SAWB), /* S_SAW */ + STATE_ENTRY(SPR_SAWG, 3, 4, a_weapon_ready, S_SAW), /* S_SAWB */ + STATE_ENTRY(SPR_SAWG, 2, 1, a_lower, S_SAWDOWN), /* S_SAWDOWN */ + STATE_ENTRY(SPR_SAWG, 2, 1, a_raise, S_SAWUP), /* S_SAWUP */ + STATE_ENTRY(SPR_SAWG, 0, 4, a_saw, S_SAW2), /* S_SAW1 */ + STATE_ENTRY(SPR_SAWG, 1, 4, a_saw, S_SAW3), /* S_SAW2 */ + STATE_ENTRY(SPR_SAWG, 1, 0, a_refire, S_SAW), /* S_SAW3 */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_weapon_ready, S_PLASMA), /* S_PLASMA */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_lower, S_PLASMADOWN), /* S_PLASMADOWN */ + STATE_ENTRY(SPR_PLSG, 0, 1, a_raise, S_PLASMAUP), /* S_PLASMAUP */ + STATE_ENTRY(SPR_PLSG, 0, 3, a_fire_plasma, S_PLASMA2), /* S_PLASMA1 */ + STATE_ENTRY(SPR_PLSG, 1, 20, a_refire, S_PLASMA), /* S_PLASMA2 */ + STATE_ENTRY(SPR_PLSF, 32768, 4, a_light1, + S_LIGHTDONE), /* S_PLASMAFLASH1 */ + STATE_ENTRY(SPR_PLSF, 32769, 4, a_light1, + S_LIGHTDONE), /* S_PLASMAFLASH2 */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_weapon_ready, S_BFG), /* S_BFG */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_lower, S_BFGDOWN), /* S_BFGDOWN */ + STATE_ENTRY(SPR_BFGG, 0, 1, a_raise, S_BFGUP), /* S_BFGUP */ + STATE_ENTRY(SPR_BFGG, 0, 20, a_bfg_sound, S_BFG2), /* S_BFG1 */ + STATE_ENTRY(SPR_BFGG, 1, 10, a_gun_flash, S_BFG3), /* S_BFG2 */ + STATE_ENTRY(SPR_BFGG, 1, 10, a_fire_bfg, S_BFG4), /* S_BFG3 */ + STATE_ENTRY(SPR_BFGG, 1, 20, a_refire, S_BFG), /* S_BFG4 */ + STATE_ENTRY(SPR_BFGF, 32768, 11, a_light1, S_BFGFLASH2), /* S_BFGFLASH1 */ + STATE_ENTRY(SPR_BFGF, 32769, 6, a_light2, S_LIGHTDONE), /* S_BFGFLASH2 */ + STATE_ENTRY(SPR_BLUD, 2, 8, NULL, S_BLOOD2), /* S_BLOOD1 */ + STATE_ENTRY(SPR_BLUD, 1, 8, NULL, S_BLOOD3), /* S_BLOOD2 */ + STATE_ENTRY(SPR_BLUD, 0, 8, NULL, S_NULL), /* S_BLOOD3 */ + STATE_ENTRY(SPR_PUFF, 32768, 4, NULL, S_PUFF2), /* S_PUFF1 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_PUFF3), /* S_PUFF2 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_PUFF4), /* S_PUFF3 */ + STATE_ENTRY(SPR_PUFF, 3, 4, NULL, S_NULL), /* S_PUFF4 */ + STATE_ENTRY(SPR_BAL1, 32768, 4, NULL, S_TBALL2), /* S_TBALL1 */ + STATE_ENTRY(SPR_BAL1, 32769, 4, NULL, S_TBALL1), /* S_TBALL2 */ + STATE_ENTRY(SPR_BAL1, 32770, 6, NULL, S_TBALLX2), /* S_TBALLX1 */ + STATE_ENTRY(SPR_BAL1, 32771, 6, NULL, S_TBALLX3), /* S_TBALLX2 */ + STATE_ENTRY(SPR_BAL1, 32772, 6, NULL, S_NULL), /* S_TBALLX3 */ + STATE_ENTRY(SPR_BAL2, 32768, 4, NULL, S_RBALL2), /* S_RBALL1 */ + STATE_ENTRY(SPR_BAL2, 32769, 4, NULL, S_RBALL1), /* S_RBALL2 */ + STATE_ENTRY(SPR_BAL2, 32770, 6, NULL, S_RBALLX2), /* S_RBALLX1 */ + STATE_ENTRY(SPR_BAL2, 32771, 6, NULL, S_RBALLX3), /* S_RBALLX2 */ + STATE_ENTRY(SPR_BAL2, 32772, 6, NULL, S_NULL), /* S_RBALLX3 */ + STATE_ENTRY(SPR_PLSS, 32768, 6, NULL, S_PLASBALL2), /* S_PLASBALL */ + STATE_ENTRY(SPR_PLSS, 32769, 6, NULL, S_PLASBALL), /* S_PLASBALL2 */ + STATE_ENTRY(SPR_PLSE, 32768, 4, NULL, S_PLASEXP2), /* S_PLASEXP */ + STATE_ENTRY(SPR_PLSE, 32769, 4, NULL, S_PLASEXP3), /* S_PLASEXP2 */ + STATE_ENTRY(SPR_PLSE, 32770, 4, NULL, S_PLASEXP4), /* S_PLASEXP3 */ + STATE_ENTRY(SPR_PLSE, 32771, 4, NULL, S_PLASEXP5), /* S_PLASEXP4 */ + STATE_ENTRY(SPR_PLSE, 32772, 4, NULL, S_NULL), /* S_PLASEXP5 */ + STATE_ENTRY(SPR_MISL, 32768, 1, NULL, S_ROCKET), /* S_ROCKET */ + STATE_ENTRY(SPR_BFS1, 32768, 4, NULL, S_BFGSHOT2), /* S_BFGSHOT */ + STATE_ENTRY(SPR_BFS1, 32769, 4, NULL, S_BFGSHOT), /* S_BFGSHOT2 */ + STATE_ENTRY(SPR_BFE1, 32768, 8, NULL, S_BFGLAND2), /* S_BFGLAND */ + STATE_ENTRY(SPR_BFE1, 32769, 8, NULL, S_BFGLAND3), /* S_BFGLAND2 */ + STATE_ENTRY(SPR_BFE1, 32770, 8, a_bfg_spray, S_BFGLAND4), /* S_BFGLAND3 */ + STATE_ENTRY(SPR_BFE1, 32771, 8, NULL, S_BFGLAND5), /* S_BFGLAND4 */ + STATE_ENTRY(SPR_BFE1, 32772, 8, NULL, S_BFGLAND6), /* S_BFGLAND5 */ + STATE_ENTRY(SPR_BFE1, 32773, 8, NULL, S_NULL), /* S_BFGLAND6 */ + STATE_ENTRY(SPR_BFE2, 32768, 8, NULL, S_BFGEXP2), /* S_BFGEXP */ + STATE_ENTRY(SPR_BFE2, 32769, 8, NULL, S_BFGEXP3), /* S_BFGEXP2 */ + STATE_ENTRY(SPR_BFE2, 32770, 8, NULL, S_BFGEXP4), /* S_BFGEXP3 */ + STATE_ENTRY(SPR_BFE2, 32771, 8, NULL, S_NULL), /* S_BFGEXP4 */ + STATE_ENTRY(SPR_MISL, 32769, 8, a_explode, S_EXPLODE2), /* S_EXPLODE1 */ + STATE_ENTRY(SPR_MISL, 32770, 6, NULL, S_EXPLODE3), /* S_EXPLODE2 */ + STATE_ENTRY(SPR_MISL, 32771, 4, NULL, S_NULL), /* S_EXPLODE3 */ + STATE_ENTRY(SPR_TFOG, 32768, 6, NULL, S_TFOG01), /* S_TFOG */ + STATE_ENTRY(SPR_TFOG, 32769, 6, NULL, S_TFOG02), /* S_TFOG01 */ + STATE_ENTRY(SPR_TFOG, 32768, 6, NULL, S_TFOG2), /* S_TFOG02 */ + STATE_ENTRY(SPR_TFOG, 32769, 6, NULL, S_TFOG3), /* S_TFOG2 */ + STATE_ENTRY(SPR_TFOG, 32770, 6, NULL, S_TFOG4), /* S_TFOG3 */ + STATE_ENTRY(SPR_TFOG, 32771, 6, NULL, S_TFOG5), /* S_TFOG4 */ + STATE_ENTRY(SPR_TFOG, 32772, 6, NULL, S_TFOG6), /* S_TFOG5 */ + STATE_ENTRY(SPR_TFOG, 32773, 6, NULL, S_TFOG7), /* S_TFOG6 */ + STATE_ENTRY(SPR_TFOG, 32774, 6, NULL, S_TFOG8), /* S_TFOG7 */ + STATE_ENTRY(SPR_TFOG, 32775, 6, NULL, S_TFOG9), /* S_TFOG8 */ + STATE_ENTRY(SPR_TFOG, 32776, 6, NULL, S_TFOG10), /* S_TFOG9 */ + STATE_ENTRY(SPR_TFOG, 32777, 6, NULL, S_NULL), /* S_TFOG10 */ + STATE_ENTRY(SPR_IFOG, 32768, 6, NULL, S_IFOG01), /* S_IFOG */ + STATE_ENTRY(SPR_IFOG, 32769, 6, NULL, S_IFOG02), /* S_IFOG01 */ + STATE_ENTRY(SPR_IFOG, 32768, 6, NULL, S_IFOG2), /* S_IFOG02 */ + STATE_ENTRY(SPR_IFOG, 32769, 6, NULL, S_IFOG3), /* S_IFOG2 */ + STATE_ENTRY(SPR_IFOG, 32770, 6, NULL, S_IFOG4), /* S_IFOG3 */ + STATE_ENTRY(SPR_IFOG, 32771, 6, NULL, S_IFOG5), /* S_IFOG4 */ + STATE_ENTRY(SPR_IFOG, 32772, 6, NULL, S_NULL), /* S_IFOG5 */ + STATE_ENTRY(SPR_PLAY, 0, -1, NULL, S_NULL), /* S_PLAY */ + STATE_ENTRY(SPR_PLAY, 0, 4, NULL, S_PLAY_RUN2), /* S_PLAY_RUN1 */ + STATE_ENTRY(SPR_PLAY, 1, 4, NULL, S_PLAY_RUN3), /* S_PLAY_RUN2 */ + STATE_ENTRY(SPR_PLAY, 2, 4, NULL, S_PLAY_RUN4), /* S_PLAY_RUN3 */ + STATE_ENTRY(SPR_PLAY, 3, 4, NULL, S_PLAY_RUN1), /* S_PLAY_RUN4 */ + STATE_ENTRY(SPR_PLAY, 4, 12, NULL, S_PLAY), /* S_PLAY_ATK1 */ + STATE_ENTRY(SPR_PLAY, 32773, 6, NULL, S_PLAY_ATK1), /* S_PLAY_ATK2 */ + STATE_ENTRY(SPR_PLAY, 6, 4, NULL, S_PLAY_PAIN2), /* S_PLAY_PAIN */ + STATE_ENTRY(SPR_PLAY, 6, 4, a_pain, S_PLAY), /* S_PLAY_PAIN2 */ + STATE_ENTRY(SPR_PLAY, 7, 10, NULL, S_PLAY_DIE2), /* S_PLAY_DIE1 */ + STATE_ENTRY(SPR_PLAY, 8, 10, a_player_screm, + S_PLAY_DIE3), /* S_PLAY_DIE2 */ + STATE_ENTRY(SPR_PLAY, 9, 10, a_fall, S_PLAY_DIE4), /* S_PLAY_DIE3 */ + STATE_ENTRY(SPR_PLAY, 10, 10, NULL, S_PLAY_DIE5), /* S_PLAY_DIE4 */ + STATE_ENTRY(SPR_PLAY, 11, 10, NULL, S_PLAY_DIE6), /* S_PLAY_DIE5 */ + STATE_ENTRY(SPR_PLAY, 12, 10, NULL, S_PLAY_DIE7), /* S_PLAY_DIE6 */ + STATE_ENTRY(SPR_PLAY, 13, -1, NULL, S_NULL), /* S_PLAY_DIE7 */ + STATE_ENTRY(SPR_PLAY, 14, 5, NULL, S_PLAY_XDIE2), /* S_PLAY_XDIE1 */ + STATE_ENTRY(SPR_PLAY, 15, 5, a_xscream, S_PLAY_XDIE3), /* S_PLAY_XDIE2 */ + STATE_ENTRY(SPR_PLAY, 16, 5, a_fall, S_PLAY_XDIE4), /* S_PLAY_XDIE3 */ + STATE_ENTRY(SPR_PLAY, 17, 5, NULL, S_PLAY_XDIE5), /* S_PLAY_XDIE4 */ + STATE_ENTRY(SPR_PLAY, 18, 5, NULL, S_PLAY_XDIE6), /* S_PLAY_XDIE5 */ + STATE_ENTRY(SPR_PLAY, 19, 5, NULL, S_PLAY_XDIE7), /* S_PLAY_XDIE6 */ + STATE_ENTRY(SPR_PLAY, 20, 5, NULL, S_PLAY_XDIE8), /* S_PLAY_XDIE7 */ + STATE_ENTRY(SPR_PLAY, 21, 5, NULL, S_PLAY_XDIE9), /* S_PLAY_XDIE8 */ + STATE_ENTRY(SPR_PLAY, 22, -1, NULL, S_NULL), /* S_PLAY_XDIE9 */ + STATE_ENTRY(SPR_POSS, 0, 10, a_look, S_POSS_STND2), /* S_POSS_STND */ + STATE_ENTRY(SPR_POSS, 1, 10, a_look, S_POSS_STND), /* S_POSS_STND2 */ + STATE_ENTRY(SPR_POSS, 0, 4, a_chase, S_POSS_RUN2), /* S_POSS_RUN1 */ + STATE_ENTRY(SPR_POSS, 0, 4, a_chase, S_POSS_RUN3), /* S_POSS_RUN2 */ + STATE_ENTRY(SPR_POSS, 1, 4, a_chase, S_POSS_RUN4), /* S_POSS_RUN3 */ + STATE_ENTRY(SPR_POSS, 1, 4, a_chase, S_POSS_RUN5), /* S_POSS_RUN4 */ + STATE_ENTRY(SPR_POSS, 2, 4, a_chase, S_POSS_RUN6), /* S_POSS_RUN5 */ + STATE_ENTRY(SPR_POSS, 2, 4, a_chase, S_POSS_RUN7), /* S_POSS_RUN6 */ + STATE_ENTRY(SPR_POSS, 3, 4, a_chase, S_POSS_RUN8), /* S_POSS_RUN7 */ + STATE_ENTRY(SPR_POSS, 3, 4, a_chase, S_POSS_RUN1), /* S_POSS_RUN8 */ + STATE_ENTRY(SPR_POSS, 4, 10, a_face_target, + S_POSS_ATK2), /* S_POSS_ATK1 */ + STATE_ENTRY(SPR_POSS, 5, 8, a_pos_attack, S_POSS_ATK3), /* S_POSS_ATK2 */ + STATE_ENTRY(SPR_POSS, 4, 8, NULL, S_POSS_RUN1), /* S_POSS_ATK3 */ + STATE_ENTRY(SPR_POSS, 6, 3, NULL, S_POSS_PAIN2), /* S_POSS_PAIN */ + STATE_ENTRY(SPR_POSS, 6, 3, a_pain, S_POSS_RUN1), /* S_POSS_PAIN2 */ + STATE_ENTRY(SPR_POSS, 7, 5, NULL, S_POSS_DIE2), /* S_POSS_DIE1 */ + STATE_ENTRY(SPR_POSS, 8, 5, a_scream, S_POSS_DIE3), /* S_POSS_DIE2 */ + STATE_ENTRY(SPR_POSS, 9, 5, a_fall, S_POSS_DIE4), /* S_POSS_DIE3 */ + STATE_ENTRY(SPR_POSS, 10, 5, NULL, S_POSS_DIE5), /* S_POSS_DIE4 */ + STATE_ENTRY(SPR_POSS, 11, -1, NULL, S_NULL), /* S_POSS_DIE5 */ + STATE_ENTRY(SPR_POSS, 12, 5, NULL, S_POSS_XDIE2), /* S_POSS_XDIE1 */ + STATE_ENTRY(SPR_POSS, 13, 5, a_xscream, S_POSS_XDIE3), /* S_POSS_XDIE2 */ + STATE_ENTRY(SPR_POSS, 14, 5, a_fall, S_POSS_XDIE4), /* S_POSS_XDIE3 */ + STATE_ENTRY(SPR_POSS, 15, 5, NULL, S_POSS_XDIE5), /* S_POSS_XDIE4 */ + STATE_ENTRY(SPR_POSS, 16, 5, NULL, S_POSS_XDIE6), /* S_POSS_XDIE5 */ + STATE_ENTRY(SPR_POSS, 17, 5, NULL, S_POSS_XDIE7), /* S_POSS_XDIE6 */ + STATE_ENTRY(SPR_POSS, 18, 5, NULL, S_POSS_XDIE8), /* S_POSS_XDIE7 */ + STATE_ENTRY(SPR_POSS, 19, 5, NULL, S_POSS_XDIE9), /* S_POSS_XDIE8 */ + STATE_ENTRY(SPR_POSS, 20, -1, NULL, S_NULL), /* S_POSS_XDIE9 */ + STATE_ENTRY(SPR_POSS, 10, 5, NULL, S_POSS_RAISE2), /* S_POSS_RAISE1 */ + STATE_ENTRY(SPR_POSS, 9, 5, NULL, S_POSS_RAISE3), /* S_POSS_RAISE2 */ + STATE_ENTRY(SPR_POSS, 8, 5, NULL, S_POSS_RAISE4), /* S_POSS_RAISE3 */ + STATE_ENTRY(SPR_POSS, 7, 5, NULL, S_POSS_RUN1), /* S_POSS_RAISE4 */ + STATE_ENTRY(SPR_SPOS, 0, 10, a_look, S_SPOS_STND2), /* S_SPOS_STND */ + STATE_ENTRY(SPR_SPOS, 1, 10, a_look, S_SPOS_STND), /* S_SPOS_STND2 */ + STATE_ENTRY(SPR_SPOS, 0, 3, a_chase, S_SPOS_RUN2), /* S_SPOS_RUN1 */ + STATE_ENTRY(SPR_SPOS, 0, 3, a_chase, S_SPOS_RUN3), /* S_SPOS_RUN2 */ + STATE_ENTRY(SPR_SPOS, 1, 3, a_chase, S_SPOS_RUN4), /* S_SPOS_RUN3 */ + STATE_ENTRY(SPR_SPOS, 1, 3, a_chase, S_SPOS_RUN5), /* S_SPOS_RUN4 */ + STATE_ENTRY(SPR_SPOS, 2, 3, a_chase, S_SPOS_RUN6), /* S_SPOS_RUN5 */ + STATE_ENTRY(SPR_SPOS, 2, 3, a_chase, S_SPOS_RUN7), /* S_SPOS_RUN6 */ + STATE_ENTRY(SPR_SPOS, 3, 3, a_chase, S_SPOS_RUN8), /* S_SPOS_RUN7 */ + STATE_ENTRY(SPR_SPOS, 3, 3, a_chase, S_SPOS_RUN1), /* S_SPOS_RUN8 */ + STATE_ENTRY(SPR_SPOS, 4, 10, a_face_target, + S_SPOS_ATK2), /* S_SPOS_ATK1 */ + STATE_ENTRY(SPR_SPOS, 32773, 10, a_s_pos_attack, + S_SPOS_ATK3), /* S_SPOS_ATK2 */ + STATE_ENTRY(SPR_SPOS, 4, 10, NULL, S_SPOS_RUN1), /* S_SPOS_ATK3 */ + STATE_ENTRY(SPR_SPOS, 6, 3, NULL, S_SPOS_PAIN2), /* S_SPOS_PAIN */ + STATE_ENTRY(SPR_SPOS, 6, 3, a_pain, S_SPOS_RUN1), /* S_SPOS_PAIN2 */ + STATE_ENTRY(SPR_SPOS, 7, 5, NULL, S_SPOS_DIE2), /* S_SPOS_DIE1 */ + STATE_ENTRY(SPR_SPOS, 8, 5, a_scream, S_SPOS_DIE3), /* S_SPOS_DIE2 */ + STATE_ENTRY(SPR_SPOS, 9, 5, a_fall, S_SPOS_DIE4), /* S_SPOS_DIE3 */ + STATE_ENTRY(SPR_SPOS, 10, 5, NULL, S_SPOS_DIE5), /* S_SPOS_DIE4 */ + STATE_ENTRY(SPR_SPOS, 11, -1, NULL, S_NULL), /* S_SPOS_DIE5 */ + STATE_ENTRY(SPR_SPOS, 12, 5, NULL, S_SPOS_XDIE2), /* S_SPOS_XDIE1 */ + STATE_ENTRY(SPR_SPOS, 13, 5, a_xscream, S_SPOS_XDIE3), /* S_SPOS_XDIE2 */ + STATE_ENTRY(SPR_SPOS, 14, 5, a_fall, S_SPOS_XDIE4), /* S_SPOS_XDIE3 */ + STATE_ENTRY(SPR_SPOS, 15, 5, NULL, S_SPOS_XDIE5), /* S_SPOS_XDIE4 */ + STATE_ENTRY(SPR_SPOS, 16, 5, NULL, S_SPOS_XDIE6), /* S_SPOS_XDIE5 */ + STATE_ENTRY(SPR_SPOS, 17, 5, NULL, S_SPOS_XDIE7), /* S_SPOS_XDIE6 */ + STATE_ENTRY(SPR_SPOS, 18, 5, NULL, S_SPOS_XDIE8), /* S_SPOS_XDIE7 */ + STATE_ENTRY(SPR_SPOS, 19, 5, NULL, S_SPOS_XDIE9), /* S_SPOS_XDIE8 */ + STATE_ENTRY(SPR_SPOS, 20, -1, NULL, S_NULL), /* S_SPOS_XDIE9 */ + STATE_ENTRY(SPR_SPOS, 11, 5, NULL, S_SPOS_RAISE2), /* S_SPOS_RAISE1 */ + STATE_ENTRY(SPR_SPOS, 10, 5, NULL, S_SPOS_RAISE3), /* S_SPOS_RAISE2 */ + STATE_ENTRY(SPR_SPOS, 9, 5, NULL, S_SPOS_RAISE4), /* S_SPOS_RAISE3 */ + STATE_ENTRY(SPR_SPOS, 8, 5, NULL, S_SPOS_RAISE5), /* S_SPOS_RAISE4 */ + STATE_ENTRY(SPR_SPOS, 7, 5, NULL, S_SPOS_RUN1), /* S_SPOS_RAISE5 */ + STATE_ENTRY(SPR_VILE, 0, 10, a_look, S_VILE_STND2), /* S_VILE_STND */ + STATE_ENTRY(SPR_VILE, 1, 10, a_look, S_VILE_STND), /* S_VILE_STND2 */ + STATE_ENTRY(SPR_VILE, 0, 2, a_vile_chase, S_VILE_RUN2), /* S_VILE_RUN1 */ + STATE_ENTRY(SPR_VILE, 0, 2, a_vile_chase, S_VILE_RUN3), /* S_VILE_RUN2 */ + STATE_ENTRY(SPR_VILE, 1, 2, a_vile_chase, S_VILE_RUN4), /* S_VILE_RUN3 */ + STATE_ENTRY(SPR_VILE, 1, 2, a_vile_chase, S_VILE_RUN5), /* S_VILE_RUN4 */ + STATE_ENTRY(SPR_VILE, 2, 2, a_vile_chase, S_VILE_RUN6), /* S_VILE_RUN5 */ + STATE_ENTRY(SPR_VILE, 2, 2, a_vile_chase, S_VILE_RUN7), /* S_VILE_RUN6 */ + STATE_ENTRY(SPR_VILE, 3, 2, a_vile_chase, S_VILE_RUN8), /* S_VILE_RUN7 */ + STATE_ENTRY(SPR_VILE, 3, 2, a_vile_chase, S_VILE_RUN9), /* S_VILE_RUN8 */ + STATE_ENTRY(SPR_VILE, 4, 2, a_vile_chase, S_VILE_RUN10), /* S_VILE_RUN9 */ + STATE_ENTRY(SPR_VILE, 4, 2, a_vile_chase, + S_VILE_RUN11), /* S_VILE_RUN10 */ + STATE_ENTRY(SPR_VILE, 5, 2, a_vile_chase, + S_VILE_RUN12), /* S_VILE_RUN11 */ + STATE_ENTRY(SPR_VILE, 5, 2, a_vile_chase, S_VILE_RUN1), /* S_VILE_RUN12 */ + STATE_ENTRY(SPR_VILE, 32774, 0, a_vile_start, + S_VILE_ATK2), /* S_VILE_ATK1 */ + STATE_ENTRY(SPR_VILE, 32774, 10, a_face_target, + S_VILE_ATK3), /* S_VILE_ATK2 */ + STATE_ENTRY(SPR_VILE, 32775, 8, a_vile_target, + S_VILE_ATK4), /* S_VILE_ATK3 */ + STATE_ENTRY(SPR_VILE, 32776, 8, a_face_target, + S_VILE_ATK5), /* S_VILE_ATK4 */ + STATE_ENTRY(SPR_VILE, 32777, 8, a_face_target, + S_VILE_ATK6), /* S_VILE_ATK5 */ + STATE_ENTRY(SPR_VILE, 32778, 8, a_face_target, + S_VILE_ATK7), /* S_VILE_ATK6 */ + STATE_ENTRY(SPR_VILE, 32779, 8, a_face_target, + S_VILE_ATK8), /* S_VILE_ATK7 */ + STATE_ENTRY(SPR_VILE, 32780, 8, a_face_target, + S_VILE_ATK9), /* S_VILE_ATK8 */ + STATE_ENTRY(SPR_VILE, 32781, 8, a_face_target, + S_VILE_ATK10), /* S_VILE_ATK9 */ + STATE_ENTRY(SPR_VILE, 32782, 8, a_vile_attack, + S_VILE_ATK11), /* S_VILE_ATK10 */ + STATE_ENTRY(SPR_VILE, 32783, 20, NULL, S_VILE_RUN1), /* S_VILE_ATK11 */ + STATE_ENTRY(SPR_VILE, 32794, 10, NULL, S_VILE_HEAL2), /* S_VILE_HEAL1 */ + STATE_ENTRY(SPR_VILE, 32795, 10, NULL, S_VILE_HEAL3), /* S_VILE_HEAL2 */ + STATE_ENTRY(SPR_VILE, 32796, 10, NULL, S_VILE_RUN1), /* S_VILE_HEAL3 */ + STATE_ENTRY(SPR_VILE, 16, 5, NULL, S_VILE_PAIN2), /* S_VILE_PAIN */ + STATE_ENTRY(SPR_VILE, 16, 5, a_pain, S_VILE_RUN1), /* S_VILE_PAIN2 */ + STATE_ENTRY(SPR_VILE, 16, 7, NULL, S_VILE_DIE2), /* S_VILE_DIE1 */ + STATE_ENTRY(SPR_VILE, 17, 7, a_scream, S_VILE_DIE3), /* S_VILE_DIE2 */ + STATE_ENTRY(SPR_VILE, 18, 7, a_fall, S_VILE_DIE4), /* S_VILE_DIE3 */ + STATE_ENTRY(SPR_VILE, 19, 7, NULL, S_VILE_DIE5), /* S_VILE_DIE4 */ + STATE_ENTRY(SPR_VILE, 20, 7, NULL, S_VILE_DIE6), /* S_VILE_DIE5 */ + STATE_ENTRY(SPR_VILE, 21, 7, NULL, S_VILE_DIE7), /* S_VILE_DIE6 */ + STATE_ENTRY(SPR_VILE, 22, 7, NULL, S_VILE_DIE8), /* S_VILE_DIE7 */ + STATE_ENTRY(SPR_VILE, 23, 5, NULL, S_VILE_DIE9), /* S_VILE_DIE8 */ + STATE_ENTRY(SPR_VILE, 24, 5, NULL, S_VILE_DIE10), /* S_VILE_DIE9 */ + STATE_ENTRY(SPR_VILE, 25, -1, NULL, S_NULL), /* S_VILE_DIE10 */ + STATE_ENTRY(SPR_FIRE, 32768, 2, a_start_fire, + S_FIRE2), /* S_FIRE1 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE3), /* S_FIRE2 */ + STATE_ENTRY(SPR_FIRE, 32768, 2, a_fire, S_FIRE4), /* S_FIRE3 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE5), /* S_FIRE4 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire_crackle, + S_FIRE6), /* S_FIRE5 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE7), /* S_FIRE6 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE8), /* S_FIRE7 */ + STATE_ENTRY(SPR_FIRE, 32769, 2, a_fire, S_FIRE9), /* S_FIRE8 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE10), /* S_FIRE9 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE11), /* S_FIRE10 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE12), /* S_FIRE11 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE13), /* S_FIRE12 */ + STATE_ENTRY(SPR_FIRE, 32770, 2, a_fire, S_FIRE14), /* S_FIRE13 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE15), /* S_FIRE14 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE16), /* S_FIRE15 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE17), /* S_FIRE16 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE18), /* S_FIRE17 */ + STATE_ENTRY(SPR_FIRE, 32771, 2, a_fire, S_FIRE19), /* S_FIRE18 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire_crackle, + S_FIRE20), /* S_FIRE19 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE21), /* S_FIRE20 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE22), /* S_FIRE21 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE23), /* S_FIRE22 */ + STATE_ENTRY(SPR_FIRE, 32772, 2, a_fire, S_FIRE24), /* S_FIRE23 */ + STATE_ENTRY(SPR_FIRE, 32773, 2, a_fire, S_FIRE25), /* S_FIRE24 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE26), /* S_FIRE25 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_FIRE27), /* S_FIRE26 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE28), /* S_FIRE27 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_FIRE29), /* S_FIRE28 */ + STATE_ENTRY(SPR_FIRE, 32774, 2, a_fire, S_FIRE30), /* S_FIRE29 */ + STATE_ENTRY(SPR_FIRE, 32775, 2, a_fire, S_NULL), /* S_FIRE30 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_SMOKE2), /* S_SMOKE1 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_SMOKE3), /* S_SMOKE2 */ + STATE_ENTRY(SPR_PUFF, 1, 4, NULL, S_SMOKE4), /* S_SMOKE3 */ + STATE_ENTRY(SPR_PUFF, 2, 4, NULL, S_SMOKE5), /* S_SMOKE4 */ + STATE_ENTRY(SPR_PUFF, 3, 4, NULL, S_NULL), /* S_SMOKE5 */ + STATE_ENTRY(SPR_FATB, 32768, 2, a_tracer, S_TRACER2), /* S_TRACER */ + STATE_ENTRY(SPR_FATB, 32769, 2, a_tracer, S_TRACER), /* S_TRACER2 */ + STATE_ENTRY(SPR_FBXP, 32768, 8, NULL, S_TRACEEXP2), /* S_TRACEEXP1 */ + STATE_ENTRY(SPR_FBXP, 32769, 6, NULL, S_TRACEEXP3), /* S_TRACEEXP2 */ + STATE_ENTRY(SPR_FBXP, 32770, 4, NULL, S_NULL), /* S_TRACEEXP3 */ + STATE_ENTRY(SPR_SKEL, 0, 10, a_look, S_SKEL_STND2), /* S_SKEL_STND */ + STATE_ENTRY(SPR_SKEL, 1, 10, a_look, S_SKEL_STND), /* S_SKEL_STND2 */ + STATE_ENTRY(SPR_SKEL, 0, 2, a_chase, S_SKEL_RUN2), /* S_SKEL_RUN1 */ + STATE_ENTRY(SPR_SKEL, 0, 2, a_chase, S_SKEL_RUN3), /* S_SKEL_RUN2 */ + STATE_ENTRY(SPR_SKEL, 1, 2, a_chase, S_SKEL_RUN4), /* S_SKEL_RUN3 */ + STATE_ENTRY(SPR_SKEL, 1, 2, a_chase, S_SKEL_RUN5), /* S_SKEL_RUN4 */ + STATE_ENTRY(SPR_SKEL, 2, 2, a_chase, S_SKEL_RUN6), /* S_SKEL_RUN5 */ + STATE_ENTRY(SPR_SKEL, 2, 2, a_chase, S_SKEL_RUN7), /* S_SKEL_RUN6 */ + STATE_ENTRY(SPR_SKEL, 3, 2, a_chase, S_SKEL_RUN8), /* S_SKEL_RUN7 */ + STATE_ENTRY(SPR_SKEL, 3, 2, a_chase, S_SKEL_RUN9), /* S_SKEL_RUN8 */ + STATE_ENTRY(SPR_SKEL, 4, 2, a_chase, S_SKEL_RUN10), /* S_SKEL_RUN9 */ + STATE_ENTRY(SPR_SKEL, 4, 2, a_chase, S_SKEL_RUN11), /* S_SKEL_RUN10 */ + STATE_ENTRY(SPR_SKEL, 5, 2, a_chase, S_SKEL_RUN12), /* S_SKEL_RUN11 */ + STATE_ENTRY(SPR_SKEL, 5, 2, a_chase, S_SKEL_RUN1), /* S_SKEL_RUN12 */ + STATE_ENTRY(SPR_SKEL, 6, 0, a_face_target, + S_SKEL_FIST2), /* S_SKEL_FIST1 */ + STATE_ENTRY(SPR_SKEL, 6, 6, a_skel_woosh, + S_SKEL_FIST3), /* S_SKEL_FIST2 */ + STATE_ENTRY(SPR_SKEL, 7, 6, a_face_target, + S_SKEL_FIST4), /* S_SKEL_FIST3 */ + STATE_ENTRY(SPR_SKEL, 8, 6, a_skel_fist, S_SKEL_RUN1), /* S_SKEL_FIST4 */ + STATE_ENTRY(SPR_SKEL, 32777, 0, a_face_target, + S_SKEL_MISS2), /* S_SKEL_MISS1 */ + STATE_ENTRY(SPR_SKEL, 32777, 10, a_face_target, + S_SKEL_MISS3), /* S_SKEL_MISS2 */ + STATE_ENTRY(SPR_SKEL, 10, 10, a_skel_missile, + S_SKEL_MISS4), /* S_SKEL_MISS3 */ + STATE_ENTRY(SPR_SKEL, 10, 10, a_face_target, + S_SKEL_RUN1), /* S_SKEL_MISS4 */ + STATE_ENTRY(SPR_SKEL, 11, 5, NULL, S_SKEL_PAIN2), /* S_SKEL_PAIN */ + STATE_ENTRY(SPR_SKEL, 11, 5, a_pain, S_SKEL_RUN1), /* S_SKEL_PAIN2 */ + STATE_ENTRY(SPR_SKEL, 11, 7, NULL, S_SKEL_DIE2), /* S_SKEL_DIE1 */ + STATE_ENTRY(SPR_SKEL, 12, 7, NULL, S_SKEL_DIE3), /* S_SKEL_DIE2 */ + STATE_ENTRY(SPR_SKEL, 13, 7, a_scream, S_SKEL_DIE4), /* S_SKEL_DIE3 */ + STATE_ENTRY(SPR_SKEL, 14, 7, a_fall, S_SKEL_DIE5), /* S_SKEL_DIE4 */ + STATE_ENTRY(SPR_SKEL, 15, 7, NULL, S_SKEL_DIE6), /* S_SKEL_DIE5 */ + STATE_ENTRY(SPR_SKEL, 16, -1, NULL, S_NULL), /* S_SKEL_DIE6 */ + STATE_ENTRY(SPR_SKEL, 16, 5, NULL, S_SKEL_RAISE2), /* S_SKEL_RAISE1 */ + STATE_ENTRY(SPR_SKEL, 15, 5, NULL, S_SKEL_RAISE3), /* S_SKEL_RAISE2 */ + STATE_ENTRY(SPR_SKEL, 14, 5, NULL, S_SKEL_RAISE4), /* S_SKEL_RAISE3 */ + STATE_ENTRY(SPR_SKEL, 13, 5, NULL, S_SKEL_RAISE5), /* S_SKEL_RAISE4 */ + STATE_ENTRY(SPR_SKEL, 12, 5, NULL, S_SKEL_RAISE6), /* S_SKEL_RAISE5 */ + STATE_ENTRY(SPR_SKEL, 11, 5, NULL, S_SKEL_RUN1), /* S_SKEL_RAISE6 */ + STATE_ENTRY(SPR_MANF, 32768, 4, NULL, S_FATSHOT2), /* S_FATSHOT1 */ + STATE_ENTRY(SPR_MANF, 32769, 4, NULL, S_FATSHOT1), /* S_FATSHOT2 */ + STATE_ENTRY(SPR_MISL, 32769, 8, NULL, S_FATSHOTX2), /* S_FATSHOTX1 */ + STATE_ENTRY(SPR_MISL, 32770, 6, NULL, S_FATSHOTX3), /* S_FATSHOTX2 */ + STATE_ENTRY(SPR_MISL, 32771, 4, NULL, S_NULL), /* S_FATSHOTX3 */ + STATE_ENTRY(SPR_FATT, 0, 15, a_look, S_FATT_STND2), /* S_FATT_STND */ + STATE_ENTRY(SPR_FATT, 1, 15, a_look, S_FATT_STND), /* S_FATT_STND2 */ + STATE_ENTRY(SPR_FATT, 0, 4, a_chase, S_FATT_RUN2), /* S_FATT_RUN1 */ + STATE_ENTRY(SPR_FATT, 0, 4, a_chase, S_FATT_RUN3), /* S_FATT_RUN2 */ + STATE_ENTRY(SPR_FATT, 1, 4, a_chase, S_FATT_RUN4), /* S_FATT_RUN3 */ + STATE_ENTRY(SPR_FATT, 1, 4, a_chase, S_FATT_RUN5), /* S_FATT_RUN4 */ + STATE_ENTRY(SPR_FATT, 2, 4, a_chase, S_FATT_RUN6), /* S_FATT_RUN5 */ + STATE_ENTRY(SPR_FATT, 2, 4, a_chase, S_FATT_RUN7), /* S_FATT_RUN6 */ + STATE_ENTRY(SPR_FATT, 3, 4, a_chase, S_FATT_RUN8), /* S_FATT_RUN7 */ + STATE_ENTRY(SPR_FATT, 3, 4, a_chase, S_FATT_RUN9), /* S_FATT_RUN8 */ + STATE_ENTRY(SPR_FATT, 4, 4, a_chase, S_FATT_RUN10), /* S_FATT_RUN9 */ + STATE_ENTRY(SPR_FATT, 4, 4, a_chase, S_FATT_RUN11), /* S_FATT_RUN10 */ + STATE_ENTRY(SPR_FATT, 5, 4, a_chase, S_FATT_RUN12), /* S_FATT_RUN11 */ + STATE_ENTRY(SPR_FATT, 5, 4, a_chase, S_FATT_RUN1), /* S_FATT_RUN12 */ + STATE_ENTRY(SPR_FATT, 6, 20, a_fat_raise, S_FATT_ATK2), /* S_FATT_ATK1 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack1, + S_FATT_ATK3), /* S_FATT_ATK2 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, S_FATT_ATK4), /* S_FATT_ATK3 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, S_FATT_ATK5), /* S_FATT_ATK4 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack2, + S_FATT_ATK6), /* S_FATT_ATK5 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, S_FATT_ATK7), /* S_FATT_ATK6 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, S_FATT_ATK8), /* S_FATT_ATK7 */ + STATE_ENTRY(SPR_FATT, 32775, 10, a_fat_attack3, + S_FATT_ATK9), /* S_FATT_ATK8 */ + STATE_ENTRY(SPR_FATT, 8, 5, a_face_target, + S_FATT_ATK10), /* S_FATT_ATK9 */ + STATE_ENTRY(SPR_FATT, 6, 5, a_face_target, + S_FATT_RUN1), /* S_FATT_ATK10 */ + STATE_ENTRY(SPR_FATT, 9, 3, NULL, S_FATT_PAIN2), /* S_FATT_PAIN */ + STATE_ENTRY(SPR_FATT, 9, 3, a_pain, S_FATT_RUN1), /* S_FATT_PAIN2 */ + STATE_ENTRY(SPR_FATT, 10, 6, NULL, S_FATT_DIE2), /* S_FATT_DIE1 */ + STATE_ENTRY(SPR_FATT, 11, 6, a_scream, S_FATT_DIE3), /* S_FATT_DIE2 */ + STATE_ENTRY(SPR_FATT, 12, 6, a_fall, S_FATT_DIE4), /* S_FATT_DIE3 */ + STATE_ENTRY(SPR_FATT, 13, 6, NULL, S_FATT_DIE5), /* S_FATT_DIE4 */ + STATE_ENTRY(SPR_FATT, 14, 6, NULL, S_FATT_DIE6), /* S_FATT_DIE5 */ + STATE_ENTRY(SPR_FATT, 15, 6, NULL, S_FATT_DIE7), /* S_FATT_DIE6 */ + STATE_ENTRY(SPR_FATT, 16, 6, NULL, S_FATT_DIE8), /* S_FATT_DIE7 */ + STATE_ENTRY(SPR_FATT, 17, 6, NULL, S_FATT_DIE9), /* S_FATT_DIE8 */ + STATE_ENTRY(SPR_FATT, 18, 6, NULL, S_FATT_DIE10), /* S_FATT_DIE9 */ + STATE_ENTRY(SPR_FATT, 19, -1, a_boss_death, S_NULL), /* S_FATT_DIE10 */ + STATE_ENTRY(SPR_FATT, 17, 5, NULL, S_FATT_RAISE2), /* S_FATT_RAISE1 */ + STATE_ENTRY(SPR_FATT, 16, 5, NULL, S_FATT_RAISE3), /* S_FATT_RAISE2 */ + STATE_ENTRY(SPR_FATT, 15, 5, NULL, S_FATT_RAISE4), /* S_FATT_RAISE3 */ + STATE_ENTRY(SPR_FATT, 14, 5, NULL, S_FATT_RAISE5), /* S_FATT_RAISE4 */ + STATE_ENTRY(SPR_FATT, 13, 5, NULL, S_FATT_RAISE6), /* S_FATT_RAISE5 */ + STATE_ENTRY(SPR_FATT, 12, 5, NULL, S_FATT_RAISE7), /* S_FATT_RAISE6 */ + STATE_ENTRY(SPR_FATT, 11, 5, NULL, S_FATT_RAISE8), /* S_FATT_RAISE7 */ + STATE_ENTRY(SPR_FATT, 10, 5, NULL, S_FATT_RUN1), /* S_FATT_RAISE8 */ + STATE_ENTRY(SPR_CPOS, 0, 10, a_look, S_CPOS_STND2), /* S_CPOS_STND */ + STATE_ENTRY(SPR_CPOS, 1, 10, a_look, S_CPOS_STND), /* S_CPOS_STND2 */ + STATE_ENTRY(SPR_CPOS, 0, 3, a_chase, S_CPOS_RUN2), /* S_CPOS_RUN1 */ + STATE_ENTRY(SPR_CPOS, 0, 3, a_chase, S_CPOS_RUN3), /* S_CPOS_RUN2 */ + STATE_ENTRY(SPR_CPOS, 1, 3, a_chase, S_CPOS_RUN4), /* S_CPOS_RUN3 */ + STATE_ENTRY(SPR_CPOS, 1, 3, a_chase, S_CPOS_RUN5), /* S_CPOS_RUN4 */ + STATE_ENTRY(SPR_CPOS, 2, 3, a_chase, S_CPOS_RUN6), /* S_CPOS_RUN5 */ + STATE_ENTRY(SPR_CPOS, 2, 3, a_chase, S_CPOS_RUN7), /* S_CPOS_RUN6 */ + STATE_ENTRY(SPR_CPOS, 3, 3, a_chase, S_CPOS_RUN8), /* S_CPOS_RUN7 */ + STATE_ENTRY(SPR_CPOS, 3, 3, a_chase, S_CPOS_RUN1), /* S_CPOS_RUN8 */ + STATE_ENTRY(SPR_CPOS, 4, 10, a_face_target, + S_CPOS_ATK2), /* S_CPOS_ATK1 */ + STATE_ENTRY(SPR_CPOS, 32773, 4, a_c_pos_attack, + S_CPOS_ATK3), /* S_CPOS_ATK2 */ + STATE_ENTRY(SPR_CPOS, 32772, 4, a_c_pos_attack, + S_CPOS_ATK4), /* S_CPOS_ATK3 */ + STATE_ENTRY(SPR_CPOS, 5, 1, a_c_pos_refire, + S_CPOS_ATK2), /* S_CPOS_ATK4 */ + STATE_ENTRY(SPR_CPOS, 6, 3, NULL, S_CPOS_PAIN2), /* S_CPOS_PAIN */ + STATE_ENTRY(SPR_CPOS, 6, 3, a_pain, S_CPOS_RUN1), /* S_CPOS_PAIN2 */ + STATE_ENTRY(SPR_CPOS, 7, 5, NULL, S_CPOS_DIE2), /* S_CPOS_DIE1 */ + STATE_ENTRY(SPR_CPOS, 8, 5, a_scream, S_CPOS_DIE3), /* S_CPOS_DIE2 */ + STATE_ENTRY(SPR_CPOS, 9, 5, a_fall, S_CPOS_DIE4), /* S_CPOS_DIE3 */ + STATE_ENTRY(SPR_CPOS, 10, 5, NULL, S_CPOS_DIE5), /* S_CPOS_DIE4 */ + STATE_ENTRY(SPR_CPOS, 11, 5, NULL, S_CPOS_DIE6), /* S_CPOS_DIE5 */ + STATE_ENTRY(SPR_CPOS, 12, 5, NULL, S_CPOS_DIE7), /* S_CPOS_DIE6 */ + STATE_ENTRY(SPR_CPOS, 13, -1, NULL, S_NULL), /* S_CPOS_DIE7 */ + STATE_ENTRY(SPR_CPOS, 14, 5, NULL, S_CPOS_XDIE2), /* S_CPOS_XDIE1 */ + STATE_ENTRY(SPR_CPOS, 15, 5, a_xscream, S_CPOS_XDIE3), /* S_CPOS_XDIE2 */ + STATE_ENTRY(SPR_CPOS, 16, 5, a_fall, S_CPOS_XDIE4), /* S_CPOS_XDIE3 */ + STATE_ENTRY(SPR_CPOS, 17, 5, NULL, S_CPOS_XDIE5), /* S_CPOS_XDIE4 */ + STATE_ENTRY(SPR_CPOS, 18, 5, NULL, S_CPOS_XDIE6), /* S_CPOS_XDIE5 */ + STATE_ENTRY(SPR_CPOS, 19, -1, NULL, S_NULL), /* S_CPOS_XDIE6 */ + STATE_ENTRY(SPR_CPOS, 13, 5, NULL, S_CPOS_RAISE2), /* S_CPOS_RAISE1 */ + STATE_ENTRY(SPR_CPOS, 12, 5, NULL, S_CPOS_RAISE3), /* S_CPOS_RAISE2 */ + STATE_ENTRY(SPR_CPOS, 11, 5, NULL, S_CPOS_RAISE4), /* S_CPOS_RAISE3 */ + STATE_ENTRY(SPR_CPOS, 10, 5, NULL, S_CPOS_RAISE5), /* S_CPOS_RAISE4 */ + STATE_ENTRY(SPR_CPOS, 9, 5, NULL, S_CPOS_RAISE6), /* S_CPOS_RAISE5 */ + STATE_ENTRY(SPR_CPOS, 8, 5, NULL, S_CPOS_RAISE7), /* S_CPOS_RAISE6 */ + STATE_ENTRY(SPR_CPOS, 7, 5, NULL, S_CPOS_RUN1), /* S_CPOS_RAISE7 */ + STATE_ENTRY(SPR_TROO, 0, 10, a_look, S_TROO_STND2), /* S_TROO_STND */ + STATE_ENTRY(SPR_TROO, 1, 10, a_look, S_TROO_STND), /* S_TROO_STND2 */ + STATE_ENTRY(SPR_TROO, 0, 3, a_chase, S_TROO_RUN2), /* S_TROO_RUN1 */ + STATE_ENTRY(SPR_TROO, 0, 3, a_chase, S_TROO_RUN3), /* S_TROO_RUN2 */ + STATE_ENTRY(SPR_TROO, 1, 3, a_chase, S_TROO_RUN4), /* S_TROO_RUN3 */ + STATE_ENTRY(SPR_TROO, 1, 3, a_chase, S_TROO_RUN5), /* S_TROO_RUN4 */ + STATE_ENTRY(SPR_TROO, 2, 3, a_chase, S_TROO_RUN6), /* S_TROO_RUN5 */ + STATE_ENTRY(SPR_TROO, 2, 3, a_chase, S_TROO_RUN7), /* S_TROO_RUN6 */ + STATE_ENTRY(SPR_TROO, 3, 3, a_chase, S_TROO_RUN8), /* S_TROO_RUN7 */ + STATE_ENTRY(SPR_TROO, 3, 3, a_chase, S_TROO_RUN1), /* S_TROO_RUN8 */ + STATE_ENTRY(SPR_TROO, 4, 8, a_face_target, S_TROO_ATK2), /* S_TROO_ATK1 */ + STATE_ENTRY(SPR_TROO, 5, 8, a_face_target, S_TROO_ATK3), /* S_TROO_ATK2 */ + STATE_ENTRY(SPR_TROO, 6, 6, a_troop_attack, + S_TROO_RUN1), /* S_TROO_ATK3 */ + STATE_ENTRY(SPR_TROO, 7, 2, NULL, S_TROO_PAIN2), /* S_TROO_PAIN */ + STATE_ENTRY(SPR_TROO, 7, 2, a_pain, S_TROO_RUN1), /* S_TROO_PAIN2 */ + STATE_ENTRY(SPR_TROO, 8, 8, NULL, S_TROO_DIE2), /* S_TROO_DIE1 */ + STATE_ENTRY(SPR_TROO, 9, 8, a_scream, S_TROO_DIE3), /* S_TROO_DIE2 */ + STATE_ENTRY(SPR_TROO, 10, 6, NULL, S_TROO_DIE4), /* S_TROO_DIE3 */ + STATE_ENTRY(SPR_TROO, 11, 6, a_fall, S_TROO_DIE5), /* S_TROO_DIE4 */ + STATE_ENTRY(SPR_TROO, 12, -1, NULL, S_NULL), /* S_TROO_DIE5 */ + STATE_ENTRY(SPR_TROO, 13, 5, NULL, S_TROO_XDIE2), /* S_TROO_XDIE1 */ + STATE_ENTRY(SPR_TROO, 14, 5, a_xscream, S_TROO_XDIE3), /* S_TROO_XDIE2 */ + STATE_ENTRY(SPR_TROO, 15, 5, NULL, S_TROO_XDIE4), /* S_TROO_XDIE3 */ + STATE_ENTRY(SPR_TROO, 16, 5, a_fall, S_TROO_XDIE5), /* S_TROO_XDIE4 */ + STATE_ENTRY(SPR_TROO, 17, 5, NULL, S_TROO_XDIE6), /* S_TROO_XDIE5 */ + STATE_ENTRY(SPR_TROO, 18, 5, NULL, S_TROO_XDIE7), /* S_TROO_XDIE6 */ + STATE_ENTRY(SPR_TROO, 19, 5, NULL, S_TROO_XDIE8), /* S_TROO_XDIE7 */ + STATE_ENTRY(SPR_TROO, 20, -1, NULL, S_NULL), /* S_TROO_XDIE8 */ + STATE_ENTRY(SPR_TROO, 12, 8, NULL, S_TROO_RAISE2), /* S_TROO_RAISE1 */ + STATE_ENTRY(SPR_TROO, 11, 8, NULL, S_TROO_RAISE3), /* S_TROO_RAISE2 */ + STATE_ENTRY(SPR_TROO, 10, 6, NULL, S_TROO_RAISE4), /* S_TROO_RAISE3 */ + STATE_ENTRY(SPR_TROO, 9, 6, NULL, S_TROO_RAISE5), /* S_TROO_RAISE4 */ + STATE_ENTRY(SPR_TROO, 8, 6, NULL, S_TROO_RUN1), /* S_TROO_RAISE5 */ + STATE_ENTRY(SPR_SARG, 0, 10, a_look, S_SARG_STND2), /* S_SARG_STND */ + STATE_ENTRY(SPR_SARG, 1, 10, a_look, S_SARG_STND), /* S_SARG_STND2 */ + STATE_ENTRY(SPR_SARG, 0, 2, a_chase, S_SARG_RUN2), /* S_SARG_RUN1 */ + STATE_ENTRY(SPR_SARG, 0, 2, a_chase, S_SARG_RUN3), /* S_SARG_RUN2 */ + STATE_ENTRY(SPR_SARG, 1, 2, a_chase, S_SARG_RUN4), /* S_SARG_RUN3 */ + STATE_ENTRY(SPR_SARG, 1, 2, a_chase, S_SARG_RUN5), /* S_SARG_RUN4 */ + STATE_ENTRY(SPR_SARG, 2, 2, a_chase, S_SARG_RUN6), /* S_SARG_RUN5 */ + STATE_ENTRY(SPR_SARG, 2, 2, a_chase, S_SARG_RUN7), /* S_SARG_RUN6 */ + STATE_ENTRY(SPR_SARG, 3, 2, a_chase, S_SARG_RUN8), /* S_SARG_RUN7 */ + STATE_ENTRY(SPR_SARG, 3, 2, a_chase, S_SARG_RUN1), /* S_SARG_RUN8 */ + STATE_ENTRY(SPR_SARG, 4, 8, a_face_target, S_SARG_ATK2), /* S_SARG_ATK1 */ + STATE_ENTRY(SPR_SARG, 5, 8, a_face_target, S_SARG_ATK3), /* S_SARG_ATK2 */ + STATE_ENTRY(SPR_SARG, 6, 8, a_sarg_attack, S_SARG_RUN1), /* S_SARG_ATK3 */ + STATE_ENTRY(SPR_SARG, 7, 2, NULL, S_SARG_PAIN2), /* S_SARG_PAIN */ + STATE_ENTRY(SPR_SARG, 7, 2, a_pain, S_SARG_RUN1), /* S_SARG_PAIN2 */ + STATE_ENTRY(SPR_SARG, 8, 8, NULL, S_SARG_DIE2), /* S_SARG_DIE1 */ + STATE_ENTRY(SPR_SARG, 9, 8, a_scream, S_SARG_DIE3), /* S_SARG_DIE2 */ + STATE_ENTRY(SPR_SARG, 10, 4, NULL, S_SARG_DIE4), /* S_SARG_DIE3 */ + STATE_ENTRY(SPR_SARG, 11, 4, a_fall, S_SARG_DIE5), /* S_SARG_DIE4 */ + STATE_ENTRY(SPR_SARG, 12, 4, NULL, S_SARG_DIE6), /* S_SARG_DIE5 */ + STATE_ENTRY(SPR_SARG, 13, -1, NULL, S_NULL), /* S_SARG_DIE6 */ + STATE_ENTRY(SPR_SARG, 13, 5, NULL, S_SARG_RAISE2), /* S_SARG_RAISE1 */ + STATE_ENTRY(SPR_SARG, 12, 5, NULL, S_SARG_RAISE3), /* S_SARG_RAISE2 */ + STATE_ENTRY(SPR_SARG, 11, 5, NULL, S_SARG_RAISE4), /* S_SARG_RAISE3 */ + STATE_ENTRY(SPR_SARG, 10, 5, NULL, S_SARG_RAISE5), /* S_SARG_RAISE4 */ + STATE_ENTRY(SPR_SARG, 9, 5, NULL, S_SARG_RAISE6), /* S_SARG_RAISE5 */ + STATE_ENTRY(SPR_SARG, 8, 5, NULL, S_SARG_RUN1), /* S_SARG_RAISE6 */ + STATE_ENTRY(SPR_HEAD, 0, 10, a_look, S_HEAD_STND), /* S_HEAD_STND */ + STATE_ENTRY(SPR_HEAD, 0, 3, a_chase, S_HEAD_RUN1), /* S_HEAD_RUN1 */ + STATE_ENTRY(SPR_HEAD, 1, 5, a_face_target, S_HEAD_ATK2), /* S_HEAD_ATK1 */ + STATE_ENTRY(SPR_HEAD, 2, 5, a_face_target, S_HEAD_ATK3), /* S_HEAD_ATK2 */ + STATE_ENTRY(SPR_HEAD, 32771, 5, a_head_attack, + S_HEAD_RUN1), /* S_HEAD_ATK3 */ + STATE_ENTRY(SPR_HEAD, 4, 3, NULL, S_HEAD_PAIN2), /* S_HEAD_PAIN */ + STATE_ENTRY(SPR_HEAD, 4, 3, a_pain, S_HEAD_PAIN3), /* S_HEAD_PAIN2 */ + STATE_ENTRY(SPR_HEAD, 5, 6, NULL, S_HEAD_RUN1), /* S_HEAD_PAIN3 */ + STATE_ENTRY(SPR_HEAD, 6, 8, NULL, S_HEAD_DIE2), /* S_HEAD_DIE1 */ + STATE_ENTRY(SPR_HEAD, 7, 8, a_scream, S_HEAD_DIE3), /* S_HEAD_DIE2 */ + STATE_ENTRY(SPR_HEAD, 8, 8, NULL, S_HEAD_DIE4), /* S_HEAD_DIE3 */ + STATE_ENTRY(SPR_HEAD, 9, 8, NULL, S_HEAD_DIE5), /* S_HEAD_DIE4 */ + STATE_ENTRY(SPR_HEAD, 10, 8, a_fall, S_HEAD_DIE6), /* S_HEAD_DIE5 */ + STATE_ENTRY(SPR_HEAD, 11, -1, NULL, S_NULL), /* S_HEAD_DIE6 */ + STATE_ENTRY(SPR_HEAD, 11, 8, NULL, S_HEAD_RAISE2), /* S_HEAD_RAISE1 */ + STATE_ENTRY(SPR_HEAD, 10, 8, NULL, S_HEAD_RAISE3), /* S_HEAD_RAISE2 */ + STATE_ENTRY(SPR_HEAD, 9, 8, NULL, S_HEAD_RAISE4), /* S_HEAD_RAISE3 */ + STATE_ENTRY(SPR_HEAD, 8, 8, NULL, S_HEAD_RAISE5), /* S_HEAD_RAISE4 */ + STATE_ENTRY(SPR_HEAD, 7, 8, NULL, S_HEAD_RAISE6), /* S_HEAD_RAISE5 */ + STATE_ENTRY(SPR_HEAD, 6, 8, NULL, S_HEAD_RUN1), /* S_HEAD_RAISE6 */ + STATE_ENTRY(SPR_BAL7, 32768, 4, NULL, S_BRBALL2), /* S_BRBALL1 */ + STATE_ENTRY(SPR_BAL7, 32769, 4, NULL, S_BRBALL1), /* S_BRBALL2 */ + STATE_ENTRY(SPR_BAL7, 32770, 6, NULL, S_BRBALLX2), /* S_BRBALLX1 */ + STATE_ENTRY(SPR_BAL7, 32771, 6, NULL, S_BRBALLX3), /* S_BRBALLX2 */ + STATE_ENTRY(SPR_BAL7, 32772, 6, NULL, S_NULL), /* S_BRBALLX3 */ + STATE_ENTRY(SPR_BOSS, 0, 10, a_look, S_BOSS_STND2), /* S_BOSS_STND */ + STATE_ENTRY(SPR_BOSS, 1, 10, a_look, S_BOSS_STND), /* S_BOSS_STND2 */ + STATE_ENTRY(SPR_BOSS, 0, 3, a_chase, S_BOSS_RUN2), /* S_BOSS_RUN1 */ + STATE_ENTRY(SPR_BOSS, 0, 3, a_chase, S_BOSS_RUN3), /* S_BOSS_RUN2 */ + STATE_ENTRY(SPR_BOSS, 1, 3, a_chase, S_BOSS_RUN4), /* S_BOSS_RUN3 */ + STATE_ENTRY(SPR_BOSS, 1, 3, a_chase, S_BOSS_RUN5), /* S_BOSS_RUN4 */ + STATE_ENTRY(SPR_BOSS, 2, 3, a_chase, S_BOSS_RUN6), /* S_BOSS_RUN5 */ + STATE_ENTRY(SPR_BOSS, 2, 3, a_chase, S_BOSS_RUN7), /* S_BOSS_RUN6 */ + STATE_ENTRY(SPR_BOSS, 3, 3, a_chase, S_BOSS_RUN8), /* S_BOSS_RUN7 */ + STATE_ENTRY(SPR_BOSS, 3, 3, a_chase, S_BOSS_RUN1), /* S_BOSS_RUN8 */ + STATE_ENTRY(SPR_BOSS, 4, 8, a_face_target, S_BOSS_ATK2), /* S_BOSS_ATK1 */ + STATE_ENTRY(SPR_BOSS, 5, 8, a_face_target, S_BOSS_ATK3), /* S_BOSS_ATK2 */ + STATE_ENTRY(SPR_BOSS, 6, 8, a_bruis_attack, + S_BOSS_RUN1), /* S_BOSS_ATK3 */ + STATE_ENTRY(SPR_BOSS, 7, 2, NULL, S_BOSS_PAIN2), /* S_BOSS_PAIN */ + STATE_ENTRY(SPR_BOSS, 7, 2, a_pain, S_BOSS_RUN1), /* S_BOSS_PAIN2 */ + STATE_ENTRY(SPR_BOSS, 8, 8, NULL, S_BOSS_DIE2), /* S_BOSS_DIE1 */ + STATE_ENTRY(SPR_BOSS, 9, 8, a_scream, S_BOSS_DIE3), /* S_BOSS_DIE2 */ + STATE_ENTRY(SPR_BOSS, 10, 8, NULL, S_BOSS_DIE4), /* S_BOSS_DIE3 */ + STATE_ENTRY(SPR_BOSS, 11, 8, a_fall, S_BOSS_DIE5), /* S_BOSS_DIE4 */ + STATE_ENTRY(SPR_BOSS, 12, 8, NULL, S_BOSS_DIE6), /* S_BOSS_DIE5 */ + STATE_ENTRY(SPR_BOSS, 13, 8, NULL, S_BOSS_DIE7), /* S_BOSS_DIE6 */ + STATE_ENTRY(SPR_BOSS, 14, -1, a_boss_death, S_NULL), /* S_BOSS_DIE7 */ + STATE_ENTRY(SPR_BOSS, 14, 8, NULL, S_BOSS_RAISE2), /* S_BOSS_RAISE1 */ + STATE_ENTRY(SPR_BOSS, 13, 8, NULL, S_BOSS_RAISE3), /* S_BOSS_RAISE2 */ + STATE_ENTRY(SPR_BOSS, 12, 8, NULL, S_BOSS_RAISE4), /* S_BOSS_RAISE3 */ + STATE_ENTRY(SPR_BOSS, 11, 8, NULL, S_BOSS_RAISE5), /* S_BOSS_RAISE4 */ + STATE_ENTRY(SPR_BOSS, 10, 8, NULL, S_BOSS_RAISE6), /* S_BOSS_RAISE5 */ + STATE_ENTRY(SPR_BOSS, 9, 8, NULL, S_BOSS_RAISE7), /* S_BOSS_RAISE6 */ + STATE_ENTRY(SPR_BOSS, 8, 8, NULL, S_BOSS_RUN1), /* S_BOSS_RAISE7 */ + STATE_ENTRY(SPR_BOS2, 0, 10, a_look, S_BOS2_STND2), /* S_BOS2_STND */ + STATE_ENTRY(SPR_BOS2, 1, 10, a_look, S_BOS2_STND), /* S_BOS2_STND2 */ + STATE_ENTRY(SPR_BOS2, 0, 3, a_chase, S_BOS2_RUN2), /* S_BOS2_RUN1 */ + STATE_ENTRY(SPR_BOS2, 0, 3, a_chase, S_BOS2_RUN3), /* S_BOS2_RUN2 */ + STATE_ENTRY(SPR_BOS2, 1, 3, a_chase, S_BOS2_RUN4), /* S_BOS2_RUN3 */ + STATE_ENTRY(SPR_BOS2, 1, 3, a_chase, S_BOS2_RUN5), /* S_BOS2_RUN4 */ + STATE_ENTRY(SPR_BOS2, 2, 3, a_chase, S_BOS2_RUN6), /* S_BOS2_RUN5 */ + STATE_ENTRY(SPR_BOS2, 2, 3, a_chase, S_BOS2_RUN7), /* S_BOS2_RUN6 */ + STATE_ENTRY(SPR_BOS2, 3, 3, a_chase, S_BOS2_RUN8), /* S_BOS2_RUN7 */ + STATE_ENTRY(SPR_BOS2, 3, 3, a_chase, S_BOS2_RUN1), /* S_BOS2_RUN8 */ + STATE_ENTRY(SPR_BOS2, 4, 8, a_face_target, S_BOS2_ATK2), /* S_BOS2_ATK1 */ + STATE_ENTRY(SPR_BOS2, 5, 8, a_face_target, S_BOS2_ATK3), /* S_BOS2_ATK2 */ + STATE_ENTRY(SPR_BOS2, 6, 8, a_bruis_attack, + S_BOS2_RUN1), /* S_BOS2_ATK3 */ + STATE_ENTRY(SPR_BOS2, 7, 2, NULL, S_BOS2_PAIN2), /* S_BOS2_PAIN */ + STATE_ENTRY(SPR_BOS2, 7, 2, a_pain, S_BOS2_RUN1), /* S_BOS2_PAIN2 */ + STATE_ENTRY(SPR_BOS2, 8, 8, NULL, S_BOS2_DIE2), /* S_BOS2_DIE1 */ + STATE_ENTRY(SPR_BOS2, 9, 8, a_scream, S_BOS2_DIE3), /* S_BOS2_DIE2 */ + STATE_ENTRY(SPR_BOS2, 10, 8, NULL, S_BOS2_DIE4), /* S_BOS2_DIE3 */ + STATE_ENTRY(SPR_BOS2, 11, 8, a_fall, S_BOS2_DIE5), /* S_BOS2_DIE4 */ + STATE_ENTRY(SPR_BOS2, 12, 8, NULL, S_BOS2_DIE6), /* S_BOS2_DIE5 */ + STATE_ENTRY(SPR_BOS2, 13, 8, NULL, S_BOS2_DIE7), /* S_BOS2_DIE6 */ + STATE_ENTRY(SPR_BOS2, 14, -1, NULL, S_NULL), /* S_BOS2_DIE7 */ + STATE_ENTRY(SPR_BOS2, 14, 8, NULL, S_BOS2_RAISE2), /* S_BOS2_RAISE1 */ + STATE_ENTRY(SPR_BOS2, 13, 8, NULL, S_BOS2_RAISE3), /* S_BOS2_RAISE2 */ + STATE_ENTRY(SPR_BOS2, 12, 8, NULL, S_BOS2_RAISE4), /* S_BOS2_RAISE3 */ + STATE_ENTRY(SPR_BOS2, 11, 8, NULL, S_BOS2_RAISE5), /* S_BOS2_RAISE4 */ + STATE_ENTRY(SPR_BOS2, 10, 8, NULL, S_BOS2_RAISE6), /* S_BOS2_RAISE5 */ + STATE_ENTRY(SPR_BOS2, 9, 8, NULL, S_BOS2_RAISE7), /* S_BOS2_RAISE6 */ + STATE_ENTRY(SPR_BOS2, 8, 8, NULL, S_BOS2_RUN1), /* S_BOS2_RAISE7 */ + STATE_ENTRY(SPR_SKUL, 32768, 10, a_look, + S_SKULL_STND2), /* S_SKULL_STND */ + STATE_ENTRY(SPR_SKUL, 32769, 10, a_look, + S_SKULL_STND), /* S_SKULL_STND2 */ + STATE_ENTRY(SPR_SKUL, 32768, 6, a_chase, + S_SKULL_RUN2), /* S_SKULL_RUN1 */ + STATE_ENTRY(SPR_SKUL, 32769, 6, a_chase, + S_SKULL_RUN1), /* S_SKULL_RUN2 */ + STATE_ENTRY(SPR_SKUL, 32770, 10, a_face_target, + S_SKULL_ATK2), /* S_SKULL_ATK1 */ + STATE_ENTRY(SPR_SKUL, 32771, 4, a_skull_attack, + S_SKULL_ATK3), /* S_SKULL_ATK2 */ + STATE_ENTRY(SPR_SKUL, 32770, 4, NULL, S_SKULL_ATK4), /* S_SKULL_ATK3 */ + STATE_ENTRY(SPR_SKUL, 32771, 4, NULL, S_SKULL_ATK3), /* S_SKULL_ATK4 */ + STATE_ENTRY(SPR_SKUL, 32772, 3, NULL, S_SKULL_PAIN2), /* S_SKULL_PAIN */ + STATE_ENTRY(SPR_SKUL, 32772, 3, a_pain, S_SKULL_RUN1), /* S_SKULL_PAIN2 */ + STATE_ENTRY(SPR_SKUL, 32773, 6, NULL, S_SKULL_DIE2), /* S_SKULL_DIE1 */ + STATE_ENTRY(SPR_SKUL, 32774, 6, a_scream, + S_SKULL_DIE3), /* S_SKULL_DIE2 */ + STATE_ENTRY(SPR_SKUL, 32775, 6, NULL, S_SKULL_DIE4), /* S_SKULL_DIE3 */ + STATE_ENTRY(SPR_SKUL, 32776, 6, a_fall, S_SKULL_DIE5), /* S_SKULL_DIE4 */ + STATE_ENTRY(SPR_SKUL, 9, 6, NULL, S_SKULL_DIE6), /* S_SKULL_DIE5 */ + STATE_ENTRY(SPR_SKUL, 10, 6, NULL, S_NULL), /* S_SKULL_DIE6 */ + STATE_ENTRY(SPR_SPID, 0, 10, a_look, S_SPID_STND2), /* S_SPID_STND */ + STATE_ENTRY(SPR_SPID, 1, 10, a_look, S_SPID_STND), /* S_SPID_STND2 */ + STATE_ENTRY(SPR_SPID, 0, 3, a_metal, S_SPID_RUN2), /* S_SPID_RUN1 */ + STATE_ENTRY(SPR_SPID, 0, 3, a_chase, S_SPID_RUN3), /* S_SPID_RUN2 */ + STATE_ENTRY(SPR_SPID, 1, 3, a_chase, S_SPID_RUN4), /* S_SPID_RUN3 */ + STATE_ENTRY(SPR_SPID, 1, 3, a_chase, S_SPID_RUN5), /* S_SPID_RUN4 */ + STATE_ENTRY(SPR_SPID, 2, 3, a_metal, S_SPID_RUN6), /* S_SPID_RUN5 */ + STATE_ENTRY(SPR_SPID, 2, 3, a_chase, S_SPID_RUN7), /* S_SPID_RUN6 */ + STATE_ENTRY(SPR_SPID, 3, 3, a_chase, S_SPID_RUN8), /* S_SPID_RUN7 */ + STATE_ENTRY(SPR_SPID, 3, 3, a_chase, S_SPID_RUN9), /* S_SPID_RUN8 */ + STATE_ENTRY(SPR_SPID, 4, 3, a_metal, S_SPID_RUN10), /* S_SPID_RUN9 */ + STATE_ENTRY(SPR_SPID, 4, 3, a_chase, S_SPID_RUN11), /* S_SPID_RUN10 */ + STATE_ENTRY(SPR_SPID, 5, 3, a_chase, S_SPID_RUN12), /* S_SPID_RUN11 */ + STATE_ENTRY(SPR_SPID, 5, 3, a_chase, S_SPID_RUN1), /* S_SPID_RUN12 */ + STATE_ENTRY(SPR_SPID, 32768, 20, a_face_target, + S_SPID_ATK2), /* S_SPID_ATK1 */ + STATE_ENTRY(SPR_SPID, 32774, 4, a_s_pos_attack, + S_SPID_ATK3), /* S_SPID_ATK2 */ + STATE_ENTRY(SPR_SPID, 32775, 4, a_s_pos_attack, + S_SPID_ATK4), /* S_SPID_ATK3 */ + STATE_ENTRY(SPR_SPID, 32775, 1, a_spid_refire, + S_SPID_ATK2), /* S_SPID_ATK4 */ + STATE_ENTRY(SPR_SPID, 8, 3, NULL, S_SPID_PAIN2), /* S_SPID_PAIN */ + STATE_ENTRY(SPR_SPID, 8, 3, a_pain, S_SPID_RUN1), /* S_SPID_PAIN2 */ + STATE_ENTRY(SPR_SPID, 9, 20, a_scream, S_SPID_DIE2), /* S_SPID_DIE1 */ + STATE_ENTRY(SPR_SPID, 10, 10, a_fall, S_SPID_DIE3), /* S_SPID_DIE2 */ + STATE_ENTRY(SPR_SPID, 11, 10, NULL, S_SPID_DIE4), /* S_SPID_DIE3 */ + STATE_ENTRY(SPR_SPID, 12, 10, NULL, S_SPID_DIE5), /* S_SPID_DIE4 */ + STATE_ENTRY(SPR_SPID, 13, 10, NULL, S_SPID_DIE6), /* S_SPID_DIE5 */ + STATE_ENTRY(SPR_SPID, 14, 10, NULL, S_SPID_DIE7), /* S_SPID_DIE6 */ + STATE_ENTRY(SPR_SPID, 15, 10, NULL, S_SPID_DIE8), /* S_SPID_DIE7 */ + STATE_ENTRY(SPR_SPID, 16, 10, NULL, S_SPID_DIE9), /* S_SPID_DIE8 */ + STATE_ENTRY(SPR_SPID, 17, 10, NULL, S_SPID_DIE10), /* S_SPID_DIE9 */ + STATE_ENTRY(SPR_SPID, 18, 30, NULL, S_SPID_DIE11), /* S_SPID_DIE10 */ + STATE_ENTRY(SPR_SPID, 18, -1, a_boss_death, S_NULL), /* S_SPID_DIE11 */ + STATE_ENTRY(SPR_BSPI, 0, 10, a_look, S_BSPI_STND2), /* S_BSPI_STND */ + STATE_ENTRY(SPR_BSPI, 1, 10, a_look, S_BSPI_STND), /* S_BSPI_STND2 */ + STATE_ENTRY(SPR_BSPI, 0, 20, NULL, S_BSPI_RUN1), /* S_BSPI_SIGHT */ + STATE_ENTRY(SPR_BSPI, 0, 3, a_baby_metal, S_BSPI_RUN2), /* S_BSPI_RUN1 */ + STATE_ENTRY(SPR_BSPI, 0, 3, a_chase, S_BSPI_RUN3), /* S_BSPI_RUN2 */ + STATE_ENTRY(SPR_BSPI, 1, 3, a_chase, S_BSPI_RUN4), /* S_BSPI_RUN3 */ + STATE_ENTRY(SPR_BSPI, 1, 3, a_chase, S_BSPI_RUN5), /* S_BSPI_RUN4 */ + STATE_ENTRY(SPR_BSPI, 2, 3, a_chase, S_BSPI_RUN6), /* S_BSPI_RUN5 */ + STATE_ENTRY(SPR_BSPI, 2, 3, a_chase, S_BSPI_RUN7), /* S_BSPI_RUN6 */ + STATE_ENTRY(SPR_BSPI, 3, 3, a_baby_metal, S_BSPI_RUN8), /* S_BSPI_RUN7 */ + STATE_ENTRY(SPR_BSPI, 3, 3, a_chase, S_BSPI_RUN9), /* S_BSPI_RUN8 */ + STATE_ENTRY(SPR_BSPI, 4, 3, a_chase, S_BSPI_RUN10), /* S_BSPI_RUN9 */ + STATE_ENTRY(SPR_BSPI, 4, 3, a_chase, S_BSPI_RUN11), /* S_BSPI_RUN10 */ + STATE_ENTRY(SPR_BSPI, 5, 3, a_chase, S_BSPI_RUN12), /* S_BSPI_RUN11 */ + STATE_ENTRY(SPR_BSPI, 5, 3, a_chase, S_BSPI_RUN1), /* S_BSPI_RUN12 */ + STATE_ENTRY(SPR_BSPI, 32768, 20, a_face_target, + S_BSPI_ATK2), /* S_BSPI_ATK1 */ + STATE_ENTRY(SPR_BSPI, 32774, 4, a_bspi_attack, + S_BSPI_ATK3), /* S_BSPI_ATK2 */ + STATE_ENTRY(SPR_BSPI, 32775, 4, NULL, S_BSPI_ATK4), /* S_BSPI_ATK3 */ + STATE_ENTRY(SPR_BSPI, 32775, 1, a_spid_refire, + S_BSPI_ATK2), /* S_BSPI_ATK4 */ + STATE_ENTRY(SPR_BSPI, 8, 3, NULL, S_BSPI_PAIN2), /* S_BSPI_PAIN */ + STATE_ENTRY(SPR_BSPI, 8, 3, a_pain, S_BSPI_RUN1), /* S_BSPI_PAIN2 */ + STATE_ENTRY(SPR_BSPI, 9, 20, a_scream, S_BSPI_DIE2), /* S_BSPI_DIE1 */ + STATE_ENTRY(SPR_BSPI, 10, 7, a_fall, S_BSPI_DIE3), /* S_BSPI_DIE2 */ + STATE_ENTRY(SPR_BSPI, 11, 7, NULL, S_BSPI_DIE4), /* S_BSPI_DIE3 */ + STATE_ENTRY(SPR_BSPI, 12, 7, NULL, S_BSPI_DIE5), /* S_BSPI_DIE4 */ + STATE_ENTRY(SPR_BSPI, 13, 7, NULL, S_BSPI_DIE6), /* S_BSPI_DIE5 */ + STATE_ENTRY(SPR_BSPI, 14, 7, NULL, S_BSPI_DIE7), /* S_BSPI_DIE6 */ + STATE_ENTRY(SPR_BSPI, 15, -1, a_boss_death, S_NULL), /* S_BSPI_DIE7 */ + STATE_ENTRY(SPR_BSPI, 15, 5, NULL, S_BSPI_RAISE2), /* S_BSPI_RAISE1 */ + STATE_ENTRY(SPR_BSPI, 14, 5, NULL, S_BSPI_RAISE3), /* S_BSPI_RAISE2 */ + STATE_ENTRY(SPR_BSPI, 13, 5, NULL, S_BSPI_RAISE4), /* S_BSPI_RAISE3 */ + STATE_ENTRY(SPR_BSPI, 12, 5, NULL, S_BSPI_RAISE5), /* S_BSPI_RAISE4 */ + STATE_ENTRY(SPR_BSPI, 11, 5, NULL, S_BSPI_RAISE6), /* S_BSPI_RAISE5 */ + STATE_ENTRY(SPR_BSPI, 10, 5, NULL, S_BSPI_RAISE7), /* S_BSPI_RAISE6 */ + STATE_ENTRY(SPR_BSPI, 9, 5, NULL, S_BSPI_RUN1), /* S_BSPI_RAISE7 */ + STATE_ENTRY(SPR_APLS, 32768, 5, NULL, S_ARACH_PLAZ2), /* S_ARACH_PLAZ */ + STATE_ENTRY(SPR_APLS, 32769, 5, NULL, S_ARACH_PLAZ), /* S_ARACH_PLAZ2 */ + STATE_ENTRY(SPR_APBX, 32768, 5, NULL, S_ARACH_PLEX2), /* S_ARACH_PLEX */ + STATE_ENTRY(SPR_APBX, 32769, 5, NULL, S_ARACH_PLEX3), /* S_ARACH_PLEX2 */ + STATE_ENTRY(SPR_APBX, 32770, 5, NULL, S_ARACH_PLEX4), /* S_ARACH_PLEX3 */ + STATE_ENTRY(SPR_APBX, 32771, 5, NULL, S_ARACH_PLEX5), /* S_ARACH_PLEX4 */ + STATE_ENTRY(SPR_APBX, 32772, 5, NULL, S_NULL), /* S_ARACH_PLEX5 */ + STATE_ENTRY(SPR_CYBR, 0, 10, a_look, S_CYBER_STND2), /* S_CYBER_STND */ + STATE_ENTRY(SPR_CYBR, 1, 10, a_look, S_CYBER_STND), /* S_CYBER_STND2 */ + STATE_ENTRY(SPR_CYBR, 0, 3, a_hoof, S_CYBER_RUN2), /* S_CYBER_RUN1 */ + STATE_ENTRY(SPR_CYBR, 0, 3, a_chase, S_CYBER_RUN3), /* S_CYBER_RUN2 */ + STATE_ENTRY(SPR_CYBR, 1, 3, a_chase, S_CYBER_RUN4), /* S_CYBER_RUN3 */ + STATE_ENTRY(SPR_CYBR, 1, 3, a_chase, S_CYBER_RUN5), /* S_CYBER_RUN4 */ + STATE_ENTRY(SPR_CYBR, 2, 3, a_chase, S_CYBER_RUN6), /* S_CYBER_RUN5 */ + STATE_ENTRY(SPR_CYBR, 2, 3, a_chase, S_CYBER_RUN7), /* S_CYBER_RUN6 */ + STATE_ENTRY(SPR_CYBR, 3, 3, a_metal, S_CYBER_RUN8), /* S_CYBER_RUN7 */ + STATE_ENTRY(SPR_CYBR, 3, 3, a_chase, S_CYBER_RUN1), /* S_CYBER_RUN8 */ + STATE_ENTRY(SPR_CYBR, 4, 6, a_face_target, + S_CYBER_ATK2), /* S_CYBER_ATK1 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_ATK3), /* S_CYBER_ATK2 */ + STATE_ENTRY(SPR_CYBR, 4, 12, a_face_target, + S_CYBER_ATK4), /* S_CYBER_ATK3 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_ATK5), /* S_CYBER_ATK4 */ + STATE_ENTRY(SPR_CYBR, 4, 12, a_face_target, + S_CYBER_ATK6), /* S_CYBER_ATK5 */ + STATE_ENTRY(SPR_CYBR, 5, 12, a_cyber_attack, + S_CYBER_RUN1), /* S_CYBER_ATK6 */ + STATE_ENTRY(SPR_CYBR, 6, 10, a_pain, S_CYBER_RUN1), /* S_CYBER_PAIN */ + STATE_ENTRY(SPR_CYBR, 7, 10, NULL, S_CYBER_DIE2), /* S_CYBER_DIE1 */ + STATE_ENTRY(SPR_CYBR, 8, 10, a_scream, S_CYBER_DIE3), /* S_CYBER_DIE2 */ + STATE_ENTRY(SPR_CYBR, 9, 10, NULL, S_CYBER_DIE4), /* S_CYBER_DIE3 */ + STATE_ENTRY(SPR_CYBR, 10, 10, NULL, S_CYBER_DIE5), /* S_CYBER_DIE4 */ + STATE_ENTRY(SPR_CYBR, 11, 10, NULL, S_CYBER_DIE6), /* S_CYBER_DIE5 */ + STATE_ENTRY(SPR_CYBR, 12, 10, a_fall, S_CYBER_DIE7), /* S_CYBER_DIE6 */ + STATE_ENTRY(SPR_CYBR, 13, 10, NULL, S_CYBER_DIE8), /* S_CYBER_DIE7 */ + STATE_ENTRY(SPR_CYBR, 14, 10, NULL, S_CYBER_DIE9), /* S_CYBER_DIE8 */ + STATE_ENTRY(SPR_CYBR, 15, 30, NULL, S_CYBER_DIE10), /* S_CYBER_DIE9 */ + STATE_ENTRY(SPR_CYBR, 15, -1, a_boss_death, S_NULL), /* S_CYBER_DIE10 */ + STATE_ENTRY(SPR_PAIN, 0, 10, a_look, S_PAIN_STND), /* S_PAIN_STND */ + STATE_ENTRY(SPR_PAIN, 0, 3, a_chase, S_PAIN_RUN2), /* S_PAIN_RUN1 */ + STATE_ENTRY(SPR_PAIN, 0, 3, a_chase, S_PAIN_RUN3), /* S_PAIN_RUN2 */ + STATE_ENTRY(SPR_PAIN, 1, 3, a_chase, S_PAIN_RUN4), /* S_PAIN_RUN3 */ + STATE_ENTRY(SPR_PAIN, 1, 3, a_chase, S_PAIN_RUN5), /* S_PAIN_RUN4 */ + STATE_ENTRY(SPR_PAIN, 2, 3, a_chase, S_PAIN_RUN6), /* S_PAIN_RUN5 */ + STATE_ENTRY(SPR_PAIN, 2, 3, a_chase, S_PAIN_RUN1), /* S_PAIN_RUN6 */ + STATE_ENTRY(SPR_PAIN, 3, 5, a_face_target, S_PAIN_ATK2), /* S_PAIN_ATK1 */ + STATE_ENTRY(SPR_PAIN, 4, 5, a_face_target, S_PAIN_ATK3), /* S_PAIN_ATK2 */ + STATE_ENTRY(SPR_PAIN, 32773, 5, a_face_target, + S_PAIN_ATK4), /* S_PAIN_ATK3 */ + STATE_ENTRY(SPR_PAIN, 32773, 0, a_pain_attack, + S_PAIN_RUN1), /* S_PAIN_ATK4 */ + STATE_ENTRY(SPR_PAIN, 6, 6, NULL, S_PAIN_PAIN2), /* S_PAIN_PAIN */ + STATE_ENTRY(SPR_PAIN, 6, 6, a_pain, S_PAIN_RUN1), /* S_PAIN_PAIN2 */ + STATE_ENTRY(SPR_PAIN, 32775, 8, NULL, S_PAIN_DIE2), /* S_PAIN_DIE1 */ + STATE_ENTRY(SPR_PAIN, 32776, 8, a_scream, S_PAIN_DIE3), /* S_PAIN_DIE2 */ + STATE_ENTRY(SPR_PAIN, 32777, 8, NULL, S_PAIN_DIE4), /* S_PAIN_DIE3 */ + STATE_ENTRY(SPR_PAIN, 32778, 8, NULL, S_PAIN_DIE5), /* S_PAIN_DIE4 */ + STATE_ENTRY(SPR_PAIN, 32779, 8, a_pain_die, + S_PAIN_DIE6), /* S_PAIN_DIE5 */ + STATE_ENTRY(SPR_PAIN, 32780, 8, NULL, S_NULL), /* S_PAIN_DIE6 */ + STATE_ENTRY(SPR_PAIN, 12, 8, NULL, S_PAIN_RAISE2), /* S_PAIN_RAISE1 */ + STATE_ENTRY(SPR_PAIN, 11, 8, NULL, S_PAIN_RAISE3), /* S_PAIN_RAISE2 */ + STATE_ENTRY(SPR_PAIN, 10, 8, NULL, S_PAIN_RAISE4), /* S_PAIN_RAISE3 */ + STATE_ENTRY(SPR_PAIN, 9, 8, NULL, S_PAIN_RAISE5), /* S_PAIN_RAISE4 */ + STATE_ENTRY(SPR_PAIN, 8, 8, NULL, S_PAIN_RAISE6), /* S_PAIN_RAISE5 */ + STATE_ENTRY(SPR_PAIN, 7, 8, NULL, S_PAIN_RUN1), /* S_PAIN_RAISE6 */ + STATE_ENTRY(SPR_SSWV, 0, 10, a_look, S_SSWV_STND2), /* S_SSWV_STND */ + STATE_ENTRY(SPR_SSWV, 1, 10, a_look, S_SSWV_STND), /* S_SSWV_STND2 */ + STATE_ENTRY(SPR_SSWV, 0, 3, a_chase, S_SSWV_RUN2), /* S_SSWV_RUN1 */ + STATE_ENTRY(SPR_SSWV, 0, 3, a_chase, S_SSWV_RUN3), /* S_SSWV_RUN2 */ + STATE_ENTRY(SPR_SSWV, 1, 3, a_chase, S_SSWV_RUN4), /* S_SSWV_RUN3 */ + STATE_ENTRY(SPR_SSWV, 1, 3, a_chase, S_SSWV_RUN5), /* S_SSWV_RUN4 */ + STATE_ENTRY(SPR_SSWV, 2, 3, a_chase, S_SSWV_RUN6), /* S_SSWV_RUN5 */ + STATE_ENTRY(SPR_SSWV, 2, 3, a_chase, S_SSWV_RUN7), /* S_SSWV_RUN6 */ + STATE_ENTRY(SPR_SSWV, 3, 3, a_chase, S_SSWV_RUN8), /* S_SSWV_RUN7 */ + STATE_ENTRY(SPR_SSWV, 3, 3, a_chase, S_SSWV_RUN1), /* S_SSWV_RUN8 */ + STATE_ENTRY(SPR_SSWV, 4, 10, a_face_target, + S_SSWV_ATK2), /* S_SSWV_ATK1 */ + STATE_ENTRY(SPR_SSWV, 5, 10, a_face_target, + S_SSWV_ATK3), /* S_SSWV_ATK2 */ + STATE_ENTRY(SPR_SSWV, 32774, 4, a_c_pos_attack, + S_SSWV_ATK4), /* S_SSWV_ATK3 */ + STATE_ENTRY(SPR_SSWV, 5, 6, a_face_target, S_SSWV_ATK5) , /* S_SSWV_ATK4 */ + STATE_ENTRY(SPR_SSWV, 32774, 4, a_c_pos_attack, + S_SSWV_ATK6), /* S_SSWV_ATK5 */ + STATE_ENTRY(SPR_SSWV, 5, 1, a_c_pos_refire, + S_SSWV_ATK2), /* S_SSWV_ATK6 */ + STATE_ENTRY(SPR_SSWV, 7, 3, NULL, S_SSWV_PAIN2), /* S_SSWV_PAIN */ + STATE_ENTRY(SPR_SSWV, 7, 3, a_pain, S_SSWV_RUN1), /* S_SSWV_PAIN2 */ + STATE_ENTRY(SPR_SSWV, 8, 5, NULL, S_SSWV_DIE2), /* S_SSWV_DIE1 */ + STATE_ENTRY(SPR_SSWV, 9, 5, a_scream, S_SSWV_DIE3), /* S_SSWV_DIE2 */ + STATE_ENTRY(SPR_SSWV, 10, 5, a_fall, S_SSWV_DIE4), /* S_SSWV_DIE3 */ + STATE_ENTRY(SPR_SSWV, 11, 5, NULL, S_SSWV_DIE5), /* S_SSWV_DIE4 */ + STATE_ENTRY(SPR_SSWV, 12, -1, NULL, S_NULL), /* S_SSWV_DIE5 */ + STATE_ENTRY(SPR_SSWV, 13, 5, NULL, S_SSWV_XDIE2), /* S_SSWV_XDIE1 */ + STATE_ENTRY(SPR_SSWV, 14, 5, a_xscream, S_SSWV_XDIE3), /* S_SSWV_XDIE2 */ + STATE_ENTRY(SPR_SSWV, 15, 5, a_fall, S_SSWV_XDIE4), /* S_SSWV_XDIE3 */ + STATE_ENTRY(SPR_SSWV, 16, 5, NULL, S_SSWV_XDIE5), /* S_SSWV_XDIE4 */ + STATE_ENTRY(SPR_SSWV, 17, 5, NULL, S_SSWV_XDIE6), /* S_SSWV_XDIE5 */ + STATE_ENTRY(SPR_SSWV, 18, 5, NULL, S_SSWV_XDIE7), /* S_SSWV_XDIE6 */ + STATE_ENTRY(SPR_SSWV, 19, 5, NULL, S_SSWV_XDIE8), /* S_SSWV_XDIE7 */ + STATE_ENTRY(SPR_SSWV, 20, 5, NULL, S_SSWV_XDIE9), /* S_SSWV_XDIE8 */ + STATE_ENTRY(SPR_SSWV, 21, -1, NULL, S_NULL), /* S_SSWV_XDIE9 */ + STATE_ENTRY(SPR_SSWV, 12, 5, NULL, S_SSWV_RAISE2), /* S_SSWV_RAISE1 */ + STATE_ENTRY(SPR_SSWV, 11, 5, NULL, S_SSWV_RAISE3), /* S_SSWV_RAISE2 */ + STATE_ENTRY(SPR_SSWV, 10, 5, NULL, S_SSWV_RAISE4), /* S_SSWV_RAISE3 */ + STATE_ENTRY(SPR_SSWV, 9, 5, NULL, S_SSWV_RAISE5), /* S_SSWV_RAISE4 */ + STATE_ENTRY(SPR_SSWV, 8, 5, NULL, S_SSWV_RUN1), /* S_SSWV_RAISE5 */ + STATE_ENTRY(SPR_KEEN, 0, -1, NULL, S_KEENSTND), /* S_KEENSTND */ + STATE_ENTRY(SPR_KEEN, 0, 6, NULL, S_COMMKEEN2), /* S_COMMKEEN */ + STATE_ENTRY(SPR_KEEN, 1, 6, NULL, S_COMMKEEN3), /* S_COMMKEEN2 */ + STATE_ENTRY(SPR_KEEN, 2, 6, a_scream, S_COMMKEEN4), /* S_COMMKEEN3 */ + STATE_ENTRY(SPR_KEEN, 3, 6, NULL, S_COMMKEEN5), /* S_COMMKEEN4 */ + STATE_ENTRY(SPR_KEEN, 4, 6, NULL, S_COMMKEEN6), /* S_COMMKEEN5 */ + STATE_ENTRY(SPR_KEEN, 5, 6, NULL, S_COMMKEEN7), /* S_COMMKEEN6 */ + STATE_ENTRY(SPR_KEEN, 6, 6, NULL, S_COMMKEEN8), /* S_COMMKEEN7 */ + STATE_ENTRY(SPR_KEEN, 7, 6, NULL, S_COMMKEEN9), /* S_COMMKEEN8 */ + STATE_ENTRY(SPR_KEEN, 8, 6, NULL, S_COMMKEEN10), /* S_COMMKEEN9 */ + STATE_ENTRY(SPR_KEEN, 9, 6, NULL, S_COMMKEEN11), /* S_COMMKEEN10 */ + STATE_ENTRY(SPR_KEEN, 10, 6, a_keen_die, S_COMMKEEN12), /* S_COMMKEEN11 */ + STATE_ENTRY(SPR_KEEN, 11, -1, NULL, S_NULL), /* S_COMMKEEN12 */ + STATE_ENTRY(SPR_KEEN, 12, 4, NULL, S_KEENPAIN2), /* S_KEENPAIN */ + STATE_ENTRY(SPR_KEEN, 12, 8, a_pain, S_KEENSTND), /* S_KEENPAIN2 */ + STATE_ENTRY(SPR_BBRN, 0, -1, NULL, S_NULL), /* S_BRAIN */ + STATE_ENTRY(SPR_BBRN, 1, 36, a_brain_pain, S_BRAIN), /* S_BRAIN_PAIN */ + STATE_ENTRY(SPR_BBRN, 0, 100, a_brain_scream, + S_BRAIN_DIE2), /* S_BRAIN_DIE1 */ + STATE_ENTRY(SPR_BBRN, 0, 10, NULL, S_BRAIN_DIE3), /* S_BRAIN_DIE2 */ + STATE_ENTRY(SPR_BBRN, 0, 10, NULL, S_BRAIN_DIE4), /* S_BRAIN_DIE3 */ + STATE_ENTRY(SPR_BBRN, 0, -1, a_brain_die, S_NULL), /* S_BRAIN_DIE4 */ + STATE_ENTRY(SPR_SSWV, 0, 10, a_look, S_BRAINEYE), /* S_BRAINEYE */ + STATE_ENTRY(SPR_SSWV, 0, 181, + a_brain_awake, S_BRAINEYE1), /* S_BRAINEYESEE */ + STATE_ENTRY(SPR_SSWV, 0, 150, + a_brain_split, S_BRAINEYE1), /* S_BRAINEYE1 */ + STATE_ENTRY(SPR_BOSF, 32768, 3, a_spawn_sound, S_SPAWN2), /* S_SPAWN1 */ + STATE_ENTRY(SPR_BOSF, 32769, 3, a_spawn_fly, S_SPAWN3), /* S_SPAWN2 */ + STATE_ENTRY(SPR_BOSF, 32770, 3, a_spawn_fly, S_SPAWN4), /* S_SPAWN3 */ + STATE_ENTRY(SPR_BOSF, 32771, 3, a_spawn_fly, S_SPAWN1), /* S_SPAWN4 */ + STATE_ENTRY(SPR_FIRE, 32768, 4, a_fire, S_SPAWNFIRE2), /* S_SPAWNFIRE1 */ + STATE_ENTRY(SPR_FIRE, 32769, 4, a_fire, S_SPAWNFIRE3), /* S_SPAWNFIRE2 */ + STATE_ENTRY(SPR_FIRE, 32770, 4, a_fire, S_SPAWNFIRE4), /* S_SPAWNFIRE3 */ + STATE_ENTRY(SPR_FIRE, 32771, 4, a_fire, S_SPAWNFIRE5), /* S_SPAWNFIRE4 */ + STATE_ENTRY(SPR_FIRE, 32772, 4, a_fire, S_SPAWNFIRE6), /* S_SPAWNFIRE5 */ + STATE_ENTRY(SPR_FIRE, 32773, 4, a_fire, S_SPAWNFIRE7), /* S_SPAWNFIRE6 */ + STATE_ENTRY(SPR_FIRE, 32774, 4, a_fire, S_SPAWNFIRE8), /* S_SPAWNFIRE7 */ + STATE_ENTRY(SPR_FIRE, 32775, 4, a_fire, S_NULL), /* S_SPAWNFIRE8 */ + STATE_ENTRY(SPR_MISL, 32769, 10, NULL, + S_BRAINEXPLODE2), /* S_BRAINEXPLODE1 */ + STATE_ENTRY(SPR_MISL, 32770, 10, NULL, + S_BRAINEXPLODE3), /* S_BRAINEXPLODE2 */ + STATE_ENTRY(SPR_MISL, 32771, 10, a_brain_explode, + S_NULL), /* S_BRAINEXPLODE3 */ + STATE_ENTRY(SPR_ARM1, 0, 6, NULL, S_ARM1A), /* S_ARM1 */ + STATE_ENTRY(SPR_ARM1, 32769, 7, NULL, S_ARM1), /* S_ARM1A */ + STATE_ENTRY(SPR_ARM2, 0, 6, NULL, S_ARM2A), /* S_ARM2 */ + STATE_ENTRY(SPR_ARM2, 32769, 6, NULL, S_ARM2), /* S_ARM2A */ + STATE_ENTRY(SPR_BAR1, 0, 6, NULL, S_BAR2), /* S_BAR1 */ + STATE_ENTRY(SPR_BAR1, 1, 6, NULL, S_BAR1), /* S_BAR2 */ + STATE_ENTRY(SPR_BEXP, 32768, 5, NULL, S_BEXP2), /* S_BEXP */ + STATE_ENTRY(SPR_BEXP, 32769, 5, a_scream, S_BEXP3), /* S_BEXP2 */ + STATE_ENTRY(SPR_BEXP, 32770, 5, NULL, S_BEXP4), /* S_BEXP3 */ + STATE_ENTRY(SPR_BEXP, 32771, 10, a_explode, S_BEXP5), /* S_BEXP4 */ + STATE_ENTRY(SPR_BEXP, 32772, 10, NULL, S_NULL), /* S_BEXP5 */ + STATE_ENTRY(SPR_FCAN, 32768, 4, NULL, S_BBAR2), /* S_BBAR1 */ + STATE_ENTRY(SPR_FCAN, 32769, 4, NULL, S_BBAR3), /* S_BBAR2 */ + STATE_ENTRY(SPR_FCAN, 32770, 4, NULL, S_BBAR1), /* S_BBAR3 */ + STATE_ENTRY(SPR_BON1, 0, 6, NULL, S_BON1A), /* S_BON1 */ + STATE_ENTRY(SPR_BON1, 1, 6, NULL, S_BON1B), /* S_BON1A */ + STATE_ENTRY(SPR_BON1, 2, 6, NULL, S_BON1C), /* S_BON1B */ + STATE_ENTRY(SPR_BON1, 3, 6, NULL, S_BON1D), /* S_BON1C */ + STATE_ENTRY(SPR_BON1, 2, 6, NULL, S_BON1E), /* S_BON1D */ + STATE_ENTRY(SPR_BON1, 1, 6, NULL, S_BON1), /* S_BON1E */ + STATE_ENTRY(SPR_BON2, 0, 6, NULL, S_BON2A), /* S_BON2 */ + STATE_ENTRY(SPR_BON2, 1, 6, NULL, S_BON2B), /* S_BON2A */ + STATE_ENTRY(SPR_BON2, 2, 6, NULL, S_BON2C), /* S_BON2B */ + STATE_ENTRY(SPR_BON2, 3, 6, NULL, S_BON2D), /* S_BON2C */ + STATE_ENTRY(SPR_BON2, 2, 6, NULL, S_BON2E), /* S_BON2D */ + STATE_ENTRY(SPR_BON2, 1, 6, NULL, S_BON2), /* S_BON2E */ + STATE_ENTRY(SPR_BKEY, 0, 10, NULL, S_BKEY2), /* S_BKEY */ + STATE_ENTRY(SPR_BKEY, 32769, 10, NULL, S_BKEY), /* S_BKEY2 */ + STATE_ENTRY(SPR_RKEY, 0, 10, NULL, S_RKEY2), /* S_RKEY */ + STATE_ENTRY(SPR_RKEY, 32769, 10, NULL, S_RKEY), /* S_RKEY2 */ + STATE_ENTRY(SPR_YKEY, 0, 10, NULL, S_YKEY2), /* S_YKEY */ + STATE_ENTRY(SPR_YKEY, 32769, 10, NULL, S_YKEY), /* S_YKEY2 */ + STATE_ENTRY(SPR_BSKU, 0, 10, NULL, S_BSKULL2), /* S_BSKULL */ + STATE_ENTRY(SPR_BSKU, 32769, 10, NULL, S_BSKULL), /* S_BSKULL2 */ + STATE_ENTRY(SPR_RSKU, 0, 10, NULL, S_RSKULL2), /* S_RSKULL */ + STATE_ENTRY(SPR_RSKU, 32769, 10, NULL, S_RSKULL), /* S_RSKULL2 */ + STATE_ENTRY(SPR_YSKU, 0, 10, NULL, S_YSKULL2), /* S_YSKULL */ + STATE_ENTRY(SPR_YSKU, 32769, 10, NULL, S_YSKULL), /* S_YSKULL2 */ + STATE_ENTRY(SPR_STIM, 0, -1, NULL, S_NULL), /* S_STIM */ + STATE_ENTRY(SPR_MEDI, 0, -1, NULL, S_NULL), /* S_MEDI */ + STATE_ENTRY(SPR_SOUL, 32768, 6, NULL, S_SOUL2), /* S_SOUL */ + STATE_ENTRY(SPR_SOUL, 32769, 6, NULL, S_SOUL3), /* S_SOUL2 */ + STATE_ENTRY(SPR_SOUL, 32770, 6, NULL, S_SOUL4), /* S_SOUL3 */ + STATE_ENTRY(SPR_SOUL, 32771, 6, NULL, S_SOUL5), /* S_SOUL4 */ + STATE_ENTRY(SPR_SOUL, 32770, 6, NULL, S_SOUL6), /* S_SOUL5 */ + STATE_ENTRY(SPR_SOUL, 32769, 6, NULL, S_SOUL), /* S_SOUL6 */ + STATE_ENTRY(SPR_PINV, 32768, 6, NULL, S_PINV2), /* S_PINV */ + STATE_ENTRY(SPR_PINV, 32769, 6, NULL, S_PINV3), /* S_PINV2 */ + STATE_ENTRY(SPR_PINV, 32770, 6, NULL, S_PINV4), /* S_PINV3 */ + STATE_ENTRY(SPR_PINV, 32771, 6, NULL, S_PINV), /* S_PINV4 */ + STATE_ENTRY(SPR_PSTR, 32768, -1, NULL, S_NULL), /* S_PSTR */ + STATE_ENTRY(SPR_PINS, 32768, 6, NULL, S_PINS2), /* S_PINS */ + STATE_ENTRY(SPR_PINS, 32769, 6, NULL, S_PINS3), /* S_PINS2 */ + STATE_ENTRY(SPR_PINS, 32770, 6, NULL, S_PINS4), /* S_PINS3 */ + STATE_ENTRY(SPR_PINS, 32771, 6, NULL, S_PINS), /* S_PINS4 */ + STATE_ENTRY(SPR_MEGA, 32768, 6, NULL, S_MEGA2), /* S_MEGA */ + STATE_ENTRY(SPR_MEGA, 32769, 6, NULL, S_MEGA3), /* S_MEGA2 */ + STATE_ENTRY(SPR_MEGA, 32770, 6, NULL, S_MEGA4), /* S_MEGA3 */ + STATE_ENTRY(SPR_MEGA, 32771, 6, NULL, S_MEGA), /* S_MEGA4 */ + STATE_ENTRY(SPR_SUIT, 32768, -1, NULL, S_NULL), /* S_SUIT */ + STATE_ENTRY(SPR_PMAP, 32768, 6, NULL, S_PMAP2), /* S_PMAP */ + STATE_ENTRY(SPR_PMAP, 32769, 6, NULL, S_PMAP3), /* S_PMAP2 */ + STATE_ENTRY(SPR_PMAP, 32770, 6, NULL, S_PMAP4), /* S_PMAP3 */ + STATE_ENTRY(SPR_PMAP, 32771, 6, NULL, S_PMAP5), /* S_PMAP4 */ + STATE_ENTRY(SPR_PMAP, 32770, 6, NULL, S_PMAP6), /* S_PMAP5 */ + STATE_ENTRY(SPR_PMAP, 32769, 6, NULL, S_PMAP), /* S_PMAP6 */ + STATE_ENTRY(SPR_PVIS, 32768, 6, NULL, S_PVIS2), /* S_PVIS */ + STATE_ENTRY(SPR_PVIS, 1, 6, NULL, S_PVIS), /* S_PVIS2 */ + STATE_ENTRY(SPR_CLIP, 0, -1, NULL, S_NULL), /* S_CLIP */ + STATE_ENTRY(SPR_AMMO, 0, -1, NULL, S_NULL), /* S_AMMO */ + STATE_ENTRY(SPR_ROCK, 0, -1, NULL, S_NULL), /* S_ROCK */ + STATE_ENTRY(SPR_BROK, 0, -1, NULL, S_NULL), /* S_BROK */ + STATE_ENTRY(SPR_CELL, 0, -1, NULL, S_NULL), /* S_CELL */ + STATE_ENTRY(SPR_CELP, 0, -1, NULL, S_NULL), /* S_CELP */ + STATE_ENTRY(SPR_SHEL, 0, -1, NULL, S_NULL), /* S_SHEL */ + STATE_ENTRY(SPR_SBOX, 0, -1, NULL, S_NULL), /* S_SBOX */ + STATE_ENTRY(SPR_BPAK, 0, -1, NULL, S_NULL), /* S_BPAK */ + STATE_ENTRY(SPR_BFUG, 0, -1, NULL, S_NULL), /* S_BFUG */ + STATE_ENTRY(SPR_MGUN, 0, -1, NULL, S_NULL), /* S_MGUN */ + STATE_ENTRY(SPR_CSAW, 0, -1, NULL, S_NULL), /* S_CSAW */ + STATE_ENTRY(SPR_LAUN, 0, -1, NULL, S_NULL), /* S_LAUN */ + STATE_ENTRY(SPR_PLAS, 0, -1, NULL, S_NULL), /* S_PLAS */ + STATE_ENTRY(SPR_SHOT, 0, -1, NULL, S_NULL), /* S_SHOT */ + STATE_ENTRY(SPR_SGN2, 0, -1, NULL, S_NULL), /* S_SHOT2 */ + STATE_ENTRY(SPR_COLU, 32768, -1, NULL, S_NULL), /* S_COLU */ + STATE_ENTRY(SPR_SMT2, 0, -1, NULL, S_NULL), /* S_STALAG */ + STATE_ENTRY(SPR_GOR1, 0, 10, NULL, S_BLOODYTWITCH2), /* S_BLOODYTWITCH */ + STATE_ENTRY(SPR_GOR1, 1, 15, NULL, S_BLOODYTWITCH3), /* S_BLOODYTWITCH2 */ + STATE_ENTRY(SPR_GOR1, 2, 8, NULL, S_BLOODYTWITCH4), /* S_BLOODYTWITCH3 */ + STATE_ENTRY(SPR_GOR1, 1, 6, NULL, S_BLOODYTWITCH), /* S_BLOODYTWITCH4 */ + STATE_ENTRY(SPR_PLAY, 13, -1, NULL, S_NULL), /* S_DEADTORSO */ + STATE_ENTRY(SPR_PLAY, 18, -1, NULL, S_NULL), /* S_DEADBOTTOM */ + STATE_ENTRY(SPR_POL2, 0, -1, NULL, S_NULL), /* S_HEADSONSTICK */ + STATE_ENTRY(SPR_POL5, 0, -1, NULL, S_NULL), /* S_GIBS */ + STATE_ENTRY(SPR_POL4, 0, -1, NULL, S_NULL), /* S_HEADONASTICK */ + STATE_ENTRY(SPR_POL3, 32768, 6, NULL, S_HEADCANDLES2), /* S_HEADCANDLES */ + STATE_ENTRY(SPR_POL3, 32769, 6, NULL, S_HEADCANDLES), /* S_HEADCANDLES2 */ + STATE_ENTRY(SPR_POL1, 0, -1, NULL, S_NULL), /* S_DEADSTICK */ + STATE_ENTRY(SPR_POL6, 0, 6, NULL, S_LIVESTICK2), /* S_LIVESTICK */ + STATE_ENTRY(SPR_POL6, 1, 8, NULL, S_LIVESTICK), /* S_LIVESTICK2 */ + STATE_ENTRY(SPR_GOR2, 0, -1, NULL, S_NULL), /* S_MEAT2 */ + STATE_ENTRY(SPR_GOR3, 0, -1, NULL, S_NULL), /* S_MEAT3 */ + STATE_ENTRY(SPR_GOR4, 0, -1, NULL, S_NULL), /* S_MEAT4 */ + STATE_ENTRY(SPR_GOR5, 0, -1, NULL, S_NULL), /* S_MEAT5 */ + STATE_ENTRY(SPR_SMIT, 0, -1, NULL, S_NULL), /* S_STALAGTITE */ + STATE_ENTRY(SPR_COL1, 0, -1, NULL, S_NULL), /* S_TALLGRNCOL */ + STATE_ENTRY(SPR_COL2, 0, -1, NULL, S_NULL), /* S_SHRTGRNCOL */ + STATE_ENTRY(SPR_COL3, 0, -1, NULL, S_NULL), /* S_TALLREDCOL */ + STATE_ENTRY(SPR_COL4, 0, -1, NULL, S_NULL), /* S_SHRTREDCOL */ + STATE_ENTRY(SPR_CAND, 32768, -1, NULL, S_NULL), /* S_CANDLESTIK */ + STATE_ENTRY(SPR_CBRA, 32768, -1, NULL, S_NULL), /* S_CANDELABRA */ + STATE_ENTRY(SPR_COL6, 0, -1, NULL, S_NULL), /* S_SKULLCOL */ + STATE_ENTRY(SPR_TRE1, 0, -1, NULL, S_NULL), /* S_TORCHTREE */ + STATE_ENTRY(SPR_TRE2, 0, -1, NULL, S_NULL), /* S_BIGTREE */ + STATE_ENTRY(SPR_ELEC, 0, -1, NULL, S_NULL), /* S_TECHPILLAR */ + STATE_ENTRY(SPR_CEYE, 32768, 6, NULL, S_EVILEYE2), /* S_EVILEYE */ + STATE_ENTRY(SPR_CEYE, 32769, 6, NULL, S_EVILEYE3), /* S_EVILEYE2 */ + STATE_ENTRY(SPR_CEYE, 32770, 6, NULL, S_EVILEYE4), /* S_EVILEYE3 */ + STATE_ENTRY(SPR_CEYE, 32769, 6, NULL, S_EVILEYE), /* S_EVILEYE4 */ + STATE_ENTRY(SPR_FSKU, 32768, 6, NULL, S_FLOATSKULL2), /* S_FLOATSKULL */ + STATE_ENTRY(SPR_FSKU, 32769, 6, NULL, S_FLOATSKULL3), /* S_FLOATSKULL2 */ + STATE_ENTRY(SPR_FSKU, 32770, 6, NULL, S_FLOATSKULL), /* S_FLOATSKULL3 */ + STATE_ENTRY(SPR_COL5, 0, 14, NULL, S_HEARTCOL2), /* S_HEARTCOL */ + STATE_ENTRY(SPR_COL5, 1, 14, NULL, S_HEARTCOL), /* S_HEARTCOL2 */ + STATE_ENTRY(SPR_TBLU, 32768, 4, NULL, S_BLUETORCH2), /* S_BLUETORCH */ + STATE_ENTRY(SPR_TBLU, 32769, 4, NULL, S_BLUETORCH3), /* S_BLUETORCH2 */ + STATE_ENTRY(SPR_TBLU, 32770, 4, NULL, S_BLUETORCH4), /* S_BLUETORCH3 */ + STATE_ENTRY(SPR_TBLU, 32771, 4, NULL, S_BLUETORCH), /* S_BLUETORCH4 */ + STATE_ENTRY(SPR_TGRN, 32768, 4, NULL, S_GREENTORCH2), /* S_GREENTORCH */ + STATE_ENTRY(SPR_TGRN, 32769, 4, NULL, S_GREENTORCH3), /* S_GREENTORCH2 */ + STATE_ENTRY(SPR_TGRN, 32770, 4, NULL, S_GREENTORCH4), /* S_GREENTORCH3 */ + STATE_ENTRY(SPR_TGRN, 32771, 4, NULL, S_GREENTORCH), /* S_GREENTORCH4 */ + STATE_ENTRY(SPR_TRED, 32768, 4, NULL, S_REDTORCH2), /* S_REDTORCH */ + STATE_ENTRY(SPR_TRED, 32769, 4, NULL, S_REDTORCH3), /* S_REDTORCH2 */ + STATE_ENTRY(SPR_TRED, 32770, 4, NULL, S_REDTORCH4), /* S_REDTORCH3 */ + STATE_ENTRY(SPR_TRED, 32771, 4, NULL, S_REDTORCH), /* S_REDTORCH4 */ + STATE_ENTRY(SPR_SMBT, 32768, 4, NULL, S_BTORCHSHRT2), /* S_BTORCHSHRT */ + STATE_ENTRY(SPR_SMBT, 32769, 4, NULL, S_BTORCHSHRT3), /* S_BTORCHSHRT2 */ + STATE_ENTRY(SPR_SMBT, 32770, 4, NULL, S_BTORCHSHRT4), /* S_BTORCHSHRT3 */ + STATE_ENTRY(SPR_SMBT, 32771, 4, NULL, S_BTORCHSHRT), /* S_BTORCHSHRT4 */ + STATE_ENTRY(SPR_SMGT, 32768, 4, NULL, S_GTORCHSHRT2), /* S_GTORCHSHRT */ + STATE_ENTRY(SPR_SMGT, 32769, 4, NULL, S_GTORCHSHRT3), /* S_GTORCHSHRT2 */ + STATE_ENTRY(SPR_SMGT, 32770, 4, NULL, S_GTORCHSHRT4), /* S_GTORCHSHRT3 */ + STATE_ENTRY(SPR_SMGT, 32771, 4, NULL, S_GTORCHSHRT), /* S_GTORCHSHRT4 */ + STATE_ENTRY(SPR_SMRT, 32768, 4, NULL, S_RTORCHSHRT2), /* S_RTORCHSHRT */ + STATE_ENTRY(SPR_SMRT, 32769, 4, NULL, S_RTORCHSHRT3), /* S_RTORCHSHRT2 */ + STATE_ENTRY(SPR_SMRT, 32770, 4, NULL, S_RTORCHSHRT4), /* S_RTORCHSHRT3 */ + STATE_ENTRY(SPR_SMRT, 32771, 4, NULL, S_RTORCHSHRT), /* S_RTORCHSHRT4 */ + STATE_ENTRY(SPR_HDB1, 0, -1, NULL, S_NULL), /* S_HANGNOGUTS */ + STATE_ENTRY(SPR_HDB2, 0, -1, NULL, S_NULL), /* S_HANGBNOBRAIN */ + STATE_ENTRY(SPR_HDB3, 0, -1, NULL, S_NULL), /* S_HANGTLOOKDN */ + STATE_ENTRY(SPR_HDB4, 0, -1, NULL, S_NULL), /* S_HANGTSKULL */ + STATE_ENTRY(SPR_HDB5, 0, -1, NULL, S_NULL), /* S_HANGTLOOKUP */ + STATE_ENTRY(SPR_HDB6, 0, -1, NULL, S_NULL), /* S_HANGTNOBRAIN */ + STATE_ENTRY(SPR_POB1, 0, -1, NULL, S_NULL), /* S_COLONGIBS */ + STATE_ENTRY(SPR_POB2, 0, -1, NULL, S_NULL), /* S_SMALLPOOL */ + STATE_ENTRY(SPR_BRS1, 0, -1, NULL, S_NULL), /* S_BRAINSTEM */ + STATE_ENTRY(SPR_TLMP, 32768, 4, NULL, S_TECHLAMP2), /* S_TECHLAMP */ + STATE_ENTRY(SPR_TLMP, 32769, 4, NULL, S_TECHLAMP3), /* S_TECHLAMP2 */ + STATE_ENTRY(SPR_TLMP, 32770, 4, NULL, S_TECHLAMP4), /* S_TECHLAMP3 */ + STATE_ENTRY(SPR_TLMP, 32771, 4, NULL, S_TECHLAMP), /* S_TECHLAMP4 */ + STATE_ENTRY(SPR_TLP2, 32768, 4, NULL, S_TECH2LAMP2), /* S_TECH2LAMP */ + STATE_ENTRY(SPR_TLP2, 32769, 4, NULL, S_TECH2LAMP3), /* S_TECH2LAMP2 */ + STATE_ENTRY(SPR_TLP2, 32770, 4, NULL, S_TECH2LAMP4), /* S_TECH2LAMP3 */ + STATE_ENTRY(SPR_TLP2, 32771, 4, NULL, S_TECH2LAMP) /* S_TECH2LAMP4 */ +}; + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = +{ + { + /* MT_PLAYER */ + + .doomednum = -1, + .spawnstate = S_PLAY, + .spawnhealth = 100, + .seestate = S_PLAY_RUN1, + .reactiontime = 0, + .painstate = S_PLAY_PAIN, + .painchance = 255, + .meleestate = S_NULL, + .missilestate = S_PLAY_ATK1, + .deathstate = S_PLAY_DIE1, + .xdeathstate = S_PLAY_XDIE1, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | + MF_PICKUP | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_PLPAIN, + .deathsound = SFX_PLDETH, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_POSSESSED */ + + .doomednum = 3004, + .spawnstate = S_POSS_STND, + .spawnhealth = 20, + .seestate = S_POSS_RUN1, + .reactiontime = 8, + .painstate = S_POSS_PAIN, + .painchance = 200, + .meleestate = 0, + .missilestate = S_POSS_ATK1, + .deathstate = S_POSS_DIE1, + .xdeathstate = S_POSS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_POSS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_PISTOL, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH1, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT1, +#endif + }, + + { + /* MT_SHOTGUY */ + + .doomednum = 9, + .spawnstate = S_SPOS_STND, + .spawnhealth = 30, + .seestate = S_SPOS_RUN1, + .reactiontime = 8, + .painstate = S_SPOS_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_SPOS_ATK1, + .deathstate = S_SPOS_DIE1, + .xdeathstate = S_SPOS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SPOS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH2, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT2, +#endif + }, + + { + /* MT_VILE */ + + .doomednum = 64, + .spawnstate = S_VILE_STND, + .spawnhealth = 700, + .seestate = S_VILE_RUN1, + .reactiontime = 8, + .painstate = S_VILE_PAIN, + .painchance = 10, + .meleestate = 0, + .missilestate = S_VILE_ATK1, + .deathstate = S_VILE_DIE1, + .xdeathstate = S_NULL, + .speed = 15, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 500, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_VIPAIN, + .deathsound = SFX_VILDTH, + .activesound = SFX_VILACT, + .seesound = SFX_VILSIT, +#endif + }, + + { + /* MT_FIRE */ + + .doomednum = -1, + .spawnstate = S_FIRE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_UNDEAD */ + + .doomednum = 66, + .spawnstate = S_SKEL_STND, + .spawnhealth = 300, + .seestate = S_SKEL_RUN1, + .reactiontime = 8, + .painstate = S_SKEL_PAIN, + .painchance = 100, + .meleestate = S_SKEL_FIST1, + .missilestate = S_SKEL_MISS1, + .deathstate = S_SKEL_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 500, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SKEL_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_SKEDTH, + .activesound = SFX_SKEACT, + .seesound = SFX_SKESIT, +#endif + }, + + { + /* MT_TRACER */ + + .doomednum = -1, + .spawnstate = S_TRACER, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_TRACEEXP1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 11 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 10, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_SKEATK, +#endif + }, + + { + /* MT_SMOKE */ + + .doomednum = -1, + .spawnstate = S_SMOKE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_FATSO */ + + .doomednum = 67, + .spawnstate = S_FATT_STND, + .spawnhealth = 600, + .seestate = S_FATT_RUN1, + .reactiontime = 8, + .painstate = S_FATT_PAIN, + .painchance = 80, + .meleestate = 0, + .missilestate = S_FATT_ATK1, + .deathstate = S_FATT_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 48 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_FATT_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_MNPAIN, + .deathsound = SFX_MANDTH, + .activesound = SFX_POSACT, + .seesound = SFX_MANSIT, +#endif + }, + + { + /* MT_FATSHOT */ + + .doomednum = -1, + .spawnstate = S_FATSHOT1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_FATSHOTX1, + .xdeathstate = S_NULL, + .speed = 20 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 8, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_CHAINGUY */ + + .doomednum = 65, + .spawnstate = S_CPOS_STND, + .spawnhealth = 70, + .seestate = S_CPOS_RUN1, + .reactiontime = 8, + .painstate = S_CPOS_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_CPOS_ATK1, + .deathstate = S_CPOS_DIE1, + .xdeathstate = S_CPOS_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_CPOS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_PODTH2, + .activesound = SFX_POSACT, + .seesound = SFX_POSIT2, +#endif + }, + + { + /* MT_TROOP */ + + .doomednum = 3001, + .spawnstate = S_TROO_STND, + .spawnhealth = 60, + .seestate = S_TROO_RUN1, + .reactiontime = 8, + .painstate = S_TROO_PAIN, + .painchance = 200, + .meleestate = S_TROO_ATK1, + .missilestate = S_TROO_ATK1, + .deathstate = S_TROO_DIE1, + .xdeathstate = S_TROO_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_TROO_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_BGDTH1, + .activesound = SFX_BGACT, + .seesound = SFX_BGSIT1, +#endif + }, + + { + /* MT_SERGEANT */ + + .doomednum = 3002, + .spawnstate = S_SARG_STND, + .spawnhealth = 150, + .seestate = S_SARG_RUN1, + .reactiontime = 8, + .painstate = S_SARG_PAIN, + .painchance = 180, + .meleestate = S_SARG_ATK1, + .missilestate = 0, + .deathstate = S_SARG_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 30 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SARG_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SGTATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SGTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SGTSIT, +#endif + }, + + { + /* MT_SHADOWS */ + + .doomednum = 58, + .spawnstate = S_SARG_STND, + .spawnhealth = 150, + .seestate = S_SARG_RUN1, + .reactiontime = 8, + .painstate = S_SARG_PAIN, + .painchance = 180, + .meleestate = S_SARG_ATK1, + .missilestate = 0, + .deathstate = S_SARG_DIE1, + .xdeathstate = S_NULL, + .speed = 10, + .radius = 30 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_SHADOW | MF_COUNTKILL, + .raisestate = S_SARG_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SGTATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SGTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SGTSIT, +#endif + }, + + { + /* MT_HEAD */ + + .doomednum = 3005, + .spawnstate = S_HEAD_STND, + .spawnhealth = 400, + .seestate = S_HEAD_RUN1, + .reactiontime = 8, + .painstate = S_HEAD_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_HEAD_ATK1, + .deathstate = S_HEAD_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 31 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | + MF_NOGRAVITY | MF_COUNTKILL, + .raisestate = S_HEAD_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_CACDTH, + .activesound = SFX_DMACT, + .seesound = SFX_CACSIT, +#endif + }, + + { + /* MT_BRUISER */ + + .doomednum = 3003, + .spawnstate = S_BOSS_STND, + .spawnhealth = 1000, + .seestate = S_BOSS_RUN1, + .reactiontime = 8, + .painstate = S_BOSS_PAIN, + .painchance = 50, + .meleestate = S_BOSS_ATK1, + .missilestate = S_BOSS_ATK1, + .deathstate = S_BOSS_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 24 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BOSS_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_BRSDTH, + .activesound = SFX_DMACT, + .seesound = SFX_BRSSIT, +#endif + }, + + { + /* MT_BRUISERSHOT */ + + .doomednum = -1, + .spawnstate = S_BRBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BRBALLX1, + .xdeathstate = S_NULL, + .speed = 15 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 8, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_KNIGHT */ + + .doomednum = 69, + .spawnstate = S_BOS2_STND, + .spawnhealth = 500, + .seestate = S_BOS2_RUN1, + .reactiontime = 8, + .painstate = S_BOS2_PAIN, + .painchance = 50, + .meleestate = S_BOS2_ATK1, + .missilestate = S_BOS2_ATK1, + .deathstate = S_BOS2_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 24 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BOS2_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_KNTDTH, + .activesound = SFX_DMACT, + .seesound = SFX_KNTSIT, +#endif + }, + + { + /* MT_SKULL */ + + .doomednum = 3006, + .spawnstate = S_SKULL_STND, + .spawnhealth = 100, + .seestate = S_SKULL_RUN1, + .reactiontime = 8, + .painstate = S_SKULL_PAIN, + .painchance = 256, + .meleestate = 0, + .missilestate = S_SKULL_ATK1, + .deathstate = S_SKULL_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 16 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 50, + .damage = 3, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SKLATK, + .painsound = SFX_DMPAIN, + .deathsound = SFX_FIRXPL, + .activesound = SFX_DMACT, + .seesound = 0, +#endif + }, + + { + /* MT_SPIDER */ + + .doomednum = 7, + .spawnstate = S_SPID_STND, + .spawnhealth = 3000, + .seestate = S_SPID_RUN1, + .reactiontime = 8, + .painstate = S_SPID_PAIN, + .painchance = 40, + .meleestate = 0, + .missilestate = S_SPID_ATK1, + .deathstate = S_SPID_DIE1, + .xdeathstate = S_NULL, + .speed = 12, + .radius = 128 * FRACUNIT, + .height = 100 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_SHOTGN, + .painsound = SFX_DMPAIN, + .deathsound = SFX_SPIDTH, + .activesound = SFX_DMACT, + .seesound = SFX_SPISIT, +#endif + }, + + { + /* MT_BABY */ + + .doomednum = 68, + .spawnstate = S_BSPI_STND, + .spawnhealth = 500, + .seestate = S_BSPI_SIGHT, + .reactiontime = 8, + .painstate = S_BSPI_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_BSPI_ATK1, + .deathstate = S_BSPI_DIE1, + .xdeathstate = S_NULL, + .speed = 12, + .radius = 64 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 600, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_BSPI_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_BSPDTH, + .activesound = SFX_BSPACT, + .seesound = SFX_BSPSIT, +#endif + }, + + { + /* MT_CYBORG */ + + .doomednum = 16, + .spawnstate = S_CYBER_STND, + .spawnhealth = 4000, + .seestate = S_CYBER_RUN1, + .reactiontime = 8, + .painstate = S_CYBER_PAIN, + .painchance = 20, + .meleestate = 0, + .missilestate = S_CYBER_ATK1, + .deathstate = S_CYBER_DIE1, + .xdeathstate = S_NULL, + .speed = 16, + .radius = 40 * FRACUNIT, + .height = 110 * FRACUNIT, + .mass = 1000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_DMPAIN, + .deathsound = SFX_CYBDTH, + .activesound = SFX_DMACT, + .seesound = SFX_CYBSIT, +#endif + }, + + { + /* MT_PAIN */ + + .doomednum = 71, + .spawnstate = S_PAIN_STND, + .spawnhealth = 400, + .seestate = S_PAIN_RUN1, + .reactiontime = 8, + .painstate = S_PAIN_PAIN, + .painchance = 128, + .meleestate = 0, + .missilestate = S_PAIN_ATK1, + .deathstate = S_PAIN_DIE1, + .xdeathstate = S_NULL, + .speed = 8, + .radius = 31 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 400, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_FLOAT | + MF_NOGRAVITY | MF_COUNTKILL, + .raisestate = S_PAIN_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_PEPAIN, + .deathsound = SFX_PEDTH, + .activesound = SFX_DMACT, + .seesound = SFX_PESIT, +#endif + }, + + { + /* MT_WOLFSS */ + + .doomednum = 84, + .spawnstate = S_SSWV_STND, + .spawnhealth = 50, + .seestate = S_SSWV_RUN1, + .reactiontime = 8, + .painstate = S_SSWV_PAIN, + .painchance = 170, + .meleestate = 0, + .missilestate = S_SSWV_ATK1, + .deathstate = S_SSWV_DIE1, + .xdeathstate = S_SSWV_XDIE1, + .speed = 8, + .radius = 20 * FRACUNIT, + .height = 56 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_SSWV_RAISE1, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = 0, + .painsound = SFX_POPAIN, + .deathsound = SFX_SSDTH, + .activesound = SFX_POSACT, + .seesound = SFX_SSSIT, +#endif + }, + + { + /* MT_KEEN */ + + .doomednum = 72, + .spawnstate = S_KEENSTND, + .spawnhealth = 100, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_KEENPAIN, + .painchance = 256, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_COMMKEEN, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 72 * FRACUNIT, + .mass = 10000000, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY | + MF_SHOOTABLE | MF_COUNTKILL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_KEENPN, + .deathsound = SFX_KEENDT, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSBRAIN */ + + .doomednum = 88, + .spawnstate = S_BRAIN, + .spawnhealth = 250, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_BRAIN_PAIN, + .painchance = 255, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BRAIN_DIE1, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 10000000, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_BOSPN, + .deathsound = SFX_BOSDTH, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSSPIT */ + + .doomednum = 89, + .spawnstate = S_BRAINEYE, + .spawnhealth = 1000, + .seestate = S_BRAINEYESEE, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BOSSTARGET */ + + .doomednum = 87, + .spawnstate = S_NULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SPAWNSHOT */ + + .doomednum = -1, + .spawnstate = S_SPAWN1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 32 * FRACUNIT, + .mass = 100, + .damage = 3, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | + MF_NOGRAVITY | MF_NOCLIP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_BOSPIT, +#endif + }, + + { + /* MT_SPAWNFIRE */ + + .doomednum = -1, + .spawnstate = S_SPAWNFIRE1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BARREL */ + + .doomednum = 2035, + .spawnstate = S_BAR1, + .spawnhealth = 20, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BEXP, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 10 * FRACUNIT, + .height = 42 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SHOOTABLE | MF_NOBLOOD, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TROOPSHOT */ + + .doomednum = -1, + .spawnstate = S_TBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_TBALLX1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 3, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_HEADSHOT */ + + .doomednum = -1, + .spawnstate = S_RBALL1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_RBALLX1, + .xdeathstate = S_NULL, + .speed = 10 * FRACUNIT, + .radius = 6 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_FIRSHT, +#endif + }, + + { + /* MT_ROCKET */ + + .doomednum = -1, + .spawnstate = S_ROCKET, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_EXPLODE1, + .xdeathstate = S_NULL, + .speed = 20 * FRACUNIT, + .radius = 11 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 20, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_BAREXP, + .activesound = SFX_NONE, + .seesound = SFX_RLAUNC, +#endif + }, + + { + /* MT_PLASMA */ + + .doomednum = -1, + .spawnstate = S_PLASBALL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_PLASEXP, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_PLASMA, +#endif + }, + + { + /* MT_BFG */ + + .doomednum = -1, + .spawnstate = S_BFGSHOT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_BFGLAND, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 100, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_RXPLOD, + .activesound = SFX_NONE, + .seesound = 0, +#endif + }, + + { + /* MT_ARACHPLAZ */ + + .doomednum = -1, + .spawnstate = S_ARACH_PLAZ, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_ARACH_PLEX, + .xdeathstate = S_NULL, + .speed = 25 * FRACUNIT, + .radius = 13 * FRACUNIT, + .height = 8 * FRACUNIT, + .mass = 100, + .damage = 5, + .flags = MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_FIRXPL, + .activesound = SFX_NONE, + .seesound = SFX_PLASMA, +#endif + }, + + { + /* MT_PUFF */ + + .doomednum = -1, + .spawnstate = S_PUFF1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_BLOOD */ + + .doomednum = -1, + .spawnstate = S_BLOOD1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TFOG */ + + .doomednum = -1, + .spawnstate = S_TFOG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_IFOG */ + + .doomednum = -1, + .spawnstate = S_IFOG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_TELEPORTMAN */ + + .doomednum = 14, + .spawnstate = S_NULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOSECTOR, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_EXTRABFG */ + + .doomednum = -1, + .spawnstate = S_BFGEXP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC0 */ + + .doomednum = 2018, + .spawnstate = S_ARM1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC1 */ + + .doomednum = 2019, + .spawnstate = S_ARM2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC2 */ + + .doomednum = 2014, + .spawnstate = S_BON1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC3 */ + + .doomednum = 2015, + .spawnstate = S_BON2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC4 */ + + .doomednum = 5, + .spawnstate = S_BKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC5 */ + + .doomednum = 13, + .spawnstate = S_RKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC6 */ + + .doomednum = 6, + .spawnstate = S_YKEY, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC7 */ + + .doomednum = 39, + .spawnstate = S_YSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC8 */ + + .doomednum = 38, + .spawnstate = S_RSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC9 */ + + .doomednum = 40, + .spawnstate = S_BSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_NOTDMATCH, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC10 */ + + .doomednum = 2011, + .spawnstate = S_STIM, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC11 */ + + .doomednum = 2012, + .spawnstate = S_MEDI, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC12 */ + + .doomednum = 2013, + .spawnstate = S_SOUL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_INV */ + + .doomednum = 2022, + .spawnstate = S_PINV, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC13 */ + + .doomednum = 2023, + .spawnstate = S_PSTR, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_INS */ + + .doomednum = 2024, + .spawnstate = S_PINS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC14 */ + + .doomednum = 2025, + .spawnstate = S_SUIT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC15 */ + + .doomednum = 2026, + .spawnstate = S_PMAP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC16 */ + + .doomednum = 2045, + .spawnstate = S_PVIS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MEGA */ + + .doomednum = 83, + .spawnstate = S_MEGA, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL | MF_COUNTITEM, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_CLIP */ + + .doomednum = 2007, + .spawnstate = S_CLIP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC17 */ + + .doomednum = 2048, + .spawnstate = S_AMMO, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC18 */ + + .doomednum = 2010, + .spawnstate = S_ROCK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC19 */ + + .doomednum = 2046, + .spawnstate = S_BROK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC20 */ + + .doomednum = 2047, + .spawnstate = S_CELL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC21 */ + + .doomednum = 17, + .spawnstate = S_CELP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC22 */ + + .doomednum = 2008, + .spawnstate = S_SHEL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC23 */ + + .doomednum = 2049, + .spawnstate = S_SBOX, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC24 */ + + .doomednum = 8, + .spawnstate = S_BPAK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC25 */ + + .doomednum = 2006, + .spawnstate = S_BFUG, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_CHAINGUN */ + + .doomednum = 2002, + .spawnstate = S_MGUN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC26 */ + + .doomednum = 2005, + .spawnstate = S_CSAW, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC27 */ + + .doomednum = 2003, + .spawnstate = S_LAUN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC28 */ + + .doomednum = 2004, + .spawnstate = S_PLAS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SHOTGUN */ + + .doomednum = 2001, + .spawnstate = S_SHOT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_SUPERSHOTGUN */ + + .doomednum = 82, + .spawnstate = S_SHOT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPECIAL, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC29 */ + + .doomednum = 85, + .spawnstate = S_TECHLAMP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC30 */ + + .doomednum = 86, + .spawnstate = S_TECH2LAMP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC31 */ + + .doomednum = 2028, + .spawnstate = S_COLU, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC32 */ + + .doomednum = 30, + .spawnstate = S_TALLGRNCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC33 */ + + .doomednum = 31, + .spawnstate = S_SHRTGRNCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC34 */ + + .doomednum = 32, + .spawnstate = S_TALLREDCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC35 */ + + .doomednum = 33, + .spawnstate = S_SHRTREDCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC36 */ + + .doomednum = 37, + .spawnstate = S_SKULLCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC37 */ + + .doomednum = 36, + .spawnstate = S_HEARTCOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC38 */ + + .doomednum = 41, + .spawnstate = S_EVILEYE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC39 */ + + .doomednum = 42, + .spawnstate = S_FLOATSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC40 */ + + .doomednum = 43, + .spawnstate = S_TORCHTREE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC41 */ + + .doomednum = 44, + .spawnstate = S_BLUETORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC42 */ + + .doomednum = 45, + .spawnstate = S_GREENTORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC43 */ + + .doomednum = 46, + .spawnstate = S_REDTORCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC44 */ + + .doomednum = 55, + .spawnstate = S_BTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC45 */ + + .doomednum = 56, + .spawnstate = S_GTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC46 */ + + .doomednum = 57, + .spawnstate = S_RTORCHSHRT, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC47 */ + + .doomednum = 47, + .spawnstate = S_STALAGTITE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC48 */ + + .doomednum = 48, + .spawnstate = S_TECHPILLAR, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC49 */ + + .doomednum = 34, + .spawnstate = S_CANDLESTIK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC50 */ + + .doomednum = 35, + .spawnstate = S_CANDELABRA, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC51 */ + + .doomednum = 49, + .spawnstate = S_BLOODYTWITCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC52 */ + + .doomednum = 50, + .spawnstate = S_MEAT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC53 */ + + .doomednum = 51, + .spawnstate = S_MEAT3, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC54 */ + + .doomednum = 52, + .spawnstate = S_MEAT4, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC55 */ + + .doomednum = 53, + .spawnstate = S_MEAT5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC56 */ + + .doomednum = 59, + .spawnstate = S_MEAT2, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 84 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC57 */ + + .doomednum = 60, + .spawnstate = S_MEAT4, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC58 */ + + .doomednum = 61, + .spawnstate = S_MEAT3, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC59 */ + + .doomednum = 62, + .spawnstate = S_MEAT5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 52 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC60 */ + + .doomednum = 63, + .spawnstate = S_BLOODYTWITCH, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 68 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC61 */ + + .doomednum = 22, + .spawnstate = S_HEAD_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC62 */ + + .doomednum = 15, + .spawnstate = S_PLAY_DIE7, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC63 */ + + .doomednum = 18, + .spawnstate = S_POSS_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC64 */ + + .doomednum = 21, + .spawnstate = S_SARG_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC65 */ + + .doomednum = 23, + .spawnstate = S_SKULL_DIE6, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC66 */ + + .doomednum = 20, + .spawnstate = S_TROO_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC67 */ + + .doomednum = 19, + .spawnstate = S_SPOS_DIE5, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC68 */ + + .doomednum = 10, + .spawnstate = S_PLAY_XDIE9, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC69 */ + + .doomednum = 12, + .spawnstate = S_PLAY_XDIE9, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC70 */ + + .doomednum = 28, + .spawnstate = S_HEADSONSTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC71 */ + + .doomednum = 24, + .spawnstate = S_GIBS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = 0, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC72 */ + + .doomednum = 27, + .spawnstate = S_HEADONASTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC73 */ + + .doomednum = 29, + .spawnstate = S_HEADCANDLES, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC74 */ + + .doomednum = 25, + .spawnstate = S_DEADSTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC75 */ + + .doomednum = 26, + .spawnstate = S_LIVESTICK, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC76 */ + + .doomednum = 54, + .spawnstate = S_BIGTREE, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 32 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC77 */ + + .doomednum = 70, + .spawnstate = S_BBAR1, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC78 */ + + .doomednum = 73, + .spawnstate = S_HANGNOGUTS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 88 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC79 */ + + .doomednum = 74, + .spawnstate = S_HANGBNOBRAIN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 88 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC80 */ + + .doomednum = 75, + .spawnstate = S_HANGTLOOKDN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC81 */ + + .doomednum = 76, + .spawnstate = S_HANGTSKULL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC82 */ + + .doomednum = 77, + .spawnstate = S_HANGTLOOKUP, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC83 */ + + .doomednum = 78, + .spawnstate = S_HANGTNOBRAIN, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 16 * FRACUNIT, + .height = 64 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC84 */ + + .doomednum = 79, + .spawnstate = S_COLONGIBS, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC85 */ + + .doomednum = 80, + .spawnstate = S_SMALLPOOL, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, + .seesound = SFX_NONE, +#endif + }, + + { + /* MT_MISC86 */ + + .doomednum = 81, + .spawnstate = S_BRAINSTEM, + .spawnhealth = 1000, + .seestate = S_NULL, + .reactiontime = 8, + .painstate = S_NULL, + .painchance = 0, + .meleestate = S_NULL, + .missilestate = S_NULL, + .deathstate = S_NULL, + .xdeathstate = S_NULL, + .speed = 0, + .radius = 20 * FRACUNIT, + .height = 16 * FRACUNIT, + .mass = 100, + .damage = 0, + .flags = MF_NOBLOCKMAP, + .raisestate = S_NULL, +#ifdef CONFIG_GAMES_NXDOOM_SOUND + .seesound = SFX_NONE, + .attacksound = SFX_NONE, + .painsound = SFX_NONE, + .deathsound = SFX_NONE, + .activesound = SFX_NONE, +#endif + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/info.h b/games/NXDoom/src/doom/info.h new file mode 100644 index 000000000..4cace8cdf --- /dev/null +++ b/games/NXDoom/src/doom/info.h @@ -0,0 +1,1378 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/info.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Thing frame/state LUT, generated by multigen utility. + * This one is the original DOOM version, preserved. + * + ****************************************************************************/ + +#ifndef __INFO__ +#define __INFO__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* Needed for action function pointer handling. */ + +#include "d_think.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + SPR_TROO, + SPR_SHTG, + SPR_PUNG, + SPR_PISG, + SPR_PISF, + SPR_SHTF, + SPR_SHT2, + SPR_CHGG, + SPR_CHGF, + SPR_MISG, + SPR_MISF, + SPR_SAWG, + SPR_PLSG, + SPR_PLSF, + SPR_BFGG, + SPR_BFGF, + SPR_BLUD, + SPR_PUFF, + SPR_BAL1, + SPR_BAL2, + SPR_PLSS, + SPR_PLSE, + SPR_MISL, + SPR_BFS1, + SPR_BFE1, + SPR_BFE2, + SPR_TFOG, + SPR_IFOG, + SPR_PLAY, + SPR_POSS, + SPR_SPOS, + SPR_VILE, + SPR_FIRE, + SPR_FATB, + SPR_FBXP, + SPR_SKEL, + SPR_MANF, + SPR_FATT, + SPR_CPOS, + SPR_SARG, + SPR_HEAD, + SPR_BAL7, + SPR_BOSS, + SPR_BOS2, + SPR_SKUL, + SPR_SPID, + SPR_BSPI, + SPR_APLS, + SPR_APBX, + SPR_CYBR, + SPR_PAIN, + SPR_SSWV, + SPR_KEEN, + SPR_BBRN, + SPR_BOSF, + SPR_ARM1, + SPR_ARM2, + SPR_BAR1, + SPR_BEXP, + SPR_FCAN, + SPR_BON1, + SPR_BON2, + SPR_BKEY, + SPR_RKEY, + SPR_YKEY, + SPR_BSKU, + SPR_RSKU, + SPR_YSKU, + SPR_STIM, + SPR_MEDI, + SPR_SOUL, + SPR_PINV, + SPR_PSTR, + SPR_PINS, + SPR_MEGA, + SPR_SUIT, + SPR_PMAP, + SPR_PVIS, + SPR_CLIP, + SPR_AMMO, + SPR_ROCK, + SPR_BROK, + SPR_CELL, + SPR_CELP, + SPR_SHEL, + SPR_SBOX, + SPR_BPAK, + SPR_BFUG, + SPR_MGUN, + SPR_CSAW, + SPR_LAUN, + SPR_PLAS, + SPR_SHOT, + SPR_SGN2, + SPR_COLU, + SPR_SMT2, + SPR_GOR1, + SPR_POL2, + SPR_POL5, + SPR_POL4, + SPR_POL3, + SPR_POL1, + SPR_POL6, + SPR_GOR2, + SPR_GOR3, + SPR_GOR4, + SPR_GOR5, + SPR_SMIT, + SPR_COL1, + SPR_COL2, + SPR_COL3, + SPR_COL4, + SPR_CAND, + SPR_CBRA, + SPR_COL6, + SPR_TRE1, + SPR_TRE2, + SPR_ELEC, + SPR_CEYE, + SPR_FSKU, + SPR_COL5, + SPR_TBLU, + SPR_TGRN, + SPR_TRED, + SPR_SMBT, + SPR_SMGT, + SPR_SMRT, + SPR_HDB1, + SPR_HDB2, + SPR_HDB3, + SPR_HDB4, + SPR_HDB5, + SPR_HDB6, + SPR_POB1, + SPR_POB2, + SPR_BRS1, + SPR_TLMP, + SPR_TLP2, + NUMSPRITES +} spritenum_t; + +typedef enum +{ + S_NULL, + S_LIGHTDONE, + S_PUNCH, + S_PUNCHDOWN, + S_PUNCHUP, + S_PUNCH1, + S_PUNCH2, + S_PUNCH3, + S_PUNCH4, + S_PUNCH5, + S_PISTOL, + S_PISTOLDOWN, + S_PISTOLUP, + S_PISTOL1, + S_PISTOL2, + S_PISTOL3, + S_PISTOL4, + S_PISTOLFLASH, + S_SGUN, + S_SGUNDOWN, + S_SGUNUP, + S_SGUN1, + S_SGUN2, + S_SGUN3, + S_SGUN4, + S_SGUN5, + S_SGUN6, + S_SGUN7, + S_SGUN8, + S_SGUN9, + S_SGUNFLASH1, + S_SGUNFLASH2, + S_DSGUN, + S_DSGUNDOWN, + S_DSGUNUP, + S_DSGUN1, + S_DSGUN2, + S_DSGUN3, + S_DSGUN4, + S_DSGUN5, + S_DSGUN6, + S_DSGUN7, + S_DSGUN8, + S_DSGUN9, + S_DSGUN10, + S_DSNR1, + S_DSNR2, + S_DSGUNFLASH1, + S_DSGUNFLASH2, + S_CHAIN, + S_CHAINDOWN, + S_CHAINUP, + S_CHAIN1, + S_CHAIN2, + S_CHAIN3, + S_CHAINFLASH1, + S_CHAINFLASH2, + S_MISSILE, + S_MISSILEDOWN, + S_MISSILEUP, + S_MISSILE1, + S_MISSILE2, + S_MISSILE3, + S_MISSILEFLASH1, + S_MISSILEFLASH2, + S_MISSILEFLASH3, + S_MISSILEFLASH4, + S_SAW, + S_SAWB, + S_SAWDOWN, + S_SAWUP, + S_SAW1, + S_SAW2, + S_SAW3, + S_PLASMA, + S_PLASMADOWN, + S_PLASMAUP, + S_PLASMA1, + S_PLASMA2, + S_PLASMAFLASH1, + S_PLASMAFLASH2, + S_BFG, + S_BFGDOWN, + S_BFGUP, + S_BFG1, + S_BFG2, + S_BFG3, + S_BFG4, + S_BFGFLASH1, + S_BFGFLASH2, + S_BLOOD1, + S_BLOOD2, + S_BLOOD3, + S_PUFF1, + S_PUFF2, + S_PUFF3, + S_PUFF4, + S_TBALL1, + S_TBALL2, + S_TBALLX1, + S_TBALLX2, + S_TBALLX3, + S_RBALL1, + S_RBALL2, + S_RBALLX1, + S_RBALLX2, + S_RBALLX3, + S_PLASBALL, + S_PLASBALL2, + S_PLASEXP, + S_PLASEXP2, + S_PLASEXP3, + S_PLASEXP4, + S_PLASEXP5, + S_ROCKET, + S_BFGSHOT, + S_BFGSHOT2, + S_BFGLAND, + S_BFGLAND2, + S_BFGLAND3, + S_BFGLAND4, + S_BFGLAND5, + S_BFGLAND6, + S_BFGEXP, + S_BFGEXP2, + S_BFGEXP3, + S_BFGEXP4, + S_EXPLODE1, + S_EXPLODE2, + S_EXPLODE3, + S_TFOG, + S_TFOG01, + S_TFOG02, + S_TFOG2, + S_TFOG3, + S_TFOG4, + S_TFOG5, + S_TFOG6, + S_TFOG7, + S_TFOG8, + S_TFOG9, + S_TFOG10, + S_IFOG, + S_IFOG01, + S_IFOG02, + S_IFOG2, + S_IFOG3, + S_IFOG4, + S_IFOG5, + S_PLAY, + S_PLAY_RUN1, + S_PLAY_RUN2, + S_PLAY_RUN3, + S_PLAY_RUN4, + S_PLAY_ATK1, + S_PLAY_ATK2, + S_PLAY_PAIN, + S_PLAY_PAIN2, + S_PLAY_DIE1, + S_PLAY_DIE2, + S_PLAY_DIE3, + S_PLAY_DIE4, + S_PLAY_DIE5, + S_PLAY_DIE6, + S_PLAY_DIE7, + S_PLAY_XDIE1, + S_PLAY_XDIE2, + S_PLAY_XDIE3, + S_PLAY_XDIE4, + S_PLAY_XDIE5, + S_PLAY_XDIE6, + S_PLAY_XDIE7, + S_PLAY_XDIE8, + S_PLAY_XDIE9, + S_POSS_STND, + S_POSS_STND2, + S_POSS_RUN1, + S_POSS_RUN2, + S_POSS_RUN3, + S_POSS_RUN4, + S_POSS_RUN5, + S_POSS_RUN6, + S_POSS_RUN7, + S_POSS_RUN8, + S_POSS_ATK1, + S_POSS_ATK2, + S_POSS_ATK3, + S_POSS_PAIN, + S_POSS_PAIN2, + S_POSS_DIE1, + S_POSS_DIE2, + S_POSS_DIE3, + S_POSS_DIE4, + S_POSS_DIE5, + S_POSS_XDIE1, + S_POSS_XDIE2, + S_POSS_XDIE3, + S_POSS_XDIE4, + S_POSS_XDIE5, + S_POSS_XDIE6, + S_POSS_XDIE7, + S_POSS_XDIE8, + S_POSS_XDIE9, + S_POSS_RAISE1, + S_POSS_RAISE2, + S_POSS_RAISE3, + S_POSS_RAISE4, + S_SPOS_STND, + S_SPOS_STND2, + S_SPOS_RUN1, + S_SPOS_RUN2, + S_SPOS_RUN3, + S_SPOS_RUN4, + S_SPOS_RUN5, + S_SPOS_RUN6, + S_SPOS_RUN7, + S_SPOS_RUN8, + S_SPOS_ATK1, + S_SPOS_ATK2, + S_SPOS_ATK3, + S_SPOS_PAIN, + S_SPOS_PAIN2, + S_SPOS_DIE1, + S_SPOS_DIE2, + S_SPOS_DIE3, + S_SPOS_DIE4, + S_SPOS_DIE5, + S_SPOS_XDIE1, + S_SPOS_XDIE2, + S_SPOS_XDIE3, + S_SPOS_XDIE4, + S_SPOS_XDIE5, + S_SPOS_XDIE6, + S_SPOS_XDIE7, + S_SPOS_XDIE8, + S_SPOS_XDIE9, + S_SPOS_RAISE1, + S_SPOS_RAISE2, + S_SPOS_RAISE3, + S_SPOS_RAISE4, + S_SPOS_RAISE5, + S_VILE_STND, + S_VILE_STND2, + S_VILE_RUN1, + S_VILE_RUN2, + S_VILE_RUN3, + S_VILE_RUN4, + S_VILE_RUN5, + S_VILE_RUN6, + S_VILE_RUN7, + S_VILE_RUN8, + S_VILE_RUN9, + S_VILE_RUN10, + S_VILE_RUN11, + S_VILE_RUN12, + S_VILE_ATK1, + S_VILE_ATK2, + S_VILE_ATK3, + S_VILE_ATK4, + S_VILE_ATK5, + S_VILE_ATK6, + S_VILE_ATK7, + S_VILE_ATK8, + S_VILE_ATK9, + S_VILE_ATK10, + S_VILE_ATK11, + S_VILE_HEAL1, + S_VILE_HEAL2, + S_VILE_HEAL3, + S_VILE_PAIN, + S_VILE_PAIN2, + S_VILE_DIE1, + S_VILE_DIE2, + S_VILE_DIE3, + S_VILE_DIE4, + S_VILE_DIE5, + S_VILE_DIE6, + S_VILE_DIE7, + S_VILE_DIE8, + S_VILE_DIE9, + S_VILE_DIE10, + S_FIRE1, + S_FIRE2, + S_FIRE3, + S_FIRE4, + S_FIRE5, + S_FIRE6, + S_FIRE7, + S_FIRE8, + S_FIRE9, + S_FIRE10, + S_FIRE11, + S_FIRE12, + S_FIRE13, + S_FIRE14, + S_FIRE15, + S_FIRE16, + S_FIRE17, + S_FIRE18, + S_FIRE19, + S_FIRE20, + S_FIRE21, + S_FIRE22, + S_FIRE23, + S_FIRE24, + S_FIRE25, + S_FIRE26, + S_FIRE27, + S_FIRE28, + S_FIRE29, + S_FIRE30, + S_SMOKE1, + S_SMOKE2, + S_SMOKE3, + S_SMOKE4, + S_SMOKE5, + S_TRACER, + S_TRACER2, + S_TRACEEXP1, + S_TRACEEXP2, + S_TRACEEXP3, + S_SKEL_STND, + S_SKEL_STND2, + S_SKEL_RUN1, + S_SKEL_RUN2, + S_SKEL_RUN3, + S_SKEL_RUN4, + S_SKEL_RUN5, + S_SKEL_RUN6, + S_SKEL_RUN7, + S_SKEL_RUN8, + S_SKEL_RUN9, + S_SKEL_RUN10, + S_SKEL_RUN11, + S_SKEL_RUN12, + S_SKEL_FIST1, + S_SKEL_FIST2, + S_SKEL_FIST3, + S_SKEL_FIST4, + S_SKEL_MISS1, + S_SKEL_MISS2, + S_SKEL_MISS3, + S_SKEL_MISS4, + S_SKEL_PAIN, + S_SKEL_PAIN2, + S_SKEL_DIE1, + S_SKEL_DIE2, + S_SKEL_DIE3, + S_SKEL_DIE4, + S_SKEL_DIE5, + S_SKEL_DIE6, + S_SKEL_RAISE1, + S_SKEL_RAISE2, + S_SKEL_RAISE3, + S_SKEL_RAISE4, + S_SKEL_RAISE5, + S_SKEL_RAISE6, + S_FATSHOT1, + S_FATSHOT2, + S_FATSHOTX1, + S_FATSHOTX2, + S_FATSHOTX3, + S_FATT_STND, + S_FATT_STND2, + S_FATT_RUN1, + S_FATT_RUN2, + S_FATT_RUN3, + S_FATT_RUN4, + S_FATT_RUN5, + S_FATT_RUN6, + S_FATT_RUN7, + S_FATT_RUN8, + S_FATT_RUN9, + S_FATT_RUN10, + S_FATT_RUN11, + S_FATT_RUN12, + S_FATT_ATK1, + S_FATT_ATK2, + S_FATT_ATK3, + S_FATT_ATK4, + S_FATT_ATK5, + S_FATT_ATK6, + S_FATT_ATK7, + S_FATT_ATK8, + S_FATT_ATK9, + S_FATT_ATK10, + S_FATT_PAIN, + S_FATT_PAIN2, + S_FATT_DIE1, + S_FATT_DIE2, + S_FATT_DIE3, + S_FATT_DIE4, + S_FATT_DIE5, + S_FATT_DIE6, + S_FATT_DIE7, + S_FATT_DIE8, + S_FATT_DIE9, + S_FATT_DIE10, + S_FATT_RAISE1, + S_FATT_RAISE2, + S_FATT_RAISE3, + S_FATT_RAISE4, + S_FATT_RAISE5, + S_FATT_RAISE6, + S_FATT_RAISE7, + S_FATT_RAISE8, + S_CPOS_STND, + S_CPOS_STND2, + S_CPOS_RUN1, + S_CPOS_RUN2, + S_CPOS_RUN3, + S_CPOS_RUN4, + S_CPOS_RUN5, + S_CPOS_RUN6, + S_CPOS_RUN7, + S_CPOS_RUN8, + S_CPOS_ATK1, + S_CPOS_ATK2, + S_CPOS_ATK3, + S_CPOS_ATK4, + S_CPOS_PAIN, + S_CPOS_PAIN2, + S_CPOS_DIE1, + S_CPOS_DIE2, + S_CPOS_DIE3, + S_CPOS_DIE4, + S_CPOS_DIE5, + S_CPOS_DIE6, + S_CPOS_DIE7, + S_CPOS_XDIE1, + S_CPOS_XDIE2, + S_CPOS_XDIE3, + S_CPOS_XDIE4, + S_CPOS_XDIE5, + S_CPOS_XDIE6, + S_CPOS_RAISE1, + S_CPOS_RAISE2, + S_CPOS_RAISE3, + S_CPOS_RAISE4, + S_CPOS_RAISE5, + S_CPOS_RAISE6, + S_CPOS_RAISE7, + S_TROO_STND, + S_TROO_STND2, + S_TROO_RUN1, + S_TROO_RUN2, + S_TROO_RUN3, + S_TROO_RUN4, + S_TROO_RUN5, + S_TROO_RUN6, + S_TROO_RUN7, + S_TROO_RUN8, + S_TROO_ATK1, + S_TROO_ATK2, + S_TROO_ATK3, + S_TROO_PAIN, + S_TROO_PAIN2, + S_TROO_DIE1, + S_TROO_DIE2, + S_TROO_DIE3, + S_TROO_DIE4, + S_TROO_DIE5, + S_TROO_XDIE1, + S_TROO_XDIE2, + S_TROO_XDIE3, + S_TROO_XDIE4, + S_TROO_XDIE5, + S_TROO_XDIE6, + S_TROO_XDIE7, + S_TROO_XDIE8, + S_TROO_RAISE1, + S_TROO_RAISE2, + S_TROO_RAISE3, + S_TROO_RAISE4, + S_TROO_RAISE5, + S_SARG_STND, + S_SARG_STND2, + S_SARG_RUN1, + S_SARG_RUN2, + S_SARG_RUN3, + S_SARG_RUN4, + S_SARG_RUN5, + S_SARG_RUN6, + S_SARG_RUN7, + S_SARG_RUN8, + S_SARG_ATK1, + S_SARG_ATK2, + S_SARG_ATK3, + S_SARG_PAIN, + S_SARG_PAIN2, + S_SARG_DIE1, + S_SARG_DIE2, + S_SARG_DIE3, + S_SARG_DIE4, + S_SARG_DIE5, + S_SARG_DIE6, + S_SARG_RAISE1, + S_SARG_RAISE2, + S_SARG_RAISE3, + S_SARG_RAISE4, + S_SARG_RAISE5, + S_SARG_RAISE6, + S_HEAD_STND, + S_HEAD_RUN1, + S_HEAD_ATK1, + S_HEAD_ATK2, + S_HEAD_ATK3, + S_HEAD_PAIN, + S_HEAD_PAIN2, + S_HEAD_PAIN3, + S_HEAD_DIE1, + S_HEAD_DIE2, + S_HEAD_DIE3, + S_HEAD_DIE4, + S_HEAD_DIE5, + S_HEAD_DIE6, + S_HEAD_RAISE1, + S_HEAD_RAISE2, + S_HEAD_RAISE3, + S_HEAD_RAISE4, + S_HEAD_RAISE5, + S_HEAD_RAISE6, + S_BRBALL1, + S_BRBALL2, + S_BRBALLX1, + S_BRBALLX2, + S_BRBALLX3, + S_BOSS_STND, + S_BOSS_STND2, + S_BOSS_RUN1, + S_BOSS_RUN2, + S_BOSS_RUN3, + S_BOSS_RUN4, + S_BOSS_RUN5, + S_BOSS_RUN6, + S_BOSS_RUN7, + S_BOSS_RUN8, + S_BOSS_ATK1, + S_BOSS_ATK2, + S_BOSS_ATK3, + S_BOSS_PAIN, + S_BOSS_PAIN2, + S_BOSS_DIE1, + S_BOSS_DIE2, + S_BOSS_DIE3, + S_BOSS_DIE4, + S_BOSS_DIE5, + S_BOSS_DIE6, + S_BOSS_DIE7, + S_BOSS_RAISE1, + S_BOSS_RAISE2, + S_BOSS_RAISE3, + S_BOSS_RAISE4, + S_BOSS_RAISE5, + S_BOSS_RAISE6, + S_BOSS_RAISE7, + S_BOS2_STND, + S_BOS2_STND2, + S_BOS2_RUN1, + S_BOS2_RUN2, + S_BOS2_RUN3, + S_BOS2_RUN4, + S_BOS2_RUN5, + S_BOS2_RUN6, + S_BOS2_RUN7, + S_BOS2_RUN8, + S_BOS2_ATK1, + S_BOS2_ATK2, + S_BOS2_ATK3, + S_BOS2_PAIN, + S_BOS2_PAIN2, + S_BOS2_DIE1, + S_BOS2_DIE2, + S_BOS2_DIE3, + S_BOS2_DIE4, + S_BOS2_DIE5, + S_BOS2_DIE6, + S_BOS2_DIE7, + S_BOS2_RAISE1, + S_BOS2_RAISE2, + S_BOS2_RAISE3, + S_BOS2_RAISE4, + S_BOS2_RAISE5, + S_BOS2_RAISE6, + S_BOS2_RAISE7, + S_SKULL_STND, + S_SKULL_STND2, + S_SKULL_RUN1, + S_SKULL_RUN2, + S_SKULL_ATK1, + S_SKULL_ATK2, + S_SKULL_ATK3, + S_SKULL_ATK4, + S_SKULL_PAIN, + S_SKULL_PAIN2, + S_SKULL_DIE1, + S_SKULL_DIE2, + S_SKULL_DIE3, + S_SKULL_DIE4, + S_SKULL_DIE5, + S_SKULL_DIE6, + S_SPID_STND, + S_SPID_STND2, + S_SPID_RUN1, + S_SPID_RUN2, + S_SPID_RUN3, + S_SPID_RUN4, + S_SPID_RUN5, + S_SPID_RUN6, + S_SPID_RUN7, + S_SPID_RUN8, + S_SPID_RUN9, + S_SPID_RUN10, + S_SPID_RUN11, + S_SPID_RUN12, + S_SPID_ATK1, + S_SPID_ATK2, + S_SPID_ATK3, + S_SPID_ATK4, + S_SPID_PAIN, + S_SPID_PAIN2, + S_SPID_DIE1, + S_SPID_DIE2, + S_SPID_DIE3, + S_SPID_DIE4, + S_SPID_DIE5, + S_SPID_DIE6, + S_SPID_DIE7, + S_SPID_DIE8, + S_SPID_DIE9, + S_SPID_DIE10, + S_SPID_DIE11, + S_BSPI_STND, + S_BSPI_STND2, + S_BSPI_SIGHT, + S_BSPI_RUN1, + S_BSPI_RUN2, + S_BSPI_RUN3, + S_BSPI_RUN4, + S_BSPI_RUN5, + S_BSPI_RUN6, + S_BSPI_RUN7, + S_BSPI_RUN8, + S_BSPI_RUN9, + S_BSPI_RUN10, + S_BSPI_RUN11, + S_BSPI_RUN12, + S_BSPI_ATK1, + S_BSPI_ATK2, + S_BSPI_ATK3, + S_BSPI_ATK4, + S_BSPI_PAIN, + S_BSPI_PAIN2, + S_BSPI_DIE1, + S_BSPI_DIE2, + S_BSPI_DIE3, + S_BSPI_DIE4, + S_BSPI_DIE5, + S_BSPI_DIE6, + S_BSPI_DIE7, + S_BSPI_RAISE1, + S_BSPI_RAISE2, + S_BSPI_RAISE3, + S_BSPI_RAISE4, + S_BSPI_RAISE5, + S_BSPI_RAISE6, + S_BSPI_RAISE7, + S_ARACH_PLAZ, + S_ARACH_PLAZ2, + S_ARACH_PLEX, + S_ARACH_PLEX2, + S_ARACH_PLEX3, + S_ARACH_PLEX4, + S_ARACH_PLEX5, + S_CYBER_STND, + S_CYBER_STND2, + S_CYBER_RUN1, + S_CYBER_RUN2, + S_CYBER_RUN3, + S_CYBER_RUN4, + S_CYBER_RUN5, + S_CYBER_RUN6, + S_CYBER_RUN7, + S_CYBER_RUN8, + S_CYBER_ATK1, + S_CYBER_ATK2, + S_CYBER_ATK3, + S_CYBER_ATK4, + S_CYBER_ATK5, + S_CYBER_ATK6, + S_CYBER_PAIN, + S_CYBER_DIE1, + S_CYBER_DIE2, + S_CYBER_DIE3, + S_CYBER_DIE4, + S_CYBER_DIE5, + S_CYBER_DIE6, + S_CYBER_DIE7, + S_CYBER_DIE8, + S_CYBER_DIE9, + S_CYBER_DIE10, + S_PAIN_STND, + S_PAIN_RUN1, + S_PAIN_RUN2, + S_PAIN_RUN3, + S_PAIN_RUN4, + S_PAIN_RUN5, + S_PAIN_RUN6, + S_PAIN_ATK1, + S_PAIN_ATK2, + S_PAIN_ATK3, + S_PAIN_ATK4, + S_PAIN_PAIN, + S_PAIN_PAIN2, + S_PAIN_DIE1, + S_PAIN_DIE2, + S_PAIN_DIE3, + S_PAIN_DIE4, + S_PAIN_DIE5, + S_PAIN_DIE6, + S_PAIN_RAISE1, + S_PAIN_RAISE2, + S_PAIN_RAISE3, + S_PAIN_RAISE4, + S_PAIN_RAISE5, + S_PAIN_RAISE6, + S_SSWV_STND, + S_SSWV_STND2, + S_SSWV_RUN1, + S_SSWV_RUN2, + S_SSWV_RUN3, + S_SSWV_RUN4, + S_SSWV_RUN5, + S_SSWV_RUN6, + S_SSWV_RUN7, + S_SSWV_RUN8, + S_SSWV_ATK1, + S_SSWV_ATK2, + S_SSWV_ATK3, + S_SSWV_ATK4, + S_SSWV_ATK5, + S_SSWV_ATK6, + S_SSWV_PAIN, + S_SSWV_PAIN2, + S_SSWV_DIE1, + S_SSWV_DIE2, + S_SSWV_DIE3, + S_SSWV_DIE4, + S_SSWV_DIE5, + S_SSWV_XDIE1, + S_SSWV_XDIE2, + S_SSWV_XDIE3, + S_SSWV_XDIE4, + S_SSWV_XDIE5, + S_SSWV_XDIE6, + S_SSWV_XDIE7, + S_SSWV_XDIE8, + S_SSWV_XDIE9, + S_SSWV_RAISE1, + S_SSWV_RAISE2, + S_SSWV_RAISE3, + S_SSWV_RAISE4, + S_SSWV_RAISE5, + S_KEENSTND, + S_COMMKEEN, + S_COMMKEEN2, + S_COMMKEEN3, + S_COMMKEEN4, + S_COMMKEEN5, + S_COMMKEEN6, + S_COMMKEEN7, + S_COMMKEEN8, + S_COMMKEEN9, + S_COMMKEEN10, + S_COMMKEEN11, + S_COMMKEEN12, + S_KEENPAIN, + S_KEENPAIN2, + S_BRAIN, + S_BRAIN_PAIN, + S_BRAIN_DIE1, + S_BRAIN_DIE2, + S_BRAIN_DIE3, + S_BRAIN_DIE4, + S_BRAINEYE, + S_BRAINEYESEE, + S_BRAINEYE1, + S_SPAWN1, + S_SPAWN2, + S_SPAWN3, + S_SPAWN4, + S_SPAWNFIRE1, + S_SPAWNFIRE2, + S_SPAWNFIRE3, + S_SPAWNFIRE4, + S_SPAWNFIRE5, + S_SPAWNFIRE6, + S_SPAWNFIRE7, + S_SPAWNFIRE8, + S_BRAINEXPLODE1, + S_BRAINEXPLODE2, + S_BRAINEXPLODE3, + S_ARM1, + S_ARM1A, + S_ARM2, + S_ARM2A, + S_BAR1, + S_BAR2, + S_BEXP, + S_BEXP2, + S_BEXP3, + S_BEXP4, + S_BEXP5, + S_BBAR1, + S_BBAR2, + S_BBAR3, + S_BON1, + S_BON1A, + S_BON1B, + S_BON1C, + S_BON1D, + S_BON1E, + S_BON2, + S_BON2A, + S_BON2B, + S_BON2C, + S_BON2D, + S_BON2E, + S_BKEY, + S_BKEY2, + S_RKEY, + S_RKEY2, + S_YKEY, + S_YKEY2, + S_BSKULL, + S_BSKULL2, + S_RSKULL, + S_RSKULL2, + S_YSKULL, + S_YSKULL2, + S_STIM, + S_MEDI, + S_SOUL, + S_SOUL2, + S_SOUL3, + S_SOUL4, + S_SOUL5, + S_SOUL6, + S_PINV, + S_PINV2, + S_PINV3, + S_PINV4, + S_PSTR, + S_PINS, + S_PINS2, + S_PINS3, + S_PINS4, + S_MEGA, + S_MEGA2, + S_MEGA3, + S_MEGA4, + S_SUIT, + S_PMAP, + S_PMAP2, + S_PMAP3, + S_PMAP4, + S_PMAP5, + S_PMAP6, + S_PVIS, + S_PVIS2, + S_CLIP, + S_AMMO, + S_ROCK, + S_BROK, + S_CELL, + S_CELP, + S_SHEL, + S_SBOX, + S_BPAK, + S_BFUG, + S_MGUN, + S_CSAW, + S_LAUN, + S_PLAS, + S_SHOT, + S_SHOT2, + S_COLU, + S_STALAG, + S_BLOODYTWITCH, + S_BLOODYTWITCH2, + S_BLOODYTWITCH3, + S_BLOODYTWITCH4, + S_DEADTORSO, + S_DEADBOTTOM, + S_HEADSONSTICK, + S_GIBS, + S_HEADONASTICK, + S_HEADCANDLES, + S_HEADCANDLES2, + S_DEADSTICK, + S_LIVESTICK, + S_LIVESTICK2, + S_MEAT2, + S_MEAT3, + S_MEAT4, + S_MEAT5, + S_STALAGTITE, + S_TALLGRNCOL, + S_SHRTGRNCOL, + S_TALLREDCOL, + S_SHRTREDCOL, + S_CANDLESTIK, + S_CANDELABRA, + S_SKULLCOL, + S_TORCHTREE, + S_BIGTREE, + S_TECHPILLAR, + S_EVILEYE, + S_EVILEYE2, + S_EVILEYE3, + S_EVILEYE4, + S_FLOATSKULL, + S_FLOATSKULL2, + S_FLOATSKULL3, + S_HEARTCOL, + S_HEARTCOL2, + S_BLUETORCH, + S_BLUETORCH2, + S_BLUETORCH3, + S_BLUETORCH4, + S_GREENTORCH, + S_GREENTORCH2, + S_GREENTORCH3, + S_GREENTORCH4, + S_REDTORCH, + S_REDTORCH2, + S_REDTORCH3, + S_REDTORCH4, + S_BTORCHSHRT, + S_BTORCHSHRT2, + S_BTORCHSHRT3, + S_BTORCHSHRT4, + S_GTORCHSHRT, + S_GTORCHSHRT2, + S_GTORCHSHRT3, + S_GTORCHSHRT4, + S_RTORCHSHRT, + S_RTORCHSHRT2, + S_RTORCHSHRT3, + S_RTORCHSHRT4, + S_HANGNOGUTS, + S_HANGBNOBRAIN, + S_HANGTLOOKDN, + S_HANGTSKULL, + S_HANGTLOOKUP, + S_HANGTNOBRAIN, + S_COLONGIBS, + S_SMALLPOOL, + S_BRAINSTEM, + S_TECHLAMP, + S_TECHLAMP2, + S_TECHLAMP3, + S_TECHLAMP4, + S_TECH2LAMP, + S_TECH2LAMP2, + S_TECH2LAMP3, + S_TECH2LAMP4, + NUMSTATES +} statenum_t; + +struct state_t +{ + actionf_t action; /* void (*action) (); */ + spritenum_t sprite; + statenum_t nextstate; + + int32_t tics; /* -1 to 181 */ + + uint16_t frame; /* 0 to 32796 */ + + /* NOTE: as far as I can tell, these fields are never used. They are set to + * zero in the state lookup table, and the only other place directly + * referencing them is P_SetPsprite, which uses them seemingly as a fixed + * point number to set some other coordinates later. However, in my + * gameplay, the check for `if (state->misc1)` never evaluates to true. + * Therefore, in my quest to save memory in one of the games largest lookup + * tables, I am removing these fields. + */ + +#if 0 + int misc1; + int misc2; +#endif +}; + +typedef struct state_t state_t; + +typedef enum +{ + MT_PLAYER, + MT_POSSESSED, + MT_SHOTGUY, + MT_VILE, + MT_FIRE, + MT_UNDEAD, + MT_TRACER, + MT_SMOKE, + MT_FATSO, + MT_FATSHOT, + MT_CHAINGUY, + MT_TROOP, + MT_SERGEANT, + MT_SHADOWS, + MT_HEAD, + MT_BRUISER, + MT_BRUISERSHOT, + MT_KNIGHT, + MT_SKULL, + MT_SPIDER, + MT_BABY, + MT_CYBORG, + MT_PAIN, + MT_WOLFSS, + MT_KEEN, + MT_BOSSBRAIN, + MT_BOSSSPIT, + MT_BOSSTARGET, + MT_SPAWNSHOT, + MT_SPAWNFIRE, + MT_BARREL, + MT_TROOPSHOT, + MT_HEADSHOT, + MT_ROCKET, + MT_PLASMA, + MT_BFG, + MT_ARACHPLAZ, + MT_PUFF, + MT_BLOOD, + MT_TFOG, + MT_IFOG, + MT_TELEPORTMAN, + MT_EXTRABFG, + MT_MISC0, + MT_MISC1, + MT_MISC2, + MT_MISC3, + MT_MISC4, + MT_MISC5, + MT_MISC6, + MT_MISC7, + MT_MISC8, + MT_MISC9, + MT_MISC10, + MT_MISC11, + MT_MISC12, + MT_INV, + MT_MISC13, + MT_INS, + MT_MISC14, + MT_MISC15, + MT_MISC16, + MT_MEGA, + MT_CLIP, + MT_MISC17, + MT_MISC18, + MT_MISC19, + MT_MISC20, + MT_MISC21, + MT_MISC22, + MT_MISC23, + MT_MISC24, + MT_MISC25, + MT_CHAINGUN, + MT_MISC26, + MT_MISC27, + MT_MISC28, + MT_SHOTGUN, + MT_SUPERSHOTGUN, + MT_MISC29, + MT_MISC30, + MT_MISC31, + MT_MISC32, + MT_MISC33, + MT_MISC34, + MT_MISC35, + MT_MISC36, + MT_MISC37, + MT_MISC38, + MT_MISC39, + MT_MISC40, + MT_MISC41, + MT_MISC42, + MT_MISC43, + MT_MISC44, + MT_MISC45, + MT_MISC46, + MT_MISC47, + MT_MISC48, + MT_MISC49, + MT_MISC50, + MT_MISC51, + MT_MISC52, + MT_MISC53, + MT_MISC54, + MT_MISC55, + MT_MISC56, + MT_MISC57, + MT_MISC58, + MT_MISC59, + MT_MISC60, + MT_MISC61, + MT_MISC62, + MT_MISC63, + MT_MISC64, + MT_MISC65, + MT_MISC66, + MT_MISC67, + MT_MISC68, + MT_MISC69, + MT_MISC70, + MT_MISC71, + MT_MISC72, + MT_MISC73, + MT_MISC74, + MT_MISC75, + MT_MISC76, + MT_MISC77, + MT_MISC78, + MT_MISC79, + MT_MISC80, + MT_MISC81, + MT_MISC82, + MT_MISC83, + MT_MISC84, + MT_MISC85, + MT_MISC86, + NUMMOBJTYPES +} mobjtype_t; + +/* Ranges for these members were obtained by analyzing the `mobjinfo` + * array at game startup. + */ + +begin_packed_struct struct mobjinfo_t +{ + uint32_t flags; /* 0 to 33557510 */ + uint32_t radius; /* 393216 to 8388608 */ + uint32_t height; /* 524288 to 7208960 */ + uint32_t mass; /* 50 to 10000000 */ + uint32_t speed; /* 0 to 1638400 */ + int16_t doomednum; /* -1 to 3006 */ + uint16_t spawnhealth; /* 20 to 4000 */ + uint16_t spawnstate; /* 0 to 963 */ + uint16_t seestate; /* 0 to 785 */ + uint16_t painstate; /* 0 to 779 */ + uint16_t painchance; /* 0 to 256 */ + uint16_t meleestate; /* 0 to 566 */ + uint16_t missilestate; /* 0 to 736 */ + uint16_t deathstate; /* 0 to 808 */ + uint16_t xdeathstate; /* 0 to 749 */ + uint16_t raisestate; /* 0 to 758 */ + uint8_t damage; /* 0 to 100 */ + uint8_t reactiontime; /* 0 to 8 */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + uint8_t seesound; /* 0 to 107 */ + uint8_t attacksound; /* 0 to 52 */ + uint8_t painsound; /* 0 to 103 */ + uint8_t deathsound; /* 0 to 104 */ + uint8_t activesound; /* 0 to 105 */ +#endif +} end_packed_struct; + +typedef struct mobjinfo_t mobjinfo_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern state_t states[NUMSTATES]; +extern const char *sprnames[]; + +/* Size before opt: 0000000000012604 + * Size after opt with sound: 0000000000007124 + * Size after opt without sound: 0000000000006028 + * + * Best improvement: 6576 bytes + * Best improvement: 6.42KB + */ + +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif /* __INFO__ */ diff --git a/games/NXDoom/src/doom/m_menu.c b/games/NXDoom/src/doom/m_menu.c new file mode 100644 index 000000000..77e878f94 --- /dev/null +++ b/games/NXDoom/src/doom/m_menu.c @@ -0,0 +1,2155 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_menu.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DOOM selection menu, options, episode etc. + * Sliders and icons. Kinda widget stuff. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomdef.h" +#include "doomkeys.h" +#include "dstrings.h" + +#include "d_main.h" +#include "deh_main.h" + +#include "i_input.h" +#include "i_joystick.h" +#include "i_swap.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_misc.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +#include "hu_stuff.h" + +#include "g_game.h" + +#include "m_argv.h" +#include "m_controls.h" +#include "p_saveg.h" +#include "p_setup.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Data. */ + +#include "m_menu.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + +#define JOY_BUTTON_MAPPED(x) ((x) >= 0) +#define JOY_BUTTON_PRESSED(x) \ + (JOY_BUTTON_MAPPED(x) && (ev->data1 & (1 << (x))) != 0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +void (*message_routine)(int response); + +/* MENU TYPEDEFS */ + +typedef struct +{ + /* 0 = no cursor here, 1 = ok, 2 = arrows ok */ + + short status; + + char name[10]; + + /* choice = menu item #. + * if status = 2, + * choice=0:leftarrow,1:rightarrow + */ + + void (*routine)(int choice); + + /* hotkey in menu */ + + char alpha_key; +} menuitem_t; + +typedef struct menu_s +{ + short numitems; /* # of menu items */ + struct menu_s *prev_menu; /* previous menu */ + menuitem_t *menu_items; /* menu items */ + void (*routine)(void); /* draw routine */ + short x; + short y; /* x,y of menu */ + short last_on; /* last item user was on in menu */ +} menu_t; + +/* DOOM MENU */ + +enum +{ + MAIN_NEWGAME = 0, + MAIN_OPTIONS, + MAIN_LOADGAME, + MAIN_SAVEGAME, + MAIN_READTHIS, + MAIN_QUITDOOM, + MAIN_END +} main_e; + +/* EPISODE SELECT */ + +enum +{ + EPISODE_EP1, + EPISODE_EP2, + EPISODE_EP3, + EPISODE_EP4, + EPISODE_END +} episodes_e; + +/* NEW GAME */ + +enum +{ + NEWGAME_KILLTHINGS, + NEWGAME_TOOROUGH, + NEWGAME_HURTME, + NEWGAME_VIOLANCE, + NEWGAME_NIGHTMARE, + NEWGAME_END +} newgame_e; + +/* OPTIONS MENU */ + +enum +{ + OPT_ENDGAME, + OPT_MESSAGES, + OPT_DETAIL, + OPT_SCRNSIZE, + OPT_EMPTY1, + OPT_MOUSESENS, + OPT_EMPTY2, + OPT_SOUNDVOL, + OPT_END +} options_e; + +enum +{ + RDTHSEMPTY1, + READ1_END +} read_e; + +enum +{ + RDTHSEMPTY2, + READ2_END +} read_e2; + +/* SOUND VOLUME MENU */ + +enum +{ + SOUND_SFXVOL, + SOUND_SFXEMPTY1, + SOUND_MUSICVOL, + SOUND_SFXEMPTY2, + SOUND_END +} sound_e; + +/* LOAD GAME MENU */ + +enum +{ + LOAD_1, + LOAD_2, + LOAD_3, + LOAD_4, + LOAD_5, + LOAD_6, + LOAD_END +} load_e; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void m_new_game(int choice); +static void m_episode(int choice); +static void m_choose_skill(int choice); +static void m_load_game(int choice); +static void m_save_game(int choice); +static void m_options(int choice); +static void m_end_game(int choice); +static void m_read_this(int choice); +static void m_read_this2(int choice); +static void m_quit_doom(int choice); + +static void m_change_messages(int choice); +static void m_change_sensitivity(int choice); +static void m_sound(int choice); +static void m_change_detail(int choice); +static void m_size_display(int choice); + +static void m_finish_read_this(int choice); +static void m_load_select(int choice); +static void m_save_select(int choice); +static void m_read_save_strings(void); +static void m_quick_save(void); +static void m_quick_load(void); + +static void m_drawmain_menu(void); +static void m_draw_read_this1(void); +static void m_draw_read_this2(void); +static void m_draw_new_game(void); +static void m_draw_episode(void); +static void m_draw_options(void); +static void m_draw_load(void); +static void m_draw_save(void); + +static void m_draw_save_load_border(int x, int y); +static void m_setup_next_menu(menu_t *menudef); +static void m_draw_thermo(int x, int y, int therm_width, int therm_dot); +static void m_write_text(int x, int y, const char *string); +static int m_string_width(const char *string); +static int m_string_height(const char *string); +static void m_start_message(const char *string, + void *routine, boolean input); +static void m_clear_menus(void); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static void m_sfx_vol(int choice); +static void m_music_vol(int choice); +static void m_draw_sound(void); +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* defaulted values */ + +int g_mouse_sensitivity = 5; + +/* Show messages has default, 0 = off, 1 = on */ + +int g_show_messages = 1; + +/* Blocky mode, has default, 0 = high, 1 = normal */ + +int g_detail_level = 0; +int screenblocks = 9; + +boolean g_menuactive; +boolean inhelpscreens; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* message x & y */ + +static int g_message_last_menu_active; + +/* timed message = no input from user */ + +static boolean g_message_needs_input; + +static char g_gammamsg[5][26] = +{ + GAMMALVL0, GAMMALVL1, GAMMALVL2, GAMMALVL3, GAMMALVL4, +}; + +/* we are going to be entering a savegame string */ + +static int g_save_string_enter; +static int g_save_slot; /* which slot to save in */ +static int g_save_char_index; /* which char we're editing */ + +/* old save description before edit */ + +static char g_save_old_string[SAVESTRINGSIZE]; + +static char g_save_game_strings[10][SAVESTRINGSIZE]; + +static char g_endstring[160]; + +static short g_item_on; /* menu item skull is on */ +static short g_skull_anim_counter; /* skull animation counter */ +static short g_which_skull; /* which skull to draw */ + +/* temp for screenblocks (0-9) */ + +static int g_screen_size; + +/* -1 = no quicksave slot picked! */ + +static int g_quick_save_slot; + +/* 1 = message to be printed */ + +static int g_message_to_print; + +/* ...and here is the message string! */ + +static const char *g_message_string; + +static int g_epi; + +/* graphic name of skulls + * warning: initializer-string for array of chars is too long + */ + +static const char *g_skull_name[2] = +{ + "M_SKULL1", + "M_SKULL2", +}; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static int g_quitsounds[8] = +{ + SFX_PLDETH, SFX_DMPAIN, SFX_POPAIN, SFX_SLOP, + SFX_TELEPT, SFX_POSIT1, SFX_POSIT3, SFX_SGTATK, +}; + +static int g_quitsounds2[8] = +{ + SFX_VILACT, SFX_GETPOW, SFX_BOSCUB, SFX_SLOP, + SFX_SKESWG, SFX_KNTDTH, SFX_BSPACT, SFX_SGTATK, +}; +#endif + +/* was the save action initiated by joypad? */ + +static boolean g_joypad_save = false; + +static char g_tempstring[90]; + +static const char *g_detail_names[2] = +{ + "M_GDHIGH", + "M_GDLOW", +}; + +static const char *g_msg_names[2] = +{ + "M_MSGOFF", + "M_MSGON", +}; + +/* current menudef */ + +static menu_t *g_current_menu; + +static menuitem_t g_main_menu[] = +{ + {1, "M_NGAME", m_new_game, 'n'}, + {1, "M_OPTION", m_options, 'o'}, + {1, "M_LOADG", m_load_game, 'l'}, + {1, "M_SAVEG", m_save_game, 's'}, + {1, "M_RDTHIS", m_read_this, 'r'}, /* Another hiccup with Special edition. */ + {1, "M_QUITG", m_quit_doom, 'q'}, +}; + +static menu_t g_main_def = +{ + MAIN_END, NULL, g_main_menu, m_drawmain_menu, 97, 64, 0, +}; + +static menuitem_t g_episode_menu[] = +{ + {1, "M_EPI1", m_episode, 'k'}, + {1, "M_EPI2", m_episode, 't'}, + {1, "M_EPI3", m_episode, 'i'}, + {1, "M_EPI4", m_episode, 't'}, +}; + +static menu_t g_epi_def = +{ + EPISODE_END, /* # of menu items */ + &g_main_def, /* previous menu */ + g_episode_menu, /* menuitem_t -> */ + m_draw_episode, /* drawing routine -> */ + 48, + 63, /* x,y */ + EPISODE_EP1 /* lastOn */ +}; + +static menuitem_t new_game_menu[] = +{ + {1, "M_JKILL", m_choose_skill, 'i'}, + {1, "M_ROUGH", m_choose_skill, 'h'}, + {1, "M_HURT", m_choose_skill, 'h'}, + {1, "M_ULTRA", m_choose_skill, 'u'}, + {1, "M_NMARE", m_choose_skill, 'n'}, +}; + +static menu_t g_new_def = +{ + NEWGAME_END, /* # of menu items */ + &g_epi_def, /* previous menu */ + new_game_menu, /* menuitem_t -> */ + m_draw_new_game, /* drawing routine -> */ + 48, + 63, /* x,y */ + NEWGAME_HURTME /* lastOn */ +}; + +static menuitem_t g_options_menu[] = +{ + { + 1, + "M_ENDGAM", + m_end_game, + 'e', + }, + { + 1, + "M_MESSG", + m_change_messages, + 'm', + }, + { + 1, + "M_DETAIL", + m_change_detail, + 'g', + }, + { + 2, + "M_SCRNSZ", + m_size_display, + 's', + }, + { + -1, + "", + 0, + '\0', + }, + { + 2, + "M_MSENS", + m_change_sensitivity, + 'm', + }, + { + -1, + "", + 0, + '\0', + }, + { + 1, + "M_SVOL", + m_sound, + 's', + }, +}; + +static menu_t g_options_def = +{ + OPT_END, &g_main_def, g_options_menu, m_draw_options, 60, 37, 0, +}; + +/* Read This! MENU 1 & 2 */ + +static menuitem_t g_read_menu1[] = +{ + {1, "", m_read_this2, 0}, +}; + +static menu_t g_read_def1 = +{ + READ1_END, &g_main_def, g_read_menu1, m_draw_read_this1, 280, 185, 0, +}; + +static menuitem_t g_read_menu2[] = +{ + {1, "", m_finish_read_this, 0}, +}; + +static menu_t g_read_def2 = +{ + READ2_END, &g_read_def1, g_read_menu2, m_draw_read_this2, 330, 175, 0, +}; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static menuitem_t g_sound_menu[] = +{ + { + 2, + "M_SFXVOL", + m_sfx_vol, + 's', + }, + { + -1, + "", + 0, + '\0', + }, + { + 2, + "M_MUSVOL", + m_music_vol, + 'm', + }, + { + -1, + "", + 0, + '\0', + }, +}; + +static menu_t g_sound_def = +{ + SOUND_END, &g_options_def, g_sound_menu, m_draw_sound, 80, 64, 0, +}; +#endif + +static menuitem_t g_load_menu[] = +{ + {1, "", m_load_select, '1'}, + {1, "", m_load_select, '2'}, + {1, "", m_load_select, '3'}, + {1, "", m_load_select, '4'}, + {1, "", m_load_select, '5'}, + {1, "", m_load_select, '6'}, +}; + +static menu_t g_load_def = +{ + LOAD_END, &g_main_def, g_load_menu, m_draw_load, 80, 54, 0, +}; + +/* SAVE GAME MENU */ + +static menuitem_t g_save_menu[] = +{ + {1, "", m_save_select, '1'}, + {1, "", m_save_select, '2'}, + {1, "", m_save_select, '3'}, + {1, "", m_save_select, '4'}, + {1, "", m_save_select, '5'}, + {1, "", m_save_select, '6'}, +}; + +static menu_t g_save_def = +{ + LOAD_END, &g_main_def, g_save_menu, m_draw_save, 80, 54, 0, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Generate a default save slot name when the user saves to + * an empty slot via the joypad. + */ + +static void set_default_save_name(int slot) +{ + /* map from IWAD or PWAD? */ + + if (w_is_iwad_lump(maplumpinfo) && strcmp(savegamedir, "")) + { + snprintf(g_save_game_strings[g_item_on], SAVESTRINGSIZE, "%s", + maplumpinfo->name); + } + else + { + char *wadname = m_string_duplicate(w_wad_name_for_lump(maplumpinfo)); + char *ext = strrchr(wadname, '.'); + + if (ext != NULL) + { + *ext = '\0'; + } + + snprintf(g_save_game_strings[g_item_on], SAVESTRINGSIZE, "%s (%s)", + maplumpinfo->name, wadname); + free(wadname); + } + + m_force_uppercase(g_save_game_strings[g_item_on]); + g_joypad_save = false; +} + +/* m_responder calls this when user is finished */ + +static void m_do_save(int slot) +{ + g_save_game(slot, g_save_game_strings[slot]); + m_clear_menus(); + + /* PICK QUICKSAVE SLOT YET? */ + + if (g_quick_save_slot == -2) g_quick_save_slot = slot; +} + +static void m_quick_save_response(int key) +{ + if (key == key_menu_confirm) + { + m_do_save(g_quick_save_slot); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + } +} + +static void m_quick_load_response(int key) +{ + if (key == key_menu_confirm) + { + m_load_select(g_quick_save_slot); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + } +} + +/* These keys evaluate to a "null" key in Vanilla Doom that allows weird + * jumping in the menus. Preserve this behavior for accuracy. + */ + +static boolean is_null_key(int key) +{ + return key == KEY_PAUSE || key == KEY_CAPSLOCK || key == KEY_SCRLCK || + key == KEY_NUMLOCK; +} + +static void m_start_message(const char *string, void *routine, boolean input) +{ + g_message_last_menu_active = g_menuactive; + g_message_to_print = 1; + g_message_string = string; + message_routine = routine; + g_message_needs_input = input; + g_menuactive = true; + return; +} + +static const char *m_select_end_message(void) +{ + const char **endmsg; + + if (logical_gamemission == doom) + { + /* Doom 1 */ + + endmsg = doom1_endmsg; + } + else + { + /* Doom 2 */ + + endmsg = doom2_endmsg; + } + + return endmsg[gametic % NUM_QUITMESSAGES]; +} + +static void m_quit_response(int key) +{ + if (key != key_menu_confirm) return; + if (!netgame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gamemode == commercial) + s_start_sound(NULL, g_quitsounds2[(gametic >> 2) & 7]); + else + s_start_sound(NULL, g_quitsounds[(gametic >> 2) & 7]); +#endif + i_wait_vbl(105); + } + + i_quit(); +} + +static void m_end_game_response(int key) +{ + if (key != key_menu_confirm) return; + + g_current_menu->last_on = g_item_on; + m_clear_menus(); + d_start_title(); +} + +static void m_draw_read_this_commercial(void) +{ + inhelpscreens = true; + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP"), PU_CACHE)); +} + +static void m_verify_nightmare(int key) +{ + if (key != key_menu_confirm) return; + + g_deferred_init_new(NEWGAME_NIGHTMARE, g_epi + 1, 1); + m_clear_menus(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* read the strings from the savegame files */ + +void m_read_save_strings(void) +{ + FILE *handle; + int i; + char name[256]; + + for (i = 0; i < LOAD_END; i++) + { + int retval; + m_str_copy(name, p_save_game_file(i), sizeof(name)); + + handle = fopen(name, "rb"); + if (handle == NULL) + { + m_str_copy(g_save_game_strings[i], EMPTYSTRING, SAVESTRINGSIZE); + g_load_menu[i].status = 0; + continue; + } + + retval = fread(&g_save_game_strings[i], 1, SAVESTRINGSIZE, handle); + fclose(handle); + g_load_menu[i].status = retval == SAVESTRINGSIZE; + } +} + +void m_draw_load(void) +{ + int i; + + v_draw_patch_direct(72, 28, w_cache_lump_name(("M_LOADG"), PU_CACHE)); + + for (i = 0; i < LOAD_END; i++) + { + m_draw_save_load_border(g_load_def.x, g_load_def.y + LINEHEIGHT * i); + m_write_text(g_load_def.x, g_load_def.y + LINEHEIGHT * i, + g_save_game_strings[i]); + } +} + +/* Draw border for the savegame description */ + +void m_draw_save_load_border(int x, int y) +{ + int i; + + v_draw_patch_direct(x - 8, y + 7, + w_cache_lump_name(("M_LSLEFT"), PU_CACHE)); + + for (i = 0; i < 24; i++) + { + v_draw_patch_direct(x, y + 7, + w_cache_lump_name(("M_LSCNTR"), PU_CACHE)); + x += 8; + } + + v_draw_patch_direct(x, y + 7, w_cache_lump_name(("M_LSRGHT"), PU_CACHE)); +} + +/* User wants to load this game */ + +void m_load_select(int choice) +{ + char name[256]; + + m_str_copy(name, p_save_game_file(choice), sizeof(name)); + + g_load_game(name); + m_clear_menus(); +} + +/* Selected from DOOM menu */ + +void m_load_game(int choice) +{ + if (netgame) + { + m_start_message((LOADNET), NULL, false); + return; + } + + m_setup_next_menu(&g_load_def); + m_read_save_strings(); +} + +/* M_SaveGame & Cie. */ + +void m_draw_save(void) +{ + int i; + + v_draw_patch_direct(72, 28, w_cache_lump_name(("M_SAVEG"), PU_CACHE)); + for (i = 0; i < LOAD_END; i++) + { + m_draw_save_load_border(g_load_def.x, g_load_def.y + LINEHEIGHT * i); + m_write_text(g_load_def.x, g_load_def.y + LINEHEIGHT * i, + g_save_game_strings[i]); + } + + if (g_save_string_enter) + { + i = m_string_width(g_save_game_strings[g_save_slot]); + m_write_text(g_load_def.x + i, + g_load_def.y + LINEHEIGHT * g_save_slot, "_"); + } +} + +/* User wants to save. Start string input for m_responder */ + +void m_save_select(int choice) +{ + int x; + int y; + + /* we are going to be intercepting all chars */ + + g_save_string_enter = 1; + + /* We need to turn on text input: */ + + x = g_load_def.x - 11; + y = g_load_def.y + choice * LINEHEIGHT - 4; + i_start_text_input(x, y, x + 8 + 24 * 8 + 8, y + LINEHEIGHT - 2); + + g_save_slot = choice; + m_str_copy(g_save_old_string, g_save_game_strings[choice], SAVESTRINGSIZE); + if (!strcmp(g_save_game_strings[choice], EMPTYSTRING)) + { + g_save_game_strings[choice][0] = 0; + + if (g_joypad_save) + { + set_default_save_name(choice); + } + } + + g_save_char_index = strlen(g_save_game_strings[choice]); +} + +/* Selected from DOOM menu */ + +void m_save_game(int choice) +{ + if (!usergame) + { + m_start_message((SAVEDEAD), NULL, false); + return; + } + + if (gamestate != GS_LEVEL) return; + + m_setup_next_menu(&g_save_def); + m_read_save_strings(); +} + +void m_quick_save(void) +{ + if (!usergame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + + if (gamestate != GS_LEVEL) return; + + if (g_quick_save_slot < 0) + { + m_start_control_panel(); + m_read_save_strings(); + m_setup_next_menu(&g_save_def); + g_quick_save_slot = -2; /* means to pick a slot now */ + return; + } + + snprintf(g_tempstring, sizeof(g_tempstring), QSPROMPT, + g_save_game_strings[g_quick_save_slot]); + m_start_message(g_tempstring, m_quick_save_response, true); +} + +static void m_quick_load(void) +{ + if (netgame) + { + m_start_message((QLOADNET), NULL, false); + return; + } + + if (g_quick_save_slot < 0) + { + m_start_message((QSAVESPOT), NULL, false); + return; + } + + snprintf(g_tempstring, sizeof(g_tempstring), QLPROMPT, + g_save_game_strings[g_quick_save_slot]); + m_start_message(g_tempstring, m_quick_load_response, true); +} + +/* Read This Menus + * Had a "quick hack to fix romero bug" + */ + +static void m_draw_read_this1(void) +{ + inhelpscreens = true; + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP2"), PU_CACHE)); +} + +/* Read This Menus - optional second page. */ + +static void m_draw_read_this2(void) +{ + inhelpscreens = true; + + /* We only ever draw the second page if this is + * gameversion == exe_doom_1_9 and gamemode == registered + */ + + v_draw_patch_direct(0, 0, w_cache_lump_name(("HELP1"), PU_CACHE)); +} + +/* Change Sfx & Music volumes */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static void m_draw_sound(void) +{ + v_draw_patch_direct(60, 38, w_cache_lump_name(("M_SVOL"), PU_CACHE)); + + m_draw_thermo(g_sound_def.x, + g_sound_def.y + LINEHEIGHT * (SOUND_SFXVOL + 1), 16, + g_sfx_volume); + + m_draw_thermo(g_sound_def.x, + g_sound_def.y + LINEHEIGHT * (SOUND_MUSICVOL + 1), 16, + g_music_volume); +} +#endif + +static void m_sound(int choice) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + m_setup_next_menu(&g_sound_def); +#endif +} + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static void m_sfx_vol(int choice) +{ + switch (choice) + { + case 0: + if (g_sfx_volume) g_sfx_volume--; + break; + case 1: + if (g_sfx_volume < 15) g_sfx_volume++; + break; + } + + s_set_sfx_volume(g_sfx_volume * 8); +} +#endif + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +static void m_music_vol(int choice) +{ + switch (choice) + { + case 0: + if (g_music_volume) g_music_volume--; + break; + case 1: + if (g_music_volume < 15) g_music_volume++; + break; + } + + s_set_music_volume(g_music_volume * 8); +} +#endif + +void m_drawmain_menu(void) +{ + v_draw_patch_direct(94, 2, w_cache_lump_name(("M_DOOM"), PU_CACHE)); +} + +void m_draw_new_game(void) +{ + v_draw_patch_direct(96, 14, w_cache_lump_name(("M_NEWG"), PU_CACHE)); + v_draw_patch_direct(54, 38, w_cache_lump_name(("M_SKILL"), PU_CACHE)); +} + +void m_new_game(int choice) +{ + if (netgame && !demoplayback) + { + m_start_message((NEWGAME), NULL, false); + return; + } + + /* Chex Quest disabled the episode select screen, as did Doom II. */ + + if (gamemode == commercial || gameversion == exe_chex) + m_setup_next_menu(&g_new_def); + else + m_setup_next_menu(&g_epi_def); +} + +void m_draw_episode(void) +{ + v_draw_patch_direct(54, 38, w_cache_lump_name(("M_EPISOD"), PU_CACHE)); +} + +void m_choose_skill(int choice) +{ + if (choice == NEWGAME_NIGHTMARE) + { + m_start_message((NIGHTMARE), m_verify_nightmare, true); + return; + } + + g_deferred_init_new(choice, g_epi + 1, 1); + m_clear_menus(); +} + +void m_episode(int choice) +{ + if ((gamemode == shareware) && choice) + { + m_start_message((SWSTRING), NULL, false); + m_setup_next_menu(&g_read_def1); + return; + } + + g_epi = choice; + m_setup_next_menu(&g_new_def); +} + +void m_draw_options(void) +{ + v_draw_patch_direct(108, 15, w_cache_lump_name(("M_OPTTTL"), PU_CACHE)); + + v_draw_patch_direct( + g_options_def.x + 175, g_options_def.y + LINEHEIGHT * OPT_DETAIL, + w_cache_lump_name((g_detail_names[g_detail_level]), PU_CACHE)); + + v_draw_patch_direct( + g_options_def.x + 120, g_options_def.y + LINEHEIGHT * OPT_MESSAGES, + w_cache_lump_name((g_msg_names[g_show_messages]), PU_CACHE)); + + m_draw_thermo(g_options_def.x, + g_options_def.y + LINEHEIGHT * (OPT_MOUSESENS + 1), 10, + g_mouse_sensitivity); + + m_draw_thermo(g_options_def.x, + g_options_def.y + LINEHEIGHT * (OPT_SCRNSIZE + 1), 9, + g_screen_size); +} + +void m_options(int choice) +{ + m_setup_next_menu(&g_options_def); +} + +/* Toggle messages on/off */ + +void m_change_messages(int choice) +{ + /* warning: unused parameter `int choice' */ + + choice = 0; + g_show_messages = 1 - g_show_messages; + + if (!g_show_messages) + players[consoleplayer].message = (MSGOFF); + else + players[consoleplayer].message = (MSGON); + + message_dontfuckwithme = true; +} + +void m_end_game(int choice) +{ + choice = 0; + if (!usergame) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + + if (netgame) + { + m_start_message((NETEND), NULL, false); + return; + } + + m_start_message((ENDGAME), m_end_game_response, true); +} + +void m_read_this(int choice) +{ + choice = 0; + m_setup_next_menu(&g_read_def1); +} + +void m_read_this2(int choice) +{ + choice = 0; + m_setup_next_menu(&g_read_def2); +} + +void m_finish_read_this(int choice) +{ + choice = 0; + m_setup_next_menu(&g_main_def); +} + +void m_quit_doom(int choice) +{ + snprintf(g_endstring, sizeof(g_endstring), "%s\n\n" DOSY, + (m_select_end_message())); + + m_start_message(g_endstring, m_quit_response, true); +} + +void m_change_sensitivity(int choice) +{ + switch (choice) + { + case 0: + if (g_mouse_sensitivity) g_mouse_sensitivity--; + break; + case 1: + if (g_mouse_sensitivity < 9) g_mouse_sensitivity++; + break; + } +} + +void m_change_detail(int choice) +{ + choice = 0; + g_detail_level = 1 - g_detail_level; + + r_set_view_size(screenblocks, g_detail_level); + + if (!g_detail_level) + players[consoleplayer].message = (DETAILHI); + else + players[consoleplayer].message = (DETAILLO); +} + +void m_size_display(int choice) +{ + switch (choice) + { + case 0: + if (g_screen_size > 0) + { + screenblocks--; + g_screen_size--; + } + break; + case 1: + if (g_screen_size < 8) + { + screenblocks++; + g_screen_size++; + } + break; + } + + r_set_view_size(screenblocks, g_detail_level); +} + +/* Menu Functions */ + +void m_draw_thermo(int x, int y, int therm_width, int therm_dot) +{ + int xx; + int i; + + xx = x; + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERML"), PU_CACHE)); + xx += 8; + for (i = 0; i < therm_width; i++) + { + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERMM"), PU_CACHE)); + xx += 8; + } + + v_draw_patch_direct(xx, y, w_cache_lump_name(("M_THERMR"), PU_CACHE)); + + v_draw_patch_direct((x + 8) + therm_dot * 8, y, + w_cache_lump_name(("M_THERMO"), PU_CACHE)); +} + +/* Find string width from hu_font chars */ + +int m_string_width(const char *string) +{ + size_t i; + int w = 0; + int c; + + for (i = 0; i < strlen(string); i++) + { + c = toupper(string[i]) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + w += 4; + else + w += SHORT(hu_font[c]->width); + } + + return w; +} + +/* Find string height from hu_font chars */ + +int m_string_height(const char *string) +{ + size_t i; + int h; + int height = SHORT(hu_font[0]->height); + + h = height; + for (i = 0; i < strlen(string); i++) + { + if (string[i] == '\n') h += height; + } + + return h; +} + +/* Write a string using the hu_font */ + +void m_write_text(int x, int y, const char *string) +{ + int w; + const char *ch; + int c; + int cx; + int cy; + + ch = string; + cx = x; + cy = y; + + while (1) + { + c = *ch++; + if (!c) break; + if (c == '\n') + { + cx = x; + cy += 12; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c >= HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = SHORT(hu_font[c]->width); + if (cx + w > SCREENWIDTH) break; + v_draw_patch_direct(cx, cy, hu_font[c]); + cx += w; + } +} + +/* CONTROL PANEL */ + +boolean m_responder(event_t *ev) +{ + int ch; + int key; + int i; + static int mousewait = 0; + static int mousey = 0; + static int lasty = 0; + static int mousex = 0; + static int lastx = 0; + int dir; + + /* In testcontrols mode, none of the function keys should do anything + * - the only key is escape to quit. + */ + + if (testcontrols) + { + if (ev->type == ev_quit || + (ev->type == ev_keydown && + (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit))) + { + i_quit(); + return true; + } + + return false; + } + + /* "close" button pressed on window? */ + + if (ev->type == ev_quit) + { + /* First click on close button = bring up quit confirm message. + * Second click on close button = confirm quit + */ + + if (g_menuactive && g_message_to_print && + message_routine == m_quit_response) + { + m_quit_response(key_menu_confirm); + } + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quit_doom(0); + } + + return true; + } + + /* key is the key pressed, ch is the actual character typed */ + + ch = 0; + key = -1; + + if (ev->type == ev_joystick) + { + /* Simulate key presses from joystick events to interact with the menu. + */ + + if (g_menuactive) + { + if (JOY_GET_DPAD(ev->data6) != JOY_DIR_NONE) + { + dir = JOY_GET_DPAD(ev->data6); + } + else if (JOY_GET_LSTICK(ev->data6) != JOY_DIR_NONE) + { + dir = JOY_GET_LSTICK(ev->data6); + } + else + { + dir = JOY_GET_RSTICK(ev->data6); + } + + if (dir & JOY_DIR_UP) + { + key = key_menu_up; + joywait = i_get_time() + 5; + } + else if (dir & JOY_DIR_DOWN) + { + key = key_menu_down; + joywait = i_get_time() + 5; + } + + if (dir & JOY_DIR_LEFT) + { + key = key_menu_left; + joywait = i_get_time() + 5; + } + else if (dir & JOY_DIR_RIGHT) + { + key = key_menu_right; + joywait = i_get_time() + 5; + } + + if (JOY_BUTTON_PRESSED(joybfire)) + { + /* Simulate a 'Y' keypress when Doom show a Y/N dialog with + * Fire button. + */ + + if (g_message_to_print && g_message_needs_input) + { + key = key_menu_confirm; + } + + /* Simulate pressing "Enter" when we are supplying a save slot + * name + */ + + else if (g_save_string_enter) + { + key = KEY_ENTER; + } + else + { + /* if selecting a save slot via joypad, set a flag */ + + if (g_current_menu == &g_save_def) + { + g_joypad_save = true; + } + + key = key_menu_forward; + } + + joywait = i_get_time() + 5; + } + + if (JOY_BUTTON_PRESSED(joybuse)) + { + /* Simulate a 'N' keypress when Doom show a Y/N dialog with Use + * button. + */ + + if (g_message_to_print && g_message_needs_input) + { + key = key_menu_abort; + } + + /* If user was entering a save name, back out */ + + else if (g_save_string_enter) + { + key = KEY_ESCAPE; + } + else + { + key = key_menu_back; + } + + joywait = i_get_time() + 5; + } + } + + if (JOY_BUTTON_PRESSED(joybmenu)) + { + key = key_menu_activate; + joywait = i_get_time() + 5; + } + } + else + { + if (ev->type == ev_mouse && mousewait < i_get_time() && g_menuactive) + { + mousey += ev->data3; + if (mousey < lasty - 30) + { + key = key_menu_down; + mousewait = i_get_time() + 5; + mousey = lasty -= 30; + } + else if (mousey > lasty + 30) + { + key = key_menu_up; + mousewait = i_get_time() + 5; + mousey = lasty += 30; + } + + mousex += ev->data2; + if (mousex < lastx - 30) + { + key = key_menu_left; + mousewait = i_get_time() + 5; + mousex = lastx -= 30; + } + else if (mousex > lastx + 30) + { + key = key_menu_right; + mousewait = i_get_time() + 5; + mousex = lastx += 30; + } + + if (ev->data1 & 1) + { + key = key_menu_forward; + mousewait = i_get_time() + 15; + } + + if (ev->data1 & 2) + { + key = key_menu_back; + mousewait = i_get_time() + 15; + } + } + else + { + if (ev->type == ev_keydown) + { + key = ev->data1; + ch = ev->data2; + } + } + } + + if (key == -1) return false; + + /* Save Game string input */ + + if (g_save_string_enter) + { + switch (key) + { + case KEY_BACKSPACE: + if (g_save_char_index > 0) + { + g_save_char_index--; + g_save_game_strings[g_save_slot][g_save_char_index] = 0; + } + break; + + case KEY_ESCAPE: + g_save_string_enter = 0; + i_stop_text_input(); + m_str_copy(g_save_game_strings[g_save_slot], g_save_old_string, + SAVESTRINGSIZE); + break; + + case KEY_ENTER: + g_save_string_enter = 0; + i_stop_text_input(); + if (g_save_game_strings[g_save_slot][0]) m_do_save(g_save_slot); + break; + + default: + /* Savegame name entry. This is complicated. + * Vanilla has a bug where the shift key is ignored when entering + * a savegame name. If vanilla_keyboard_mapping is on, we want + * to emulate this bug by using ev->data1. But if it's turned off, + * it implies the user doesn't care about Vanilla emulation: + * instead, use ev->data3 which gives the fully-translated and + * modified key input. + */ + + if (ev->type != ev_keydown) + { + break; + } + + if (vanilla_keyboard_mapping) + { + ch = ev->data1; + } + else + { + ch = ev->data3; + } + + ch = toupper(ch); + + if (ch != ' ' && + (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE)) + { + break; + } + + if (ch >= 32 && ch <= 127 && + g_save_char_index < SAVESTRINGSIZE - 1 && + m_string_width(g_save_game_strings[g_save_slot]) < + (SAVESTRINGSIZE - 2) * 8) + { + g_save_game_strings[g_save_slot][g_save_char_index++] = ch; + g_save_game_strings[g_save_slot][g_save_char_index] = 0; + } + + break; + } + + return true; + } + + /* Take care of any messages that need input */ + + if (g_message_to_print) + { + if (g_message_needs_input) + { + if (key != ' ' && key != KEY_ESCAPE && key != key_menu_confirm && + key != key_menu_abort) + { + return false; + } + } + + g_menuactive = g_message_last_menu_active; + g_message_to_print = 0; + if (message_routine) message_routine(key); + + g_menuactive = false; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + return true; + } + + if ((devparm && key == key_menu_help) || + (key != 0 && key == key_menu_screenshot)) + { + g_screenshot(); + return true; + } + + /* F-Keys */ + + if (!g_menuactive) + { + if (key == key_menu_decscreen) /* Screen size down */ + { + if (automapactive || chat_on) return false; + m_size_display(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + return true; + } + else if (key == key_menu_incscreen) /* Screen size up */ + { + if (automapactive || chat_on) return false; + m_size_display(1); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + return true; + } + else if (key == key_menu_help) /* Help key */ + { + m_start_control_panel(); + + if (gameversion >= exe_ultimate) + g_current_menu = &g_read_def2; + else + g_current_menu = &g_read_def1; + + g_item_on = 0; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_save) /* Save */ + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_save_game(0); + return true; + } + else if (key == key_menu_load) /* Load */ + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_load_game(0); + return true; + } + else if (key == key_menu_volume) /* Sound Volume */ + { + m_start_control_panel(); + g_item_on = SOUND_SFXVOL; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + g_current_menu = &g_sound_def; + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_detail) /* Detail toggle */ + { + m_change_detail(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_qsave) /* Quicksave */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quick_save(); + return true; + } + else if (key == key_menu_endgame) /* End game */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_end_game(0); + return true; + } + else if (key == key_menu_messages) /* Toggle messages */ + { + m_change_messages(0); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + else if (key == key_menu_qload) /* Quickload */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quick_load(); + return true; + } + else if (key == key_menu_quit) /* Quit DOOM */ + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + m_quit_doom(0); + return true; + } + else if (key == key_menu_gamma) /* gamma toggle */ + { + usegamma++; + if (usegamma > 4) usegamma = 0; + players[consoleplayer].message = (g_gammamsg[usegamma]); + i_set_palette(w_cache_lump_name(("PLAYPAL"), PU_CACHE)); + return true; + } + } + + /* Pop-up menu? */ + + if (!g_menuactive) + { + if (key == key_menu_activate) + { + m_start_control_panel(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + return true; + } + + return false; + } + + /* Keys usable within menu */ + + if (key == key_menu_down) + { + /* Move down to next item */ + + do + { + if (g_item_on + 1 > g_current_menu->numitems - 1) + g_item_on = 0; + else + g_item_on++; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + } + while (g_current_menu->menu_items[g_item_on].status == -1); + + return true; + } + else if (key == key_menu_up) + { + /* Move back up to previous item */ + + do + { + if (!g_item_on) + g_item_on = g_current_menu->numitems - 1; + else + g_item_on--; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + } + while (g_current_menu->menu_items[g_item_on].status == -1); + + return true; + } + else if (key == key_menu_left) + { + /* Slide slider left */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + g_current_menu->menu_items[g_item_on].routine(0); + } + + return true; + } + else if (key == key_menu_right) + { + /* Slide slider right */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + g_current_menu->menu_items[g_item_on].routine(1); + } + + return true; + } + else if (key == key_menu_forward) + { + /* Activate menu item */ + + if (g_current_menu->menu_items[g_item_on].routine && + g_current_menu->menu_items[g_item_on].status) + { + g_current_menu->last_on = g_item_on; + if (g_current_menu->menu_items[g_item_on].status == 2) + { + g_current_menu->menu_items[g_item_on].routine( + 1); /* right arrow */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_STNMOV); +#endif + } + else + { + g_current_menu->menu_items[g_item_on].routine(g_item_on); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PISTOL); +#endif + } + } + + return true; + } + else if (key == key_menu_activate) + { + /* Deactivate menu */ + + g_current_menu->last_on = g_item_on; + m_clear_menus(); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHX); +#endif + return true; + } + else if (key == key_menu_back) + { + /* Go back to previous menu */ + + g_current_menu->last_on = g_item_on; + if (g_current_menu->prev_menu) + { + g_current_menu = g_current_menu->prev_menu; + g_item_on = g_current_menu->last_on; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_SWTCHN); +#endif + } + + return true; + } + + /* Keyboard shortcut? + * Vanilla Doom has a weird behavior where it jumps to the scroll bars + * when the certain keys are pressed, so emulate this. + */ + + else if (ch != 0 || is_null_key(key)) + { + for (i = g_item_on + 1; i < g_current_menu->numitems; i++) + { + if (g_current_menu->menu_items[i].alpha_key == ch) + { + g_item_on = i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + return true; + } + } + + for (i = 0; i <= g_item_on; i++) + { + if (g_current_menu->menu_items[i].alpha_key == ch) + { + g_item_on = i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_PSTOP); +#endif + return true; + } + } + } + + return false; +} + +void m_start_control_panel(void) +{ + /* intro might call this repeatedly */ + + if (g_menuactive) return; + + g_menuactive = 1; + g_current_menu = &g_main_def; /* JDC */ + g_item_on = g_current_menu->last_on; /* JDC */ +} + +/* Display OPL debug messages - hack for GENMIDI development. */ + +#if 0 +static void m_draw_opl_dev(void) +{ + char debug[1024]; + char *curr; + char *p; + int line; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + i_opl_dev_messages(debug, sizeof(debug)); +#endif + curr = debug; + line = 0; + + for (; ; ) + { + p = strchr(curr, '\n'); + + if (p != NULL) + { + *p = '\0'; + } + + m_write_text(0, line * 8, curr); + ++line; + + if (p == NULL) + { + break; + } + + curr = p + 1; + } +} +#endif + +/* m_drawer + * Called after the view has been rendered, + * but before it has been blitted. + */ + +void m_drawer(void) +{ + static short x; + static short y; + unsigned int i; + unsigned int max; + char string[80]; + const char *name; + int start; + + inhelpscreens = false; + + /* Horiz. & Vertically center string and print it. */ + + if (g_message_to_print) + { + start = 0; + y = SCREENHEIGHT / 2 - m_string_height(g_message_string) / 2; + while (g_message_string[start] != '\0') + { + boolean foundnewline = false; + + for (i = 0; g_message_string[start + i] != '\0'; i++) + { + if (g_message_string[start + i] == '\n') + { + m_str_copy(string, g_message_string + start, + sizeof(string)); + if (i < sizeof(string)) + { + string[i] = '\0'; + } + + foundnewline = true; + start += i + 1; + break; + } + } + + if (!foundnewline) + { + m_str_copy(string, g_message_string + start, sizeof(string)); + start += strlen(string); + } + + x = SCREENWIDTH / 2 - m_string_width(string) / 2; + m_write_text(x, y, string); + y += SHORT(hu_font[0]->height); + } + + return; + } + + if (!g_menuactive) return; + + if (g_current_menu->routine) + g_current_menu->routine(); /* call Draw routine */ + + /* DRAW MENU */ + + x = g_current_menu->x; + y = g_current_menu->y; + max = g_current_menu->numitems; + + for (i = 0; i < max; i++) + { + name = (g_current_menu->menu_items[i].name); + + if (name[0] && w_check_num_for_name(name) > 0) + { + v_draw_patch_direct(x, y, w_cache_lump_name(name, PU_CACHE)); + } + + y += LINEHEIGHT; + } + + /* DRAW SKULL */ + + v_draw_patch_direct( + x + SKULLXOFF, g_current_menu->y - 5 + g_item_on * LINEHEIGHT, + w_cache_lump_name((g_skull_name[g_which_skull]), PU_CACHE)); +} + +void m_clear_menus(void) +{ + g_menuactive = 0; +} + +void m_setup_next_menu(menu_t *menudef) +{ + g_current_menu = menudef; + g_item_on = g_current_menu->last_on; +} + +void m_ticker(void) +{ + if (--g_skull_anim_counter <= 0) + { + g_which_skull ^= 1; + g_skull_anim_counter = 8; + } +} + +void m_init(void) +{ + g_current_menu = &g_main_def; + g_menuactive = 0; + g_item_on = g_current_menu->last_on; + g_which_skull = 0; + g_skull_anim_counter = 10; + g_screen_size = screenblocks - 3; + g_message_to_print = 0; + g_message_string = NULL; + g_message_last_menu_active = g_menuactive; + g_quick_save_slot = -1; + + /* Here we could catch other version dependencies, like HELP1/2, and four + * episodes. + */ + + /* The same hacks were used in the original Doom EXEs. */ + + if (gameversion >= exe_ultimate) + { + g_main_menu[MAIN_READTHIS].routine = m_read_this2; + g_read_def2.prev_menu = NULL; + } + + if (gameversion >= exe_final && gameversion <= exe_final2) + { + g_read_def2.routine = m_draw_read_this_commercial; + } + + if (gamemode == commercial) + { + g_main_menu[MAIN_READTHIS] = g_main_menu[MAIN_QUITDOOM]; + g_main_def.numitems--; + g_main_def.y += 8; + g_new_def.prev_menu = &g_main_def; + g_read_def1.routine = m_draw_read_this_commercial; + g_read_def1.x = 330; + g_read_def1.y = 165; + g_read_menu1[RDTHSEMPTY1].routine = m_finish_read_this; + } + + /* Versions of doom.exe before the Ultimate Doom release only had + * three episodes; if we're emulating one of those then don't try + * to show episode four. If we are, then do show episode four + * (should crash if missing). + */ + + if (gameversion < exe_ultimate) + { + g_epi_def.numitems--; + } + + /* chex.exe shows only one episode. */ + + else if (gameversion == exe_chex) + { + g_epi_def.numitems = 1; + } +} diff --git a/games/NXDoom/src/doom/m_menu.h b/games/NXDoom/src/doom/m_menu.h new file mode 100644 index 000000000..545f3eb8a --- /dev/null +++ b/games/NXDoom/src/doom/m_menu.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_menu.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Menu widget stuff, episode selection and such. + * + ****************************************************************************/ + +#ifndef __M_MENU__ +#define __M_MENU__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int g_detail_level; +extern int screenblocks; + +extern boolean inhelpscreens; +extern int g_show_messages; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* MENUS + * + * Called by main loop, + * saves config file and calls i_quit when user exits. + * Even when the menu is not displayed, + * this can resize the view and change game parameters. + * Does all the real work of the menu interaction. + */ + +boolean m_responder(event_t *ev); + +/* Called by main loop, only used for menu (skull cursor) animation. */ + +void m_ticker(void); + +/* Called by main loop, draws the menus directly into the screen buffer. */ + +void m_drawer(void); + +/* Called by d_doom_main, loads the config file. */ + +void m_init(void); + +/* Called by intro code to force menu up upon a keypress, does nothing if + * menu is already up. + */ + +void m_start_control_panel(void); + +#endif /* __M_MENU__ */ diff --git a/games/NXDoom/src/doom/m_random.c b/games/NXDoom/src/doom/m_random.c new file mode 100644 index 000000000..1c846cc0c --- /dev/null +++ b/games/NXDoom/src/doom/m_random.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_random.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Random number LUT. + * + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* m_random + * Returns a 0-255 number + */ + +static const unsigned char rndtable[256] = +{ + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, + 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110, + 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, 52, 140, 202, + 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178, + 252, 182, 202, 182, 141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156, + 198, 225, 193, 219, 93, 122, 175, 249, 0, 175, 143, 70, 239, 46, 246, + 163, 53, 163, 109, 168, 135, 2, 235, 25, 92, 20, 145, 138, 77, 69, + 166, 78, 176, 173, 212, 166, 113, 94, 161, 41, 50, 239, 49, 111, 164, + 70, 60, 2, 37, 171, 75, 136, 156, 11, 56, 42, 146, 138, 229, 73, + 146, 77, 61, 98, 196, 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, + 170, 247, 181, 113, 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, + 166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, + 143, 224, 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, + 95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226, + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17, + 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242, + 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, 120, 163, 236, + 249, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int rndindex = 0; +int prndindex = 0; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Which one is deterministic? */ + +int p_random(void) +{ + prndindex = (prndindex + 1) & 0xff; + return rndtable[prndindex]; +} + +int m_random(void) +{ + rndindex = (rndindex + 1) & 0xff; + return rndtable[rndindex]; +} + +void m_clear_random(void) +{ + rndindex = prndindex = 0; +} + +/* inspired by the same routine in Eternity, thanks haleyjd */ + +int p_sub_random(void) +{ + int r = p_random(); + return r - p_random(); +} diff --git a/games/NXDoom/src/doom/m_random.h b/games/NXDoom/src/doom/m_random.h new file mode 100644 index 000000000..f5f5cf74a --- /dev/null +++ b/games/NXDoom/src/doom/m_random.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/m_random.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef __M_RANDOM__ +#define __M_RANDOM__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Returns a number from 0 to 255, from a lookup table. */ + +int m_random(void); + +/* As m_random, but used only by the play simulation. */ + +int p_random(void); + +/* Fix randoms for demos. */ + +void m_clear_random(void); + +/* Defined version of p_random() - p_random() */ + +int p_sub_random(void); + +#endif /* __M_RANDOM__ */ diff --git a/games/NXDoom/src/doom/p_ceilng.c b/games/NXDoom/src/doom/p_ceilng.c new file mode 100644 index 000000000..582ad604e --- /dev/null +++ b/games/NXDoom/src/doom/p_ceilng.c @@ -0,0 +1,327 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_ceilng.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: Ceiling aninmation (lowering, crushing, raising) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* CEILINGS */ + +ceiling_t *activeceilings[MAXCEILINGS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* t_move_ceiling */ + +void t_move_ceiling(ceiling_t *ceiling) +{ + result_e res; + + switch (ceiling->direction) + { + case 0: /* IN STASIS */ + break; + case 1: /* UP */ + res = t_move_plane(ceiling->sector, ceiling->speed, ceiling->topheight, + false, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + break; + default: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_STNMOV); +#endif + /* ? */ + + break; + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case CEIL_RAISETOHIGHEST: + p_remove_active_ceiling(ceiling); + break; + + case CEIL_SILENTCRUSHANDRAISE: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_PSTOP); +#endif + case CEIL_FASTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + ceiling->direction = -1; + break; + + default: + break; + } + } + break; + + case -1: /* DOWN */ + res = t_move_plane(ceiling->sector, ceiling->speed, + ceiling->bottomheight, ceiling->crush, 1, ceiling->direction); + + if (!(leveltime & 7)) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + break; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + default: + s_start_sound(&ceiling->sector->soundorg, SFX_STNMOV); +#else + default: + break; +#endif + } + } + + if (res == pastdest) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&ceiling->sector->soundorg, SFX_PSTOP); +#endif + case CEIL_CRUSHANDRAISE: + ceiling->speed = CEILSPEED; + case CEIL_FASTCRUSHANDRAISE: + ceiling->direction = 1; + break; + + case CEIL_LOWERANDCRUSH: + case CEIL_LOWERTOFLOOR: + p_remove_active_ceiling(ceiling); + break; + + default: + break; + } + } + else /* ( res != pastdest ) */ + { + if (res == crushed) + { + switch (ceiling->type) + { + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + case CEIL_LOWERANDCRUSH: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + +/* ev_do_ceiling + * Move a ceiling up/down and all around! + */ + +int ev_do_ceiling(line_t *line, ceiling_e type) +{ + int secnum; + int rtn; + sector_t *sec; + ceiling_t *ceiling; + + secnum = -1; + rtn = 0; + + /* Reactivate in-stasis ceilings... for certain types. */ + + switch (type) + { + case CEIL_FASTCRUSHANDRAISE: + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + p_activate_in_stasis_ceiling(line); + default: + break; + } + + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + /* new door thinker */ + + rtn = 1; + ceiling = z_malloc(sizeof(*ceiling), PU_LEVSPEC, 0); + p_add_thinker(&ceiling->thinker); + sec->specialdata = ceiling; + ceiling->thinker.function.acp1 = (actionf_p1)t_move_ceiling; + ceiling->sector = sec; + ceiling->crush = false; + + switch (type) + { + case CEIL_FASTCRUSHANDRAISE: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8 * FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case CEIL_SILENTCRUSHANDRAISE: + case CEIL_CRUSHANDRAISE: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case CEIL_LOWERANDCRUSH: + case CEIL_LOWERTOFLOOR: + ceiling->bottomheight = sec->floorheight; + if (type != CEIL_LOWERTOFLOOR) + { + ceiling->bottomheight += 8 * FRACUNIT; + } + + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case CEIL_RAISETOHIGHEST: + ceiling->topheight = p_find_heighest_ceiling_surrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + } + + ceiling->tag = sec->tag; + ceiling->type = type; + p_add_active_ceiling(ceiling); + } + + return rtn; +} + +/* Add an active ceiling */ + +void p_add_active_ceiling(ceiling_t *c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == NULL) + { + activeceilings[i] = c; + return; + } + } +} + +/* Remove a ceiling's thinker */ + +void p_remove_active_ceiling(ceiling_t *c) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == c) + { + activeceilings[i]->sector->specialdata = NULL; + p_remove_thinker(&activeceilings[i]->thinker); + activeceilings[i] = NULL; + break; + } + } +} + +/* Restart a ceiling that's in-stasis */ + +void p_activate_in_stasis_ceiling(line_t *line) +{ + int i; + + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && + (activeceilings[i]->direction == 0)) + { + activeceilings[i]->direction = activeceilings[i]->olddirection; + activeceilings[i]->thinker.function.acp1 = + (actionf_p1)t_move_ceiling; + } + } +} + +/* ev_ceiling_crush_stop + * Stop a ceiling from crushing! + */ + +int ev_ceiling_crush_stop(line_t *line) +{ + int i; + int rtn; + + rtn = 0; + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] && (activeceilings[i]->tag == line->tag) && + (activeceilings[i]->direction != 0)) + { + activeceilings[i]->olddirection = activeceilings[i]->direction; + activeceilings[i]->thinker.function.acv = (actionf_v)NULL; + activeceilings[i]->direction = 0; /* in-stasis */ + rtn = 1; + } + } + + return rtn; +} diff --git a/games/NXDoom/src/doom/p_doors.c b/games/NXDoom/src/doom/p_doors.c new file mode 100644 index 000000000..324fefa4b --- /dev/null +++ b/games/NXDoom/src/doom/p_doors.c @@ -0,0 +1,802 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_doors.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: Door animation code (opening/closing) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_main.h" +#include "doomdef.h" +#include "i_system.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/* Data. */ + +#include "dstrings.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Sliding door frame information */ + +#if 0 +slidename_t g_slide_frame_names[MAXSLIDEDOORS] = +{ +{ + "GDOORF1", "GDOORF2", "GDOORF3", "GDOORF4", /* front */ + "GDOORB1", "GDOORB2", "GDOORB3", "GDOORB4" /* back */ +}, +{ + "\0", "\0", "\0", "\0" +} +}; + +slideframe_t g_slide_frames[MAXSLIDEDOORS]; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* VERTICAL DOORS */ + +void t_vertical_door(vldoor_t *door) +{ + result_e res; + + switch (door->direction) + { + case 0: /* WAITING */ + if (!--door->topcountdown) + { + switch (door->type) + { + case VLD_BLAZERAISE: + door->direction = -1; /* time to go back down */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_NORMAL: + door->direction = -1; /* time to go back down */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_CLOSE30THENOPEN: + door->direction = 1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + break; + + case 2: /* INITIAL WAIT */ + if (!--door->topcountdown) + { + switch (door->type) + { + case VLD_RAISEIN5MINS: + door->direction = 1; + door->type = VLD_NORMAL; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + + break; + + case -1: /* DOWN */ + res = t_move_plane(door->sector, door->speed, + door->sector->floorheight, false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case VLD_BLAZERAISE: + case VLD_BLAZECLOSE: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_NORMAL: + case VLD_CLOSE: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ + break; + + case VLD_CLOSE30THENOPEN: + door->direction = 0; + door->topcountdown = TICRATE * 30; + break; + + default: + break; + } + } + else if (res == crushed) + { + switch (door->type) + { + case VLD_BLAZECLOSE: + case VLD_CLOSE: /* DO NOT GO BACK UP! */ + break; + + default: + door->direction = 1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + } + } + break; + + case 1: /* UP */ + res = t_move_plane(door->sector, door->speed, door->topheight, + false, 1, door->direction); + + if (res == pastdest) + { + switch (door->type) + { + case VLD_BLAZERAISE: + case VLD_NORMAL: + door->direction = 0; /* wait at top */ + door->topcountdown = door->topwait; + break; + + case VLD_CLOSE30THENOPEN: + case VLD_BLAZEOPEN: + case VLD_OPEN: + door->sector->specialdata = NULL; + p_remove_thinker(&door->thinker); /* unlink and free */ + break; + + default: + break; + } + } + + break; + } +} + +/* ev_do_locked_door + * Move a locked door up/down + */ + +int ev_do_locked_door(line_t *line, vldoor_e type, mobj_t *thing) +{ + player_t *p; + + p = thing->player; + + if (!p) return 0; + + switch (line->special) + { + case 99: /* Blue Lock */ + case 133: + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = (PD_BLUEO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + break; + + case 134: /* Red Lock */ + case 135: + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = (PD_REDO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + break; + + case 136: /* Yellow Lock */ + case 137: + if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) + { + p->message = (PD_YELLOWO); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return 0; + } + + break; + } + + return ev_do_door(line, type); +} + +int ev_do_door(line_t *line, vldoor_e type) +{ + int secnum; + int rtn; + sector_t *sec; + vldoor_t *door; + + secnum = -1; + rtn = 0; + + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + /* new door thinker */ + + rtn = 1; + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + + switch (type) + { + case VLD_BLAZECLOSE: + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_BDCLS); +#endif + break; + + case VLD_CLOSE: + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->direction = -1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_CLOSE30THENOPEN: + door->topheight = sec->ceilingheight; + door->direction = -1; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&door->sector->soundorg, SFX_DORCLS); +#endif + break; + + case VLD_BLAZERAISE: + case VLD_BLAZEOPEN: + door->direction = 1; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->speed = VDOORSPEED * 4; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (door->topheight != sec->ceilingheight) + s_start_sound(&door->sector->soundorg, SFX_BDOPN); +#endif + break; + + case VLD_NORMAL: + case VLD_OPEN: + door->direction = 1; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (door->topheight != sec->ceilingheight) + s_start_sound(&door->sector->soundorg, SFX_DOROPN); +#endif + break; + + default: + break; + } + } + + return rtn; +} + +/* ev_vertical_door : open a door manually, no tag value */ + +void ev_vertical_door(line_t *line, mobj_t *thing) +{ + player_t *player; + sector_t *sec; + vldoor_t *door; + int side; + + side = 0; /* only front sides can be used */ + + /* Check for locks */ + + player = thing->player; + + switch (line->special) + { + case 26: /* Blue Lock */ + case 32: + if (!player) return; + + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = (PD_BLUEK); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + + case 27: /* Yellow Lock */ + case 34: + if (!player) return; + + if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) + { + player->message = (PD_YELLOWK); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + + case 28: /* Red Lock */ + case 33: + if (!player) return; + + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = (PD_REDK); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_OOF); +#endif + return; + } + break; + } + + /* if the sector has an active thinker, use it */ + + if (line->sidenum[side ^ 1] == -1) + { + i_error("ev_vertical_door: DR special type on 1-sided linedef"); + } + + sec = sides[line->sidenum[side ^ 1]].sector; + + if (sec->specialdata) + { + door = sec->specialdata; + switch (line->special) + { + case 1: /* ONLY FOR "RAISE" DOORS, NOT "OPEN"s */ + case 26: + case 27: + case 28: + case 117: + if (door->direction == -1) + door->direction = 1; /* go back up */ + else + { + if (!thing->player) return; /* JDC: bad guys never close doors */ + + /* When is a door not a door? + * In Vanilla, door->direction is set, even though + * "specialdata" might not actually point at a door. + */ + + if (door->thinker.function.acp1 == + (actionf_p1)t_vertical_door) + { + door->direction = -1; /* start going down immediately */ + } + else if (door->thinker.function.acp1 == + (actionf_p1)t_plat_raise) + { + /* Erm, this is a plat, not a door. + * This notably causes a problem in ep1-0500.lmp where + * a plat and a door are cross-referenced; the door + * doesn't open on 64-bit. + * The direction field in vldoor_t corresponds to the wait + * field in plat_t. Let's set that to -1 instead. + */ + + plat_t *plat; + + plat = (plat_t *)door; + plat->wait = -1; + } + else + { + /* This isn't a door OR a plat. Now we're in trouble. */ + + fprintf(stderr, "ev_vertical_door: Tried to close " + "something that wasn't a door.\n"); + + /* Try closing it anyway. At least it will work on 32-bit + * machines. + */ + + door->direction = -1; + } + } + + return; + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* for proper sound */ + + switch (line->special) + { + case 117: /* BLAZING DOOR RAISE */ + case 118: /* BLAZING DOOR OPEN */ + s_start_sound(&sec->soundorg, SFX_BDOPN); + break; + + case 1: /* NORMAL DOOR SOUND */ + case 31: + s_start_sound(&sec->soundorg, SFX_DOROPN); + break; + + default: /* LOCKED DOOR SOUND */ + s_start_sound(&sec->soundorg, SFX_DOROPN); + break; + } +#endif + + /* new door thinker */ + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + + switch (line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = VLD_NORMAL; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = VLD_OPEN; + line->special = 0; + break; + + case 117: /* blazing door raise */ + door->type = VLD_BLAZERAISE; + door->speed = VDOORSPEED * 4; + break; + case 118: /* blazing door open */ + door->type = VLD_BLAZEOPEN; + line->special = 0; + door->speed = VDOORSPEED * 4; + break; + } + + /* find the top and bottom of the movement range */ + + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; +} + +/* Spawn a door that closes after 30 seconds */ + +void p_spawn_door_close_in30(sector_t *sec) +{ + vldoor_t *door; + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + + p_add_thinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->direction = 0; + door->type = VLD_NORMAL; + door->speed = VDOORSPEED; + door->topcountdown = 30 * TICRATE; +} + +/* Spawn a door that opens after 5 minutes */ + +void p_spawn_door_raise_in_5min(sector_t *sec, int secnum) +{ + vldoor_t *door; + + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + + p_add_thinker(&door->thinker); + + sec->specialdata = door; + sec->special = 0; + + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + door->sector = sec; + door->direction = 2; + door->type = VLD_RAISEIN5MINS; + door->speed = VDOORSPEED; + door->topheight = p_find_lowest_ceiling_surrounding(sec); + door->topheight -= 4 * FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * TICRATE; +} + +/* UNUSED + * Separate into p_slidoor.c? + */ + +#if 0 /* ABANDONED TO THE MISTS OF TIME!!! */ + +/* ev_sliding_door : slide a door horizontally + * (animate midtexture, then set noblocking line) + */ + +void p_init_sliding_door_frames(void) +{ + int i; + int f1; + int f2; + int f3; + int f4; + + /* DOOM II ONLY... */ + + if (gamemode != commercial) return; + + for (i = 0; i < MAXSLIDEDOORS; i++) + { + if (!g_slide_frame_names[i].front_frame1[0]) break; + + f1 = r_texture_num_for_name(g_slide_frame_names[i].front_frame1); + f2 = r_texture_num_for_name(g_slide_frame_names[i].front_frame2); + f3 = r_texture_num_for_name(g_slide_frame_names[i].front_frame3); + f4 = r_texture_num_for_name(g_slide_frame_names[i].front_frame4); + + g_slide_frames[i].front_frames[0] = f1; + g_slide_frames[i].front_frames[1] = f2; + g_slide_frames[i].front_frames[2] = f3; + g_slide_frames[i].front_frames[3] = f4; + + f1 = r_texture_num_for_name(g_slide_frame_names[i].back_frame1); + f2 = r_texture_num_for_name(g_slide_frame_names[i].back_frame2); + f3 = r_texture_num_for_name(g_slide_frame_names[i].back_frame3); + f4 = r_texture_num_for_name(g_slide_frame_names[i].back_frame4); + + g_slide_frames[i].back_frames[0] = f1; + g_slide_frames[i].back_frames[1] = f2; + g_slide_frames[i].back_frames[2] = f3; + g_slide_frames[i].back_frames[3] = f4; + } +} + +/* Return index into "g_slide_frames" array for which door type to use */ + +static int p_find_sliding_door(line_t *line) +{ + int i; + int val; + + for (i = 0; i < MAXSLIDEDOORS; i++) + { + val = sides[line->sidenum[0]].midtexture; + if (val == g_slide_frames[i].front_frames[0]) return i; + } + + return -1; +} + +static void t_sliding_door(slidedoor_t *door) +{ + switch (door->status) + { + case sd_opening: + if (!door->timer--) + { + if (++door->frame == SNUMFRAMES) + { + /* IF DOOR IS DONE OPENING... */ + + sides[door->line->sidenum[0]].midtexture = 0; + sides[door->line->sidenum[1]].midtexture = 0; + door->line->flags &= ML_BLOCKING ^ 0xff; + + if (door->type == SDT_OPENONLY) + { + door->frontsector->specialdata = NULL; + p_remove_thinker(&door->thinker); + break; + } + + door->timer = SDOORWAIT; + door->status = sd_waiting; + } + else + { + /* IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... */ + + door->timer = SWAITTICS; + + sides[door->line->sidenum[0]].midtexture = + g_slide_frames[door->which_door_index] + .front_frames[door->frame]; + sides[door->line->sidenum[1]].midtexture = + g_slide_frames[door->which_door_index] + .back_frames[door->frame]; + } + } + break; + + case sd_waiting: + + /* IF DOOR IS DONE WAITING... */ + + if (!door->timer--) + { + /* CAN DOOR CLOSE? */ + + if (door->frontsector->thinglist != NULL || + door->backsector->thinglist != NULL) + { + door->timer = SDOORWAIT; + break; + } + + /* door->frame = SNUMFRAMES-1; */ + + door->status = sd_closing; + door->timer = SWAITTICS; + } + break; + + case sd_closing: + if (!door->timer--) + { + if (--door->frame < 0) + { + /* IF DOOR IS DONE CLOSING... */ + + door->line->flags |= ML_BLOCKING; + door->frontsector->specialdata = NULL; + p_remove_thinker(&door->thinker); + break; + } + else + { + /* IF DOOR NEEDS TO ANIMATE TO NEXT FRAME... */ + + door->timer = SWAITTICS; + + sides[door->line->sidenum[0]].midtexture = + g_slide_frames[door->which_door_index] + .front_frames[door->frame]; + sides[door->line->sidenum[1]].midtexture = + g_slide_frames[door->which_door_index] + .back_frames[door->frame]; + } + } + break; + } +} + +void ev_sliding_door(line_t *line, mobj_t *thing) +{ + sector_t *sec; + slidedoor_t *door; + + /* DOOM II ONLY... */ + + if (gamemode != commercial) return; + + /* Make sure door isn't already being animated */ + + sec = line->frontsector; + door = NULL; + if (sec->specialdata) + { + if (!thing->player) return; + + door = sec->specialdata; + if (door->type == SDT_OPENANDCLOSE) + { + if (door->status == sd_waiting) door->status = sd_closing; + } + else + return; + } + + /* Init sliding door vars */ + + if (!door) + { + door = z_malloc(sizeof(*door), PU_LEVSPEC, 0); + p_add_thinker(&door->thinker); + sec->specialdata = door; + + door->type = SDT_OPENANDCLOSE; + door->status = sd_opening; + door->which_door_index = p_find_sliding_door(line); + + if (door->which_door_index < 0) + i_error("ev_sliding_door: Can't use texture for sliding door!"); + + door->frontsector = sec; + door->backsector = line->backsector; + door->thinker.function = t_sliding_door; + door->timer = SWAITTICS; + door->frame = 0; + door->line = line; + } +} +#endif /* UNUSED */ diff --git a/games/NXDoom/src/doom/p_enemy.c b/games/NXDoom/src/doom/p_enemy.c new file mode 100644 index 000000000..cf10325af --- /dev/null +++ b/games/NXDoom/src/doom/p_enemy.c @@ -0,0 +1,1932 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_enemy.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "m_random.h" + +#include "doomdef.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "g_game.h" + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TRACEANGLE 0xc000000 + +#define FATSPREAD (ANG90 / 8) + +#define SKULLSPEED (20 * FRACUNIT) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Data. */ + +typedef enum +{ + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void a_fall(mobj_t *actor); +void a_refire(player_t *player, pspdef_t *psp); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* p_new_chase_dir related LUT. */ + +dirtype_t opposite[] = +{ + DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, + DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR, +}; + +dirtype_t diags[] = +{ + DI_NORTHWEST, + DI_NORTHEAST, + DI_SOUTHWEST, + DI_SOUTHEAST, +}; + +/* ENEMY THINKING + * Enemies are always spawned with targetplayer = -1, threshold = 0 + * Most monsters are spawned unaware of all players, but some can be made + * preaware + */ + +/* Called by p_noise_alert. + * Recursively traverse adjacent sectors, sound blocking lines cut off + * traversal. + */ + +mobj_t *soundtarget; + +fixed_t xspeed[8] = +{ + FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000, 0, 47000 +}; + +fixed_t yspeed[8] = +{ + 0, 47000, FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000 +}; + +mobj_t *corpsehit; +mobj_t *vileobj; +fixed_t viletryx; +fixed_t viletryy; + +mobj_t *braintargets[32]; +int numbraintargets; +int braintargeton = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_recursive_sound(sector_t *sec, int soundblocks) +{ + int i; + line_t *check; + sector_t *other; + + /* wake up all monsters in this sector */ + + if (sec->validcount == validcount && + sec->soundtraversed <= soundblocks + 1) + { + return; /* already flooded */ + } + + sec->validcount = validcount; + sec->soundtraversed = soundblocks + 1; + sec->soundtarget = soundtarget; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + if (!(check->flags & ML_TWOSIDED)) continue; + + p_line_opening(check); + + if (openrange <= 0) continue; /* closed door */ + + if (sides[check->sidenum[0]].sector == sec) + other = sides[check->sidenum[1]].sector; + else + other = sides[check->sidenum[0]].sector; + + if (check->flags & ML_SOUNDBLOCK) + { + if (!soundblocks) p_recursive_sound(other, 1); + } + else + p_recursive_sound(other, soundblocks); + } +} + +static boolean p_check_missile_range(mobj_t *actor) +{ + fixed_t dist; + + if (!p_check_sight(actor, actor->target)) return false; + + if (actor->flags & MF_JUSTHIT) + { + /* the target just hit the enemy, so fight back! */ + + actor->flags &= ~MF_JUSTHIT; + return true; + } + + if (actor->reactiontime) return false; /* do not attack yet */ + + /* OPTIMIZE: get this from a global checksight */ + + dist = p_approx_distance(actor->x - actor->target->x, + actor->y - actor->target->y) - + 64 * FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128 * FRACUNIT; /* no melee attack, so fire more */ + + dist >>= FRACBITS; + + if (actor->type == MT_VILE) + { + if (dist > 14 * 64) return false; /* too far away */ + } + + if (actor->type == MT_UNDEAD) + { + if (dist < 196) return false; /* close for fist attack */ + dist >>= 1; + } + + if (actor->type == MT_CYBORG || actor->type == MT_SPIDER || + actor->type == MT_SKULL) + { + dist >>= 1; + } + + if (dist > 200) dist = 200; + + if (actor->type == MT_CYBORG && dist > 160) dist = 160; + + if (p_random() < dist) return false; + + return true; +} + +/* p_move + * Move in the current direction, returns false if the move is blocked. + */ + +static boolean p_move(mobj_t *actor) +{ + fixed_t tryx; + fixed_t tryy; + + line_t *ld; + + /* warning: 'catch', 'throw', and 'try' are all C++ reserved words */ + + boolean try_ok; + boolean good; + + if (actor->movedir == DI_NODIR) return false; + + if ((unsigned)actor->movedir >= 8) i_error("Weird actor->movedir!"); + + tryx = actor->x + actor->info->speed * xspeed[actor->movedir]; + tryy = actor->y + actor->info->speed * yspeed[actor->movedir]; + + try_ok = p_try_move(actor, tryx, tryy); + + if (!try_ok) + { + /* open any specials */ + + if (actor->flags & MF_FLOAT && floatok) + { + /* must adjust height */ + + if (actor->z < tmfloorz) + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + return true; + } + + if (!numspechit) return false; + + actor->movedir = DI_NODIR; + good = false; + while (numspechit--) + { + ld = spechit[numspechit]; + + /* if the special is not a door that can be opened, return false */ + + if (p_use_special_line(actor, ld, 0)) good = true; + } + + return good; + } + else + { + actor->flags &= ~MF_INFLOAT; + } + + if (!(actor->flags & MF_FLOAT)) actor->z = actor->floorz; + return true; +} + +/* p_try_walk + * + * Attempts to move actor on in its current (ob->moveangle) direction. + * If blocked by either a wall or an actor returns FALSE + * If move is either clear or blocked only by a door, returns TRUE and + * sets... + * If a door is in the way, an OpenDoor call is made to start it opening. + */ + +static boolean p_try_walk(mobj_t *actor) +{ + if (!p_move(actor)) + { + return false; + } + + actor->movecount = p_random() & 15; + return true; +} + +static void p_new_chase_dir(mobj_t *actor) +{ + fixed_t deltax; + fixed_t deltay; + + dirtype_t d[3]; + + int tdir; + dirtype_t olddir; + + dirtype_t turnaround; + + if (!actor->target) i_error("p_new_chase_dir: called with no target"); + + olddir = actor->movedir; + turnaround = opposite[olddir]; + + deltax = actor->target->x - actor->x; + deltay = actor->target->y - actor->y; + + if (deltax > 10 * FRACUNIT) + d[1] = DI_EAST; + else if (deltax < -10 * FRACUNIT) + d[1] = DI_WEST; + else + d[1] = DI_NODIR; + + if (deltay < -10 * FRACUNIT) + d[2] = DI_SOUTH; + else if (deltay > 10 * FRACUNIT) + d[2] = DI_NORTH; + else + d[2] = DI_NODIR; + + /* try direct route */ + + if (d[1] != DI_NODIR && d[2] != DI_NODIR) + { + actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)]; + if (actor->movedir != (int)turnaround && p_try_walk(actor)) return; + } + + /* try other directions */ + + if (p_random() > 200 || abs(deltay) > abs(deltax)) + { + tdir = d[1]; + d[1] = d[2]; + d[2] = tdir; + } + + if (d[1] == turnaround) d[1] = DI_NODIR; + if (d[2] == turnaround) d[2] = DI_NODIR; + + if (d[1] != DI_NODIR) + { + actor->movedir = d[1]; + if (p_try_walk(actor)) + { + return; /* either moved forward or attacked */ + } + } + + if (d[2] != DI_NODIR) + { + actor->movedir = d[2]; + + if (p_try_walk(actor)) return; + } + + /* there is no direct path to the player, so pick another direction. */ + + if (olddir != DI_NODIR) + { + actor->movedir = olddir; + + if (p_try_walk(actor)) return; + } + + /* randomly determine direction of search */ + + if (p_random() & 1) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + { + if (tdir != (int)turnaround) + { + actor->movedir = tdir; + + if (p_try_walk(actor)) return; + } + } + } + else + { + for (tdir = DI_SOUTHEAST; tdir != (DI_EAST - 1); tdir--) + { + if (tdir != (int)turnaround) + { + actor->movedir = tdir; + + if (p_try_walk(actor)) return; + } + } + } + + if (turnaround != DI_NODIR) + { + actor->movedir = turnaround; + if (p_try_walk(actor)) return; + } + + actor->movedir = DI_NODIR; /* can not move */ +} + +/* p_look_for_players + * If allaround is false, only look 180 degrees in front. Returns true if a + * player is targeted. + */ + +static boolean p_look_for_players(mobj_t *actor, boolean allaround) +{ + int c; + int stop; + player_t *player; + angle_t an; + fixed_t dist; + + c = 0; + stop = (actor->lastlook - 1) & 3; + + for (; ; actor->lastlook = (actor->lastlook + 1) & 3) + { + if (!playeringame[actor->lastlook]) continue; + + if (c++ == 2 || actor->lastlook == stop) + { + return false; /* done looking */ + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) continue; /* dead */ + + if (!p_check_sight(actor, player->mo)) continue; /* out of sight */ + + if (!allaround) + { + an = r_point_to_angle2(actor->x, actor->y, player->mo->x, + player->mo->y) - + actor->angle; + + if (an > ANG90 && an < ANG270) + { + dist = p_approx_distance(player->mo->x - actor->x, + player->mo->y - actor->y); + + /* if real close, react anyway */ + + if (dist > MELEERANGE) continue; /* behind back */ + } + } + + actor->target = player->mo; + return true; + } + + return false; +} + +/* pit_vile_check + * Detect a corpse that could be raised. + */ + +static boolean pit_vile_check(mobj_t *thing) +{ + int maxdist; + boolean check; + + if (!(thing->flags & MF_CORPSE)) return true; /* not a monster */ + + if (thing->tics != -1) return true; /* not lying still yet */ + + if (thing->info->raisestate == S_NULL) + { + return true; /* monster doesn't have a raise state */ + } + + maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; + + if (abs(thing->x - viletryx) > maxdist || + abs(thing->y - viletryy) > maxdist) + { + return true; /* not actually touching */ + } + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + corpsehit->height <<= 2; + check = p_check_position(corpsehit, corpsehit->x, corpsehit->y); + corpsehit->height >>= 2; + + if (!check) return true; /* doesn't fit here */ + + return false; /* got one, so stop checking */ +} + +/* Check whether the death of the specified monster type is allowed + * to trigger the end of episode special action. + * + * This behavior changed in v1.9, the most notable effect of which + * was to break uac_dead.wad + */ + +static boolean check_boss_end(mobjtype_t motype) +{ + if (gameversion < exe_ultimate) + { + if (gamemap != 8) + { + return false; + } + + /* Baron death on later episodes is nothing special. */ + + if (motype == MT_BRUISER && gameepisode != 1) + { + return false; + } + + return true; + } + else + { + /* New logic that appeared in Ultimate Doom. + * Looks like the logic was overhauled while adding in the + * episode 4 support. Now bosses only trigger on their + * specific episode. + */ + + switch (gameepisode) + { + case 1: + return gamemap == 8 && motype == MT_BRUISER; + + case 2: + return gamemap == 8 && motype == MT_CYBORG; + + case 3: + return gamemap == 8 && motype == MT_SPIDER; + + case 4: + return (gamemap == 6 && motype == MT_CYBORG) || + (gamemap == 8 && motype == MT_SPIDER); + + default: + return gamemap == 8; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_noise_alert + * If a monster yells at a player, + * it will alert other monsters to the player. + */ + +void p_noise_alert(mobj_t *target, mobj_t *emmiter) +{ + soundtarget = target; + validcount++; + p_recursive_sound(emmiter->subsector->sector, 0); +} + +boolean p_check_melee_range(mobj_t *actor) +{ + mobj_t *pl; + fixed_t dist; + fixed_t range; + + if (!actor->target) return false; + + pl = actor->target; + dist = p_approx_distance(pl->x - actor->x, pl->y - actor->y); + + if (gameversion < exe_doom_1_5) + range = MELEERANGE; + else + range = MELEERANGE - 20 * FRACUNIT + pl->info->radius; + + if (dist >= range) return false; + + if (!p_check_sight(actor, actor->target)) return false; + + return true; +} + +/* a_keen_die + * DOOM II special, map 32. Uses special tag 666. + */ + +void a_keen_die(mobj_t *mo) +{ + thinker_t *th; + mobj_t *mo2; + line_t junk; + + a_fall(mo); + + /* scan the remaining thinkers to see if all Keens are dead */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)p_mobj_thinker) continue; + + mo2 = (mobj_t *)th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + { + return; /* other Keen not dead */ + } + } + + junk.tag = 666; + ev_do_door(&junk, VLD_OPEN); +} + +/* ACTION ROUTINES */ + +/* a_look + * Stay in state until a player is sighted. + */ + +void a_look(mobj_t *actor) +{ + mobj_t *targ; + + actor->threshold = 0; /* any shot will wake up */ + targ = actor->subsector->sector->soundtarget; + + if (targ && (targ->flags & MF_SHOOTABLE)) + { + actor->target = targ; + + if (actor->flags & MF_AMBUSH) + { + if (p_check_sight(actor, actor->target)) goto seeyou; + } + else + { + goto seeyou; + } + } + + if (!p_look_for_players(actor, false)) return; + + /* go into chase state */ + +seeyou: +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (actor->info->seesound) + { + int sound; + + switch (actor->info->seesound) + { + case SFX_POSIT1: + case SFX_POSIT2: + case SFX_POSIT3: + sound = SFX_POSIT1 + p_random() % 3; + break; + + case SFX_BGSIT1: + case SFX_BGSIT2: + sound = SFX_BGSIT1 + p_random() % 2; + break; + + default: + sound = actor->info->seesound; + break; + } + + if (actor->type == MT_SPIDER || actor->type == MT_CYBORG) + { + /* full volume */ + + s_start_sound(NULL, sound); + } + else + s_start_sound(actor, sound); + } +#endif + + p_set_mobj_state(actor, actor->info->seestate); +} + +/* a_chase + * Actor has a melee attack, so it tries to close as fast as possible + */ + +void a_chase(mobj_t *actor) +{ + int delta; + + if (actor->reactiontime) actor->reactiontime--; + + /* modify target threshold */ + + if (actor->threshold) + { + if (gameversion > exe_doom_1_2 && + (!actor->target || actor->target->health <= 0)) + { + actor->threshold = 0; + } + else + actor->threshold--; + } + + /* turn towards movement direction if not there yet */ + + if (actor->movedir < 8) + { + actor->angle &= (7u << 29); + delta = actor->angle - (actor->movedir << 29); + + if (delta > 0) + actor->angle -= ANG90 / 2; + else if (delta < 0) + actor->angle += ANG90 / 2; + } + + if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) + { + /* look for a new target */ + + if (p_look_for_players(actor, true)) return; /* got a new target */ + + p_set_mobj_state(actor, actor->info->spawnstate); + return; + } + + /* do not attack twice in a row */ + + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) p_new_chase_dir(actor); + return; + } + + /* check for melee attack */ + + if (actor->info->meleestate && p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (actor->info->attacksound) + s_start_sound(actor, actor->info->attacksound); +#endif + + p_set_mobj_state(actor, actor->info->meleestate); + return; + } + + /* check for missile attack */ + + if (actor->info->missilestate) + { + if (gameskill < sk_nightmare && !fastparm && actor->movecount) + { + goto nomissile; + } + + if (!p_check_missile_range(actor)) goto nomissile; + + p_set_mobj_state(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + /* ? */ + +nomissile: + + /* possibly choose another target */ + + if (netgame && !actor->threshold && !p_check_sight(actor, actor->target)) + { + if (p_look_for_players(actor, true)) return; /* got a new target */ + } + + /* chase towards player */ + + if (--actor->movecount < 0 || !p_move(actor)) + { + p_new_chase_dir(actor); + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* make active sound */ + + if (actor->info->activesound && p_random() < 3) + { + s_start_sound(actor, actor->info->activesound); + } +#endif +} + +void a_face_target(mobj_t *actor) +{ + if (!actor->target) return; + + actor->flags &= ~MF_AMBUSH; + + actor->angle = r_point_to_angle2(actor->x, actor->y, actor->target->x, + actor->target->y); + + if (actor->target->flags & MF_SHADOW) actor->angle += p_sub_random() << 21; +} + +void a_pos_attack(mobj_t *actor) +{ + int angle; + int damage; + int slope; + + if (!actor->target) return; + + a_face_target(actor); + angle = actor->angle; + slope = p_aim_line_attack(actor, angle, MISSILERANGE); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_PISTOL); +#endif + angle += p_sub_random() << 20; + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); +} + +void a_s_pos_attack(mobj_t *actor) +{ + int i; + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SHOTGN); +#endif + a_face_target(actor); + bangle = actor->angle; + slope = p_aim_line_attack(actor, bangle, MISSILERANGE); + + for (i = 0; i < 3; i++) + { + angle = bangle + (p_sub_random() << 20); + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); + } +} + +void a_c_pos_attack(mobj_t *actor) +{ + int angle; + int bangle; + int damage; + int slope; + + if (!actor->target) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SHOTGN); +#endif + a_face_target(actor); + bangle = actor->angle; + slope = p_aim_line_attack(actor, bangle, MISSILERANGE); + + angle = bangle + (p_sub_random() << 20); + damage = ((p_random() % 5) + 1) * 3; + p_line_attack(actor, angle, MISSILERANGE, slope, damage); +} + +void a_c_pos_refire(mobj_t *actor) +{ + /* keep firing unless target got out of sight */ + + a_face_target(actor); + + if (p_random() < 40) return; + + if (!actor->target || actor->target->health <= 0 || + !p_check_sight(actor, actor->target)) + { + p_set_mobj_state(actor, actor->info->seestate); + } +} + +void a_spid_refire(mobj_t *actor) +{ + /* keep firing unless target got out of sight */ + + a_face_target(actor); + + if (p_random() < 10) return; + + if (!actor->target || actor->target->health <= 0 || + !p_check_sight(actor, actor->target)) + { + p_set_mobj_state(actor, actor->info->seestate); + } +} + +void a_bspi_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_ARACHPLAZ); +} + +void a_troop_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + if (p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_CLAW); +#endif + damage = (p_random() % 8 + 1) * 3; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_TROOPSHOT); +} + +void a_sarg_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + + if (gameversion >= exe_doom_1_5) + { + if (!p_check_melee_range(actor)) return; + } + + damage = ((p_random() % 10) + 1) * 4; + + if (gameversion <= exe_doom_1_2) + p_line_attack(actor, actor->angle, MELEERANGE, 0, damage); + else + p_damage_mobj(actor->target, actor, actor, damage); +} + +void a_head_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + if (p_check_melee_range(actor)) + { + damage = (p_random() % 6 + 1) * 10; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_HEADSHOT); +} + +void a_cyber_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + p_spawn_missile(actor, actor->target, MT_ROCKET); +} + +void a_bruis_attack(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + if (p_check_melee_range(actor)) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_CLAW); +#endif + damage = (p_random() % 8 + 1) * 10; + p_damage_mobj(actor->target, actor, actor, damage); + return; + } + + /* launch a missile */ + + p_spawn_missile(actor, actor->target, MT_BRUISERSHOT); +} + +void a_skel_missile(mobj_t *actor) +{ + mobj_t *mo; + + if (!actor->target) return; + + a_face_target(actor); + actor->z += 16 * FRACUNIT; /* so missile spawns higher */ + mo = p_spawn_missile(actor, actor->target, MT_TRACER); + actor->z -= 16 * FRACUNIT; /* back to normal */ + + mo->x += mo->momx; + mo->y += mo->momy; + mo->tracer = actor->target; +} + +void a_tracer(mobj_t *actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t *dest; + mobj_t *th; + + if (gametic & 3) return; + + /* spawn a puff of smoke behind the rocket */ + + p_spawn_puff(actor->x, actor->y, actor->z); + + th = p_spawn_mobj(actor->x - actor->momx, actor->y - actor->momy, actor->z, + MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= p_random() & 3; + if (th->tics < 1) th->tics = 1; + + /* adjust direction */ + + dest = actor->tracer; + + if (!dest || dest->health <= 0) return; + + /* change angle */ + + exact = r_point_to_angle2(actor->x, actor->y, dest->x, dest->y); + + if (exact != actor->angle) + { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) actor->angle = exact; + } + } + + exact = actor->angle >> ANGLETOFINESHIFT; + actor->momx = fixed_mul(actor->info->speed, finecosine[exact]); + actor->momy = fixed_mul(actor->info->speed, finesine[exact]); + + /* change slope */ + + dist = p_approx_distance(dest->x - actor->x, dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) dist = 1; + slope = (dest->z + 40 * FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT / 8; + else + actor->momz += FRACUNIT / 8; +} + +void a_skel_woosh(mobj_t *actor) +{ + if (!actor->target) return; + a_face_target(actor); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SKESWG); +#endif +} + +void a_skel_fist(mobj_t *actor) +{ + int damage; + + if (!actor->target) return; + + a_face_target(actor); + + if (p_check_melee_range(actor)) + { + damage = ((p_random() % 10) + 1) * 6; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SKEPCH); +#endif + p_damage_mobj(actor->target, actor, actor, damage); + } +} + +/* a_vile_chase + * Check for resurrecting a body + */ + +void a_vile_chase(mobj_t *actor) +{ + int xl; + int xh; + int yl; + int yh; + + int bx; + int by; + + mobjinfo_t *info; + mobj_t *temp; + + if (actor->movedir != DI_NODIR) + { + /* check for corpses to raise */ + + viletryx = actor->x + actor->info->speed * xspeed[actor->movedir]; + viletryy = actor->y + actor->info->speed * yspeed[actor->movedir]; + + xl = (viletryx - bmaporgx - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + xh = (viletryx - bmaporgx + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yl = (viletryy - bmaporgy - MAXRADIUS * 2) >> MAPBLOCKSHIFT; + yh = (viletryy - bmaporgy + MAXRADIUS * 2) >> MAPBLOCKSHIFT; + + vileobj = actor; + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + /* Call pit_vile_check to check whether object is a corpse that + * can be raised. + */ + + if (!p_block_things_iterator(bx, by, pit_vile_check)) + { + /* got one! */ + + temp = actor->target; + actor->target = corpsehit; + a_face_target(actor); + actor->target = temp; + + p_set_mobj_state(actor, S_VILE_HEAL1); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(corpsehit, SFX_SLOP); +#endif + info = corpsehit->info; + + p_set_mobj_state(corpsehit, info->raisestate); + corpsehit->height <<= 2; + corpsehit->flags = info->flags; + corpsehit->health = info->spawnhealth; + corpsehit->target = NULL; + + return; + } + } + } + } + + /* Return to normal attack. */ + + a_chase(actor); +} + +/* Keep fire in front of player unless out of sight */ + +void a_fire(mobj_t *actor) +{ + mobj_t *dest; + mobj_t *target; + unsigned an; + + dest = actor->tracer; + if (!dest) return; + + target = p_subst_null_mobj(actor->target); + + /* don't move it if the vile lost sight */ + + if (!p_check_sight(target, dest)) return; + + an = dest->angle >> ANGLETOFINESHIFT; + + p_unset_thing_position(actor); + actor->x = dest->x + fixed_mul(24 * FRACUNIT, finecosine[an]); + actor->y = dest->y + fixed_mul(24 * FRACUNIT, finesine[an]); + actor->z = dest->z; + p_set_thing_position(actor); +} + +void a_vile_start(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_VILATK); +#endif +} + +void a_start_fire(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_FLAMST); +#endif + a_fire(actor); +} + +void a_fire_crackle(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_FLAME); +#endif + a_fire(actor); +} + +/* a_vile_target + * Spawn the hellfire + */ + +void a_vile_target(mobj_t *actor) +{ + mobj_t *fog; + + if (!actor->target) return; + + a_face_target(actor); + + fog = p_spawn_mobj(actor->target->x, actor->target->x, actor->target->z, + MT_FIRE); + + actor->tracer = fog; + fog->target = actor; + fog->tracer = actor->target; + a_fire(fog); +} + +void a_vile_attack(mobj_t *actor) +{ + mobj_t *fire; + int an; + + if (!actor->target) return; + + a_face_target(actor); + + if (!p_check_sight(actor, actor->target)) return; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_BAREXP); +#endif + p_damage_mobj(actor->target, actor, actor, 20); + actor->target->momz = 1000 * FRACUNIT / actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) return; + + /* move the fire between the vile and the player */ + + fire->x = actor->target->x - fixed_mul(24 * FRACUNIT, finecosine[an]); + fire->y = actor->target->y - fixed_mul(24 * FRACUNIT, finesine[an]); + p_radius_attack(fire, actor, 70); +} + +/* Mancubus attack, + * firing three missiles (bruisers) in three different directions? + * Doesn't look like it. + */ + +void a_fat_raise(mobj_t *actor) +{ + a_face_target(actor); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_MANATK); +#endif +} + +void a_fat_attack1(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + /* Change direction to ... */ + + actor->angle += FATSPREAD; + target = p_subst_null_mobj(actor->target); + p_spawn_missile(actor, target, MT_FATSHOT); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +void a_fat_attack2(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + /* Now here choose opposite deviation. */ + + actor->angle -= FATSPREAD; + target = p_subst_null_mobj(actor->target); + p_spawn_missile(actor, target, MT_FATSHOT); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD * 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +void a_fat_attack3(mobj_t *actor) +{ + mobj_t *mo; + mobj_t *target; + int an; + + a_face_target(actor); + + target = p_subst_null_mobj(actor->target); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle -= FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); + + mo = p_spawn_missile(actor, target, MT_FATSHOT); + mo->angle += FATSPREAD / 2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = fixed_mul(mo->info->speed, finecosine[an]); + mo->momy = fixed_mul(mo->info->speed, finesine[an]); +} + +/* SkullAttack + * Fly at the player like a missile. + */ + +void a_skull_attack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + int dist; + + if (!actor->target) return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, actor->info->attacksound); +#endif + a_face_target(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = fixed_mul(SKULLSPEED, finecosine[an]); + actor->momy = fixed_mul(SKULLSPEED, finesine[an]); + dist = p_approx_distance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) dist = 1; + actor->momz = (dest->z + (dest->height >> 1) - actor->z) / dist; +} + +/* a_pain_shoot_skull + * Spawn a lost soul and launch it at the target + */ + +void a_pain_shoot_skull(mobj_t *actor, angle_t angle) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t *newmobj; + angle_t an; + int prestep; + int count; + thinker_t *currentthinker; + + /* count total number of skull currently on the level */ + + count = 0; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if ((currentthinker->function.acp1 == (actionf_p1)p_mobj_thinker) && + ((mobj_t *)currentthinker)->type == MT_SKULL) + count++; + currentthinker = currentthinker->next; + } + + /* if there are already 20 skulls on the level, don't spit another one */ + + if (count > 20) return; + + /* okay, there's player for another one */ + + an = angle >> ANGLETOFINESHIFT; + + prestep = 4 * FRACUNIT + + 3 * (actor->info->radius + mobjinfo[MT_SKULL].radius) / 2; + + x = actor->x + fixed_mul(prestep, finecosine[an]); + y = actor->y + fixed_mul(prestep, finesine[an]); + z = actor->z + 8 * FRACUNIT; + + newmobj = p_spawn_mobj(x, y, z, MT_SKULL); + + /* Check for movements. */ + + if (!p_try_move(newmobj, newmobj->x, newmobj->y)) + { + /* kill it immediately */ + + p_damage_mobj(newmobj, actor, actor, 10000); + return; + } + + newmobj->target = actor->target; + a_skull_attack(newmobj); +} + +/* a_pain_attack + * Spawn a lost soul and launch it at the target + */ + +void a_pain_attack(mobj_t *actor) +{ + if (!actor->target) return; + + a_face_target(actor); + a_pain_shoot_skull(actor, actor->angle); +} + +void a_pain_die(mobj_t *actor) +{ + a_fall(actor); + a_pain_shoot_skull(actor, actor->angle + ANG90); + a_pain_shoot_skull(actor, actor->angle + ANG180); + a_pain_shoot_skull(actor, actor->angle + ANG270); +} + +void a_scream(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case SFX_PODTH1: + case SFX_PODTH2: + case SFX_PODTH3: + sound = SFX_PODTH1 + p_random() % 3; + break; + + case SFX_BGDTH1: + case SFX_BGDTH2: + sound = SFX_BGDTH1 + p_random() % 2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + /* Check for bosses. */ + + if (actor->type == MT_SPIDER || actor->type == MT_CYBORG) + { + s_start_sound(NULL, sound); /* full volume */ + } + else + s_start_sound(actor, sound); +#endif +} + +void a_xscream(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(actor, SFX_SLOP); +#endif +} + +void a_pain(mobj_t *actor) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (actor->info->painsound) s_start_sound(actor, actor->info->painsound); +#endif +} + +void a_fall(mobj_t *actor) +{ + /* actor is on ground, it can be walked over */ + + actor->flags &= ~MF_SOLID; + + /* So change this if corpse objects are meant to be obstacles. */ +} + +void a_explode(mobj_t *thingy) +{ + p_radius_attack(thingy, thingy->target, 128); +} + +/* a_boss_death + * Possibly trigger special effects if on first boss level + */ + +void a_boss_death(mobj_t *mo) +{ + thinker_t *th; + mobj_t *mo2; + line_t junk; + int i; + + if (gamemode == commercial) + { + if (gamemap != 7) return; + + if ((mo->type != MT_FATSO) && (mo->type != MT_BABY)) return; + } + else + { + if (!check_boss_end(mo->type)) + { + return; + } + } + + /* make sure there is a player alive for victory */ + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].health > 0) break; + } + + if (i == MAXPLAYERS) return; /* no one left alive, so do not end game */ + + /* scan the remaining thinkers to see if all bosses are dead */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)p_mobj_thinker) continue; + + mo2 = (mobj_t *)th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + { + return; /* other boss not dead */ + } + } + + /* victory! */ + + if (gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + ev_do_floor(&junk, FLOOR_RAISETOTEXTURE); + return; + } + } + } + else + { + switch (gameepisode) + { + case 1: + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + break; + + case 4: + switch (gamemap) + { + case 6: + junk.tag = 666; + ev_do_door(&junk, VLD_BLAZEOPEN); + return; + break; + + case 8: + junk.tag = 666; + ev_do_floor(&junk, FLOOR_LOWERFLOORTOLOWEST); + return; + break; + } + } + } + + g_exit_level(); +} + +void a_hoof(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_HOOF); +#endif + a_chase(mo); +} + +void a_metal(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_METAL); +#endif + a_chase(mo); +} + +void a_baby_metal(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_BSPWLK); +#endif + a_chase(mo); +} + +void a_open_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBOPN); +#endif +} + +void a_load_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBLOAD); +#endif +} + +void a_close_shotgun2(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DBCLS); +#endif + a_refire(player, psp); +} + +void a_brain_awake(mobj_t *mo) +{ + thinker_t *thinker; + mobj_t *m; + + /* find all the target spots */ + + numbraintargets = 0; + braintargeton = 0; + + for (thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + if (thinker->function.acp1 != (actionf_p1)p_mobj_thinker) + { + continue; /* not a mobj */ + } + + m = (mobj_t *)thinker; + + if (m->type == MT_BOSSTARGET) + { + braintargets[numbraintargets] = m; + numbraintargets++; + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSSIT); +#endif +} + +void a_brain_pain(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSPN); +#endif +} + +void a_brain_scream(mobj_t *mo) +{ + int x; + int y; + int z; + mobj_t *th; + + for (x = mo->x - 196 * FRACUNIT; x < mo->x + 320 * FRACUNIT; + x += FRACUNIT * 8) + { + y = mo->y - 320 * FRACUNIT; + z = 128 + p_random() * 2 * FRACUNIT; + th = p_spawn_mobj(x, y, z, MT_ROCKET); + th->momz = p_random() * 512; + + p_set_mobj_state(th, S_BRAINEXPLODE1); + + th->tics -= p_random() & 7; + if (th->tics < 1) th->tics = 1; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSDTH); +#endif +} + +void a_brain_explode(mobj_t *mo) +{ + int x; + int y; + int z; + mobj_t *th; + + x = mo->x + p_sub_random() * 2048; + y = mo->y; + z = 128 + p_random() * 2 * FRACUNIT; + th = p_spawn_mobj(x, y, z, MT_ROCKET); + th->momz = p_random() * 512; + + p_set_mobj_state(th, S_BRAINEXPLODE1); + + th->tics -= p_random() & 7; + if (th->tics < 1) th->tics = 1; +} + +void a_brain_die(mobj_t *mo) +{ + g_exit_level(); +} + +void a_brain_split(mobj_t *mo) +{ + mobj_t *targ; + mobj_t *newmobj; + + static int easy = 0; + + easy ^= 1; + if (gameskill <= sk_easy && (!easy)) return; + + /* shoot a cube at current target */ + + targ = braintargets[braintargeton]; + if (numbraintargets == 0) + { + i_error("a_brain_split: numbraintargets was 0 (vanilla crashes here)"); + } + + braintargeton = (braintargeton + 1) % numbraintargets; + + /* spawn brain missile */ + + newmobj = p_spawn_missile(mo, targ, MT_SPAWNSHOT); + newmobj->target = targ; + newmobj->reactiontime = + ((targ->y - mo->y) / newmobj->momy) / newmobj->state->tics; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(NULL, SFX_BOSPIT); +#endif +} + +void a_spawn_fly(mobj_t *mo) +{ + mobj_t *newmobj; + mobj_t *fog; + mobj_t *targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) return; /* still flying */ + + targ = p_subst_null_mobj(mo->target); + + /* First spawn teleport fog. */ + + fog = p_spawn_mobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(fog, SFX_TELEPT); +#else + UNUSED(fog); +#endif + + /* Randomly select monster to spawn. */ + + r = p_random(); + + /* Probability distribution (kind of :), decreasing likelihood. */ + + if (r < 50) + type = MT_TROOP; + else if (r < 90) + type = MT_SERGEANT; + else if (r < 120) + type = MT_SHADOWS; + else if (r < 130) + type = MT_PAIN; + else if (r < 160) + type = MT_HEAD; + else if (r < 162) + type = MT_VILE; + else if (r < 172) + type = MT_UNDEAD; + else if (r < 192) + type = MT_BABY; + else if (r < 222) + type = MT_FATSO; + else if (r < 246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = p_spawn_mobj(targ->x, targ->y, targ->z, type); + if (p_look_for_players(newmobj, true)) + p_set_mobj_state(newmobj, newmobj->info->seestate); + + /* telefrag anything in this spot */ + + p_teleport_move(newmobj, newmobj->x, newmobj->y); + + /* remove self (i.e., cube). */ + + p_remove_mobj(mo); +} + +/* travelling cube sound */ + +void a_spawn_sound(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_BOSCUB); +#endif + a_spawn_fly(mo); +} + +void a_player_screm(mobj_t *mo) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* Default death sound. */ + + int sound = SFX_PLDETH; + + if ((gamemode == commercial) && (mo->health < -50)) + { + /* IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING */ + + sound = SFX_PDIEHI; + } + + s_start_sound(mo, sound); +#else + UNUSED(mo); +#endif +} diff --git a/games/NXDoom/src/doom/p_floor.c b/games/NXDoom/src/doom/p_floor.c new file mode 100644 index 000000000..656240785 --- /dev/null +++ b/games/NXDoom/src/doom/p_floor.c @@ -0,0 +1,569 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_floor.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Floor animation: raising stairs. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "p_local.h" +#include "z_zone.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* e6y */ + +#define STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE 10 + +/* FLOORS */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Move a plane (floor or ceiling) and check for crushing */ + +result_e t_move_plane(sector_t *sector, fixed_t speed, fixed_t dest, + boolean crush, int floor_or_ceiling, int direction) +{ + boolean flag; + fixed_t lastpos; + + switch (floor_or_ceiling) + { + case 0: /* FLOOR */ + switch (direction) + { + case -1: /* DOWN */ + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } + } + + break; + + case 1: /* UP */ + if (sector->floorheight + speed > dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->floorheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + /* COULD GET CRUSHED */ + + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = p_change_sector(sector, crush); + if (flag == true) + { + if (crush == true) return crushed; + sector->floorheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } + } + break; + } + break; + + case 1: /* CEILING */ + switch (direction) + { + case -1: /* DOWN */ + if (sector->ceilingheight - speed < dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = p_change_sector(sector, crush); + + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + /* COULD GET CRUSHED */ + + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = p_change_sector(sector, crush); + + if (flag == true) + { + if (crush == true) return crushed; + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } + } + break; + + case 1: /* UP */ + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = p_change_sector(sector, crush); + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + + /* return crushed; */ + } + + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = p_change_sector(sector, crush); + +#if 0 /* UNUSED */ + if (flag == true) + { + sector->ceilingheight = lastpos; + p_change_sector(sector, crush); + return crushed; + } +#endif + } + break; + } + break; + } + + return ok; +} + +/* MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN) */ + +void t_move_floor(floormove_t *floor) +{ + result_e res; + + res = t_move_plane(floor->sector, floor->speed, floor->floordestheight, + floor->crush, 0, floor->direction); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(leveltime & 7)) s_start_sound(&floor->sector->soundorg, SFX_STNMOV); +#endif + + if (res == pastdest) + { + floor->sector->specialdata = NULL; + + if (floor->direction == 1) + { + switch (floor->type) + { + case FLOOR_DONUTRAISE: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + else if (floor->direction == -1) + { + switch (floor->type) + { + case FLOOR_LOWERANDCHANGE: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + default: + break; + } + } + + p_remove_thinker(&floor->thinker); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&floor->sector->soundorg, SFX_PSTOP); +#endif + } +} + +/* HANDLE FLOOR TYPES */ + +int ev_do_floor(line_t *line, floor_e floortype) +{ + int secnum; + int rtn; + int i; + sector_t *sec; + floormove_t *floor; + + secnum = -1; + rtn = 0; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + /* ALREADY MOVING? IF SO, KEEP GOING... */ + + if (sec->specialdata) continue; + + /* new floor thinker */ + + rtn = 1; + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + p_add_thinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = floortype; + floor->crush = false; + + switch (floortype) + { + case FLOOR_LOWERFLOOR: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_highest_floor_surrounding(sec); + break; + + case FLOOR_LOWERFLOORTOLOWEST: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_floor_surrounding(sec); + break; + + case FLOOR_TURBOLOWER: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = p_find_highest_floor_surrounding(sec); + if (gameversion <= exe_doom_1_2 || + floor->floordestheight != sec->floorheight) + floor->floordestheight += 8 * FRACUNIT; + break; + + case FLOOR_RAISEFLOORCRUSH: + floor->crush = true; + case FLOOR_RAISEFLOOR: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_ceiling_surrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= + (8 * FRACUNIT) * (floortype == FLOOR_RAISEFLOORCRUSH); + break; + + case FLOOR_RAISEFLOORTURBO: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = + p_find_next_highest_floor(sec, sec->floorheight); + break; + + case FLOOR_RAISEFLOORTONEAREST: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + p_find_next_highest_floor(sec, sec->floorheight); + break; + + case FLOOR_RAISEFLOOR24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + floor->sector->floorheight + 24 * FRACUNIT; + break; + + case FLOOR_RAISEFLOOR512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + floor->sector->floorheight + 512 * FRACUNIT; + break; + + case FLOOR_RAISEFLOOR24ANDCHANGE: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + floor->sector->floorheight + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + break; + + case FLOOR_RAISETOTEXTURE: + { + int minsize = INT_MAX; + side_t *side; + + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (two_sided(secnum, i)) + { + side = get_side(secnum, i, 0); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = get_side(secnum, i, 1); + if (side->bottomtexture >= 0) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + + floor->floordestheight = floor->sector->floorheight + minsize; + } + break; + + case FLOOR_LOWERANDCHANGE: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = p_find_lowest_floor_surrounding(sec); + floor->texture = sec->floorpic; + + for (i = 0; i < sec->linecount; i++) + { + if (two_sided(secnum, i)) + { + if (get_side(secnum, i, 0)->sector - sectors == secnum) + { + sec = get_sector(secnum, i, 1); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + else + { + sec = get_sector(secnum, i, 0); + + if (sec->floorheight == floor->floordestheight) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + break; + } + } + } + } + + default: + break; + } + } + + return rtn; +} + +/* BUILD A STAIRCASE! */ + +int ev_build_stairs(line_t *line, stair_e type) +{ + int secnum; + int height; + int i; + int newsecnum; + int texture; + int is_ok; + int rtn; + + sector_t *sec; + sector_t *tsec; + + floormove_t *floor; + + fixed_t stairsize = 0; + fixed_t speed = 0; + + secnum = -1; + rtn = 0; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + /* ALREADY MOVING? IF SO, KEEP GOING... */ + + if (sec->specialdata) continue; + + /* new floor thinker */ + + rtn = 1; + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + p_add_thinker(&floor->thinker); + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->direction = 1; + floor->sector = sec; + + switch (type) + { + case STAIR_BUILD8: + speed = FLOORSPEED / 4; + stairsize = 8 * FRACUNIT; + break; + case STAIR_TURBO16: + speed = FLOORSPEED * 4; + stairsize = 16 * FRACUNIT; + break; + } + + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + /* Initialize */ + + floor->type = FLOOR_LOWERFLOOR; + + /* e6y + * Uninitialized crush field will not be equal to 0 or 1 (true) + * with high probability. So, initialize it with any other value + */ + + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + + texture = sec->floorpic; + + /* Find next sector to raise + * 1. Find 2-sided line with same sector side[0] + * 2. Other side is the next sector to raise + */ + + do + { + is_ok = 0; + for (i = 0; i < sec->linecount; i++) + { + if (!((sec->lines[i])->flags & ML_TWOSIDED)) continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = tsec - sectors; + + if (secnum != newsecnum) continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = tsec - sectors; + + if (tsec->floorpic != texture) continue; + + height += stairsize; + + if (tsec->specialdata) continue; + + sec = tsec; + secnum = newsecnum; + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + + p_add_thinker(&floor->thinker); + + sec->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + + /* Initialize */ + + floor->type = FLOOR_LOWERFLOOR; + + /* e6y + * Uninitialized crush field will not be equal to 0 or 1 + * (true) with high probability. So, initialize it with any + * other value + */ + + floor->crush = STAIRS_UNINITIALIZED_CRUSH_FIELD_VALUE; + is_ok = 1; + break; + } + } + while (is_ok); + } + + return rtn; +} diff --git a/games/NXDoom/src/doom/p_inter.c b/games/NXDoom/src/doom/p_inter.c new file mode 100644 index 000000000..cd77f8ca7 --- /dev/null +++ b/games/NXDoom/src/doom/p_inter.c @@ -0,0 +1,890 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_inter.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Handling interactions (i.e., collisions). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Data. */ + +#include "doomdef.h" +#include "dstrings.h" + +#include "deh_main.h" +#include "deh_misc.h" +#include "doomstat.h" + +#include "i_system.h" +#include "m_random.h" + +#include "am_map.h" + +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "p_inter.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BONUSADD 6 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* a weapon is found with two clip loads, a big item has five clip loads */ + +int maxammo[NUMAMMO] = +{ + 200, 50, 300, 50 +}; + +int clipammo[NUMAMMO] = +{ + 10, 4, 20, 1 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* GET STUFF */ + +/* p_give_ammo + * + * Num is the number of clip loads, not the individual count (0= 1/2 clip). + * + * Returns false if the ammo can't be picked up at all + */ + +static boolean p_give_ammo(player_t *player, ammotype_t ammo, int num) +{ + int oldammo; + + if (ammo == am_noammo) return false; + + if (ammo >= NUMAMMO) i_error("p_give_ammo: bad type %i", ammo); + + if (player->ammo[ammo] == player->maxammo[ammo]) return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo] / 2; + + if (gameskill == sk_baby || gameskill == sk_nightmare) + { + /* give double ammo in trainer mode, you'll need in nightmare */ + + num <<= 1; + } + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + /* If non zero ammo, don't change up weapons, player was lower on purpose. + */ + + if (oldammo) return true; + + /* We were down to zero, so select a new weapon. Preferences are not user + * selectable. + */ + + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + + break; + + case am_shell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + } + + break; + + case am_cell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + { + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + } + + break; + + case am_misl: + if (player->readyweapon == wp_fist) + { + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + } + + default: + break; + } + + return true; +} + +/* p_give_weapon + * + * The weapon name may have a MF_DROPPED flag ored in. + */ + +static boolean p_give_weapon(player_t *player, weapontype_t weapon, + boolean dropped) +{ + boolean gaveammo; + boolean gaveweapon; + + if (netgame && (deathmatch != 2) && !dropped) + { + /* leave placed weapons forever on net games */ + + if (player->weaponowned[weapon]) return false; + + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + + if (deathmatch) + p_give_ammo(player, weaponinfo[weapon].ammo, 5); + else + p_give_ammo(player, weaponinfo[weapon].ammo, 2); + player->pendingweapon = weapon; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player == &players[consoleplayer]) s_start_sound(NULL, SFX_WPNUP); +#endif + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + /* give one clip with a dropped weapon, two clips with a found weapon */ + + if (dropped) + gaveammo = p_give_ammo(player, weaponinfo[weapon].ammo, 1); + else + gaveammo = p_give_ammo(player, weaponinfo[weapon].ammo, 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + + return (gaveweapon || gaveammo); +} + +/* p_give_body + * Returns false if the body isn't needed at all + */ + +static boolean p_give_body(player_t *player, int num) +{ + if (player->health >= MAXHEALTH) return false; + + player->health += num; + if (player->health > MAXHEALTH) player->health = MAXHEALTH; + player->mo->health = player->health; + + return true; +} + +/* p_give_armor + * Returns false if the armor is worse than the current armor. + */ + +static boolean p_give_armor(player_t *player, int armortype) +{ + int hits; + + hits = armortype * 100; + if (player->armorpoints >= hits) return false; /* don't pick up */ + + player->armortype = armortype; + player->armorpoints = hits; + + return true; +} + +static void p_give_card(player_t *player, card_t card) +{ + if (player->cards[card]) return; + + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + +static void p_kill_mobj(mobj_t *source, mobj_t *target) +{ + mobjtype_t item; + mobj_t *mo; + + target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY); + + if (target->type != MT_SKULL) target->flags &= ~MF_NOGRAVITY; + + target->flags |= MF_CORPSE | MF_DROPOFF; + target->height >>= 2; + + if (source && source->player) + { + /* count for intermission */ + + if (target->flags & MF_COUNTKILL) source->player->killcount++; + + if (target->player) source->player->frags[target->player - players]++; + } + else if (!netgame && (target->flags & MF_COUNTKILL)) + { + /* count all monster deaths, even those caused by other monsters */ + + players[0].killcount++; + } + + if (target->player) + { + /* count environment kills against you */ + + if (!source) target->player->frags[target->player - players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + p_drop_weapon(target->player); + + if (target->player == &players[consoleplayer] && automapactive) + { + /* don't die in auto map, switch view prior to dying */ + + am_stop(); + } + } + + if (target->health < -target->info->spawnhealth && + target->info->xdeathstate) + { + p_set_mobj_state(target, target->info->xdeathstate); + } + else + p_set_mobj_state(target, target->info->deathstate); + + target->tics -= p_random() & 3; + + if (target->tics < 1) target->tics = 1; + + /* In Chex Quest, monsters don't drop items. */ + + if (gameversion == exe_chex) + { + return; + } + + /* Drop stuff. + * This determines the kind of object spawned during the death frame of a + * thing. + */ + + switch (target->type) + { + case MT_WOLFSS: + case MT_POSSESSED: + item = MT_CLIP; + break; + + case MT_SHOTGUY: + item = MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = MT_CHAINGUN; + break; + + default: + return; + } + + mo = p_spawn_mobj(target->x, target->y, ONFLOORZ, item); + mo->flags |= MF_DROPPED; /* special versions of items */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +boolean p_give_power(player_t *player, int /* powertype_t */ power) +{ + if (power == pw_invulnerability) + { + player->powers[power] = INVULNTICS; + return true; + } + + if (power == pw_invisibility) + { + player->powers[power] = INVISTICS; + player->mo->flags |= MF_SHADOW; + return true; + } + + if (power == pw_infrared) + { + player->powers[power] = INFRATICS; + return true; + } + + if (power == pw_ironfeet) + { + player->powers[power] = IRONTICS; + return true; + } + + if (power == pw_strength) + { + p_give_body(player, 100); + player->powers[power] = 1; + return true; + } + + if (player->powers[power]) return false; /* already got it */ + + player->powers[power] = 1; + return true; +} + +void p_touch_special_thing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + int i; + fixed_t delta; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sound; +#endif + + delta = special->z - toucher->z; + + if (delta > toucher->height || delta < -8 * FRACUNIT) + { + return; /* out of reach */ + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_ITEMUP; +#endif + player = toucher->player; + + /* Dead thing touching. Can happen with a sliding player corpse. */ + + if (toucher->health <= 0) return; + + /* Identify by sprite. */ + + switch (special->sprite) + { + /* armor */ + + case SPR_ARM1: + if (!p_give_armor(player, deh_green_armor_class)) return; + player->message = (GOTARMOR); + break; + + case SPR_ARM2: + if (!p_give_armor(player, deh_blue_armor_class)) return; + player->message = (GOTMEGA); + break; + + /* bonus items */ + + case SPR_BON1: + player->health++; /* can go over 100% */ + if (player->health > deh_max_health) player->health = deh_max_health; + player->mo->health = player->health; + player->message = (GOTHTHBONUS); + break; + + case SPR_BON2: + player->armorpoints++; /* can go over 100% */ + if (player->armorpoints > deh_max_armor && gameversion > exe_doom_1_2) + { + player->armorpoints = deh_max_armor; + } + + /* deh_green_armor_class only applies to the green armor shirt; + * for the armor helmets, armortype 1 is always used. + */ + + if (!player->armortype) player->armortype = 1; + player->message = (GOTARMBONUS); + break; + + case SPR_SOUL: + player->health += deh_soulsphere_health; + if (player->health > deh_max_soulsphere) + player->health = deh_max_soulsphere; + player->mo->health = player->health; + player->message = (GOTSUPER); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_MEGA: + if (gamemode != commercial) return; + player->health = deh_megasphere_health; + player->mo->health = player->health; + + /* We always give armor type 2 for the megasphere; dehacked only + * affects the MegaArmor. + */ + + p_give_armor(player, 2); + player->message = (GOTMSPHERE); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + /* cards + * leave cards for everyone + */ + + case SPR_BKEY: + if (!player->cards[it_bluecard]) player->message = (GOTBLUECARD); + p_give_card(player, it_bluecard); + if (!netgame) break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) player->message = (GOTYELWCARD); + p_give_card(player, it_yellowcard); + if (!netgame) break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) player->message = (GOTREDCARD); + p_give_card(player, it_redcard); + if (!netgame) break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) player->message = (GOTBLUESKUL); + p_give_card(player, it_blueskull); + if (!netgame) break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) player->message = (GOTYELWSKUL); + p_give_card(player, it_yellowskull); + if (!netgame) break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) player->message = (GOTREDSKULL); + p_give_card(player, it_redskull); + if (!netgame) break; + return; + + /* medikits, heals */ + + case SPR_STIM: + if (!p_give_body(player, 10)) return; + player->message = (GOTSTIM); + break; + + case SPR_MEDI: + if (!p_give_body(player, 25)) return; + + if (player->health < 25) + player->message = (GOTMEDINEED); + else + player->message = (GOTMEDIKIT); + break; + + /* power ups */ + + case SPR_PINV: + if (!p_give_power(player, pw_invulnerability)) return; + player->message = (GOTINVUL); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PSTR: + if (!p_give_power(player, pw_strength)) return; + player->message = (GOTBERSERK); + if (player->readyweapon != wp_fist) player->pendingweapon = wp_fist; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PINS: + if (!p_give_power(player, pw_invisibility)) return; + player->message = (GOTINVIS); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_SUIT: + if (!p_give_power(player, pw_ironfeet)) return; + player->message = (GOTSUIT); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PMAP: + if (!p_give_power(player, pw_allmap)) return; + player->message = (GOTMAP); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + case SPR_PVIS: + if (!p_give_power(player, pw_infrared)) return; + player->message = (GOTVISOR); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (gameversion > exe_doom_1_2) sound = SFX_GETPOW; +#endif + break; + + /* ammo */ + + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!p_give_ammo(player, am_clip, 0)) return; + } + else + { + if (!p_give_ammo(player, am_clip, 1)) return; + } + + player->message = (GOTCLIP); + break; + + case SPR_AMMO: + if (!p_give_ammo(player, am_clip, 5)) return; + player->message = (GOTCLIPBOX); + break; + + case SPR_ROCK: + if (!p_give_ammo(player, am_misl, 1)) return; + player->message = (GOTROCKET); + break; + + case SPR_BROK: + if (!p_give_ammo(player, am_misl, 5)) return; + player->message = (GOTROCKBOX); + break; + + case SPR_CELL: + if (!p_give_ammo(player, am_cell, 1)) return; + player->message = (GOTCELL); + break; + + case SPR_CELP: + if (!p_give_ammo(player, am_cell, 5)) return; + player->message = (GOTCELLBOX); + break; + + case SPR_SHEL: + if (!p_give_ammo(player, am_shell, 1)) return; + player->message = (GOTSHELLS); + break; + + case SPR_SBOX: + if (!p_give_ammo(player, am_shell, 5)) return; + player->message = (GOTSHELLBOX); + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i = 0; i < NUMAMMO; i++) + player->maxammo[i] *= 2; + player->backpack = true; + } + + for (i = 0; i < NUMAMMO; i++) + p_give_ammo(player, i, 1); + player->message = (GOTBACKPACK); + break; + + /* weapons */ + + case SPR_BFUG: + if (!p_give_weapon(player, wp_bfg, false)) return; + player->message = (GOTBFG9000); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_MGUN: + if (!p_give_weapon(player, wp_chaingun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTCHAINGUN); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_CSAW: + if (!p_give_weapon(player, wp_chainsaw, false)) return; + player->message = (GOTCHAINSAW); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_LAUN: + if (!p_give_weapon(player, wp_missile, false)) return; + player->message = (GOTLAUNCHER); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_PLAS: + if (!p_give_weapon(player, wp_plasma, false)) return; + player->message = (GOTPLASMA); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_SHOT: + if (!p_give_weapon(player, wp_shotgun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTSHOTGUN); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + case SPR_SGN2: + if (!p_give_weapon(player, wp_supershotgun, + (special->flags & MF_DROPPED) != 0)) + return; + player->message = (GOTSHOTGUN2); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_WPNUP; +#endif + break; + + default: + i_error("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) player->itemcount++; + p_remove_mobj(special); + player->bonuscount += BONUSADD; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player == &players[consoleplayer]) s_start_sound(NULL, sound); +#endif +} + +/* p_damage_mobj + * Damages both enemies and players "inflictor" is the thing that caused the + * damage creature or missile, can be NULL (slime, etc) "source" is the thing + * to target after taking damage creature or NULL + * + * Source and inflictor are the same for melee attacks. + * Source can be NULL for slime, barrel explosions and other environmental + * stuff. + */ + +void p_damage_mobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, + int damage) +{ + unsigned ang; + int saved; + player_t *player; + fixed_t thrust; + int temp; + + if (!(target->flags & MF_SHOOTABLE)) return; /* shouldn't happen... */ + + if (target->health <= 0) return; + + if (target->flags & MF_SKULLFLY) + { + target->momx = target->momy = target->momz = 0; + } + + player = target->player; + if (player && gameskill == sk_baby) + damage >>= 1; /* take half damage in trainer mode */ + + /* Some close combat weapons should not + * inflict thrust and push the victim out of reach, + * thus kick away unless using the chainsaw. + */ + + if (inflictor && !(target->flags & MF_NOCLIP) && + (!source || !source->player || + source->player->readyweapon != wp_chainsaw)) + { + ang = r_point_to_angle2(inflictor->x, inflictor->y, + target->x, target->y); + + thrust = damage * (FRACUNIT >> 3) * 100 / target->info->mass; + + /* make fall forwards sometimes */ + + if (damage < 40 && damage > target->health && + target->z - inflictor->z > 64 * FRACUNIT && (p_random() & 1)) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += fixed_mul(thrust, finecosine[ang]); + target->momy += fixed_mul(thrust, finesine[ang]); + } + + /* player specific */ + + if (player) + { + /* end of game hell hack */ + + if (target->subsector->sector->special == 11 && + damage >= target->health) + { + damage = target->health - 1; + } + + /* Below certain threshold, ignore damage in GOD mode, or with INVUL + * power. + */ + + if (damage < 1000 && ((player->cheats & CF_GODMODE) || + player->powers[pw_invulnerability])) + { + return; + } + + if (player->armortype) + { + if (player->armortype == 1) + saved = damage / 3; + else + saved = damage / 2; + + if (player->armorpoints <= saved) + { + /* armor is used up */ + + saved = player->armorpoints; + player->armortype = 0; + } + + player->armorpoints -= saved; + damage -= saved; + } + + player->health -= damage; /* mirror mobj health here for Dave */ + if (player->health < 0) player->health = 0; + + player->attacker = source; + player->damagecount += damage; /* add damage after armor / invuln */ + + if (player->damagecount > 100) + { + player->damagecount = 100; /* teleport stomp does 10k points... */ + } + + temp = damage < 100 ? damage : 100; + + if (player == &players[consoleplayer]) + { + i_tactile(40, 10, 40 + temp * 2); + } + } + + /* do the damage */ + + target->health -= damage; + if (target->health <= 0) + { + p_kill_mobj(source, target); + return; + } + + if ((p_random() < target->info->painchance) && + !(target->flags & MF_SKULLFLY)) + { + target->flags |= MF_JUSTHIT; /* fight back! */ + + p_set_mobj_state(target, target->info->painstate); + } + + target->reactiontime = 0; /* we're awake now... */ + + if ((!target->threshold || target->type == MT_VILE) && source && + (source != target || gameversion < exe_doom_1_5) && + source->type != MT_VILE) + { + /* if not intent on another player, chase after this one */ + + target->target = source; + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] && + target->info->seestate != S_NULL) + p_set_mobj_state(target, target->info->seestate); + } +} diff --git a/games/NXDoom/src/doom/p_inter.h b/games/NXDoom/src/doom/p_inter.h new file mode 100644 index 000000000..450349e9c --- /dev/null +++ b/games/NXDoom/src/doom/p_inter.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_inter.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef __P_INTER__ +#define __P_INTER__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean p_give_power(player_t *, int); + +#endif /* __P_INTER__ */ diff --git a/games/NXDoom/src/doom/p_lights.c b/games/NXDoom/src/doom/p_lights.c new file mode 100644 index 000000000..019e1c941 --- /dev/null +++ b/games/NXDoom/src/doom/p_lights.c @@ -0,0 +1,314 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_lights.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Handle Sector base lighting effects. + * Muzzle flash? + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* FIRELIGHT FLICKER */ + +/* t_fire_flicker */ + +static void t_fire_flicker(fireflicker_t *flick) +{ + int amount; + + if (--flick->count) return; + + amount = (p_random() & 3) * 16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_spawn_fire_flicker(sector_t *sector) +{ + fireflicker_t *flick; + + /* Note that we are resetting sector attributes. Nothing special about it + * during gameplay. + */ + + sector->special = 0; + + flick = z_malloc(sizeof(*flick), PU_LEVSPEC, 0); + + p_add_thinker(&flick->thinker); + + flick->thinker.function.acp1 = (actionf_p1)t_fire_flicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = p_find_min_surrounding(sector, sector->lightlevel) + 16; + flick->count = 4; +} + +/* BROKEN LIGHT FLASHING */ + +/* t_light_flash + * Do flashing lights. + */ + +void t_light_flash(lightflash_t *flash) +{ + if (--flash->count) return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash->sector->lightlevel = flash->minlight; + flash->count = (p_random() & flash->mintime) + 1; + } + else + { + flash->sector->lightlevel = flash->maxlight; + flash->count = (p_random() & flash->maxtime) + 1; + } +} + +/* p_spawn_light_flash + * After the map has been loaded, scan each sector for specials that spawn + * thinkers + */ + +void p_spawn_light_flash(sector_t *sector) +{ + lightflash_t *flash; + + /* nothing special about it during gameplay */ + + sector->special = 0; + + flash = z_malloc(sizeof(*flash), PU_LEVSPEC, 0); + + p_add_thinker(&flash->thinker); + + flash->thinker.function.acp1 = (actionf_p1)t_light_flash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = p_find_min_surrounding(sector, sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (p_random() & flash->maxtime) + 1; +} + +/* STROBE LIGHT FLASHING */ + +void t_strobe_flash(strobe_t *flash) +{ + if (--flash->count) return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash->sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash->sector->lightlevel = flash->minlight; + flash->count = flash->darktime; + } +} + +/* p_spawn_strobe_flash + * After the map has been loaded, scan each sector for specials that spawn + * thinkers + */ + +void p_spawn_strobe_flash(sector_t *sector, int fast_or_slow, int in_sync) +{ + strobe_t *flash; + + flash = z_malloc(sizeof(*flash), PU_LEVSPEC, 0); + + p_add_thinker(&flash->thinker); + + flash->sector = sector; + flash->darktime = fast_or_slow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function.acp1 = (actionf_p1)t_strobe_flash; + flash->maxlight = sector->lightlevel; + flash->minlight = p_find_min_surrounding(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) flash->minlight = 0; + + /* nothing special about it during gameplay */ + + sector->special = 0; + + if (!in_sync) + flash->count = (p_random() & 7) + 1; + else + flash->count = 1; +} + +/* Start strobing lights (usually from a trigger) */ + +void ev_start_light_strobing(line_t *line) +{ + int secnum; + sector_t *sec; + + secnum = -1; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + if (sec->specialdata) continue; + + p_spawn_strobe_flash(sec, SLOWDARK, 0); + } +} + +/* TURN LINE'S TAG LIGHTS OFF */ + +void ev_turn_tag_lights_off(line_t *line) +{ + int i; + int j; + int min; + sector_t *sector; + sector_t *tsec; + line_t *templine; + + sector = sectors; + + for (j = 0; j < numsectors; j++, sector++) + { + if (sector->tag == line->tag) + { + min = sector->lightlevel; + for (i = 0; i < sector->linecount; i++) + { + templine = sector->lines[i]; + tsec = get_next_sector(templine, sector); + if (!tsec) continue; + if (tsec->lightlevel < min) min = tsec->lightlevel; + } + + sector->lightlevel = min; + } + } +} + +/* TURN LINE'S TAG LIGHTS ON */ + +void ev_light_turn_on(line_t *line, int bright) +{ + int i; + int j; + sector_t *sector; + sector_t *temp; + line_t *templine; + + sector = sectors; + + for (i = 0; i < numsectors; i++, sector++) + { + if (sector->tag == line->tag) + { + /* bright = 0 means to search for highest light level surrounding + * sector + */ + + if (!bright) + { + for (j = 0; j < sector->linecount; j++) + { + templine = sector->lines[j]; + temp = get_next_sector(templine, sector); + + if (!temp) continue; + + if (temp->lightlevel > bright) bright = temp->lightlevel; + } + } + + sector->lightlevel = bright; + } + } +} + +/* Spawn glowing light */ + +void t_glow(glow_t *g) +{ + switch (g->direction) + { + case -1: /* DOWN */ + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: /* UP */ + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + +void p_spawn_glowing_light(sector_t *sector) +{ + glow_t *g; + + g = z_malloc(sizeof(*g), PU_LEVSPEC, 0); + + p_add_thinker(&g->thinker); + + g->sector = sector; + g->minlight = p_find_min_surrounding(sector, sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function.acp1 = (actionf_p1)t_glow; + g->direction = -1; + + sector->special = 0; +} diff --git a/games/NXDoom/src/doom/p_local.h b/games/NXDoom/src/doom/p_local.h new file mode 100644 index 000000000..30bd51d63 --- /dev/null +++ b/games/NXDoom/src/doom/p_local.h @@ -0,0 +1,268 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_local.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Play functions, animation, global header. + * + ****************************************************************************/ + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#ifndef __R_LOCAL__ +#include "r_local.h" +#endif + +/* P_SPEC */ + +#include "p_spec.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FLOATSPEED (FRACUNIT * 4) + +#define MAXHEALTH 100 +#define VIEWHEIGHT (41 * FRACUNIT) + +/* mapblocks are used to check movement against lines and things */ + +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS * FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS + 7) +#define MAPBMASK (MAPBLOCKSIZE - 1) +#define MAPBTOFRAC (MAPBLOCKSHIFT - FRACBITS) + +/* player radius for movement checking */ + +#define PLAYERRADIUS 16 * FRACUNIT + +/* MAXRADIUS is for precalculated sector block boxes the spider demon is + * larger, but we do not have any moving sectors nearby + */ + +#define MAXRADIUS 32 * FRACUNIT + +#define GRAVITY FRACUNIT +#define MAXMOVE (30 * FRACUNIT) + +#define USERANGE (64 * FRACUNIT) +#define MELEERANGE (64 * FRACUNIT) +#define MISSILERANGE (32 * 64 * FRACUNIT) + +/* follow a player exclusively for 3 seconds */ + +#define BASETHRESHOLD 100 + +#define ONFLOORZ INT_MIN +#define ONCEILINGZ INT_MAX + +/* Time interval for item respawning. */ + +#define ITEMQUESIZE 128 + +/* Extended MAXINTERCEPTS, to allow for intercepts overrun emulation. */ + +#define MAXINTERCEPTS_ORIGINAL 128 +#define MAXINTERCEPTS (MAXINTERCEPTS_ORIGINAL + 61) + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +/* fraggle: I have increased the size of this buffer. In the original Doom, + * overrunning past this limit caused other bits of memory to be overwritten, + * affecting demo playback. However, in doing so, the limit was still + * exceeded. So we have to support more than 8 specials. + * + * We keep the original limit, to detect what variables in memory were + * overwritten (see SpechitOverrun()) + */ + +#define MAXSPECIALCROSS 20 +#define MAXSPECIALCROSS_ORIGINAL 8 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* P_MAPUTL */ + +typedef struct +{ + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + +typedef struct +{ + fixed_t frac; /* along trace line */ + boolean isaline; + union + { + mobj_t *thing; + line_t *line; + } d; +} intercept_t; + +typedef boolean (*traverser_t)(intercept_t *in); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* P_TICK */ + +/* both the head and tail of the thinker list */ + +extern thinker_t thinkercap; + +extern mapthing_t itemrespawnque[ITEMQUESIZE]; +extern int itemrespawntime[ITEMQUESIZE]; +extern int iquehead; +extern int iquetail; + +extern intercept_t intercepts[MAXINTERCEPTS]; +extern intercept_t *intercept_p; + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; + +extern divline_t trace; + +/* If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". */ + +extern boolean floatok; +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; + +extern line_t *ceilingline; + +extern line_t *spechit[MAXSPECIALCROSS]; +extern int numspechit; + +extern mobj_t *linetarget; /* who got hit (or NULL) */ + +extern fixed_t attackrange; + +/* slopes to top and bottom of target */ + +extern fixed_t topslope; +extern fixed_t bottomslope; + +extern byte *rejectmatrix; /* for fast sight rejection */ +extern short *blockmaplump; /* offsets in blockmap are from here */ +extern short *blockmap; +extern int bmapwidth; +extern int bmapheight; /* in mapblocks */ +extern fixed_t bmaporgx; +extern fixed_t bmaporgy; /* origin of block map */ +extern mobj_t **blocklinks; /* for thing chains */ + +extern int maxammo[NUMAMMO]; +extern int clipammo[NUMAMMO]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_init_thinkers(void); +void p_add_thinker(thinker_t *thinker); +void p_remove_thinker(thinker_t *thinker); + +/* P_PSPR */ + +void p_setup_psprites(player_t *curplayer); +void p_move_psprites(player_t *curplayer); +void p_drop_weapon(player_t *player); + +/* P_USER */ + +void p_player_think(player_t *player); + +/* P_MOBJ */ + +void p_respawn_specials(void); + +mobj_t *p_spawn_mobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); + +void p_remove_mobj(mobj_t *th); +mobj_t *p_subst_null_mobj(mobj_t *th); +boolean p_set_mobj_state(mobj_t *mobj, statenum_t state); +void p_mobj_thinker(mobj_t *mobj); + +void p_spawn_puff(fixed_t x, fixed_t y, fixed_t z); +void p_spawn_blood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t *p_spawn_missile(mobj_t *source, mobj_t *dest, mobjtype_t type); +void p_spawn_player_missile(mobj_t *source, mobjtype_t type); + +/* P_ENEMY */ + +void p_noise_alert(mobj_t *target, mobj_t *emmiter); + +fixed_t p_approx_distance(fixed_t dx, fixed_t dy); +int p_point_on_line_side(fixed_t x, fixed_t y, line_t *line); +int p_point_on_divline_side(fixed_t x, fixed_t y, divline_t *line); +int p_box_on_line_side(fixed_t *tmbox, line_t *ld); + +void p_line_opening(line_t *linedef); + +boolean p_block_lines_iterator(int x, int y, boolean (*func)(line_t *)); +boolean p_block_things_iterator(int x, int y, boolean (*func)(mobj_t *)); + +boolean p_path_traverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean (*trav)(intercept_t *)); + +void p_unset_thing_position(mobj_t *thing); +void p_set_thing_position(mobj_t *thing); + +/* P_MAP */ + +boolean p_check_position(mobj_t *thing, fixed_t x, fixed_t y); +boolean p_try_move(mobj_t *thing, fixed_t x, fixed_t y); +boolean p_teleport_move(mobj_t *thing, fixed_t x, fixed_t y); +void p_slide_move(mobj_t *mo); +boolean p_check_sight(mobj_t *t1, mobj_t *t2); +void p_use_lines(player_t *player); + +boolean p_change_sector(sector_t *sector, boolean crunch); + +fixed_t p_aim_line_attack(mobj_t *t1, angle_t angle, fixed_t distance); + +void p_line_attack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage); + +void p_radius_attack(mobj_t *spot, mobj_t *source, int damage); + +/* P_INTER */ + +void p_touch_special_thing(mobj_t *special, mobj_t *toucher); + +void p_damage_mobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, + int damage); + +#endif /* __P_LOCAL__ */ diff --git a/games/NXDoom/src/doom/p_map.c b/games/NXDoom/src/doom/p_map.c new file mode 100644 index 000000000..2b4aa4386 --- /dev/null +++ b/games/NXDoom/src/doom/p_map.c @@ -0,0 +1,1470 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_map.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Movement, collision handling. + * Shooting and aiming. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_misc.h" + +#include "i_system.h" +#include "m_bbox.h" +#include "m_random.h" + +#include "doomdef.h" +#include "m_argv.h" +#include "m_misc.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Spechit overrun magic value. + * + * This is the value used by PrBoom-plus. I think the value below is + * actually better and works with more demos. However, I think + * it's better for the spechits emulation to be compatible with + * PrBoom-plus, at least so that the big spechits emulation list + * on Doomworld can also be used with Chocolate Doom. + */ + +#define DEFAULT_SPECHIT_MAGIC 0x01C09C98 + +/* This is from a post by myk on the Doomworld forums, + * outputted from entryway's spechit_magic generator for + * s205n546.lmp. The _exact_ value of this isn't too + * important; as long as it is in the right general + * range, it will usually work. Otherwise, we can use + * the generator (hacked doom2.exe) and provide it + * with -spechit. + */ + +/* #define DEFAULT_SPECHIT_MAGIC 0x84f968e8 */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +fixed_t tmbbox[4]; +mobj_t *tmthing; +int tmflags; +fixed_t tmx; +fixed_t tmy; + +/* If "floatok" true, move would be ok + * if within "tmfloorz - tmceilingz". + */ + +boolean floatok; + +fixed_t tmfloorz; +fixed_t tmceilingz; +fixed_t tmdropoffz; + +/* keep track of the line that lowers the ceiling, + * so missiles don't explode against sky hack walls + */ + +line_t *ceilingline; + +/* keep track of special lines as they are hit, + * but don't process them until the move is proven valid + */ + +line_t *spechit[MAXSPECIALCROSS]; +int numspechit; + +/* SLIDE MOVE + * Allows the player to slide along any angled walls. + */ + +fixed_t bestslidefrac; +fixed_t secondslidefrac; + +line_t *bestslideline; +line_t *secondslideline; + +mobj_t *slidemo; + +fixed_t tmxmove; +fixed_t tmymove; + +/* p_line_attack */ + +mobj_t *linetarget; /* who got hit (or NULL) */ +mobj_t *shootthing; + +/* Height if not aiming up or down + * ???: use slope for monsters? + */ + +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +fixed_t aimslope; + +/* USE LINES */ + +mobj_t *usething; + +/* RADIUS ATTACK */ + +mobj_t *bombsource; +mobj_t *bombspot; +int bombdamage; + +/* SECTOR HEIGHT CHANGING + * After modifying a sectors floor or ceiling height, + * call this routine to adjust the positions + * of all things that touch the sector. + * + * If anything doesn't fit anymore, true will be returned. + * + * If crunch is true, they will take damage as they are being crushed. + * + * If Crunch is false, you should set the sector height back the way it was + * and call p_change_sector again to undo the changes. + */ + +boolean crushchange; +boolean nofit; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* TELEPORT MOVE */ + +static boolean pit_stomp_thing(mobj_t *thing) +{ + fixed_t blockdist; + + if (!(thing->flags & MF_SHOOTABLE)) return true; + + blockdist = thing->radius + tmthing->radius; + + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + { + return true; /* didn't hit it */ + } + + /* don't clip against self */ + + if (thing == tmthing) return true; + + /* monsters don't stomp things except on boss level */ + + if (!tmthing->player && gamemap != 30) return false; + + p_damage_mobj(thing, tmthing, tmthing, 10000); + + return true; +} + +/* Code to emulate the behavior of Vanilla Doom when encountering an overrun + * of the spechit array. This is by Andrey Budko (e6y) and comes from his + * PrBoom plus port. A big thanks to Andrey for this. + */ + +static void spechit_overrun(line_t *ld) +{ + static unsigned int baseaddr = 0; + unsigned int addr; + + if (baseaddr == 0) + { + int p; + + /* This is the first time we have had an overrun. Work out + * what base address we are going to use. + * Allow a spechit value to be specified on the command line. + */ + + /* @category compat + * @arg + * + * Use the specified magic value when emulating spechit overruns. + */ + + p = m_check_parm_with_args("-spechit", 1); + + if (p > 0) + { + m_str_to_int(myargv[p + 1], (int *)&baseaddr); + } + else + { + baseaddr = DEFAULT_SPECHIT_MAGIC; + } + } + + /* Calculate address used in doom2.exe */ + + addr = baseaddr + (ld - lines) * 0x3e; + + switch (numspechit) + { + case 9: + case 10: + case 11: + case 12: + tmbbox[numspechit - 9] = addr; + break; + case 13: + crushchange = addr; + break; + case 14: + nofit = addr; + break; + default: + fprintf(stderr, + "SpechitOverrun: Warning: unable to emulate" + "an overrun where numspechit=%i\n", + numspechit); + break; + } +} + +/* pit_check_line + * Adjusts tmfloorz and tmceilingz as lines are contacted + */ + +static boolean pit_check_line(line_t *ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || + tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || + tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + { + return true; + } + + if (p_box_on_line_side(tmbbox, ld) != -1) return true; + + /* A line has been hit */ + + /* The moving thing's destination position will cross + * the given line. + * If this should not be allowed, return false. + * If the line is special, keep track of it + * to process later if the move is proven ok. + * NOTE: specials are NOT sorted by order, + * so two special lines that are only 8 pixels apart + * could be crossed in either order. + */ + + if (!ld->backsector) return false; /* one sided line */ + + if (!(tmthing->flags & MF_MISSILE)) + { + if (ld->flags & ML_BLOCKING) + { + return false; /* explicitly blocking everything */ + } + + if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS) + { + return false; /* block monsters only */ + } + } + + /* set openrange, opentop, openbottom */ + + p_line_opening(ld); + + /* adjust floor / ceiling heights */ + + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + } + + if (openbottom > tmfloorz) tmfloorz = openbottom; + + if (lowfloor < tmdropoffz) tmdropoffz = lowfloor; + + /* if contacted a special line, add it to the list */ + + if (ld->special) + { + spechit[numspechit] = ld; + numspechit++; + + /* fraggle: spechits overrun emulation code from prboom-plus */ + + if (numspechit > MAXSPECIALCROSS_ORIGINAL) + { + spechit_overrun(ld); + } + } + + return true; +} + +/* pit_radius_attack + * "bombsource" is the creature that caused the explosion at "bombspot". + */ + +static boolean pit_radius_attack(mobj_t *thing) +{ + fixed_t dx; + fixed_t dy; + fixed_t dist; + + if (!(thing->flags & MF_SHOOTABLE)) return true; + + /* Boss spider and cyborg take no damage from concussion. */ + + if (thing->type == MT_CYBORG || thing->type == MT_SPIDER) return true; + + dx = abs(thing->x - bombspot->x); + dy = abs(thing->y - bombspot->y); + + dist = dx > dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) dist = 0; + + if (dist >= bombdamage) return true; /* out of range */ + + if (p_check_sight(thing, bombspot)) + { + /* must be in direct path */ + + p_damage_mobj(thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + +/* p_things_height_clip + * Takes a valid thing and adjusts the thing->floorz, thing->ceilingz, and + * possibly thing->z. + * + * This is called for all nearby monsters whenever a sector changes height. + * + * If the thing doesn't fit, the z will be set to the lowest value and false + * will be returned. + */ + +static boolean p_things_height_clip(mobj_t *thing) +{ + boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + p_check_position(thing, thing->x, thing->y); + + /* what about stranding a monster partially off an edge? */ + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + + if (onfloor) + { + /* walking monsters rise and fall with the floor */ + + thing->z = thing->floorz; + } + else + { + /* don't adjust a floating monster unless forced to */ + + if (thing->z + thing->height > thing->ceilingz) + thing->z = thing->ceilingz - thing->height; + } + + if (thing->ceilingz - thing->floorz < thing->height) return false; + + return true; +} + +/* p_hit_slide_line + * Adjusts the xmove / ymove so that the next move will slide along the wall. + */ + +static void p_hit_slide_line(line_t *ld) +{ + int side; + + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + + fixed_t movelen; + fixed_t newlen; + + if (ld->slopetype == ST_HORIZONTAL) + { + tmymove = 0; + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + tmxmove = 0; + return; + } + + side = p_point_on_line_side(slidemo->x, slidemo->y, ld); + + lineangle = r_point_to_angle2(0, 0, ld->dx, ld->dy); + + if (side == 1) lineangle += ANG180; + + moveangle = r_point_to_angle2(0, 0, tmxmove, tmymove); + deltaangle = moveangle - lineangle; + + if (deltaangle > ANG180) deltaangle += ANG180; + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + + movelen = p_approx_distance(tmxmove, tmymove); + newlen = fixed_mul(movelen, finecosine[deltaangle]); + + tmxmove = fixed_mul(newlen, finecosine[lineangle]); + tmymove = fixed_mul(newlen, finesine[lineangle]); +} + +/* ptr_slide_traverse */ + +static boolean ptr_slide_traverse(intercept_t *in) +{ + line_t *li; + + if (!in->isaline) i_error("ptr_slide_traverse: not a line?"); + + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) + { + if (p_point_on_line_side(slidemo->x, slidemo->y, li)) + { + return true; /* don't hit the back side */ + } + + goto isblocking; + } + + /* set openrange, opentop, openbottom */ + + p_line_opening(li); + + if (openrange < slidemo->height) + { + goto isblocking; /* doesn't fit */ + } + + if (opentop - slidemo->z < slidemo->height) + { + goto isblocking; /* mobj is too high */ + } + + if (openbottom - slidemo->z > 24 * FRACUNIT) + { + goto isblocking; /* too big a step up */ + } + + /* this line doesn't block movement */ + + return true; + + /* the line does block movement, + * see if it is closer than best so far + */ + +isblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; /* stop */ +} + +static boolean ptr_use_traverse(intercept_t *in) +{ + int side; + + if (!in->d.line->special) + { + p_line_opening(in->d.line); + if (openrange <= 0) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(usething, SFX_NOWAY); +#endif + + /* can't use through a wall */ + + return false; + } + + /* not a special line, but keep checking */ + + return true; + } + + side = 0; + if (p_point_on_line_side(usething->x, usething->y, in->d.line) == 1) + { + side = 1; + } + + /* return false; don't use back side */ + + p_use_special_line(usething, in->d.line, side); + + /* can't use for than one special line in a row */ + + return false; +} + +/* ptr_aim_traverse + * Sets linetaget and aimslope when a target is aimed at. + */ + +static boolean ptr_aim_traverse(intercept_t *in) +{ + line_t *li; + mobj_t *th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if (!(li->flags & ML_TWOSIDED)) return false; /* stop */ + + /* Crosses a two sided line. + * A two sided line will restrict + * the possible target ranges. + */ + + p_line_opening(li); + + if (openbottom >= opentop) return false; /* stop */ + + dist = fixed_mul(attackrange, in->frac); + + if (li->backsector == NULL || + li->frontsector->floorheight != li->backsector->floorheight) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > bottomslope) bottomslope = slope; + } + + if (li->backsector == NULL || + li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = fixed_div(opentop - shootz, dist); + if (slope < topslope) topslope = slope; + } + + if (topslope <= bottomslope) return false; /* stop */ + + return true; /* shot continues */ + } + + /* shoot a thing */ + + th = in->d.thing; + if (th == shootthing) return true; /* can't shoot self */ + + if (!(th->flags & MF_SHOOTABLE)) return true; /* corpse or something */ + + /* check angles to see if the thing can be aimed at */ + + dist = fixed_mul(attackrange, in->frac); + thingtopslope = fixed_div(th->z + th->height - shootz, dist); + + if (thingtopslope < bottomslope) return true; /* shot over the thing */ + + thingbottomslope = fixed_div(th->z - shootz, dist); + + if (thingbottomslope > topslope) return true; /* shot under the thing */ + + /* this thing can be hit! */ + + if (thingtopslope > topslope) thingtopslope = topslope; + + if (thingbottomslope < bottomslope) thingbottomslope = bottomslope; + + aimslope = (thingtopslope + thingbottomslope) / 2; + linetarget = th; + + return false; /* don't go any farther */ +} + +/* ptr_shoot_traverse */ + +static boolean ptr_shoot_traverse(intercept_t *in) +{ + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + line_t *li; + + mobj_t *th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + li = in->d.line; + + if (li->special) p_shoot_special_line(shootthing, li); + + if (!(li->flags & ML_TWOSIDED)) goto hitline; + + /* crosses a two sided line */ + + p_line_opening(li); + + dist = fixed_mul(attackrange, in->frac); + + /* e6y: emulation of missed back side on two-sided lines. + * backsector can be NULL when emulating missing back side. + */ + + if (li->backsector == NULL) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > aimslope) goto hitline; + + slope = fixed_div(opentop - shootz, dist); + if (slope < aimslope) goto hitline; + } + else + { + if (li->frontsector->floorheight + != li->backsector->floorheight) + { + slope = fixed_div(openbottom - shootz, dist); + if (slope > aimslope) goto hitline; + } + + if (li->frontsector->ceilingheight + != li->backsector->ceilingheight) + { + slope = fixed_div(opentop - shootz, dist); + if (slope < aimslope) goto hitline; + } + } + + /* shot continues */ + + return true; + + /* hit line */ + + hitline: + + /* position a bit closer */ + + frac = in->frac - fixed_div(4 * FRACUNIT, attackrange); + x = trace.x + fixed_mul(trace.dx, frac); + y = trace.y + fixed_mul(trace.dy, frac); + z = shootz + fixed_mul(aimslope, fixed_mul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + /* don't shoot the sky! */ + + if (z > li->frontsector->ceilingheight) return false; + + /* it's a sky hack wall */ + + if (li->backsector && + li->backsector->ceilingpic == skyflatnum) + { + return false; + } + } + + /* Spawn bullet puffs. */ + + p_spawn_puff(x, y, z); + + /* don't go any farther */ + + return false; + } + + /* shoot a thing */ + + th = in->d.thing; + if (th == shootthing) return true; /* can't shoot self */ + + if (!(th->flags & MF_SHOOTABLE)) return true; /* corpse or something */ + + /* check angles to see if the thing can be aimed at */ + + dist = fixed_mul(attackrange, in->frac); + thingtopslope = fixed_div(th->z + th->height - shootz, dist); + + if (thingtopslope < aimslope) return true; /* shot over the thing */ + + thingbottomslope = fixed_div(th->z - shootz, dist); + + if (thingbottomslope > aimslope) return true; /* shot under the thing */ + + /* hit thing position a bit closer */ + + frac = in->frac - fixed_div(10 * FRACUNIT, attackrange); + + x = trace.x + fixed_mul(trace.dx, frac); + y = trace.y + fixed_mul(trace.dy, frac); + z = shootz + fixed_mul(aimslope, fixed_mul(frac, attackrange)); + + /* Spawn bullet puffs or blod spots, + * depending on target type. + */ + + if (in->d.thing->flags & MF_NOBLOOD) + p_spawn_puff(x, y, z); + else + p_spawn_blood(x, y, z, la_damage); + + if (la_damage) p_damage_mobj(th, shootthing, shootthing, la_damage); + + /* don't go any farther */ + + return false; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_teleport_move */ + +boolean p_teleport_move(mobj_t *thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t *newsubsec; + + /* kill anything occupying the position */ + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = r_point_in_subsector(x, y); + ceilingline = NULL; + + /* The base floor/ceiling is from the subsector + * that contains the point. + * Any contacted lines the step closer together + * will adjust them. + */ + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + /* stomp on any things contacted */ + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + if (!p_block_things_iterator(bx, by, pit_stomp_thing)) + { + return false; + } + } + } + + /* the move is ok, + * so link the thing into its new position + */ + + p_unset_thing_position(thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + p_set_thing_position(thing); + + return true; +} + +boolean pit_check_thing(mobj_t *thing) +{ + fixed_t blockdist; + boolean solid; + int damage; + + if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE))) return true; + + blockdist = thing->radius + tmthing->radius; + + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + { + return true; /* didn't hit it */ + } + + /* don't clip against self */ + + if (thing == tmthing) return true; + + /* check for skulls slamming into things */ + + if (tmthing->flags & MF_SKULLFLY) + { + damage = ((p_random() % 8) + 1) * tmthing->info->damage; + + p_damage_mobj(thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + p_set_mobj_state(tmthing, tmthing->info->spawnstate); + + return false; /* stop moving */ + } + + /* missiles can hit other things */ + + if (tmthing->flags & MF_MISSILE) + { + /* see if it went over / under */ + + if (tmthing->z > thing->z + thing->height) + { + return true; /* overhead */ + } + + if (tmthing->z + tmthing->height < thing->z) + { + return true; /* underneath */ + } + + if (tmthing->target && + (tmthing->target->type == thing->type || + (tmthing->target->type == MT_KNIGHT && + thing->type == MT_BRUISER) || + (tmthing->target->type == MT_BRUISER && + thing->type == MT_KNIGHT))) + { + /* Don't hit same species as originator. */ + + if (thing == tmthing->target) return true; + + /* sdh: Add deh_species_infighting here. We can override the + * "monsters of the same species can't hurt each other" behavior + * through dehacked patches + */ + + if (thing->type != MT_PLAYER && !deh_species_infighting) + { + /* Explode, but do no damage. + * Let players missile other players. + */ + + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + /* didn't do any damage */ + + return !(thing->flags & MF_SOLID); + } + + /* damage / explode */ + + damage = ((p_random() % 8) + 1) * tmthing->info->damage; + p_damage_mobj(thing, tmthing, tmthing->target, damage); + + /* don't traverse any more */ + + return false; + } + + /* check for special pickup */ + + if (thing->flags & MF_SPECIAL) + { + solid = (thing->flags & MF_SOLID) != 0; + if (tmflags & MF_PICKUP) + { + /* can remove thing */ + + p_touch_special_thing(thing, tmthing); + } + + return !solid; + } + + return !(thing->flags & MF_SOLID); +} + +/* MOVEMENT CLIPPING */ + +/* p_check_position + * This is purely informative, nothing is modified + * (except things picked up). + * + * in: + * a mobj_t (can be valid or invalid) + * a position to be checked + * (doesn't need to be related to the mobj_t->x,y) + * + * during: + * special things are touched if MF_PICKUP + * early out on solid lines? + * + * out: + * newsubsec + * floorz + * ceilingz + * tmdropoffz + * the lowest point contacted + * (monsters won't move to a dropoff) + * speciallines[] + * numspeciallines + */ + +boolean p_check_position(mobj_t *thing, fixed_t x, fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t *newsubsec; + + tmthing = thing; + tmflags = thing->flags; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = r_point_in_subsector(x, y); + ceilingline = NULL; + + /* The base floor / ceiling is from the subsector + * that contains the point. + * Any contacted lines the step closer together + * will adjust them. + */ + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + if (tmflags & MF_NOCLIP) return true; + + /* Check things first, possibly picking things up. + * The bounding box is extended by MAXRADIUS + * because mobj_ts are grouped into mapblocks + * based on their origin point, and can overlap + * into adjacent blocks by up to MAXRADIUS units. + */ + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + if (!p_block_things_iterator(bx, by, pit_check_thing)) + { + return false; + } + } + } + + /* check lines */ + + xl = (tmbbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT; + + for (bx = xl; bx <= xh; bx++) + { + for (by = yl; by <= yh; by++) + { + if (!p_block_lines_iterator(bx, by, pit_check_line)) + { + return false; + } + } + } + + return true; +} + +/* p_try_move + * Attempt to move to a new position, + * crossing special lines unless MF_TELEPORT is set. + */ + +boolean p_try_move(mobj_t *thing, fixed_t x, fixed_t y) +{ + fixed_t oldx; + fixed_t oldy; + int side; + int oldside; + line_t *ld; + + floatok = false; + if (!p_check_position(thing, x, y)) + { + return false; /* solid wall or thing */ + } + + if (!(thing->flags & MF_NOCLIP)) + { + if (tmceilingz - tmfloorz < thing->height) + { + return false; /* doesn't fit */ + } + + floatok = true; + + if (!(thing->flags & MF_TELEPORT) && + tmceilingz - thing->z < thing->height) + { + return false; /* mobj must lower itself to fit */ + } + + if (!(thing->flags & MF_TELEPORT) && + tmfloorz - thing->z > 24 * FRACUNIT) + { + return false; /* too big a step up */ + } + + if (!(thing->flags & (MF_DROPOFF | MF_FLOAT)) && + tmfloorz - tmdropoffz > 24 * FRACUNIT) + { + return false; /* don't stand over a dropoff */ + } + } + + /* the move is ok, + * so link the thing into its new position + */ + + p_unset_thing_position(thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->x = x; + thing->y = y; + + p_set_thing_position(thing); + + /* if any special lines were hit, do the effect */ + + if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP))) + { + while (numspechit--) + { + /* see if the line was crossed */ + + ld = spechit[numspechit]; + side = p_point_on_line_side(thing->x, thing->y, ld); + oldside = p_point_on_line_side(oldx, oldy, ld); + if (side != oldside) + { + if (ld->special) + { + p_cross_special_line(ld - lines, oldside, thing); + } + } + } + } + + return true; +} + +/* p_slide_move + * The momx / momy move is bad, so try to slide + * along a wall. + * Find the first line hit, move flush to it, + * and slide along it + * + * This is a kludgy mess. + */ + +void p_slide_move(mobj_t *mo) +{ + fixed_t leadx; + fixed_t leady; + fixed_t trailx; + fixed_t traily; + fixed_t newx; + fixed_t newy; + int hitcount; + + slidemo = mo; + hitcount = 0; + +retry: + if (++hitcount == 3) goto stairstep; /* don't loop forever */ + + /* trace along the three leading corners */ + + if (mo->momx > 0) + { + leadx = mo->x + mo->radius; + trailx = mo->x - mo->radius; + } + else + { + leadx = mo->x - mo->radius; + trailx = mo->x + mo->radius; + } + + if (mo->momy > 0) + { + leady = mo->y + mo->radius; + traily = mo->y - mo->radius; + } + else + { + leady = mo->y - mo->radius; + traily = mo->y + mo->radius; + } + + bestslidefrac = FRACUNIT + 1; + + p_path_traverse(leadx, leady, leadx + mo->momx, leady + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + p_path_traverse(trailx, leady, trailx + mo->momx, leady + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + p_path_traverse(leadx, traily, leadx + mo->momx, traily + mo->momy, + PT_ADDLINES, ptr_slide_traverse); + + /* move up to the wall */ + + if (bestslidefrac == FRACUNIT + 1) + { + /* the move most have hit the middle, so stairstep */ + + stairstep: + if (!p_try_move(mo, mo->x, mo->y + mo->momy)) + p_try_move(mo, mo->x + mo->momx, mo->y); + return; + } + + /* fudge a bit to make sure it doesn't hit */ + + bestslidefrac -= 0x800; + if (bestslidefrac > 0) + { + newx = fixed_mul(mo->momx, bestslidefrac); + newy = fixed_mul(mo->momy, bestslidefrac); + + if (!p_try_move(mo, mo->x + newx, mo->y + newy)) goto stairstep; + } + + /* Now continue along the wall. + * First calculate remainder. + */ + + bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); + + if (bestslidefrac > FRACUNIT) bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) return; + + tmxmove = fixed_mul(mo->momx, bestslidefrac); + tmymove = fixed_mul(mo->momy, bestslidefrac); + + p_hit_slide_line(bestslideline); /* clip the moves */ + + mo->momx = tmxmove; + mo->momy = tmymove; + + if (!p_try_move(mo, mo->x + tmxmove, mo->y + tmymove)) + { + goto retry; + } +} + +/* p_aim_line_attack */ + +fixed_t p_aim_line_attack(mobj_t *t1, angle_t angle, fixed_t distance) +{ + fixed_t x2; + fixed_t y2; + + t1 = p_subst_null_mobj(t1); + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + + /* can't shoot outside view angles */ + + topslope = (SCREENHEIGHT / 2) * FRACUNIT / (SCREENWIDTH / 2); + bottomslope = -(SCREENHEIGHT / 2) * FRACUNIT / (SCREENWIDTH / 2); + + attackrange = distance; + linetarget = NULL; + + p_path_traverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, + ptr_aim_traverse); + + if (linetarget) return aimslope; + + return 0; +} + +/* p_line_attack + * + * If damage == 0, it is just a test trace that will leave linetarget set. + */ + +void p_line_attack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance >> FRACBITS) * finecosine[angle]; + y2 = t1->y + (distance >> FRACBITS) * finesine[angle]; + shootz = t1->z + (t1->height >> 1) + 8 * FRACUNIT; + attackrange = distance; + aimslope = slope; + + p_path_traverse(t1->x, t1->y, x2, y2, PT_ADDLINES | PT_ADDTHINGS, + ptr_shoot_traverse); +} + +/* p_use_lines + * Looks for special lines in front of the player to activate. + */ + +void p_use_lines(player_t *player) +{ + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE >> FRACBITS) * finecosine[angle]; + y2 = y1 + (USERANGE >> FRACBITS) * finesine[angle]; + + p_path_traverse(x1, y1, x2, y2, PT_ADDLINES, ptr_use_traverse); +} + +/* p_radius_attack + * Source is the creature that caused the explosion at spot. + */ + +void p_radius_attack(mobj_t *spot, mobj_t *source, int damage) +{ + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage + MAXRADIUS) << FRACBITS; + yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT; + yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT; + xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT; + xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y = yl; y <= yh; y++) + { + for (x = xl; x <= xh; x++) + { + p_block_things_iterator(x, y, pit_radius_attack); + } + } +} + +/* pit_change_sector */ + +static boolean pit_change_sector(mobj_t *thing) +{ + mobj_t *mo; + + if (p_things_height_clip(thing)) + { + return true; /* keep checking */ + } + + /* crunch bodies to giblets */ + + if (thing->health <= 0) + { + p_set_mobj_state(thing, S_GIBS); + + if (gameversion > exe_doom_1_2) thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + + /* keep checking */ + + return true; + } + + /* crunch dropped items */ + + if (thing->flags & MF_DROPPED) + { + p_remove_mobj(thing); + + return true; /* keep checking */ + } + + if (!(thing->flags & MF_SHOOTABLE)) + { + return true; /* assume it is bloody gibs or something */ + } + + nofit = true; + + if (crushchange && !(leveltime & 3)) + { + p_damage_mobj(thing, NULL, NULL, 10); + + /* spray blood in a random direction */ + + mo = p_spawn_mobj(thing->x, thing->y, thing->z + thing->height / 2, + MT_BLOOD); + + mo->momx = p_sub_random() << 12; + mo->momy = p_sub_random() << 12; + } + + /* keep checking (crush other things) */ + + return true; +} + +/* p_change_sector */ + +boolean p_change_sector(sector_t *sector, boolean crunch) +{ + int x; + int y; + + nofit = false; + crushchange = crunch; + + /* re-check heights for all things near the moving sector */ + + for (x = sector->blockbox[BOXLEFT]; x <= sector->blockbox[BOXRIGHT]; x++) + { + for (y = sector->blockbox[BOXBOTTOM]; y <= sector->blockbox[BOXTOP]; + y++) + { + p_block_things_iterator(x, y, pit_change_sector); + } + } + + return nofit; +} diff --git a/games/NXDoom/src/doom/p_maputl.c b/games/NXDoom/src/doom/p_maputl.c new file mode 100644 index 000000000..f5c193274 --- /dev/null +++ b/games/NXDoom/src/doom/p_maputl.c @@ -0,0 +1,939 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_maputl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2005, 2006 Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Movement/collision utility functions, + * as used by function in p_map.c. + * BLOCKMAP Iterator functions, + * and some PIT_* functions to use for iteration. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_bbox.h" +#include "m_misc.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Intercepts Overrun emulation, from PrBoom-plus. + * Thanks to Andrey Budko (entryway) for researching this and his + * implementation of Intercepts Overrun emulation in PrBoom-plus + * which this is based on. + */ + +typedef struct +{ + int len; + void *addr; + boolean int16_array; +} intercepts_overrun_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +fixed_t p_intercept_vector(divline_t *v2, divline_t *v1); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Intercepts memory table. This is where various variables are located + * in memory in Vanilla Doom. When the intercepts table overflows, we + * need to write to them. + * + * Almost all of the values to overwrite are 32-bit integers, except for + * playerstarts, which is effectively an array of 16-bit integers and + * must be treated differently. + */ + +static intercepts_overrun_t g_intercepts_overrun[] = +{ + {4, NULL, false}, + {4, NULL, /* &earlyout, */ false}, + {4, NULL, /* &intercept_p, */ false}, + {4, &lowfloor, false}, + {4, &openbottom, false}, + {4, &opentop, false}, + {4, &openrange, false}, + {4, NULL, false}, + {120, NULL, /* &activeplats, */ false}, + {8, NULL, false}, + {4, &bulletslope, false}, + {4, NULL, /* &swingx, */ false}, + {4, NULL, /* &swingy, */ false}, + {4, NULL, false}, + {40, &playerstarts, true}, + {4, NULL, /* &blocklinks, */ false}, + {4, &bmapwidth, false}, + {4, NULL, /* &blockmap, */ false}, + {4, &bmaporgx, false}, + {4, &bmaporgy, false}, + {4, NULL, /* &blockmaplump, */ false}, + {4, &bmapheight, false}, + {0, NULL, false}, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; + +/* INTERCEPT ROUTINES */ + +intercept_t intercepts[MAXINTERCEPTS]; +intercept_t *intercept_p; + +divline_t trace; +boolean earlyout; +int ptflags; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_make_divline(line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +/* Overwrite a specific memory location with a value. */ + +static void intercepts_mem_overrun(int location, int value) +{ + int i; + int offset; + int index; + void *addr; + + i = 0; + offset = 0; + + /* Search down the array until we find the right entry */ + + while (g_intercepts_overrun[i].len != 0) + { + if (offset + g_intercepts_overrun[i].len > location) + { + addr = g_intercepts_overrun[i].addr; + + /* Write the value to the memory location. + * 16-bit and 32-bit values are written differently. + */ + + if (addr != NULL) + { + if (g_intercepts_overrun[i].int16_array) + { + index = (location - offset) / 2; + ((short *)addr)[index] = value & 0xffff; + ((short *)addr)[index + 1] = (value >> 16) & 0xffff; + } + else + { + index = (location - offset) / 4; + ((int *)addr)[index] = value; + } + } + + break; + } + + offset += g_intercepts_overrun[i].len; + ++i; + } +} + +/* Emulate overruns of the intercepts[] array. */ + +static void intercepts_overrun(int num_intercepts, intercept_t *intercept) +{ + int location; + + if (num_intercepts <= MAXINTERCEPTS_ORIGINAL) + { + return; /* No overrun */ + } + + location = (num_intercepts - MAXINTERCEPTS_ORIGINAL - 1) * 12; + + /* Overwrite memory that is overwritten in Vanilla Doom, using + * the values from the intercept structure. + * + * Note: the ->d.{thing,line} member should really have its + * address translated into the correct address value for + * Vanilla Doom. + */ + + intercepts_mem_overrun(location, intercept->frac); + intercepts_mem_overrun(location + 4, intercept->isaline); + intercepts_mem_overrun(location + 8, (intptr_t)intercept->d.thing); +} + +/* pit_add_line_intercepts. + * Looks for lines in the given block + * that intercept the given trace + * to add to the intercepts list. + * + * A line is crossed if its endpoints + * are on opposite sides of the trace. + * Returns true if earlyout and a solid line hit. + */ + +static boolean pit_add_line_intercepts(line_t *ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + /* avoid precision problems with two routines */ + + if (trace.dx > FRACUNIT * 16 || trace.dy > FRACUNIT * 16 || + trace.dx < -FRACUNIT * 16 || trace.dy < -FRACUNIT * 16) + { + s1 = p_point_on_divline_side(ld->v1->x, ld->v1->y, &trace); + s2 = p_point_on_divline_side(ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = p_point_on_line_side(trace.x, trace.y, ld); + s2 = p_point_on_line_side(trace.x + trace.dx, trace.y + trace.dy, ld); + } + + if (s1 == s2) return true; /* line isn't crossed */ + + /* hit the line */ + + p_make_divline(ld, &dl); + frac = p_intercept_vector(&trace, &dl); + + if (frac < 0) return true; /* behind source */ + + /* try to early out the check */ + + if (earlyout && frac < FRACUNIT && !ld->backsector) + { + return false; /* stop checking */ + } + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercepts_overrun(intercept_p - intercepts, intercept_p); + intercept_p++; + + return true; /* continue */ +} + +static boolean pit_add_thing_intercepts(mobj_t *thing) +{ + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + int s1; + int s2; + + boolean tracepositive; + + divline_t dl; + + fixed_t frac; + + tracepositive = (trace.dx ^ trace.dy) > 0; + + /* check a corner to corner cross-section for hit */ + + if (tracepositive) + { + x1 = thing->x - thing->radius; + y1 = thing->y + thing->radius; + + x2 = thing->x + thing->radius; + y2 = thing->y - thing->radius; + } + else + { + x1 = thing->x - thing->radius; + y1 = thing->y - thing->radius; + + x2 = thing->x + thing->radius; + y2 = thing->y + thing->radius; + } + + s1 = p_point_on_divline_side(x1, y1, &trace); + s2 = p_point_on_divline_side(x2, y2, &trace); + + if (s1 == s2) return true; /* line isn't crossed */ + + dl.x = x1; + dl.y = y1; + dl.dx = x2 - x1; + dl.dy = y2 - y1; + + frac = p_intercept_vector(&trace, &dl); + + if (frac < 0) return true; /* behind source */ + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercepts_overrun(intercept_p - intercepts, intercept_p); + intercept_p++; + + return true; /* keep going */ +} + +/* p_traverse_intercepts + * Returns true if the traverser function returns true for all lines. + */ + +static boolean p_traverse_intercepts(traverser_t func, fixed_t maxfrac) +{ + int count; + fixed_t dist; + intercept_t *scan; + intercept_t *in; + + count = intercept_p - intercepts; + + in = 0; /* shut up compiler warning */ + + while (count--) + { + dist = INT_MAX; + for (scan = intercepts; scan < intercept_p; scan++) + { + if (scan->frac < dist) + { + dist = scan->frac; + in = scan; + } + } + + if (dist > maxfrac) return true; /* checked everything in range */ + +#if 0 /* UNUSED */ + + /* don't check these yet, there may be others inserted */ + + in = scan = intercepts; + for (scan = intercepts; scan < intercept_p; scan++) + { + if (scan->frac > maxfrac) *in++ = *scan; + } + + intercept_p = in; + return false; +#endif + + if (!func(in)) return false; /* don't bother going farther */ + + in->frac = INT_MAX; + } + + return true; /* everything was traversed */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_approx_distance + * Gives an estimation of distance (not exact) + */ + +fixed_t p_approx_distance(fixed_t dx, fixed_t dy) +{ + dx = abs(dx); + dy = abs(dy); + if (dx < dy) return dx + dy - (dx >> 1); + return dx + dy - (dy >> 1); +} + +/* p_point_on_line_side + * Returns 0 or 1 + */ + +int p_point_on_line_side(fixed_t x, fixed_t y, line_t *line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->v1->x) return line->dy > 0; + + return line->dy < 0; + } + + if (!line->dy) + { + if (y <= line->v1->y) return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->v1->x); + dy = (y - line->v1->y); + + left = fixed_mul(line->dy >> FRACBITS, dx); + right = fixed_mul(dy, line->dx >> FRACBITS); + + if (right < left) return 0; /* front side */ + + return 1; /* back side */ +} + +/* p_box_on_line_side + * Considers the line to be infinite + * Returns side 0 or 1, -1 if box crosses the line. + */ + +int p_box_on_line_side(fixed_t *tmbox, line_t *ld) +{ + int p1 = 0; + int p2 = 0; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = tmbox[BOXTOP] > ld->v1->y; + p2 = tmbox[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_VERTICAL: + p1 = tmbox[BOXRIGHT] < ld->v1->x; + p2 = tmbox[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_POSITIVE: + p1 = p_point_on_line_side(tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = p_point_on_line_side(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + p1 = p_point_on_line_side(tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = p_point_on_line_side(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) return p1; + return -1; +} + +/* p_point_on_divline_side + * Returns 0 or 1. + */ + +int p_point_on_divline_side(fixed_t x, fixed_t y, divline_t *line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->x) return line->dy > 0; + + return line->dy < 0; + } + + if (!line->dy) + { + if (y <= line->y) return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + + /* try to quickly decide by looking at sign bits */ + + if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000) + { + if ((line->dy ^ dx) & 0x80000000) return 1; /* (left is negative) */ + return 0; + } + + left = fixed_mul(line->dy >> 8, dx >> 8); + right = fixed_mul(dy >> 8, line->dx >> 8); + + if (right < left) return 0; /* front side */ + + return 1; /* back side */ +} + +/* p_intercept_vector + * + * Returns the fractional intercept point along the first divline. + * This is only called by the addthings and addlines traversers. + */ + +fixed_t p_intercept_vector(divline_t *v2, divline_t *v1) +{ +#if 1 + fixed_t frac; + fixed_t num; + fixed_t den; + + den = fixed_mul(v1->dy >> 8, v2->dx) - fixed_mul(v1->dx >> 8, v2->dy); + + if (den == 0) return 0; + + num = fixed_mul((v1->x - v2->x) >> 8, v1->dy) + + fixed_mul((v2->y - v1->y) >> 8, v1->dx); + + frac = fixed_div(num, den); + + return frac; +#else /* UNUSED, float debug. */ + float frac; + float num; + float den; + float v1x; + float v1y; + float v1dx; + float v1dy; + float v2x; + float v2y; + float v2dx; + float v2dy; + + v1x = (float)v1->x / FRACUNIT; + v1y = (float)v1->y / FRACUNIT; + v1dx = (float)v1->dx / FRACUNIT; + v1dy = (float)v1->dy / FRACUNIT; + v2x = (float)v2->x / FRACUNIT; + v2y = (float)v2->y / FRACUNIT; + v2dx = (float)v2->dx / FRACUNIT; + v2dy = (float)v2->dy / FRACUNIT; + + den = v1dy * v2dx - v1dx * v2dy; + + if (den == 0) return 0; /* parallel */ + + num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx; + frac = num / den; + + return frac * FRACUNIT; +#endif +} + +/* p_line_opening + * Sets opentop and openbottom to the window through a two sided line. + * OPTIMIZE: keep this precalculated + */ + +void p_line_opening(line_t *p_linedef) +{ + sector_t *front; + sector_t *back; + + if (p_linedef->sidenum[1] == -1) + { + /* single sided line */ + + openrange = 0; + return; + } + + front = p_linedef->frontsector; + back = p_linedef->backsector; + + if (front->ceilingheight < back->ceilingheight) + opentop = front->ceilingheight; + else + opentop = back->ceilingheight; + + if (front->floorheight > back->floorheight) + { + openbottom = front->floorheight; + lowfloor = back->floorheight; + } + else + { + openbottom = back->floorheight; + lowfloor = front->floorheight; + } + + openrange = opentop - openbottom; +} + +/* THING POSITION SETTING */ + +/* p_unset_thing_position + * + * Unlinks a thing from block map and sectors. + * + * On each position change, BLOCKMAP and other lookups maintaining lists to + * things inside these structures need to be updated. + */ + +void p_unset_thing_position(mobj_t *thing) +{ + int blockx; + int blocky; + + if (!(thing->flags & MF_NOSECTOR)) + { + /* inert things don't need to be in blockmap? unlink from subsector */ + + if (thing->snext) thing->snext->sprev = thing->sprev; + + if (thing->sprev) + thing->sprev->snext = thing->snext; + else + thing->subsector->sector->thinglist = thing->snext; + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + /* inert things don't need to be in blockmap unlink from block map */ + + if (thing->bnext) thing->bnext->bprev = thing->bprev; + + if (thing->bprev) + thing->bprev->bnext = thing->bnext; + else + { + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && + blocky < bmapheight) + { + blocklinks[blocky * bmapwidth + blockx] = thing->bnext; + } + } + } +} + +/* p_set_thing_position + * Links a thing into both a block and a subsector based on it's x y. + * Sets thing->subsector properly + */ + +void p_set_thing_position(mobj_t *thing) +{ + subsector_t *ss; + sector_t *sec; + int blockx; + int blocky; + mobj_t **link; + + /* link into subsector */ + + ss = r_point_in_subsector(thing->x, thing->y); + thing->subsector = ss; + + if (!(thing->flags & MF_NOSECTOR)) + { + /* invisible things don't go into the sector links */ + + sec = ss->sector; + + thing->sprev = NULL; + thing->snext = sec->thinglist; + + if (sec->thinglist) sec->thinglist->sprev = thing; + + sec->thinglist = thing; + } + + /* link into blockmap */ + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + /* inert things don't need to be in blockmap */ + + blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT; + blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT; + + if (blockx >= 0 && blockx < bmapwidth && blocky >= 0 && + blocky < bmapheight) + { + link = &blocklinks[blocky * bmapwidth + blockx]; + thing->bprev = NULL; + thing->bnext = *link; + if (*link) (*link)->bprev = thing; + + *link = thing; + } + else + { + /* thing is off the map */ + + thing->bnext = thing->bprev = NULL; + } + } +} + +/* BLOCK MAP ITERATORS + * For each line/thing in the given mapblock, + * call the passed PIT_* function. + * If the function returns false, + * exit with false without checking anything else. + */ + +/* p_block_lines_iterator + * The validcount flags are used to avoid checking lines + * that are marked in multiple mapblocks, + * so increment validcount before the first call + * to p_block_lines_iterator, then make one or more calls + * to it. + */ + +boolean p_block_lines_iterator(int x, int y, boolean (*func)(line_t *)) +{ + int offset; + short *list; + line_t *ld; + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + { + return true; + } + + offset = y * bmapwidth + x; + + offset = *(blockmap + offset); + + for (list = blockmaplump + offset; *list != -1; list++) + { + ld = &lines[*list]; + + if (ld->validcount == validcount) + continue; /* line has already been checked */ + + ld->validcount = validcount; + + if (!func(ld)) return false; + } + + return true; /* everything was checked */ +} + +boolean p_block_things_iterator(int x, int y, boolean (*func)(mobj_t *)) +{ + mobj_t *mobj; + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + { + return true; + } + + LINKED_LIST_CHECK_NO_CYCLE(mobj_t, blocklinks[y * bmapwidth + x], bnext); + + for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext) + { + if (!func(mobj)) return false; + } + + return true; +} + +/* p_path_traverse + * + * Traces a line from x1,y1 to x2,y2, calling the traverser function for + * each. + * Returns true if the traverser function returns true for all lines. + */ + +boolean p_path_traverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean (*trav)(intercept_t *)) +{ + fixed_t xt1; + fixed_t yt1; + fixed_t xt2; + fixed_t yt2; + + fixed_t xstep; + fixed_t ystep; + + fixed_t partial; + + fixed_t xintercept; + fixed_t yintercept; + + int mapx; + int mapy; + + int mapxstep; + int mapystep; + + int count; + + earlyout = (flags & PT_EARLYOUT) != 0; + + validcount++; + intercept_p = intercepts; + + if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0) + { + x1 += FRACUNIT; /* don't side exactly on a line */ + } + + if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0) + { + y1 += FRACUNIT; /* don't side exactly on a line */ + } + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1 >> MAPBLOCKSHIFT; + yt1 = y1 >> MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2 >> MAPBLOCKSHIFT; + yt2 = y2 >> MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + ystep = fixed_div(y2 - y1, abs(x2 - x1)); + } + else if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1); + ystep = fixed_div(y2 - y1, abs(x2 - x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256 * FRACUNIT; + } + + yintercept = (y1 >> MAPBTOFRAC) + fixed_mul(partial, ystep); + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1 >> MAPBTOFRAC) & (FRACUNIT - 1)); + xstep = fixed_div(x2 - x1, abs(y2 - y1)); + } + else if (yt2 < yt1) + { + mapystep = -1; + partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1); + xstep = fixed_div(x2 - x1, abs(y2 - y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256 * FRACUNIT; + } + + xintercept = (x1 >> MAPBTOFRAC) + fixed_mul(partial, xstep); + + /* Step through map blocks. + * Count is present to prevent a round off error + * from skipping the break. + */ + + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + { + if (!p_block_lines_iterator(mapx, mapy, pit_add_line_intercepts)) + { + return false; /* early out */ + } + } + + if (flags & PT_ADDTHINGS) + { + if (!p_block_things_iterator(mapx, mapy, pit_add_thing_intercepts)) + { + return false; /* early out */ + } + } + + if (mapx == xt2 && mapy == yt2) + { + break; + } + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + /* go through the sorted list */ + + return p_traverse_intercepts(trav, FRACUNIT); +} diff --git a/games/NXDoom/src/doom/p_mobj.c b/games/NXDoom/src/doom/p_mobj.c new file mode 100644 index 000000000..4b81fa45a --- /dev/null +++ b/games/NXDoom/src/doom/p_mobj.c @@ -0,0 +1,1126 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_mobj.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Moving object handling. Spawn functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +#include "hu_stuff.h" +#include "st_stuff.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Use a heuristic approach to detect infinite state cycles: Count the number + * of times the loop in p_set_mobj_state() executes and exit with an error + * once an arbitrary very large limit is reached. + */ + +#define MOBJ_CYCLE_LIMIT 1000000 + +#define STOPSPEED 0x1000 +#define FRICTION 0xe800 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int test; + +mapthing_t itemrespawnque[ITEMQUESIZE]; +int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void g_player_reborn(int player); +void p_spawn_map_thing(mapthing_t *mthing); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: p_explode_missile + ****************************************************************************/ + +static void p_explode_missile(mobj_t *mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + p_set_mobj_state(mo, mobjinfo[mo->type].deathstate); + + mo->tics -= p_random() & 3; + + if (mo->tics < 1) mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (mo->info->deathsound) s_start_sound(mo, mo->info->deathsound); +#endif +} + +/**************************************************************************** + * Name: p_xy_movement + ****************************************************************************/ + +static void p_xy_movement(mobj_t *mo) +{ + fixed_t ptryx; + fixed_t ptryy; + player_t *player; + fixed_t xmove; + fixed_t ymove; + + if (!mo->momx && !mo->momy) + { + if (mo->flags & MF_SKULLFLY) + { + /* the skull slammed into something */ + + mo->flags &= ~MF_SKULLFLY; + mo->momx = mo->momy = mo->momz = 0; + + p_set_mobj_state(mo, mo->info->spawnstate); + } + + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + do + { + if (xmove > MAXMOVE / 2 || ymove > MAXMOVE / 2) + { + ptryx = mo->x + xmove / 2; + ptryy = mo->y + ymove / 2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + + if (!p_try_move(mo, ptryx, ptryy)) + { + /* blocked move */ + + if (mo->player) + { + /* try to slide along it */ + + p_slide_move(mo); + } + else if (mo->flags & MF_MISSILE) + { + /* explode a missile */ + + if (ceilingline && ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + { + /* Hack to prevent missiles exploding + * against the sky. + * Does not handle sky floors. + */ + + p_remove_mobj(mo); + return; + } + + p_explode_missile(mo); + } + else + mo->momx = mo->momy = 0; + } + } + while (xmove || ymove); + + /* slow down */ + + if (player && player->cheats & CF_NOMOMENTUM) + { + /* debug option for no sliding at all */ + + mo->momx = mo->momy = 0; + return; + } + + if (mo->flags & (MF_MISSILE | MF_SKULLFLY)) + return; /* no friction for missiles ever */ + + if (mo->z > mo->floorz) return; /* no friction when airborne */ + + if (mo->flags & MF_CORPSE) + { + /* do not stop sliding + * if halfway off a step with some momentum + */ + + if (mo->momx > FRACUNIT / 4 || mo->momx < -FRACUNIT / 4 || + mo->momy > FRACUNIT / 4 || mo->momy < -FRACUNIT / 4) + { + if (mo->floorz != mo->subsector->sector->floorheight) return; + } + } + + if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && + mo->momy > -STOPSPEED && mo->momy < STOPSPEED && + (!player || + (player->cmd.forwardmove == 0 && player->cmd.sidemove == 0))) + { + /* if in a walking frame, stop moving */ + + if (player && + (unsigned)((player->mo->state - states) - S_PLAY_RUN1) < 4) + p_set_mobj_state(player->mo, S_PLAY); + + mo->momx = 0; + mo->momy = 0; + } + else + { + mo->momx = fixed_mul(mo->momx, FRICTION); + mo->momy = fixed_mul(mo->momy, FRICTION); + } +} + +/**************************************************************************** + * Name: p_z_movement + ****************************************************************************/ + +static void p_z_movement(mobj_t *mo) +{ + fixed_t dist; + fixed_t delta; + + /* check for smooth step up */ + + if (mo->player && mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz - mo->z; + + mo->player->deltaviewheight = + (VIEWHEIGHT - mo->player->viewheight) >> 3; + } + + mo->z += mo->momz; /* adjust height */ + + if (mo->flags & MF_FLOAT && mo->target) + { + /* float down towards target if too close */ + + if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) + { + dist = p_approx_distance(mo->x - mo->target->x, + mo->y - mo->target->y); + + delta = (mo->target->z + (mo->height >> 1)) - mo->z; + + if (delta < 0 && dist < -(delta * 3)) + mo->z -= FLOATSPEED; + else if (delta > 0 && dist < (delta * 3)) + mo->z += FLOATSPEED; + } + } + + /* clip movement */ + + if (mo->z <= mo->floorz) + { + /* hit the floor */ + + /* Note (id): + * somebody left this after the setting momz to 0, + * kinda useless there. + * + * cph - This was the a bug in the linuxdoom-1.10 source which + * caused it not to sync Doom 2 v1.9 demos. Someone + * added the above comment and moved up the following code. So + * demos would desync in close lost soul fights. + * Note that this only applies to original Doom 1 or Doom2 demos - not + * Final Doom and Ultimate Doom. So we test demo_compatibility *and* + * gamemission. (Note we assume that Doom1 is always Ult Doom, which + * seems to hold for most published demos.) + * + * fraggle - cph got the logic here slightly wrong. There are three + * versions of Doom 1.9: + * + * * The version used in registered doom 1.9 + doom2 - no bounce + * * The version used in ultimate doom - has bounce + * * The version used in final doom - has bounce + * + * So we need to check that this is either retail or commercial + * (but not doom2) + */ + + int correct_lost_soul_bounce = gameversion >= exe_ultimate; + + if (correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) + { + /* the skull slammed into something */ + + mo->momz = -mo->momz; + } + + if (mo->momz < 0) + { + if (mo->player && mo->momz < -GRAVITY * 8) + { + /* Squat down. + * Decrease viewheight for a moment after hitting the ground + * (hard), and utter appropriate sound. + */ + + mo->player->deltaviewheight = mo->momz >> 3; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_OOF); +#endif + } + + mo->momz = 0; + } + + mo->z = mo->floorz; + + /* cph 2001/05/26 - + * See lost soul bouncing comment above. We need this here for bug + * compatibility with original Doom2 v1.9 - if a soul is charging and + * hit by a raising floor this incorrectly reverses its Y momentum. + */ + + if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; + + if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP)) + { + p_explode_missile(mo); + return; + } + } + else if (!(mo->flags & MF_NOGRAVITY)) + { + if (mo->momz == 0) + mo->momz = -GRAVITY * 2; + else + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + /* hit the ceiling */ + + if (mo->momz > 0) + { + mo->momz = 0; + } + + mo->z = mo->ceilingz - mo->height; + + if (mo->flags & MF_SKULLFLY) + { + /* the skull slammed into something */ + + mo->momz = -mo->momz; + } + + if ((mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP)) + { + p_explode_missile(mo); + return; + } + } +} + +/**************************************************************************** + * Name: p_nightmare_respawn + ****************************************************************************/ + +static void p_nightmare_respawn(mobj_t *mobj) +{ + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t *ss; + mobj_t *mo; + mapthing_t *mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + /* something is occupying it's position? */ + + if (!p_check_position(mobj, x, y)) return; /* no respawn */ + + /* spawn a teleport fog at old spot because of removal of the body? */ + + mo = p_spawn_mobj(mobj->x, mobj->y, mobj->subsector->sector->floorheight, + MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* initiate teleport sound */ + + s_start_sound(mo, SFX_TELEPT); +#endif + + /* spawn a teleport fog at the new spot */ + + ss = r_point_in_subsector(x, y); + + mo = p_spawn_mobj(x, y, ss->sector->floorheight, MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_TELEPT); +#endif + + mthing = &mobj->spawnpoint; /* spawn the new monster */ + + /* spawn it */ + + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + /* inherit attributes from deceased one */ + + mo = p_spawn_mobj(x, y, z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle / 45); + + if (mthing->options & MTF_AMBUSH) mo->flags |= MF_AMBUSH; + + mo->reactiontime = 18; + + /* remove the old monster, */ + + p_remove_mobj(mobj); +} + +/**************************************************************************** + * Name: p_check_missile_spawn + * + * Description: + * Moves the missile forward a bit and possibly explodes it right there. + * + ****************************************************************************/ + +static void p_check_missile_spawn(mobj_t *th) +{ + th->tics -= p_random() & 3; + if (th->tics < 1) th->tics = 1; + + /* move a little forward so an angle can be computed if it immediately + * explodes + */ + + th->x += (th->momx >> 1); + th->y += (th->momy >> 1); + th->z += (th->momz >> 1); + + if (!p_try_move(th, th->x, th->y)) p_explode_missile(th); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: p_set_mobj_state + * + * Description: + * Returns true if the mobj is still present. + * + ****************************************************************************/ + +boolean p_set_mobj_state(mobj_t *mobj, statenum_t state) +{ + state_t *st; + int cycle_counter = 0; + + do + { + if (state == S_NULL) + { + mobj->state = (state_t *)S_NULL; + p_remove_mobj(mobj); + return false; + } + + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + /* Modified handling. + * Call action functions when the state is set + */ + + if (st->action.acp1) st->action.acp1(mobj); + + state = st->nextstate; + + if (cycle_counter++ > MOBJ_CYCLE_LIMIT) + { + i_error("p_set_mobj_state: Infinite state cycle detected!"); + } + } + while (!mobj->tics); + + return true; +} + +/**************************************************************************** + * Name: p_mobj_thinker + ****************************************************************************/ + +void p_mobj_thinker(mobj_t *mobj) +{ + /* momentum movement */ + + if (mobj->momx || mobj->momy || (mobj->flags & MF_SKULLFLY)) + { + p_xy_movement(mobj); + + /* FIXME: decent NOP/NULL/Nil function pointer please. */ + + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; /* mobj was removed */ + } + + if ((mobj->z != mobj->floorz) || mobj->momz) + { + p_z_movement(mobj); + + /* FIXME: decent NOP/NULL/Nil function pointer please. */ + + if (mobj->thinker.function.acv == (actionf_v)(-1)) + return; /* mobj was removed */ + } + + /* cycle through states, calling action functions at transitions */ + + if (mobj->tics != -1) + { + mobj->tics--; + + /* you can cycle through multiple states in a tic */ + + if (!mobj->tics) + { + if (!p_set_mobj_state(mobj, mobj->state->nextstate)) + { + return; /* freed itself */ + } + } + } + else + { + /* check for nightmare respawn */ + + if (!(mobj->flags & MF_COUNTKILL)) return; + + if (!respawnmonsters) return; + + mobj->movecount++; + + if (mobj->movecount < 12 * TICRATE) return; + + if (leveltime & 31) return; + + if (p_random() > 4) return; + + p_nightmare_respawn(mobj); + } +} + +/**************************************************************************** + * Name: p_spawn_mobj + ****************************************************************************/ + +mobj_t *p_spawn_mobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) +{ + mobj_t *mobj; + state_t *st; + mobjinfo_t *info; + + mobj = z_malloc(sizeof(*mobj), PU_LEVEL, NULL); + memset(mobj, 0, sizeof(*mobj)); + info = &mobjinfo[type]; + + mobj->type = type; + mobj->info = info; + mobj->x = x; + mobj->y = y; + mobj->radius = info->radius; + mobj->height = info->height; + mobj->flags = info->flags; + mobj->health = info->spawnhealth; + + if (gameskill != sk_nightmare) mobj->reactiontime = info->reactiontime; + + mobj->lastlook = p_random() % MAXPLAYERS; + + /* do not set the state with p_set_mobj_state, + * because action routines can not be called yet + */ + + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + /* set subsector and/or block links */ + + p_set_thing_position(mobj); + + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + if (z == ONFLOORZ) + mobj->z = mobj->floorz; + else if (z == ONCEILINGZ) + mobj->z = mobj->ceilingz - mobj->info->height; + else + mobj->z = z; + + mobj->thinker.function.acp1 = (actionf_p1)p_mobj_thinker; + + p_add_thinker(&mobj->thinker); + + return mobj; +} + +/**************************************************************************** + * Name: p_remove_mobj + ****************************************************************************/ + +void p_remove_mobj(mobj_t *mobj) +{ + if ((mobj->flags & MF_SPECIAL) && !(mobj->flags & MF_DROPPED) && + (mobj->type != MT_INV) && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead + 1) & (ITEMQUESIZE - 1); + + /* lose one off the end? */ + + if (iquehead == iquetail) + { + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); + } + } + + /* unlink from sector and block lists */ + + p_unset_thing_position(mobj); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* stop any playing sound */ + + s_stop_sound(mobj); +#endif + + p_remove_thinker((thinker_t *)mobj); /* free block */ +} + +/**************************************************************************** + * Name: p_respawn_specials + ****************************************************************************/ + +void p_respawn_specials(void) +{ + fixed_t x; + fixed_t y; + fixed_t z; + + subsector_t *ss; + mobj_t *mo; + mapthing_t *mthing; + + int i; + + /* only respawn items in deathmatch */ + + if (deathmatch != 2) return; + + /* nothing left to respawn? */ + + if (iquehead == iquetail) return; + + /* wait at least 30 seconds */ + + if (leveltime - itemrespawntime[iquetail] < 30 * TICRATE) return; + + mthing = &itemrespawnque[iquetail]; + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + /* spawn a teleport fog at the new spot */ + + ss = r_point_in_subsector(x, y); + mo = p_spawn_mobj(x, y, ss->sector->floorheight, MT_IFOG); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(mo, SFX_ITMBK); +#endif + + /* find which type to spawn */ + + for (i = 0; i < NUMMOBJTYPES; i++) + { + if (mthing->type == mobjinfo[i].doomednum) break; + } + + if (i >= NUMMOBJTYPES) + { + i_error("p_respawn_specials: Failed to find mobj type with doomednum " + "%d when respawning thing. This would cause a buffer overrun " + "in vanilla Doom", + mthing->type); + } + + /* spawn it */ + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = p_spawn_mobj(x, y, z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle / 45); + + /* pull it from the que */ + + iquetail = (iquetail + 1) & (ITEMQUESIZE - 1); +} + +/**************************************************************************** + * Name: p_spawn_player + * + * Description: + * Called when a player is spawned on the level. + * Most of the player structure stays unchanged between levels. + * + ****************************************************************************/ + +void p_spawn_player(mapthing_t *mthing) +{ + player_t *p; + fixed_t x; + fixed_t y; + fixed_t z; + + mobj_t *mobj; + + int i; + + if (mthing->type == 0) + { + return; + } + + /* not playing? */ + + if (!playeringame[mthing->type - 1]) return; + + p = &players[mthing->type - 1]; + + if (p->playerstate == PST_REBORN) g_player_reborn(mthing->type - 1); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = p_spawn_mobj(x, y, z, MT_PLAYER); + + /* set color translations for player sprites */ + + if (mthing->type > 1) mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT; + + mobj->angle = ANG45 * (mthing->angle / 45); + mobj->player = p; + mobj->health = p->health; + + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + p->message = NULL; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + p_setup_psprites(p); /* setup gun psprite */ + + /* give all cards in death match mode */ + + if (deathmatch) + { + for (i = 0; i < NUMCARDS; i++) + { + p->cards[i] = true; + } + } + + if (mthing->type - 1 == consoleplayer) + { + st_start(); /* wake up the status bar */ + hu_start(); /* wake up the heads up text */ + } +} + +/**************************************************************************** + * Name: p_spawn_map_thing + * + * Description: + * The fields of the mapthing should already be in host byte order. + * + ****************************************************************************/ + +void p_spawn_map_thing(mapthing_t *mthing) +{ + int i; + int bit; + mobj_t *mobj; + fixed_t x; + fixed_t y; + fixed_t z; + + /* count deathmatch start positions */ + + if (mthing->type == 11) + { + if (deathmatch_p < &deathmatchstarts[10]) + { + memcpy(deathmatch_p, mthing, sizeof(*mthing)); + deathmatch_p++; + } + + return; + } + + if (mthing->type <= 0) + { + /* Thing type 0 is actually "player -1 start". + * For some reason, Vanilla Doom accepts/ignores this. + */ + + return; + } + + /* check for players specially */ + + if (mthing->type <= 4) + { + /* save spots for respawning in network games */ + + playerstarts[mthing->type - 1] = *mthing; + playerstartsingame[mthing->type - 1] = true; + if (!deathmatch) p_spawn_player(mthing); + + return; + } + + /* check for appropriate skill level */ + + if (!netgame && (mthing->options & 16)) return; + + if (gameskill == sk_baby) + { + bit = 1; + } + else if (gameskill == sk_nightmare) + { + bit = 4; + } + else + { + /* avoid undefined behavior (left shift by negative value and rhs too + * big) by accurately emulating what doom.exe did: reduce mod 32. For + * more details check: + * https://github.com/chocolate-doom/chocolate-doom/issues/1677 + */ + + bit = (int)(1U << ((gameskill - 1) & 0x1f)); + } + + if (!(mthing->options & bit)) return; + + /* find which type to spawn */ + + for (i = 0; i < NUMMOBJTYPES; i++) + { + if (mthing->type == mobjinfo[i].doomednum) break; + } + + if (i == NUMMOBJTYPES) + i_error("p_spawn_map_thing: Unknown type %i at (%i, %i)", mthing->type, + mthing->x, mthing->y); + + /* don't spawn keycards and players in deathmatch */ + + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) return; + + /* don't spawn any monsters if -nomonsters */ + + if (nomonsters && (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL))) + { + return; + } + + /* spawn it */ + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = p_spawn_mobj(x, y, z, i); + mobj->spawnpoint = *mthing; + + if (mobj->tics > 0) mobj->tics = 1 + (p_random() % mobj->tics); + if (mobj->flags & MF_COUNTKILL) totalkills++; + if (mobj->flags & MF_COUNTITEM) totalitems++; + + mobj->angle = ANG45 * (mthing->angle / 45); + if (mthing->options & MTF_AMBUSH) mobj->flags |= MF_AMBUSH; +} + +/* GAME SPAWN FUNCTIONS */ + +/**************************************************************************** + * Name: p_spawn_puff + ****************************************************************************/ + +void p_spawn_puff(fixed_t x, fixed_t y, fixed_t z) +{ + mobj_t *th; + + z += (p_sub_random() << 10); + + th = p_spawn_mobj(x, y, z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= p_random() & 3; + + if (th->tics < 1) th->tics = 1; + + /* don't make punches spark on the wall */ + + if (attackrange == MELEERANGE) p_set_mobj_state(th, S_PUFF3); +} + +/**************************************************************************** + * Name: p_spawn_blood + ****************************************************************************/ + +void p_spawn_blood(fixed_t x, fixed_t y, fixed_t z, int damage) +{ + mobj_t *th; + + z += (p_sub_random() << 10); + th = p_spawn_mobj(x, y, z, MT_BLOOD); + th->momz = FRACUNIT * 2; + th->tics -= p_random() & 3; + + if (th->tics < 1) th->tics = 1; + + if (damage <= 12 && damage >= 9) + p_set_mobj_state(th, S_BLOOD2); + else if (damage < 9) + p_set_mobj_state(th, S_BLOOD3); +} + +/**************************************************************************** + * Name: p_subst_null_mobj + * + * Description: + * Certain functions assume that a mobj_t pointer is non-NULL, + * causing a crash in some situations where it is NULL. Vanilla + * Doom did not crash because of the lack of proper memory + * protection. This function substitutes NULL pointers for + * pointers to a dummy mobj, to avoid a crash. + * + ****************************************************************************/ + +mobj_t *p_subst_null_mobj(mobj_t *mobj) +{ + if (mobj == NULL) + { + static mobj_t dummy_mobj; + + dummy_mobj.x = 0; + dummy_mobj.y = 0; + dummy_mobj.z = 0; + dummy_mobj.flags = 0; + + mobj = &dummy_mobj; + } + + return mobj; +} + +/**************************************************************************** + * Name: p_spawn_missile + ****************************************************************************/ + +mobj_t *p_spawn_missile(mobj_t *source, mobj_t *dest, mobjtype_t type) +{ + mobj_t *th; + angle_t an; + int dist; + + th = p_spawn_mobj(source->x, source->y, + source->z + 4 * 8 * FRACUNIT, type); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (th->info->seesound) s_start_sound(th, th->info->seesound); +#endif + + th->target = source; /* where it came from */ + an = r_point_to_angle2(source->x, source->y, dest->x, dest->y); + + /* fuzzy player */ + + if (dest->flags & MF_SHADOW) an += p_sub_random() << 20; + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = fixed_mul(th->info->speed, finecosine[an]); + th->momy = fixed_mul(th->info->speed, finesine[an]); + + dist = p_approx_distance(dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) dist = 1; + + th->momz = (dest->z - source->z) / dist; + p_check_missile_spawn(th); + + return th; +} + +/**************************************************************************** + * Name: p_spawn_player_missile + * + * Description: + * Tries to aim at a nearby monster + * + ****************************************************************************/ + +void p_spawn_player_missile(mobj_t *source, mobjtype_t type) +{ + mobj_t *th; + angle_t an; + + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t slope; + + /* see which target is to be aimed at */ + + an = source->angle; + slope = p_aim_line_attack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + slope = p_aim_line_attack(source, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an -= 2 << 26; + slope = p_aim_line_attack(source, an, 16 * 64 * FRACUNIT); + } + + if (!linetarget) + { + an = source->angle; + slope = 0; + } + } + + x = source->x; + y = source->y; + z = source->z + 4 * 8 * FRACUNIT; + + th = p_spawn_mobj(x, y, z, type); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (th->info->seesound) s_start_sound(th, th->info->seesound); +#endif + + th->target = source; + th->angle = an; + th->momx = fixed_mul(th->info->speed, finecosine[an >> ANGLETOFINESHIFT]); + th->momy = fixed_mul(th->info->speed, finesine[an >> ANGLETOFINESHIFT]); + th->momz = fixed_mul(th->info->speed, slope); + + p_check_missile_spawn(th); +} diff --git a/games/NXDoom/src/doom/p_mobj.h b/games/NXDoom/src/doom/p_mobj.h new file mode 100644 index 000000000..6cd73f3a6 --- /dev/null +++ b/games/NXDoom/src/doom/p_mobj.h @@ -0,0 +1,357 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_mobj.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Map Objects, MObj, definition and handling. + * + ****************************************************************************/ + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Basics. */ + +#include "m_fixed.h" +#include "tables.h" + +/* We need the thinker_t stuff. */ + +#include "d_think.h" + +/* We need the WAD data structure for Map things, from the THINGS lump. */ + +#include "doomdata.h" + +/* States are tied to finite states are tied to animation frames. + * Needs precompiled tables/data structures. + */ + +#include "info.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* NOTES: mobj_t + * + * mobj_ts are used to tell the refresh where to draw an image, + * tell the world simulation when objects are contacted, + * and tell the sound driver how to position a sound. + * + * The refresh uses the next and prev links to follow + * lists of things in sectors as they are being drawn. + * The sprite, frame, and angle elements determine which patch_t + * is used to draw the sprite if it is visible. + * The sprite and frame values are almost always set + * from state_t structures. + * The statescr.exe utility generates the states.h and states.c + * files that contain the sprite/frame numbers from the + * statescr.txt source file. + * The xyz origin point represents a point at the bottom middle + * of the sprite (between the feet of a biped). + * This is the default origin position for patch_ts grabbed + * with lumpy.exe. + * A walking creature will have its z equal to the floor + * it is standing on. + * + * The sound code uses the x,y, and subsector fields + * to do stereo positioning of any sound effited by the mobj_t. + * + * The play simulation uses the blocklinks, x,y,z, radius, height + * to determine when mobj_ts are touching each other, + * touching lines in the map, or hit by trace lines (gunshots, + * lines of sight, etc). + * The mobj_t->flags element has various bit flags + * used by the simulation. + * + * Every mobj_t is linked into a single sector + * based on its origin coordinates. + * The subsector_t is found with r_point_in_subsector(x,y), + * and the sector_t can be found with subsector->sector. + * The sector links are only used by the rendering code, + * the play simulation does not care about them at all. + * + * Any mobj_t that needs to be acted upon by something else + * in the play world (block movement, be shot, etc) will also + * need to be linked into the blockmap. + * If the thing has the MF_NOBLOCK flag set, it will not use + * the block links. It can still interact with other things, + * but only as the instigator (missiles will run into other + * things, but nothing can run into a missile). + * Each block in the grid is 128*128 units, and knows about + * every line_t that it contains a piece of, and every + * interactable mobj_t that has its origin contained. + * + * A valid mobj_t is a mobj_t that has the proper subsector_t + * filled in for its xy coordinates and is linked into the + * sector from which the subsector was made, or has the + * MF_NOSECTOR flag set (the subsector_t needs to be valid + * even if MF_NOSECTOR is set), and is linked into a blockmap + * block or has the MF_NOBLOCKMAP flag set. + * Links should only be modified by the P_[Un]SetThingPosition() + * functions. + * Do not change the MF_NO? flags while a thing is valid. + * + * Any questions? + */ + +/* Misc. mobj flags */ + +typedef enum +{ + MF_SPECIAL = 1, /* Call P_SpecialThing when touched. */ + MF_SOLID = 2, /* Blocks. */ + MF_SHOOTABLE = 4, /* Can be hit. */ + + /* Don't use the sector links (invisible but touchable). */ + + MF_NOSECTOR = 8, + + /* Don't use the blocklinks (inert but displayable) */ + + MF_NOBLOCKMAP = 16, + + /* Not to be activated by sound, deaf monster. */ + + MF_AMBUSH = 32, + + /* Will try to attack right back. */ + + MF_JUSTHIT = 64, + + /* Will take at least one step before attacking. */ + + MF_JUSTATTACKED = 128, + + /* On level spawning (initial position), hang from ceiling instead of stand + * on floor. + */ + + MF_SPAWNCEILING = 256, + + /* Don't apply gravity (every tic), that is, object will float, keeping + * current height or changing it actively. + */ + + MF_NOGRAVITY = 512, + + /* Movement flags. This allows jumps from high places. */ + + MF_DROPOFF = 0x400, + + /* For players, will pick up items. */ + + MF_PICKUP = 0x800, + + /* Player cheat. ??? */ + + MF_NOCLIP = 0x1000, + + /* Player: keep info about sliding along walls. */ + + MF_SLIDE = 0x2000, + + /* Allow moves to any height, no gravity. + * For active floaters, e.g. cacodemons, pain elementals. + */ + + MF_FLOAT = 0x4000, + + /* Don't cross lines ??? or look at heights on teleport. */ + + MF_TELEPORT = 0x8000, + + /* Don't hit same species, explode on block. + * Player missiles as well as fireballs of various kinds. + */ + + MF_MISSILE = 0x10000, + + /* Dropped by a demon, not level spawned. + * E.g. ammo clips dropped by dying former humans. + */ + + MF_DROPPED = 0x20000, + + /* Use fuzzy draw (shadow demons or spectres), temporary player + * invisibility powerup. + */ + + MF_SHADOW = 0x40000, + + /* Flag: don't bleed when shot (use puff), barrels and shootable furniture + * shall not bleed. + */ + + MF_NOBLOOD = 0x80000, + + /* Don't stop moving halfway off a step, that is, have dead bodies slide + * down all the way. + */ + + MF_CORPSE = 0x100000, + + /* Floating to a height for a move, ??? don't auto float to target's + * height. + */ + + MF_INFLOAT = 0x200000, + + /* On kill, count this enemy object towards intermission kill total. + * Happy gathering. + */ + + MF_COUNTKILL = 0x400000, + + /* On picking up, count this item object towards intermission item total. */ + + MF_COUNTITEM = 0x800000, + + /* Special handling: skull in flight. + * Neither a cacodemon nor a missile. + */ + + MF_SKULLFLY = 0x1000000, + + /* Don't spawn this object in death match mode (e.g. key cards). + */ + + MF_NOTDMATCH = 0x2000000, + + /* Player sprites in multiplayer modes are modified using an internal color + * lookup table for re-indexing. + * + * If 0x4 0x8 or 0xc, use a translation table for player colormaps + */ + + MF_TRANSLATION = 0xc000000, + + /* Hmm ???. */ + + MF_TRANSSHIFT = 26 +} mobjflag_t; + +/* Map Object definition. */ + +typedef struct mobj_s +{ + /* List: thinker links. */ + + thinker_t thinker; + + /* Info for drawing: position. */ + + fixed_t x; + fixed_t y; + fixed_t z; + + /* More list: links in sector (if needed) */ + + struct mobj_s *snext; + struct mobj_s *sprev; + + /* More drawing info: to determine current sprite. */ + + angle_t angle; /* orientation */ + spritenum_t sprite; /* used to find patch_t and flip value */ + int frame; /* might be ORed with FF_FULLBRIGHT */ + + /* Interaction info, by BLOCKMAP. + * Links in blocks (if needed). + */ + + struct mobj_s *bnext; + struct mobj_s *bprev; + + struct subsector_s *subsector; + + /* The closest interval over all contacted Sectors. */ + + fixed_t floorz; + fixed_t ceilingz; + + /* For movement checking. */ + + fixed_t radius; + fixed_t height; + + /* Momentums, used to update position. */ + + fixed_t momx; + fixed_t momy; + fixed_t momz; + + /* If == validcount, already checked. */ + + int validcount; + + mobjtype_t type; + mobjinfo_t *info; /* &mobjinfo[mobj->type] */ + + int tics; /* state tic counter */ + state_t *state; + int flags; + int health; + + /* Movement direction, movement generation (zig-zagging). */ + + int movedir; /* 0-7 */ + int movecount; /* when 0, select a new dir */ + + /* Thing being chased/attacked (or NULL), + * also the originator for missiles. + */ + + struct mobj_s *target; + + /* Reaction time: if non 0, don't attack yet. + * Used by player to freeze a bit after teleporting. + */ + + int reactiontime; + + /* If >0, the target will be chased + * no matter what (even if shot) + */ + + int threshold; + + /* Additional info record for player avatars only. + * Only valid if type == MT_PLAYER + */ + + struct player_s *player; + + /* Player number last looked for. */ + + int lastlook; + + /* For nightmare re-spawn. */ + + mapthing_t spawnpoint; + + /* Thing being chased/attacked for tracers. */ + + struct mobj_s *tracer; +} mobj_t; + +#endif /* __P_MOBJ__ */ diff --git a/games/NXDoom/src/doom/p_plats.c b/games/NXDoom/src/doom/p_plats.c new file mode 100644 index 000000000..8cbbf7052 --- /dev/null +++ b/games/NXDoom/src/doom/p_plats.c @@ -0,0 +1,336 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_plats.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Plats (i.e. elevator platforms) code, raising/lowering. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "m_random.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +plat_t *activeplats[MAXPLATS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Move a plat up and down */ + +void t_plat_raise(plat_t *plat) +{ + result_e res; + + switch (plat->status) + { + case up: + res = t_move_plane(plat->sector, plat->speed, + plat->high, plat->crush, 0, 1); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (plat->type == PLAT_RAISEANDCHANGE || + plat->type == PLAT_RAISETONEARESTANDCHANGE) + { + if (!(leveltime & 7)) + s_start_sound(&plat->sector->soundorg, SFX_STNMOV); + } +#endif + + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTART); +#endif + } + else + { + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTOP); +#endif + + switch (plat->type) + { + case PLAT_BLAZEDWUS: + case PLAT_DOWNWAITUPSTAY: + p_remove_active_plat(plat); + break; + + case PLAT_RAISEANDCHANGE: + case PLAT_RAISETONEARESTANDCHANGE: + p_remove_active_plat(plat); + break; + + default: + break; + } + } + } + break; + + case down: + res = t_move_plane(plat->sector, plat->speed, plat->low, false, 0, -1); + + if (res == pastdest) + { + plat->count = plat->wait; + plat->status = waiting; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTOP); +#endif + } + break; + + case waiting: + if (!--plat->count) + { + if (plat->sector->floorheight == plat->low) + plat->status = up; + else + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&plat->sector->soundorg, SFX_PSTART); +#endif + } + + case in_stasis: + break; + } +} + +/* Do Platforms + * "amount" is only used for SOME platforms. + */ + +int ev_do_plat(line_t *line, plattype_e type, int amount) +{ + plat_t *plat; + int secnum; + int rtn; + sector_t *sec; + + secnum = -1; + rtn = 0; + + /* Activate all plats that are in_stasis */ + + switch (type) + { + case PLAT_PERPETUALRAISE: + p_activate_in_stasis(line->tag); + break; + + default: + break; + } + + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + sec = §ors[secnum]; + + if (sec->specialdata) continue; + + /* Find lowest & highest floors around sector */ + + rtn = 1; + plat = z_malloc(sizeof(*plat), PU_LEVSPEC, 0); + p_add_thinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->specialdata = plat; + plat->thinker.function.acp1 = (actionf_p1)t_plat_raise; + plat->crush = false; + plat->tag = line->tag; + + switch (type) + { + case PLAT_RAISETONEARESTANDCHANGE: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = p_find_next_highest_floor(sec, sec->floorheight); + plat->wait = 0; + plat->status = up; + + /* NO MORE DAMAGE, IF APPLICABLE */ + + sec->special = 0; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_STNMOV); +#endif + break; + + case PLAT_RAISEANDCHANGE: + plat->speed = PLATSPEED / 2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount * FRACUNIT; + plat->wait = 0; + plat->status = up; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_STNMOV); +#endif + break; + + case PLAT_DOWNWAITUPSTAY: + plat->speed = PLATSPEED * 4; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = TICRATE * PLATWAIT; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + + case PLAT_BLAZEDWUS: + plat->speed = PLATSPEED * 8; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = TICRATE * PLATWAIT; + plat->status = down; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + + case PLAT_PERPETUALRAISE: + plat->speed = PLATSPEED; + plat->low = p_find_lowest_floor_surrounding(sec); + + if (plat->low > sec->floorheight) plat->low = sec->floorheight; + + plat->high = p_find_highest_floor_surrounding(sec); + + if (plat->high < sec->floorheight) plat->high = sec->floorheight; + + plat->wait = TICRATE * PLATWAIT; + plat->status = p_random() & 1; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&sec->soundorg, SFX_PSTART); +#endif + break; + } + + p_add_active_plat(plat); + } + + return rtn; +} + +void p_activate_in_stasis(int tag) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + { + if (activeplats[i] && (activeplats[i])->tag == tag && + (activeplats[i])->status == in_stasis) + { + (activeplats[i])->status = (activeplats[i])->oldstatus; + (activeplats[i])->thinker.function.acp1 = (actionf_p1)t_plat_raise; + } + } +} + +void ev_stop_plat(line_t *line) +{ + int j; + + for (j = 0; j < MAXPLATS; j++) + { + if (activeplats[j] && ((activeplats[j])->status != in_stasis) && + ((activeplats[j])->tag == line->tag)) + { + (activeplats[j])->oldstatus = (activeplats[j])->status; + (activeplats[j])->status = in_stasis; + (activeplats[j])->thinker.function.acv = (actionf_v)NULL; + } + } +} + +void p_add_active_plat(plat_t *plat) +{ + int i; + + for (i = 0; i < MAXPLATS; i++) + { + if (activeplats[i] == NULL) + { + activeplats[i] = plat; + return; + } + } + + i_error("p_add_active_plat: no more plats!"); +} + +void p_remove_active_plat(plat_t *plat) +{ + int i; + for (i = 0; i < MAXPLATS; i++) + { + if (plat == activeplats[i]) + { + (activeplats[i])->sector->specialdata = NULL; + p_remove_thinker(&(activeplats[i])->thinker); + activeplats[i] = NULL; + + return; + } + } + + i_error("p_remove_active_plat: can't find plat!"); +} diff --git a/games/NXDoom/src/doom/p_pspr.c b/games/NXDoom/src/doom/p_pspr.c new file mode 100644 index 000000000..42a58f23a --- /dev/null +++ b/games/NXDoom/src/doom/p_pspr.c @@ -0,0 +1,788 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_pspr.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Weapon sprite animation, weapon objects. + * Action functions for weapons. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomdef.h" + +#include "deh_misc.h" + +#include "m_random.h" +#include "p_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" + +/* Data. */ + +#include "p_pspr.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LOWERSPEED FRACUNIT * 6 +#define RAISESPEED FRACUNIT * 6 + +#define WEAPONBOTTOM 128 * FRACUNIT +#define WEAPONTOP 32 * FRACUNIT + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +fixed_t swingx; +fixed_t swingy; + +fixed_t bulletslope; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_set_psprite(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp; + state_t *state; + + psp = &player->psprites[position]; + + do + { + if (!stnum) + { + /* object removed itself */ + + psp->state = NULL; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; /* could be 0 */ + +#if 0 /* Unused as far as I can tell */ + if (state->misc1) + { + /* coordinate set */ + + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } +#endif + + /* Call action routine. + * Modified handling. + */ + + if (state->action.acp2) + { + state->action.acp2(player, psp); + if (!psp->state) break; + } + + stnum = psp->state->nextstate; + } + while (!psp->tics); + + /* an initial state of 0 could cycle through */ +} + +#if 0 /* UNUSED */ +static void p_calc_swing(player_t *player) +{ + fixed_t swing; + int angle; + + /* OPTIMIZE: tablify this. + * A LUT would allow for different modes, and add flexibility. + */ + + swing = player->bob; + + angle = (FINEANGLES / 70 * leveltime) & FINEMASK; + swingx = fixed_mul(swing, finesine[angle]); + + angle = (FINEANGLES / 70 * leveltime + FINEANGLES / 2) & FINEMASK; + swingy = -fixed_mul(swingx, finesine[angle]); +} +#endif + +/* p_bringup_weapon + * Starts bringing the pending weapon up from the bottom of the screen. Uses + * player + */ + +static void p_bringup_weapon(player_t *player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (player->pendingweapon == wp_chainsaw) + s_start_sound(player->mo, SFX_SAWUP); +#endif + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + player->psprites[ps_weapon].sy = WEAPONBOTTOM; + + p_set_psprite(player, ps_weapon, newstate); +} + +/* Doom does not check the bounds of the ammo array. As a result, + * it is possible to use an ammo type > 4 that overflows into the + * maxammo array and affects that instead. Through dehacked, for + * example, it is possible to make a weapon that decreases the max + * number of ammo for another weapon. Emulate this. + */ + +static void decrease_ammo(player_t *player, int ammonum, int amount) +{ + if (ammonum < NUMAMMO) + { + player->ammo[ammonum] -= amount; + } + else + { + player->maxammo[ammonum - NUMAMMO] -= amount; + } +} + +/* p_bullet_slope + * Sets a slope so a near miss is at approximately + * the height of the intended target + */ + +static void p_bullet_slope(mobj_t *mo) +{ + angle_t an; + + /* see which target is to be aimed at */ + + an = mo->angle; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + + if (!linetarget) + { + an += 1 << 26; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + if (!linetarget) + { + an -= 2 << 26; + bulletslope = p_aim_line_attack(mo, an, 16 * 64 * FRACUNIT); + } + } +} + +static void p_gunshot(mobj_t *mo, boolean accurate) +{ + angle_t angle; + int damage; + + damage = 5 * (p_random() % 3 + 1); + angle = mo->angle; + + if (!accurate) angle += p_sub_random() << 18; + + p_line_attack(mo, angle, MISSILERANGE, bulletslope, damage); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_check_ammo + * Returns true if there is enough ammo to shoot. + * If not, selects the next weapon to use. + */ + +boolean p_check_ammo(player_t *player) +{ + ammotype_t ammo; + int count; + + ammo = weaponinfo[player->readyweapon].ammo; + + /* Minimal amount for one shot varies. */ + + if (player->readyweapon == wp_bfg) + count = deh_bfg_cells_per_shot; + else if (player->readyweapon == wp_supershotgun) + count = 2; /* Double barrel. */ + else + count = 1; /* Regular. */ + + /* Some do not need ammunition anyway. + * Return if current ammunition sufficient. + */ + + if (ammo == am_noammo || player->ammo[ammo] >= count) return true; + + /* Out of ammo, pick a weapon to change to. + * Preferences are set here. + */ + + do + { + if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && + (gamemode != shareware)) + { + player->pendingweapon = wp_plasma; + } + else if (player->weaponowned[wp_supershotgun] && + player->ammo[am_shell] > 2 && (gamemode == commercial)) + { + player->pendingweapon = wp_supershotgun; + } + else if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) + { + player->pendingweapon = wp_chaingun; + } + else if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) + { + player->pendingweapon = wp_shotgun; + } + else if (player->ammo[am_clip]) + { + player->pendingweapon = wp_pistol; + } + else if (player->weaponowned[wp_chainsaw]) + { + player->pendingweapon = wp_chainsaw; + } + else if (player->weaponowned[wp_missile] && player->ammo[am_misl]) + { + player->pendingweapon = wp_missile; + } + else if (player->weaponowned[wp_bfg] && player->ammo[am_cell] > 40 && + (gamemode != shareware)) + { + player->pendingweapon = wp_bfg; + } + else + { + /* If everything fails. */ + + player->pendingweapon = wp_fist; + } + } + while (player->pendingweapon == wp_nochange); + + /* Now set appropriate weapon overlay. */ + + p_set_psprite(player, ps_weapon, + weaponinfo[player->readyweapon].downstate); + return false; +} + +static void p_fire_weapon(player_t *player) +{ + statenum_t newstate; + + if (!p_check_ammo(player)) return; + + p_set_mobj_state(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + p_set_psprite(player, ps_weapon, newstate); + p_noise_alert(player->mo, player->mo); +} + +/* p_drop_weapon + * Player died, so put the weapon away. + */ + +void p_drop_weapon(player_t *player) +{ + p_set_psprite(player, ps_weapon, + weaponinfo[player->readyweapon].downstate); +} + +/* a_weapon_ready + * The player can fire the weapon or change to another weapon at this time. + * Follows after getting weapon up, or after previous attack/fire sequence. + */ + +void a_weapon_ready(player_t *player, pspdef_t *psp) +{ + statenum_t newstate; + int angle; + + /* get out of attack state */ + + if (player->mo->state == &states[S_PLAY_ATK1] || + player->mo->state == &states[S_PLAY_ATK2]) + { + p_set_mobj_state(player->mo, S_PLAY); + } + + if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW]) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWIDL); +#endif + } + + /* check for change if player is dead, put the weapon away */ + + if (player->pendingweapon != wp_nochange || !player->health) + { + /* change weapon (pending weapon should already be validated) */ + + newstate = weaponinfo[player->readyweapon].downstate; + p_set_psprite(player, ps_weapon, newstate); + return; + } + + /* check for fire the missile launcher and bfg do not auto fire */ + + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown || (player->readyweapon != wp_missile && + player->readyweapon != wp_bfg)) + { + player->attackdown = true; + p_fire_weapon(player); + return; + } + } + else + player->attackdown = false; + + /* bob the weapon based on movement speed */ + + angle = (128 * leveltime) & FINEMASK; + psp->sx = FRACUNIT + fixed_mul(player->bob, finecosine[angle]); + angle &= FINEANGLES / 2 - 1; + psp->sy = WEAPONTOP + fixed_mul(player->bob, finesine[angle]); +} + +/* a_refire + * The player can re-fire the weapon without lowering it entirely. + */ + +void a_refire(player_t *player, pspdef_t *psp) +{ + /* check for fire (if a weaponchange is pending, let it go through instead) + */ + + if ((player->cmd.buttons & BT_ATTACK) && + player->pendingweapon == wp_nochange && player->health) + { + player->refire++; + p_fire_weapon(player); + } + else + { + player->refire = 0; + p_check_ammo(player); + } +} + +void a_check_reload(player_t *player, pspdef_t *psp) +{ + p_check_ammo(player); +#if 0 + if (player->ammo[am_shell] < 2) + { + p_set_psprite(player, ps_weapon, S_DSNR1); + } +#endif +} + +/* a_lower + * Lowers current weapon, and changes weapon at bottom. + */ + +void a_lower(player_t *player, pspdef_t *psp) +{ + psp->sy += LOWERSPEED; + + /* Is already down. */ + + if (psp->sy < WEAPONBOTTOM) return; + + /* Player is dead. */ + + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + + /* don't bring weapon back up */ + + return; + } + + /* The old weapon has been lowered off the screen, so change the weapon and + * start raising it + */ + + if (!player->health) + { + /* Player is dead, so keep the weapon off screen. */ + + p_set_psprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + p_bringup_weapon(player); +} + +void a_raise(player_t *player, pspdef_t *psp) +{ + statenum_t newstate; + + psp->sy -= RAISESPEED; + + if (psp->sy > WEAPONTOP) return; + + psp->sy = WEAPONTOP; + + /* The weapon has been raised all the way, so change to the ready state. */ + + newstate = weaponinfo[player->readyweapon].readystate; + + p_set_psprite(player, ps_weapon, newstate); +} + +void a_gun_flash(player_t *player, pspdef_t *psp) +{ + p_set_mobj_state(player->mo, S_PLAY_ATK2); + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); +} + +/* WEAPON ATTACKS */ + +void a_punch(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + + damage = (p_random() % 10 + 1) << 1; + + if (player->powers[pw_strength]) damage *= 10; + + angle = player->mo->angle; + angle += p_sub_random() << 18; + slope = p_aim_line_attack(player->mo, angle, MELEERANGE); + p_line_attack(player->mo, angle, MELEERANGE, slope, damage); + + /* turn to face target */ + + if (linetarget) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PUNCH); +#endif + player->mo->angle = r_point_to_angle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + } +} + +void a_saw(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int damage; + int slope; + + damage = 2 * (p_random() % 10 + 1); + angle = player->mo->angle; + angle += p_sub_random() << 18; + + /* use meleerange + 1 se the puff doesn't skip the flash */ + + slope = p_aim_line_attack(player->mo, angle, MELEERANGE + 1); + p_line_attack(player->mo, angle, MELEERANGE + 1, slope, damage); + + if (!linetarget) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWFUL); +#endif + return; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SAWHIT); +#endif + + /* turn to face target */ + + angle = r_point_to_angle2(player->mo->x, player->mo->y, linetarget->x, + linetarget->y); + if (angle - player->mo->angle > ANG180) + { + if ((signed int)(angle - player->mo->angle) < -ANG90 / 20) + player->mo->angle = angle + ANG90 / 21; + else + player->mo->angle -= ANG90 / 20; + } + else + { + if (angle - player->mo->angle > ANG90 / 20) + player->mo->angle = angle - ANG90 / 21; + else + player->mo->angle += ANG90 / 20; + } + + player->mo->flags |= MF_JUSTATTACKED; +} + +void a_fire_missile(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + p_spawn_player_missile(player->mo, MT_ROCKET); +} + +void a_fire_bfg(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, + deh_bfg_cells_per_shot); + p_spawn_player_missile(player->mo, MT_BFG); +} + +void a_fire_plasma(player_t *player, pspdef_t *psp) +{ + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate + + (p_random() & 1)); + + p_spawn_player_missile(player->mo, MT_PLASMA); +} + +void a_fire_pistol(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PISTOL); +#endif + + p_set_mobj_state(player->mo, S_PLAY_ATK2); + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + p_gunshot(player->mo, !player->refire); +} + +void a_fire_shotgun(player_t *player, pspdef_t *psp) +{ + int i; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_SHOTGN); +#endif + p_set_mobj_state(player->mo, S_PLAY_ATK2); + + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + + for (i = 0; i < 7; i++) + p_gunshot(player->mo, false); +} + +void a_fire_shotgun2(player_t *player, pspdef_t *psp) +{ + int i; + angle_t angle; + int damage; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_DSHTGN); +#endif + p_set_mobj_state(player->mo, S_PLAY_ATK2); + + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 2); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate); + + p_bullet_slope(player->mo); + + for (i = 0; i < 20; i++) + { + damage = 5 * (p_random() % 3 + 1); + angle = player->mo->angle; + angle += p_sub_random() << ANGLETOFINESHIFT; + p_line_attack(player->mo, angle, MISSILERANGE, + bulletslope + (p_sub_random() << 5), damage); + } +} + +void a_fire_cgun(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_PISTOL); +#endif + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) return; + + p_set_mobj_state(player->mo, S_PLAY_ATK2); + decrease_ammo(player, weaponinfo[player->readyweapon].ammo, 1); + + p_set_psprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate + psp->state - + &states[S_CHAIN1]); + + p_bullet_slope(player->mo); + + p_gunshot(player->mo, !player->refire); +} + +/* ? */ + +void a_light0(player_t *player, pspdef_t *psp) +{ + player->extralight = 0; +} + +void a_light1(player_t *player, pspdef_t *psp) +{ + player->extralight = 1; +} + +void a_light2(player_t *player, pspdef_t *psp) +{ + player->extralight = 2; +} + +/* a_bfg_spray + * Spawn a BFG explosion on every monster in view + */ + +void a_bfg_spray(mobj_t *mo) +{ + int i; + int j; + int damage; + angle_t an; + + /* offset angles from its attack angle */ + + for (i = 0; i < 40; i++) + { + an = mo->angle - ANG90 / 2 + ANG90 / 40 * i; + + /* mo->target is the originator (player) of the missile */ + + p_aim_line_attack(mo->target, an, 16 * 64 * FRACUNIT); + + if (!linetarget) continue; + + p_spawn_mobj(linetarget->x, linetarget->y, + linetarget->z + (linetarget->height >> 2), MT_EXTRABFG); + + damage = 0; + for (j = 0; j < 15; j++) + damage += (p_random() & 7) + 1; + + p_damage_mobj(linetarget, mo->target, mo->target, damage); + } +} + +/* a_bfg_sound */ + +void a_bfg_sound(player_t *player, pspdef_t *psp) +{ +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(player->mo, SFX_BFG); +#endif +} + +/* p_setup_psprites + * Called at start of level for each player. + */ + +void p_setup_psprites(player_t *player) +{ + int i; + + /* remove all psprites */ + + for (i = 0; i < NUMPSPRITES; i++) + player->psprites[i].state = NULL; + + /* spawn the gun */ + + player->pendingweapon = player->readyweapon; + p_bringup_weapon(player); +} + +/* p_move_psprites + * Called every tic by player thinking routine. + */ + +void p_move_psprites(player_t *player) +{ + int i; + pspdef_t *psp; + + psp = &player->psprites[0]; + for (i = 0; i < NUMPSPRITES; i++, psp++) + { + /* a null state means not active */ + + if (psp->state) + { + /* drop tic count and possibly change state */ + + /* a -1 tic count never changes */ + + if (psp->tics != -1) + { + psp->tics--; + if (!psp->tics) + { + p_set_psprite(player, i, psp->state->nextstate); + } + } + } + } + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/games/NXDoom/src/doom/p_pspr.h b/games/NXDoom/src/doom/p_pspr.h new file mode 100644 index 000000000..83ab57f6a --- /dev/null +++ b/games/NXDoom/src/doom/p_pspr.h @@ -0,0 +1,88 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_pspr.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Sprite animation. + * + ****************************************************************************/ + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Basic data types. + * Needs fixed point, and BAM angles. + */ + +#include "m_fixed.h" +#include "tables.h" + +/* Needs to include the precompiled sprite animation tables. + * + * Header generated by multigen utility. + * + * This includes all the data for thing animation, + * i.e. the Thing Attributes table and the Frame Sequence table. + */ + +#include "info.h" + +/* Frame flags: + * handles maximum brightness (torches, muzzle flare, light sources) + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FF_FULLBRIGHT 0x8000 /* flag in thing->frame */ +#define FF_FRAMEMASK 0x7fff + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Overlay psprites are scaled shapes drawn directly on the view screen, + * coordinates are given for a 320*200 view screen. + */ + +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + +typedef struct +{ + state_t *state; /* a NULL state means not active */ + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern fixed_t bulletslope; + +#endif /* __P_PSPR__ */ diff --git a/games/NXDoom/src/doom/p_saveg.c b/games/NXDoom/src/doom/p_saveg.c new file mode 100644 index 000000000..5c5439cb9 --- /dev/null +++ b/games/NXDoom/src/doom/p_saveg.c @@ -0,0 +1,2129 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_saveg.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "dstrings.h" +#include "i_system.h" +#include "p_local.h" +#include "p_saveg.h" +#include "z_zone.h" + +/* State. */ + +#include "doomstat.h" +#include "g_game.h" +#include "m_misc.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Enum values are 32-bit integers. */ + +#define saveg_read_enum saveg_read32 +#define saveg_write_enum saveg_write32 + +/* think_t + * This is just an actionf_t. + */ + +#define saveg_read_think_t saveg_read_actionf_t +#define saveg_write_think_t saveg_write_actionf_t + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum +{ + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_endspecials +} specials_e; + +/* Thinkers */ + +typedef enum +{ + tc_end, + tc_mobj +} thinkerclass_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +FILE *save_stream; +int savegamelength; +boolean savegame_error; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Endian-safe integer read/write functions */ + +static byte saveg_read8(void) +{ + byte result = -1; + + if (fread(&result, 1, 1, save_stream) < 1) + { + if (!savegame_error) + { + fprintf(stderr, "saveg_read8: Unexpected end of file while " + "reading save game\n"); + + savegame_error = true; + } + } + + return result; +} + +static void saveg_write8(byte value) +{ + if (fwrite(&value, 1, 1, save_stream) < 1) + { + if (!savegame_error) + { + fprintf(stderr, "saveg_write8: Error while writing save game\n"); + + savegame_error = true; + } + } +} + +static short saveg_read16(void) +{ + int result; + + result = saveg_read8(); + result |= saveg_read8() << 8; + + return result; +} + +static void saveg_write16(short value) +{ + saveg_write8(value & 0xff); + saveg_write8((value >> 8) & 0xff); +} + +static int saveg_read32(void) +{ + int result; + + result = saveg_read8(); + result |= saveg_read8() << 8; + result |= saveg_read8() << 16; + result |= saveg_read8() << 24; + + return result; +} + +static void saveg_write32(int value) +{ + saveg_write8(value & 0xff); + saveg_write8((value >> 8) & 0xff); + saveg_write8((value >> 16) & 0xff); + saveg_write8((value >> 24) & 0xff); +} + +/* Pad to 4-byte boundaries */ + +static void saveg_read_pad(void) +{ + unsigned long pos; + int padding; + int i; + + pos = ftell(save_stream); + + padding = (4 - (pos & 3)) & 3; + + for (i = 0; i < padding; ++i) + { + saveg_read8(); + } +} + +static void saveg_write_pad(void) +{ + unsigned long pos; + int padding; + int i; + + pos = ftell(save_stream); + + padding = (4 - (pos & 3)) & 3; + + for (i = 0; i < padding; ++i) + { + saveg_write8(0); + } +} + +/* Pointers */ + +static void *saveg_readp(void) +{ + return (void *)(intptr_t)saveg_read32(); +} + +static void saveg_writep(const void *p) +{ + saveg_write32((intptr_t)p); +} + +/* Structure read/write functions */ + +/* mapthing_t */ + +static void saveg_read_mapthing_t(mapthing_t *str) +{ + /* short x; */ + + str->x = saveg_read16(); + + /* short y; */ + + str->y = saveg_read16(); + + /* short angle; */ + + str->angle = saveg_read16(); + + /* short type; */ + + str->type = saveg_read16(); + + /* short options; */ + + str->options = saveg_read16(); +} + +static void saveg_write_mapthing_t(mapthing_t *str) +{ + /* short x; */ + + saveg_write16(str->x); + + /* short y; */ + + saveg_write16(str->y); + + /* short angle; */ + + saveg_write16(str->angle); + + /* short type; */ + + saveg_write16(str->type); + + /* short options; */ + + saveg_write16(str->options); +} + +/* actionf_t */ + +static void saveg_read_actionf_t(actionf_t *str) +{ + /* actionf_p1 acp1; */ + + str->acp1 = saveg_readp(); +} + +static void saveg_write_actionf_t(actionf_t *str) +{ + /* actionf_p1 acp1; */ + + saveg_writep(str->acp1); +} + +/* thinker_t */ + +static void saveg_read_thinker_t(thinker_t *str) +{ + /* struct thinker_s* prev; */ + + str->prev = saveg_readp(); + + /* struct thinker_s* next; */ + + str->next = saveg_readp(); + + /* think_t function; */ + + saveg_read_think_t(&str->function); +} + +static void saveg_write_thinker_t(thinker_t *str) +{ + /* struct thinker_s* prev; */ + + saveg_writep(str->prev); + + /* struct thinker_s* next; */ + + saveg_writep(str->next); + + /* think_t function; */ + + saveg_write_think_t(&str->function); +} + +/* mobj_t */ + +static void saveg_read_mobj_t(mobj_t *str) +{ + int pl; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* fixed_t x; */ + + str->x = saveg_read32(); + + /* fixed_t y; */ + + str->y = saveg_read32(); + + /* fixed_t z; */ + + str->z = saveg_read32(); + + /* struct mobj_s* snext; */ + + str->snext = saveg_readp(); + + /* struct mobj_s* sprev; */ + + str->sprev = saveg_readp(); + + /* angle_t angle; */ + + str->angle = saveg_read32(); + + /* spritenum_t sprite; */ + + str->sprite = saveg_read_enum(); + + /* int frame; */ + + str->frame = saveg_read32(); + + /* struct mobj_s* bnext; */ + + str->bnext = saveg_readp(); + + /* struct mobj_s* bprev; */ + + str->bprev = saveg_readp(); + + /* struct subsector_s* subsector; */ + + str->subsector = saveg_readp(); + + /* fixed_t floorz; */ + + str->floorz = saveg_read32(); + + /* fixed_t ceilingz; */ + + str->ceilingz = saveg_read32(); + + /* fixed_t radius; */ + + str->radius = saveg_read32(); + + /* fixed_t height; */ + + str->height = saveg_read32(); + + /* fixed_t momx; */ + + str->momx = saveg_read32(); + + /* fixed_t momy; */ + + str->momy = saveg_read32(); + + /* fixed_t momz; */ + + str->momz = saveg_read32(); + + /* int validcount; */ + + str->validcount = saveg_read32(); + + /* mobjtype_t type; */ + + str->type = saveg_read_enum(); + + /* mobjinfo_t* info; */ + + str->info = saveg_readp(); + + /* int tics; */ + + str->tics = saveg_read32(); + + /* state_t* state; */ + + str->state = &states[saveg_read32()]; + + /* int flags; */ + + str->flags = saveg_read32(); + + /* int health; */ + + str->health = saveg_read32(); + + /* int movedir; */ + + str->movedir = saveg_read32(); + + /* int movecount; */ + + str->movecount = saveg_read32(); + + /* struct mobj_s* target; */ + + str->target = saveg_readp(); + + /* int reactiontime; */ + + str->reactiontime = saveg_read32(); + + /* int threshold; */ + + str->threshold = saveg_read32(); + + /* struct player_s* player; */ + + pl = saveg_read32(); + + if (pl > 0) + { + str->player = &players[pl - 1]; + str->player->mo = str; + } + else + { + str->player = NULL; + } + + /* int lastlook; */ + + str->lastlook = saveg_read32(); + + /* mapthing_t spawnpoint; */ + + saveg_read_mapthing_t(&str->spawnpoint); + + /* struct mobj_s* tracer; */ + + str->tracer = saveg_readp(); +} + +static void saveg_write_mobj_t(mobj_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* fixed_t x; */ + + saveg_write32(str->x); + + /* fixed_t y; */ + + saveg_write32(str->y); + + /* fixed_t z; */ + + saveg_write32(str->z); + + /* struct mobj_s* snext; */ + + saveg_writep(str->snext); + + /* struct mobj_s* sprev; */ + + saveg_writep(str->sprev); + + /* angle_t angle; */ + + saveg_write32(str->angle); + + /* spritenum_t sprite; */ + + saveg_write_enum(str->sprite); + + /* int frame; */ + + saveg_write32(str->frame); + + /* struct mobj_s* bnext; */ + + saveg_writep(str->bnext); + + /* struct mobj_s* bprev; */ + + saveg_writep(str->bprev); + + /* struct subsector_s* subsector; */ + + saveg_writep(str->subsector); + + /* fixed_t floorz; */ + + saveg_write32(str->floorz); + + /* fixed_t ceilingz; */ + + saveg_write32(str->ceilingz); + + /* fixed_t radius; */ + + saveg_write32(str->radius); + + /* fixed_t height; */ + + saveg_write32(str->height); + + /* fixed_t momx; */ + + saveg_write32(str->momx); + + /* fixed_t momy; */ + + saveg_write32(str->momy); + + /* fixed_t momz; */ + + saveg_write32(str->momz); + + /* int validcount; */ + + saveg_write32(str->validcount); + + /* mobjtype_t type; */ + + saveg_write_enum(str->type); + + /* mobjinfo_t* info; */ + + saveg_writep(str->info); + + /* int tics; */ + + saveg_write32(str->tics); + + /* state_t* state; */ + + saveg_write32(str->state - states); + + /* int flags; */ + + saveg_write32(str->flags); + + /* int health; */ + + saveg_write32(str->health); + + /* int movedir; */ + + saveg_write32(str->movedir); + + /* int movecount; */ + + saveg_write32(str->movecount); + + /* struct mobj_s* target; */ + + saveg_writep(str->target); + + /* int reactiontime; */ + + saveg_write32(str->reactiontime); + + /* int threshold; */ + + saveg_write32(str->threshold); + + /* struct player_s* player; */ + + if (str->player) + { + saveg_write32(str->player - players + 1); + } + else + { + saveg_write32(0); + } + + /* int lastlook; */ + + saveg_write32(str->lastlook); + + /* mapthing_t spawnpoint; */ + + saveg_write_mapthing_t(&str->spawnpoint); + + /* struct mobj_s* tracer; */ + + saveg_writep(str->tracer); +} + +/* ticcmd_t */ + +static void saveg_read_ticcmd_t(ticcmd_t *str) +{ + /* signed char forwardmove; */ + + str->forwardmove = saveg_read8(); + + /* signed char sidemove; */ + + str->sidemove = saveg_read8(); + + /* short angleturn; */ + + str->angleturn = saveg_read16(); + + /* short consistency; */ + + str->consistency = saveg_read16(); + + /* byte chatchar; */ + + str->chatchar = saveg_read8(); + + /* byte buttons; */ + + str->buttons = saveg_read8(); +} + +static void saveg_write_ticcmd_t(ticcmd_t *str) +{ + /* signed char forwardmove; */ + + saveg_write8(str->forwardmove); + + /* signed char sidemove; */ + + saveg_write8(str->sidemove); + + /* short angleturn; */ + + saveg_write16(str->angleturn); + + /* short consistency; */ + + saveg_write16(str->consistency); + + /* byte chatchar; */ + + saveg_write8(str->chatchar); + + /* byte buttons; */ + + saveg_write8(str->buttons); +} + +/* pspdef_t */ + +static void saveg_read_pspdef_t(pspdef_t *str) +{ + int state; + + /* state_t* state; */ + + state = saveg_read32(); + + if (state > 0) + { + str->state = &states[state]; + } + else + { + str->state = NULL; + } + + /* int tics; */ + + str->tics = saveg_read32(); + + /* fixed_t sx; */ + + str->sx = saveg_read32(); + + /* fixed_t sy; */ + + str->sy = saveg_read32(); +} + +static void saveg_write_pspdef_t(pspdef_t *str) +{ + /* state_t* state; */ + + if (str->state) + { + saveg_write32(str->state - states); + } + else + { + saveg_write32(0); + } + + /* int tics; */ + + saveg_write32(str->tics); + + /* fixed_t sx; */ + + saveg_write32(str->sx); + + /* fixed_t sy; */ + + saveg_write32(str->sy); +} + +/* player_t */ + +static void saveg_read_player_t(player_t *str) +{ + int i; + + /* mobj_t* mo; */ + + str->mo = saveg_readp(); + + /* playerstate_t playerstate; */ + + str->playerstate = saveg_read_enum(); + + /* ticcmd_t cmd; */ + + saveg_read_ticcmd_t(&str->cmd); + + /* fixed_t viewz; */ + + str->viewz = saveg_read32(); + + /* fixed_t viewheight; */ + + str->viewheight = saveg_read32(); + + /* fixed_t deltaviewheight; */ + + str->deltaviewheight = saveg_read32(); + + /* fixed_t bob; */ + + str->bob = saveg_read32(); + + /* int health; */ + + str->health = saveg_read32(); + + /* int armorpoints; */ + + str->armorpoints = saveg_read32(); + + /* int armortype; */ + + str->armortype = saveg_read32(); + + /* int powers[NUMPOWERS]; */ + + for (i = 0; i < NUMPOWERS; ++i) + { + str->powers[i] = saveg_read32(); + } + + /* boolean cards[NUMCARDS]; */ + + for (i = 0; i < NUMCARDS; ++i) + { + str->cards[i] = saveg_read32(); + } + + /* boolean backpack; */ + + str->backpack = saveg_read32(); + + /* int frags[MAXPLAYERS]; */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + str->frags[i] = saveg_read32(); + } + + /* weapontype_t readyweapon; */ + + str->readyweapon = saveg_read_enum(); + + /* weapontype_t pendingweapon; */ + + str->pendingweapon = saveg_read_enum(); + + /* boolean weaponowned[NUMWEAPONS]; */ + + for (i = 0; i < NUMWEAPONS; ++i) + { + str->weaponowned[i] = saveg_read32(); + } + + /* int ammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + str->ammo[i] = saveg_read32(); + } + + /* int maxammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + str->maxammo[i] = saveg_read32(); + } + + /* int attackdown; */ + + str->attackdown = saveg_read32(); + + /* int usedown; */ + + str->usedown = saveg_read32(); + + /* int cheats; */ + + str->cheats = saveg_read32(); + + /* int refire; */ + + str->refire = saveg_read32(); + + /* int killcount; */ + + str->killcount = saveg_read32(); + + /* int itemcount; */ + + str->itemcount = saveg_read32(); + + /* int secretcount; */ + + str->secretcount = saveg_read32(); + + /* char* message; */ + + str->message = saveg_readp(); + + /* int damagecount; */ + + str->damagecount = saveg_read32(); + + /* int bonuscount; */ + + str->bonuscount = saveg_read32(); + + /* mobj_t* attacker; */ + + str->attacker = saveg_readp(); + + /* int extralight; */ + + str->extralight = saveg_read32(); + + /* int fixedcolormap; */ + + str->fixedcolormap = saveg_read32(); + + /* int colormap; */ + + str->colormap = saveg_read32(); + + /* pspdef_t psprites[NUMPSPRITES]; */ + + for (i = 0; i < NUMPSPRITES; ++i) + { + saveg_read_pspdef_t(&str->psprites[i]); + } + + /* boolean didsecret; */ + + str->didsecret = saveg_read32(); +} + +static void saveg_write_player_t(player_t *str) +{ + int i; + + /* mobj_t* mo; */ + + saveg_writep(str->mo); + + /* playerstate_t playerstate; */ + + saveg_write_enum(str->playerstate); + + /* ticcmd_t cmd; */ + + saveg_write_ticcmd_t(&str->cmd); + + /* fixed_t viewz; */ + + saveg_write32(str->viewz); + + /* fixed_t viewheight; */ + + saveg_write32(str->viewheight); + + /* fixed_t deltaviewheight; */ + + saveg_write32(str->deltaviewheight); + + /* fixed_t bob; */ + + saveg_write32(str->bob); + + /* int health; */ + + saveg_write32(str->health); + + /* int armorpoints; */ + + saveg_write32(str->armorpoints); + + /* int armortype; */ + + saveg_write32(str->armortype); + + /* int powers[NUMPOWERS]; */ + + for (i = 0; i < NUMPOWERS; ++i) + { + saveg_write32(str->powers[i]); + } + + /* boolean cards[NUMCARDS]; */ + + for (i = 0; i < NUMCARDS; ++i) + { + saveg_write32(str->cards[i]); + } + + /* boolean backpack; */ + + saveg_write32(str->backpack); + + /* int frags[MAXPLAYERS]; */ + + for (i = 0; i < MAXPLAYERS; ++i) + { + saveg_write32(str->frags[i]); + } + + /* weapontype_t readyweapon; */ + + saveg_write_enum(str->readyweapon); + + /* weapontype_t pendingweapon; */ + + saveg_write_enum(str->pendingweapon); + + /* boolean weaponowned[NUMWEAPONS]; */ + + for (i = 0; i < NUMWEAPONS; ++i) + { + saveg_write32(str->weaponowned[i]); + } + + /* int ammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + saveg_write32(str->ammo[i]); + } + + /* int maxammo[NUMAMMO]; */ + + for (i = 0; i < NUMAMMO; ++i) + { + saveg_write32(str->maxammo[i]); + } + + /* int attackdown; */ + + saveg_write32(str->attackdown); + + /* int usedown; */ + + saveg_write32(str->usedown); + + /* int cheats; */ + + saveg_write32(str->cheats); + + /* int refire; */ + + saveg_write32(str->refire); + + /* int killcount; */ + + saveg_write32(str->killcount); + + /* int itemcount; */ + + saveg_write32(str->itemcount); + + /* int secretcount; */ + + saveg_write32(str->secretcount); + + /* char* message; */ + + saveg_writep(str->message); + + /* int damagecount; */ + + saveg_write32(str->damagecount); + + /* int bonuscount; */ + + saveg_write32(str->bonuscount); + + /* mobj_t* attacker; */ + + saveg_writep(str->attacker); + + /* int extralight; */ + + saveg_write32(str->extralight); + + /* int fixedcolormap; */ + + saveg_write32(str->fixedcolormap); + + /* int colormap; */ + + saveg_write32(str->colormap); + + /* pspdef_t psprites[NUMPSPRITES]; */ + + for (i = 0; i < NUMPSPRITES; ++i) + { + saveg_write_pspdef_t(&str->psprites[i]); + } + + /* boolean didsecret; */ + + saveg_write32(str->didsecret); +} + +/* ceiling_t */ + +static void saveg_read_ceiling_t(ceiling_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* ceiling_e type; */ + + str->type = saveg_read_enum(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t bottomheight; */ + + str->bottomheight = saveg_read32(); + + /* fixed_t topheight; */ + + str->topheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int tag; */ + + str->tag = saveg_read32(); + + /* int olddirection; */ + + str->olddirection = saveg_read32(); +} + +static void saveg_write_ceiling_t(ceiling_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* ceiling_e type; */ + + saveg_write_enum(str->type); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t bottomheight; */ + + saveg_write32(str->bottomheight); + + /* fixed_t topheight; */ + + saveg_write32(str->topheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int tag; */ + + saveg_write32(str->tag); + + /* int olddirection; */ + + saveg_write32(str->olddirection); +} + +/* vldoor_t */ + +static void saveg_read_vldoor_t(vldoor_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* vldoor_e type; */ + + str->type = saveg_read_enum(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t topheight; */ + + str->topheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int topwait; */ + + str->topwait = saveg_read32(); + + /* int topcountdown; */ + + str->topcountdown = saveg_read32(); +} + +static void saveg_write_vldoor_t(vldoor_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* vldoor_e type; */ + + saveg_write_enum(str->type); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t topheight; */ + + saveg_write32(str->topheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int topwait; */ + + saveg_write32(str->topwait); + + /* int topcountdown; */ + + saveg_write32(str->topcountdown); +} + +/* floormove_t */ + +static void saveg_read_floormove_t(floormove_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* floor_e type; */ + + str->type = saveg_read_enum(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int direction; */ + + str->direction = saveg_read32(); + + /* int newspecial; */ + + str->newspecial = saveg_read32(); + + /* short texture; */ + + str->texture = saveg_read16(); + + /* fixed_t floordestheight; */ + + str->floordestheight = saveg_read32(); + + /* fixed_t speed; */ + + str->speed = saveg_read32(); +} + +static void saveg_write_floormove_t(floormove_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* floor_e type; */ + + saveg_write_enum(str->type); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int direction; */ + + saveg_write32(str->direction); + + /* int newspecial; */ + + saveg_write32(str->newspecial); + + /* short texture; */ + + saveg_write16(str->texture); + + /* fixed_t floordestheight; */ + + saveg_write32(str->floordestheight); + + /* fixed_t speed; */ + + saveg_write32(str->speed); +} + +/* plat_t */ + +static void saveg_read_plat_t(plat_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* fixed_t speed; */ + + str->speed = saveg_read32(); + + /* fixed_t low; */ + + str->low = saveg_read32(); + + /* fixed_t high; */ + + str->high = saveg_read32(); + + /* int wait; */ + + str->wait = saveg_read32(); + + /* int count; */ + + str->count = saveg_read32(); + + /* plat_e status; */ + + str->status = saveg_read_enum(); + + /* plat_e oldstatus; */ + + str->oldstatus = saveg_read_enum(); + + /* boolean crush; */ + + str->crush = saveg_read32(); + + /* int tag; */ + + str->tag = saveg_read32(); + + /* plattype_e type; */ + + str->type = saveg_read_enum(); +} + +static void saveg_write_plat_t(plat_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* fixed_t speed; */ + + saveg_write32(str->speed); + + /* fixed_t low; */ + + saveg_write32(str->low); + + /* fixed_t high; */ + + saveg_write32(str->high); + + /* int wait; */ + + saveg_write32(str->wait); + + /* int count; */ + + saveg_write32(str->count); + + /* plat_e status; */ + + saveg_write_enum(str->status); + + /* plat_e oldstatus; */ + + saveg_write_enum(str->oldstatus); + + /* boolean crush; */ + + saveg_write32(str->crush); + + /* int tag; */ + + saveg_write32(str->tag); + + /* plattype_e type; */ + + saveg_write_enum(str->type); +} + +/* lightflash_t */ + +static void saveg_read_lightflash_t(lightflash_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int count; */ + + str->count = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxtime; */ + + str->maxtime = saveg_read32(); + + /* int mintime; */ + + str->mintime = saveg_read32(); +} + +static void saveg_write_lightflash_t(lightflash_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int count; */ + + saveg_write32(str->count); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxtime; */ + + saveg_write32(str->maxtime); + + /* int mintime; */ + + saveg_write32(str->mintime); +} + +/* strobe_t */ + +static void saveg_read_strobe_t(strobe_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int count; */ + + str->count = saveg_read32(); + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int darktime; */ + + str->darktime = saveg_read32(); + + /* int brighttime; */ + + str->brighttime = saveg_read32(); +} + +static void saveg_write_strobe_t(strobe_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int count; */ + + saveg_write32(str->count); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int darktime; */ + + saveg_write32(str->darktime); + + /* int brighttime; */ + + saveg_write32(str->brighttime); +} + +/* glow_t */ + +static void saveg_read_glow_t(glow_t *str) +{ + int sector; + + /* thinker_t thinker; */ + + saveg_read_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + sector = saveg_read32(); + str->sector = §ors[sector]; + + /* int minlight; */ + + str->minlight = saveg_read32(); + + /* int maxlight; */ + + str->maxlight = saveg_read32(); + + /* int direction; */ + + str->direction = saveg_read32(); +} + +static void saveg_write_glow_t(glow_t *str) +{ + /* thinker_t thinker; */ + + saveg_write_thinker_t(&str->thinker); + + /* sector_t* sector; */ + + saveg_write32(str->sector - sectors); + + /* int minlight; */ + + saveg_write32(str->minlight); + + /* int maxlight; */ + + saveg_write32(str->maxlight); + + /* int direction; */ + + saveg_write32(str->direction); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Write the header for a savegame */ + +void p_write_save_game_header(char *description) +{ + char name[VERSIONSIZE]; + int i; + + for (i = 0; description[i] != '\0'; ++i) + saveg_write8(description[i]); + for (; i < SAVESTRINGSIZE; ++i) + saveg_write8(0); + + memset(name, 0, sizeof(name)); + snprintf(name, sizeof(name), "version %i", g_vanilla_version_code()); + + for (i = 0; i < VERSIONSIZE; ++i) + saveg_write8(name[i]); + + saveg_write8(gameskill); + saveg_write8(gameepisode); + saveg_write8(gamemap); + + for (i = 0; i < MAXPLAYERS; i++) + saveg_write8(playeringame[i]); + + saveg_write8((leveltime >> 16) & 0xff); + saveg_write8((leveltime >> 8) & 0xff); + saveg_write8(leveltime & 0xff); +} + +/* Get the filename of a temporary file to write the savegame to. After + * the file has been successfully saved, it will be renamed to the + * real file. + */ + +char *p_temp_save_game_file(void) +{ + static char *filename = NULL; + + if (filename == NULL) + { + filename = m_string_join(savegamedir, "temp.dsg", NULL); + } + + return filename; +} + +/* Get the filename of the save game file to use for the specified slot. */ + +char *p_save_game_file(int slot) +{ + static char *filename = NULL; + static size_t filename_size = 0; + char basename[32]; + + if (filename == NULL) + { + filename_size = strlen(savegamedir) + 32; + filename = malloc(filename_size); + } + + snprintf(basename, 32, SAVEGAMENAME "%d.dsg", slot); + snprintf(filename, filename_size, "%s%s", savegamedir, basename); + + return filename; +} + +/* Read the header for a savegame */ + +boolean p_read_save_game_header(void) +{ + int i; + byte a, b, c; + char vcheck[VERSIONSIZE]; + char read_vcheck[VERSIONSIZE]; + + /* skip the description field */ + + for (i = 0; i < SAVESTRINGSIZE; ++i) + saveg_read8(); + + for (i = 0; i < VERSIONSIZE; ++i) + read_vcheck[i] = saveg_read8(); + + memset(vcheck, 0, sizeof(vcheck)); + snprintf(vcheck, sizeof(vcheck), "version %i", g_vanilla_version_code()); + if (strcmp(read_vcheck, vcheck) != 0) return false; /* bad version */ + + gameskill = saveg_read8(); + gameepisode = saveg_read8(); + gamemap = saveg_read8(); + + for (i = 0; i < MAXPLAYERS; i++) + playeringame[i] = saveg_read8(); + + /* get the times */ + + a = saveg_read8(); + b = saveg_read8(); + c = saveg_read8(); + leveltime = (a << 16) + (b << 8) + c; + + return true; +} + +/* Read the end of file marker. Returns true if read successfully. */ + +boolean p_read_save_game_eof(void) +{ + int value; + + value = saveg_read8(); + + return value == SAVEGAME_EOF; +} + +/* Write the end of file marker */ + +void p_write_save_game_eof(void) +{ + saveg_write8(SAVEGAME_EOF); +} + +void p_archive_players(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + saveg_write_pad(); + + saveg_write_player_t(&players[i]); + } +} + +void p_unarchive_players(void) +{ + int i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + saveg_read_pad(); + + saveg_read_player_t(&players[i]); + + /* will be set when unarc thinker */ + + players[i].mo = NULL; + players[i].message = NULL; + players[i].attacker = NULL; + } +} + +void p_archive_world(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + /* do sectors */ + + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + saveg_write16(sec->floorheight >> FRACBITS); + saveg_write16(sec->ceilingheight >> FRACBITS); + saveg_write16(sec->floorpic); + saveg_write16(sec->ceilingpic); + saveg_write16(sec->lightlevel); + saveg_write16(sec->special); /* needed? */ + + saveg_write16(sec->tag); /* needed? */ + } + + /* do lines */ + + for (i = 0, li = lines; i < numlines; i++, li++) + { + saveg_write16(li->flags); + saveg_write16(li->special); + saveg_write16(li->tag); + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) continue; + + si = &sides[li->sidenum[j]]; + + saveg_write16(si->textureoffset >> FRACBITS); + saveg_write16(si->rowoffset >> FRACBITS); + saveg_write16(si->toptexture); + saveg_write16(si->bottomtexture); + saveg_write16(si->midtexture); + } + } +} + +void p_unarchive_world(void) +{ + int i; + int j; + sector_t *sec; + line_t *li; + side_t *si; + + /* do sectors */ + + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + sec->floorheight = saveg_read16() << FRACBITS; + sec->ceilingheight = saveg_read16() << FRACBITS; + sec->floorpic = saveg_read16(); + sec->ceilingpic = saveg_read16(); + sec->lightlevel = saveg_read16(); + sec->special = saveg_read16(); /* needed? */ + + sec->tag = saveg_read16(); /* needed? */ + + sec->specialdata = 0; + sec->soundtarget = 0; + } + + /* do lines */ + + for (i = 0, li = lines; i < numlines; i++, li++) + { + li->flags = saveg_read16(); + li->special = saveg_read16(); + li->tag = saveg_read16(); + for (j = 0; j < 2; j++) + { + if (li->sidenum[j] == -1) continue; + si = &sides[li->sidenum[j]]; + si->textureoffset = saveg_read16() << FRACBITS; + si->rowoffset = saveg_read16() << FRACBITS; + si->toptexture = saveg_read16(); + si->bottomtexture = saveg_read16(); + si->midtexture = saveg_read16(); + } + } +} + +void p_archive_thinkers(void) +{ + thinker_t *th; + + /* save off the current thinkers */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)p_mobj_thinker) + { + saveg_write8(tc_mobj); + saveg_write_pad(); + saveg_write_mobj_t((mobj_t *)th); + + continue; + } + } + + /* add a terminating marker */ + + saveg_write8(tc_end); +} + +void p_unarchive_thinkers(void) +{ + byte tclass; + thinker_t *currentthinker; + thinker_t *next; + mobj_t *mobj; + + /* remove all the current thinkers */ + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + next = currentthinker->next; + + if (currentthinker->function.acp1 == (actionf_p1)p_mobj_thinker) + p_remove_mobj((mobj_t *)currentthinker); + else + z_free(currentthinker); + + currentthinker = next; + } + + p_init_thinkers(); + + /* read in saved thinkers */ + + while (1) + { + tclass = saveg_read8(); + switch (tclass) + { + case tc_end: + return; /* end of list */ + + case tc_mobj: + saveg_read_pad(); + mobj = z_malloc(sizeof(*mobj), PU_LEVEL, NULL); + saveg_read_mobj_t(mobj); + + mobj->target = NULL; + mobj->tracer = NULL; + p_set_thing_position(mobj); + mobj->info = &mobjinfo[mobj->type]; + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + mobj->thinker.function.acp1 = (actionf_p1)p_mobj_thinker; + p_add_thinker(&mobj->thinker); + break; + + default: + i_error("Unknown tclass %i in savegame", tclass); + } + } +} + +/* Things to handle: + * + * t_move_ceiling, (ceiling_t: sector_t * swizzle), - active list + * t_vertical_door, (vldoor_t: sector_t * swizzle), + * t_move_floor, (floormove_t: sector_t * swizzle), + * t_light_flash, (lightflash_t: sector_t * swizzle), + * t_strobe_flash, (strobe_t: sector_t *), + * t_glow, (glow_t: sector_t *), + * t_plat_raise, (plat_t: sector_t *), - active list + */ + +void p_archive_specials(void) +{ + thinker_t *th; + int i; + + /* save off the current thinkers */ + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acv == (actionf_v)NULL) + { + for (i = 0; i < MAXCEILINGS; i++) + { + if (activeceilings[i] == (ceiling_t *)th) break; + } + + if (i < MAXCEILINGS) + { + saveg_write8(tc_ceiling); + saveg_write_pad(); + saveg_write_ceiling_t((ceiling_t *)th); + } + + continue; + } + + if (th->function.acp1 == (actionf_p1)t_move_ceiling) + { + saveg_write8(tc_ceiling); + saveg_write_pad(); + saveg_write_ceiling_t((ceiling_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_vertical_door) + { + saveg_write8(tc_door); + saveg_write_pad(); + saveg_write_vldoor_t((vldoor_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_move_floor) + { + saveg_write8(tc_floor); + saveg_write_pad(); + saveg_write_floormove_t((floormove_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_plat_raise) + { + saveg_write8(tc_plat); + saveg_write_pad(); + saveg_write_plat_t((plat_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_light_flash) + { + saveg_write8(tc_flash); + saveg_write_pad(); + saveg_write_lightflash_t((lightflash_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_strobe_flash) + { + saveg_write8(tc_strobe); + saveg_write_pad(); + saveg_write_strobe_t((strobe_t *)th); + continue; + } + + if (th->function.acp1 == (actionf_p1)t_glow) + { + saveg_write8(tc_glow); + saveg_write_pad(); + saveg_write_glow_t((glow_t *)th); + continue; + } + } + + /* add a terminating marker */ + + saveg_write8(tc_endspecials); +} + +void p_unarchive_specials(void) +{ + byte tclass; + ceiling_t *ceiling; + vldoor_t *door; + floormove_t *floor; + plat_t *plat; + lightflash_t *flash; + strobe_t *strobe; + glow_t *glow; + + /* read in saved thinkers */ + + while (1) + { + tclass = saveg_read8(); + + switch (tclass) + { + case tc_endspecials: + return; /* end of list */ + + case tc_ceiling: + saveg_read_pad(); + ceiling = z_malloc(sizeof(*ceiling), PU_LEVEL, NULL); + saveg_read_ceiling_t(ceiling); + ceiling->sector->specialdata = ceiling; + + if (ceiling->thinker.function.acp1) + ceiling->thinker.function.acp1 = (actionf_p1)t_move_ceiling; + + p_add_thinker(&ceiling->thinker); + p_add_active_ceiling(ceiling); + break; + + case tc_door: + saveg_read_pad(); + door = z_malloc(sizeof(*door), PU_LEVEL, NULL); + saveg_read_vldoor_t(door); + door->sector->specialdata = door; + door->thinker.function.acp1 = (actionf_p1)t_vertical_door; + p_add_thinker(&door->thinker); + break; + + case tc_floor: + saveg_read_pad(); + floor = z_malloc(sizeof(*floor), PU_LEVEL, NULL); + saveg_read_floormove_t(floor); + floor->sector->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + p_add_thinker(&floor->thinker); + break; + + case tc_plat: + saveg_read_pad(); + plat = z_malloc(sizeof(*plat), PU_LEVEL, NULL); + saveg_read_plat_t(plat); + plat->sector->specialdata = plat; + + if (plat->thinker.function.acp1) + plat->thinker.function.acp1 = (actionf_p1)t_plat_raise; + + p_add_thinker(&plat->thinker); + p_add_active_plat(plat); + break; + + case tc_flash: + saveg_read_pad(); + flash = z_malloc(sizeof(*flash), PU_LEVEL, NULL); + saveg_read_lightflash_t(flash); + flash->thinker.function.acp1 = (actionf_p1)t_light_flash; + p_add_thinker(&flash->thinker); + break; + + case tc_strobe: + saveg_read_pad(); + strobe = z_malloc(sizeof(*strobe), PU_LEVEL, NULL); + saveg_read_strobe_t(strobe); + strobe->thinker.function.acp1 = (actionf_p1)t_strobe_flash; + p_add_thinker(&strobe->thinker); + break; + + case tc_glow: + saveg_read_pad(); + glow = z_malloc(sizeof(*glow), PU_LEVEL, NULL); + saveg_read_glow_t(glow); + glow->thinker.function.acp1 = (actionf_p1)t_glow; + p_add_thinker(&glow->thinker); + break; + + default: + i_error("P_UnarchiveSpecials:Unknown tclass %i " + "in savegame", + tclass); + } + } +} diff --git a/games/NXDoom/src/doom/p_saveg.h b/games/NXDoom/src/doom/p_saveg.h new file mode 100644 index 000000000..8c27b6dc7 --- /dev/null +++ b/games/NXDoom/src/doom/p_saveg.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_saveg.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Savegame I/O, archiving, persistence. + * + ****************************************************************************/ + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SAVEGAME_EOF 0x1d +#define VERSIONSIZE 16 + +/* maximum size of a savegame description */ + +#define SAVESTRINGSIZE 24 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern FILE *save_stream; +extern boolean savegame_error; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* temporary filename to use while saving. */ + +char *p_temp_save_game_file(void); + +/* filename to use for a savegame slot */ + +char *p_save_game_file(int slot); + +/* Savegame file header read/write functions */ + +boolean p_read_save_game_header(void); +void p_write_save_game_header(char *description); + +/* Savegame end-of-file read/write functions */ + +boolean p_read_save_game_eof(void); +void p_write_save_game_eof(void); + +/* Persistent storage/archiving. + * These are the load / save game routines. + */ + +void p_archive_players(void); +void p_unarchive_players(void); +void p_archive_world(void); +void p_unarchive_world(void); +void p_archive_thinkers(void); +void p_unarchive_thinkers(void); +void p_archive_specials(void); +void p_unarchive_specials(void); + +#endif /* __P_SAVEG__ */ diff --git a/games/NXDoom/src/doom/p_setup.c b/games/NXDoom/src/doom/p_setup.c new file mode 100644 index 000000000..3dc608abf --- /dev/null +++ b/games/NXDoom/src/doom/p_setup.c @@ -0,0 +1,834 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_setup.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Do all the WAD I/O, get map description, set up initial state and misc. + * LUTs. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "z_zone.h" + +#include "deh_main.h" +#include "i_swap.h" +#include "m_argv.h" +#include "m_bbox.h" + +#include "g_game.h" + +#include "i_system.h" +#include "w_wad.h" + +#include "doomdef.h" +#include "p_local.h" +#include "p_rejectpad.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#endif + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Maintain single and multi player starting spots. */ + +#define MAX_DEATHMATCH_STARTS 10 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void p_spawn_map_thing(mapthing_t *mthing); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int totallines; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* MAP related Lookup tables. + * Store VERTICES, LINEDEFS, SIDEDEFS, etc. + */ + +int numvertices; +vertex_t *vertices; + +int numsegs; +seg_t *segs; + +int numsectors; +sector_t *sectors; + +int numsubsectors; +subsector_t *subsectors; + +int numnodes; +node_t *nodes; + +int numlines; +line_t *lines; + +int numsides; +side_t *sides; + +/* BLOCKMAP + * Created from axis aligned bounding box + * of the map, a rectangular array of + * blocks of size ... + * Used to speed up collision detection + * by spatial subdivision in 2D. + */ + +/* Blockmap size. */ + +int bmapwidth; +int bmapheight; /* size in mapblocks */ +short *blockmap; /* int for larger maps */ + +/* offsets in blockmap are from here */ + +short *blockmaplump; + +/* origin of block map */ + +fixed_t bmaporgx; +fixed_t bmaporgy; + +/* for thing chains */ + +mobj_t **blocklinks; + +/* REJECT + * For fast sight rejection. + * Speeds up enemy AI by skipping detailed LineOf Sight calculation. + * Without special effect, this could be used as a PVS lookup as well. + */ + +byte *rejectmatrix; + +mapthing_t deathmatchstarts[MAX_DEATHMATCH_STARTS]; +mapthing_t *deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; +boolean playerstartsingame[MAXPLAYERS]; + +/* pointer to the current map lump info struct */ + +lumpinfo_t *maplumpinfo; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_load_vertices(int lump) +{ + byte *data; + int i; + mapvertex_t *ml; + vertex_t *li; + + /* Determine number of lumps: total lump length / vertex record length. */ + + numvertices = w_lump_length(lump) / sizeof(mapvertex_t); + + /* Allocate zone memory for buffer. */ + + vertices = z_malloc(numvertices * sizeof(vertex_t), PU_LEVEL, 0); + + /* Load data into cache. */ + + data = w_cache_lump_num(lump, PU_STATIC); + + ml = (mapvertex_t *)data; + li = vertices; + + /* Copy and convert vertex coordinates, internal representation as fixed. */ + + for (i = 0; i < numvertices; i++, li++, ml++) + { + li->x = SHORT(ml->x) << FRACBITS; + li->y = SHORT(ml->y) << FRACBITS; + } + + /* Free buffer memory. */ + + w_release_lump_num(lump); +} + +static sector_t *get_sector_at_null_address(void) +{ + static boolean null_sector_is_initialized = false; + static sector_t null_sector; + + if (!null_sector_is_initialized) + { + memset(&null_sector, 0, sizeof(null_sector)); + i_get_memory_value(0, &null_sector.floorheight, 4); + i_get_memory_value(4, &null_sector.ceilingheight, 4); + null_sector_is_initialized = true; + } + + return &null_sector; +} + +static void p_load_segs(int lump) +{ + byte *data; + int i; + mapseg_t *ml; + seg_t *li; + line_t *ldef; + int linedef_int; + int side; + int sidenum; + + numsegs = w_lump_length(lump) / sizeof(mapseg_t); + segs = z_malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + memset(segs, 0, numsegs * sizeof(seg_t)); + data = w_cache_lump_num(lump, PU_STATIC); + + ml = (mapseg_t *)data; + li = segs; + for (i = 0; i < numsegs; i++, li++, ml++) + { + li->v1 = &vertices[SHORT(ml->v1)]; + li->v2 = &vertices[SHORT(ml->v2)]; + + li->angle = (SHORT(ml->angle)) << FRACBITS; + li->offset = (SHORT(ml->offset)) << FRACBITS; + linedef_int = SHORT(ml->linedef); + ldef = &lines[linedef_int]; + li->linedef = ldef; + side = SHORT(ml->side); + + /* e6y: check for wrong indexes */ + + if ((unsigned)ldef->sidenum[side] >= (unsigned)numsides) + { + i_error("p_load_segs: linedef %d for seg %d references a " + "non-existent sidedef %d", + linedef_int, i, (unsigned)ldef->sidenum[side]); + } + + li->sidedef = &sides[ldef->sidenum[side]]; + li->frontsector = sides[ldef->sidenum[side]].sector; + + if (ldef->flags & ML_TWOSIDED) + { + sidenum = ldef->sidenum[side ^ 1]; + + /* If the sidenum is out of range, this may be a "glass hack" + * impassable window. Point at side #0 (this may not be + * the correct Vanilla behavior; however, it seems to work for + * OTTAWAU.WAD, which is the one place I've seen this trick + * used). + */ + + if (sidenum < 0 || sidenum >= numsides) + { + li->backsector = get_sector_at_null_address(); + } + else + { + li->backsector = sides[sidenum].sector; + } + } + else + { + li->backsector = 0; + } + } + + w_release_lump_num(lump); +} + +static void p_load_subsectors(int lump) +{ + byte *data; + int i; + mapsubsector_t *ms; + subsector_t *ss; + + numsubsectors = w_lump_length(lump) / sizeof(mapsubsector_t); + subsectors = z_malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0); + data = w_cache_lump_num(lump, PU_STATIC); + + ms = (mapsubsector_t *)data; + memset(subsectors, 0, numsubsectors * sizeof(subsector_t)); + ss = subsectors; + + for (i = 0; i < numsubsectors; i++, ss++, ms++) + { + ss->numlines = SHORT(ms->numsegs); + ss->firstline = SHORT(ms->firstseg); + } + + w_release_lump_num(lump); +} + +static void p_load_sectors(int lump) +{ + byte *data; + int i; + mapsector_t *ms; + sector_t *ss; + + numsectors = w_lump_length(lump) / sizeof(mapsector_t); + sectors = z_malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0); + memset(sectors, 0, numsectors * sizeof(sector_t)); + data = w_cache_lump_num(lump, PU_STATIC); + + ms = (mapsector_t *)data; + ss = sectors; + for (i = 0; i < numsectors; i++, ss++, ms++) + { + ss->floorheight = SHORT(ms->floorheight) << FRACBITS; + ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS; + ss->floorpic = r_flat_num_for_name(ms->floorpic); + ss->ceilingpic = r_flat_num_for_name(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = NULL; + } + + w_release_lump_num(lump); +} + +static void p_load_nodes(int lump) +{ + byte *data; + int i; + int j; + int k; + mapnode_t *mn; + node_t *no; + + numnodes = w_lump_length(lump) / sizeof(mapnode_t); + nodes = z_malloc(numnodes * sizeof(node_t), PU_LEVEL, 0); + data = w_cache_lump_num(lump, PU_STATIC); + + mn = (mapnode_t *)data; + no = nodes; + + for (i = 0; i < numnodes; i++, no++, mn++) + { + no->x = SHORT(mn->x) << FRACBITS; + no->y = SHORT(mn->y) << FRACBITS; + no->dx = SHORT(mn->dx) << FRACBITS; + no->dy = SHORT(mn->dy) << FRACBITS; + for (j = 0; j < 2; j++) + { + no->children[j] = SHORT(mn->children[j]); + for (k = 0; k < 4; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS; + } + } + + w_release_lump_num(lump); +} + +static void p_load_things(int lump) +{ + byte *data; + int i; + mapthing_t *mt; + mapthing_t spawnthing; + int numthings; + boolean spawn; + + data = w_cache_lump_num(lump, PU_STATIC); + numthings = w_lump_length(lump) / sizeof(mapthing_t); + + mt = (mapthing_t *)data; + for (i = 0; i < numthings; i++, mt++) + { + spawn = true; + + /* Do not spawn cool, new monsters if !commercial */ + + if (gamemode != commercial) + { + switch (SHORT(mt->type)) + { + case 68: /* Arachnotron */ + case 64: /* Archvile */ + case 88: /* Boss Brain */ + case 89: /* Boss Shooter */ + case 69: /* Hell Knight */ + case 67: /* Mancubus */ + case 71: /* Pain Elemental */ + case 65: /* Former Human Commando */ + case 66: /* Revenant */ + case 84: /* Wolf SS */ + spawn = false; + break; + } + } + + if (spawn == false) break; + + /* Do spawn all other stuff. */ + + spawnthing.x = SHORT(mt->x); + spawnthing.y = SHORT(mt->y); + spawnthing.angle = SHORT(mt->angle); + spawnthing.type = SHORT(mt->type); + spawnthing.options = SHORT(mt->options); + + p_spawn_map_thing(&spawnthing); + } + + if (!deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !playerstartsingame[i]) + { + i_error("p_load_things: Player %d start missing (vanilla " + "crashes here)", + i + 1); + } + + playerstartsingame[i] = false; + } + } + + w_release_lump_num(lump); +} + +/* p_load_linedefs + * Also counts secret lines for intermissions. + */ + +static void p_load_linedefs(int lump) +{ + byte *data; + int i; + maplinedef_t *mld; + line_t *ld; + vertex_t *v1; + vertex_t *v2; + + numlines = w_lump_length(lump) / sizeof(maplinedef_t); + lines = z_malloc(numlines * sizeof(line_t), PU_LEVEL, 0); + memset(lines, 0, numlines * sizeof(line_t)); + data = w_cache_lump_num(lump, PU_STATIC); + + mld = (maplinedef_t *)data; + ld = lines; + for (i = 0; i < numlines; i++, mld++, ld++) + { + ld->flags = SHORT(mld->flags); + ld->special = SHORT(mld->special); + ld->tag = SHORT(mld->tag); + v1 = ld->v1 = &vertices[SHORT(mld->v1)]; + v2 = ld->v2 = &vertices[SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + + if (!ld->dx) + ld->slopetype = ST_VERTICAL; + else if (!ld->dy) + ld->slopetype = ST_HORIZONTAL; + else + { + if (fixed_div(ld->dy, ld->dx) > 0) + ld->slopetype = ST_POSITIVE; + else + ld->slopetype = ST_NEGATIVE; + } + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + + if (ld->sidenum[0] != -1) + ld->frontsector = sides[ld->sidenum[0]].sector; + else + ld->frontsector = 0; + + if (ld->sidenum[1] != -1) + ld->backsector = sides[ld->sidenum[1]].sector; + else + ld->backsector = 0; + } + + w_release_lump_num(lump); +} + +static void p_load_side_defs(int lump) +{ + byte *data; + int i; + mapsidedef_t *msd; + side_t *sd; + + numsides = w_lump_length(lump) / sizeof(mapsidedef_t); + sides = z_malloc(numsides * sizeof(side_t), PU_LEVEL, 0); + memset(sides, 0, numsides * sizeof(side_t)); + data = w_cache_lump_num(lump, PU_STATIC); + + msd = (mapsidedef_t *)data; + sd = sides; + for (i = 0; i < numsides; i++, msd++, sd++) + { + sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS; + sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS; + sd->toptexture = r_texture_num_for_name(msd->toptexture); + sd->bottomtexture = r_texture_num_for_name(msd->bottomtexture); + sd->midtexture = r_texture_num_for_name(msd->midtexture); + sd->sector = §ors[SHORT(msd->sector)]; + } + + w_release_lump_num(lump); +} + +static void p_load_block_map(int lump) +{ + int i; + int count; + int lumplen; + + lumplen = w_lump_length(lump); + count = lumplen / 2; + + blockmaplump = z_malloc(lumplen, PU_LEVEL, NULL); + w_read_lump(lump, blockmaplump); + blockmap = blockmaplump + 4; + + /* Swap all short integers to native byte ordering. */ + + for (i = 0; i < count; i++) + { + blockmaplump[i] = SHORT(blockmaplump[i]); + } + + /* Read the header */ + + bmaporgx = blockmaplump[0] << FRACBITS; + bmaporgy = blockmaplump[1] << FRACBITS; + bmapwidth = blockmaplump[2]; + bmapheight = blockmaplump[3]; + + /* Clear out mobj chains */ + + count = sizeof(*blocklinks) * bmapwidth * bmapheight; + blocklinks = z_malloc(count, PU_LEVEL, 0); + memset(blocklinks, 0, count); +} + +/* p_group_lines + * Builds sector line lists and subsector sector numbers. + * Finds block bounding boxes for sectors. + */ + +static void p_group_lines(void) +{ + line_t **linebuffer; + int i; + int j; + line_t *li; + sector_t *sector; + subsector_t *ss; + seg_t *seg; + fixed_t bbox[4]; + int block; + + /* look up sector number for each subsector */ + + ss = subsectors; + for (i = 0; i < numsubsectors; i++, ss++) + { + seg = &segs[ss->firstline]; + ss->sector = seg->sidedef->sector; + } + + /* count number of lines in each sector */ + + li = lines; + totallines = 0; + for (i = 0; i < numlines; i++, li++) + { + totallines++; + li->frontsector->linecount++; + + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + totallines++; + } + } + + /* build line tables for each sector */ + + linebuffer = z_malloc(totallines * sizeof(line_t *), PU_LEVEL, 0); + + for (i = 0; i < numsectors; ++i) + { + /* Assign the line buffer for this sector */ + + sectors[i].lines = linebuffer; + linebuffer += sectors[i].linecount; + + /* Reset linecount to zero so in the next stage we can count + * lines into the list. + */ + + sectors[i].linecount = 0; + } + + /* Assign lines to sectors */ + + for (i = 0; i < numlines; ++i) + { + li = &lines[i]; + + if (li->frontsector != NULL) + { + sector = li->frontsector; + + sector->lines[sector->linecount] = li; + ++sector->linecount; + } + + if (li->backsector != NULL && li->frontsector != li->backsector) + { + sector = li->backsector; + + sector->lines[sector->linecount] = li; + ++sector->linecount; + } + } + + /* Generate bounding boxes for sectors */ + + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + m_clear_box(bbox); + + for (j = 0; j < sector->linecount; j++) + { + li = sector->lines[j]; + + m_add_to_box(bbox, li->v1->x, li->v1->y); + m_add_to_box(bbox, li->v2->x, li->v2->y); + } + + /* set the degenmobj_t to the middle of the bounding box */ + + sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2; + sector->soundorg.y = (bbox[BOXTOP] + bbox[BOXBOTTOM]) / 2; + + /* adjust bounding box to map blocks */ + + block = (bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight - 1 : block; + sector->blockbox[BOXTOP] = block; + + block = (bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM] = block; + + block = (bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth - 1 : block; + sector->blockbox[BOXRIGHT] = block; + + block = (bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT] = block; + } +} + +static void p_load_reject(int lumpnum) +{ + int minlength; + int lumplen; + + /* Calculate the size that the REJECT lump *should* be. */ + + minlength = (numsectors * numsectors + 7) / 8; + + /* If the lump meets the minimum length, it can be loaded directly. + * Otherwise, we need to allocate a buffer of the correct size + * and pad it with appropriate data. + */ + + lumplen = w_lump_length(lumpnum); + + if (lumplen >= minlength) + { + rejectmatrix = w_cache_lump_num(lumpnum, PU_LEVEL); + } + else + { + rejectmatrix = z_malloc(minlength, PU_LEVEL, &rejectmatrix); + w_read_lump(lumpnum, rejectmatrix); + + pad_reject_array(rejectmatrix + lumplen, minlength - lumplen, + totallines); + } +} + +/* p_setup_level */ + +void p_setup_level(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + for (i = 0; i < MAXPLAYERS; i++) + { + players[i].killcount = players[i].secretcount = players[i].itemcount = + 0; + } + + /* Initial height of PointOfView will be set by player think. */ + + players[consoleplayer].viewz = 1; + + /* Make sure all sounds are stopped before z_free_tags. */ + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start(); +#endif + + z_free_tags(PU_LEVEL, PU_PURGELEVEL - 1); + + /* UNUSED w_profile (); */ + + p_init_thinkers(); + + /* if working with a development map, reload it */ + + w_reload(); + + /* find map name */ + + if (gamemode == commercial) + { + if (map < 10) + snprintf(lumpname, 9, "map0%d", (int8_t)map); + else + snprintf(lumpname, 9, "map%d", (int8_t)map); + } + else + { + lumpname[0] = 'E'; + lumpname[1] = '0' + episode; + lumpname[2] = 'M'; + lumpname[3] = '0' + map; + lumpname[4] = 0; + } + + lumpnum = w_get_num_for_name(lumpname); + + maplumpinfo = lumpinfo[lumpnum]; + + leveltime = 0; + + /* note: most of this ordering is important */ + + p_load_block_map(lumpnum + ML_BLOCKMAP); + p_load_vertices(lumpnum + ML_VERTICES); + p_load_sectors(lumpnum + ML_SECTORS); + p_load_side_defs(lumpnum + ML_SIDEDEFS); + + p_load_linedefs(lumpnum + ML_LINEDEFS); + p_load_subsectors(lumpnum + ML_SSECTORS); + p_load_nodes(lumpnum + ML_NODES); + p_load_segs(lumpnum + ML_SEGS); + + p_group_lines(); + p_load_reject(lumpnum + ML_REJECT); + + bodyqueslot = 0; + deathmatch_p = deathmatchstarts; + p_load_things(lumpnum + ML_THINGS); + + /* if deathmatch, randomly spawn the active players */ + + if (deathmatch) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + players[i].mo = NULL; + g_death_match_spawn_player(i); + } + } + } + + /* clear special respawning que */ + + iquehead = iquetail = 0; + + /* set up world state */ + + p_spawn_specials(); + + /* preload graphics */ + + if (precache) r_precache_level(); +} + +void p_init(void) +{ + p_init_switch_list(); + p_init_pic_anims(); + r_init_sprites(sprnames); +} diff --git a/games/NXDoom/src/doom/p_setup.h b/games/NXDoom/src/doom/p_setup.h new file mode 100644 index 000000000..ee4cdb563 --- /dev/null +++ b/games/NXDoom/src/doom/p_setup.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_setup.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Setup a game, startup stuff. + * + ****************************************************************************/ + +#ifndef __P_SETUP__ +#define __P_SETUP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "w_wad.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lumpinfo_t *maplumpinfo; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* NOT called by W_Ticker. Fixme. */ + +void p_setup_level(int episode, int map, int playermask, skill_t skill); + +/* Called by startup code. */ + +void p_init(void); + +#endif /* __P_SETUP__ */ diff --git a/games/NXDoom/src/doom/p_sight.c b/games/NXDoom/src/doom/p_sight.c new file mode 100644 index 000000000..84167065e --- /dev/null +++ b/games/NXDoom/src/doom/p_sight.c @@ -0,0 +1,405 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_sight.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * LineOfSight/Visibility checks, uses REJECT Lookup Table. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "doomstat.h" + +#include "i_system.h" +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* p_check_sight */ + +fixed_t sightzstart; /* eye z of looker */ +fixed_t topslope; +fixed_t bottomslope; /* slopes to top and bottom of target */ + +divline_t strace; /* from t1 to t2 */ +fixed_t t2x; +fixed_t t2y; + +int sightcounts[2]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* PTR_SightTraverse() for Doom 1.2 sight calculations + * taken from prboom-plus/src/p_sight.c:69-102 + */ + +static boolean ptr_sight_traverse(intercept_t *in) +{ + line_t *li; + fixed_t slope; + + li = in->d.line; + + /* crosses a two sided line */ + + p_line_opening(li); + + /* quick test for totally closed doors */ + + if (openbottom >= opentop) + { + return false; /* stop */ + } + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = fixed_div(openbottom - sightzstart, in->frac); + if (slope > bottomslope) bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = fixed_div(opentop - sightzstart, in->frac); + if (slope < topslope) topslope = slope; + } + + if (topslope <= bottomslope) return false; /* stop */ + + return true; /* keep going */ +} + +/* p_divline_side + * Returns side 0 (front), 1 (back), or 2 (on). + */ + +static int p_divline_side(fixed_t x, fixed_t y, divline_t *node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x == node->x) return 2; + + if (x <= node->x) return node->dy > 0; + + return node->dy < 0; + } + + if (!node->dy) + { + if (x == node->y) return 2; + + if (y <= node->y) return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + left = (node->dy >> FRACBITS) * (dx >> FRACBITS); + right = (dy >> FRACBITS) * (node->dx >> FRACBITS); + + if (right < left) return 0; /* front side */ + + if (left == right) return 2; + return 1; /* back side */ +} + +/* p_intercept_vector2 + * Returns the fractional intercept point along the first divline. + * This is only called by the addthings and addlines traversers. + */ + +static fixed_t p_intercept_vector2(divline_t *v2, divline_t *v1) +{ + fixed_t frac; + fixed_t num; + fixed_t den; + + den = fixed_mul(v1->dy >> 8, v2->dx) - fixed_mul(v1->dx >> 8, v2->dy); + + if (den == 0) return 0; + + num = fixed_mul((v1->x - v2->x) >> 8, v1->dy) + + fixed_mul((v2->y - v1->y) >> 8, v1->dx); + frac = fixed_div(num, den); + + return frac; +} + +/* p_cross_subsector + * Returns true if strace crosses the given subsector successfully. + */ + +static boolean p_cross_subsector(int num) +{ + seg_t *seg; + line_t *line; + int s1; + int s2; + int count; + subsector_t *sub; + sector_t *front; + sector_t *back; + fixed_t l_opentop; + fixed_t l_openbottom; + divline_t divl; + vertex_t *v1; + vertex_t *v2; + fixed_t frac; + fixed_t slope; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (num >= numsubsectors) + i_error("p_cross_subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sub = &subsectors[num]; + + /* check lines */ + + count = sub->numlines; + seg = &segs[sub->firstline]; + + for (; count; seg++, count--) + { + line = seg->linedef; + + /* already checked other side? */ + + if (line->validcount == validcount) continue; + + line->validcount = validcount; + + v1 = line->v1; + v2 = line->v2; + s1 = p_divline_side(v1->x, v1->y, &strace); + s2 = p_divline_side(v2->x, v2->y, &strace); + + /* line isn't crossed? */ + + if (s1 == s2) continue; + + divl.x = v1->x; + divl.y = v1->y; + divl.dx = v2->x - v1->x; + divl.dy = v2->y - v1->y; + s1 = p_divline_side(strace.x, strace.y, &divl); + s2 = p_divline_side(t2x, t2y, &divl); + + /* line isn't crossed? */ + + if (s1 == s2) continue; + + /* Backsector may be NULL if this is an "impassable + * glass" hack line. + */ + + if (line->backsector == NULL) + { + return false; + } + + /* stop because it is not two sided anyway + * might do this after updating validcount? + */ + + if (!(line->flags & ML_TWOSIDED)) return false; + + /* crosses a two sided line */ + + front = seg->frontsector; + back = seg->backsector; + + /* no wall to block sight with? */ + + if (front->floorheight == back->floorheight && + front->ceilingheight == back->ceilingheight) + continue; + + /* possible occluder + * because of ceiling height differences + */ + + if (front->ceilingheight < back->ceilingheight) + l_opentop = front->ceilingheight; + else + l_opentop = back->ceilingheight; + + /* because of ceiling height differences */ + + if (front->floorheight > back->floorheight) + l_openbottom = front->floorheight; + else + l_openbottom = back->floorheight; + + /* quick test for totally closed doors */ + + if (l_openbottom >= l_opentop) return false; /* stop */ + + frac = p_intercept_vector2(&strace, &divl); + + if (front->floorheight != back->floorheight) + { + slope = fixed_div(l_openbottom - sightzstart, frac); + if (slope > bottomslope) bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + slope = fixed_div(l_opentop - sightzstart, frac); + if (slope < topslope) topslope = slope; + } + + if (topslope <= bottomslope) return false; /* stop */ + } + + /* passed the subsector ok */ + + return true; +} + +/* p_cross_bsp_node + * Returns true if strace crosses the given node successfully. + */ + +static boolean p_cross_bsp_node(int bspnum) +{ + node_t *bsp; + int side; + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + { + return p_cross_subsector(0); + } + else + { + return p_cross_subsector(bspnum & (~NF_SUBSECTOR)); + } + } + + bsp = &nodes[bspnum]; + + /* decide which side the start point is on */ + + side = p_divline_side(strace.x, strace.y, (divline_t *)bsp); + if (side == 2) side = 0; /* an "on" should cross both sides */ + + /* cross the starting side */ + + if (!p_cross_bsp_node(bsp->children[side])) return false; + + /* the partition plane is crossed here */ + + if (side == p_divline_side(t2x, t2y, (divline_t *)bsp)) + { + /* the line doesn't touch the other side */ + + return true; + } + + /* cross the ending side */ + + return p_cross_bsp_node(bsp->children[side ^ 1]); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_check_sight + * Returns true if a straight line between t1 and t2 is unobstructed. + * Uses REJECT. + */ + +boolean p_check_sight(mobj_t *t1, mobj_t *t2) +{ + int s1; + int s2; + int pnum; + int bytenum; + int bitnum; + + /* First check for trivial rejection. */ + + /* Determine subsector entries in REJECT table. */ + + s1 = (t1->subsector->sector - sectors); + s2 = (t2->subsector->sector - sectors); + pnum = s1 * numsectors + s2; + bytenum = pnum >> 3; + bitnum = 1 << (pnum & 7); + + /* Check in REJECT table. */ + + if (rejectmatrix[bytenum] & bitnum) + { + sightcounts[0]++; + + /* can't possibly be connected */ + + return false; + } + + /* An unobstructed LOS is possible. + * Now look from eyes of t1 to any part of t2. + */ + + sightcounts[1]++; + + validcount++; + + sightzstart = t1->z + t1->height - (t1->height >> 2); + topslope = (t2->z + t2->height) - sightzstart; + bottomslope = (t2->z) - sightzstart; + + if (gameversion <= exe_doom_1_2) + { + return p_path_traverse(t1->x, t1->y, t2->x, t2->y, + PT_EARLYOUT | PT_ADDLINES, + ptr_sight_traverse); + } + + strace.x = t1->x; + strace.y = t1->y; + t2x = t2->x; + t2y = t2->y; + strace.dx = t2->x - t1->x; + strace.dy = t2->y - t1->y; + + /* the head node is the last node output */ + + return p_cross_bsp_node(numnodes - 1); +} diff --git a/games/NXDoom/src/doom/p_spec.c b/games/NXDoom/src/doom/p_spec.c new file mode 100644 index 000000000..2b095210d --- /dev/null +++ b/games/NXDoom/src/doom/p_spec.c @@ -0,0 +1,1376 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_spec.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Implements special effects: + * Texture animation, height or lighting changes according to adjacent + * sectors, respective utility functions, etc. + * Line Tag handling. Line and Sector triggers. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomdef.h" +#include "doomstat.h" + +#include "deh_main.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_random.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "p_local.h" +#include "r_local.h" + +#include "g_game.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXANIMS 32 + +/* Animating line specials */ + +#define MAXLINEANIMS 64 + +/* version <= 1.2 did not have a limit and could handle up to 66 scrolling + * linedefs before displaying adverse effects. All other versions have a + * limit of 64. + */ + +#define MAXLINEANIMS1_2 66 + +/* Thanks to entryway for the Vanilla overflow emulation. + * 20 adjoining sectors max! + */ + +#define MAX_ADJOINING_SECTORS 20 + +/* Donut overrun emulation + * + * Derived from the code from PrBoom+. Thanks go to Andrey Budko (entryway) + * as usual :-) + */ + +#define DONUT_FLOORHEIGHT_DEFAULT 0x00000000 +#define DONUT_FLOORPIC_DEFAULT 0x16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Animating textures and planes + * There is another anim_t used in wi_stuff, unrelated. + */ + +typedef struct +{ + boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; +} anim_t; + +/* source animation definition */ + +typedef struct +{ + int istexture; /* if false, it is a flat */ + char endname[9]; + char startname[9]; + int speed; +} animdef_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* p_init_pic_anims */ + +/* Floor/ceiling animation sequences, defined by first and last frame, i.e. + * the flat (64x64 tile) name to be used. + * + * The full animation sequence is given using all the flats between the start + * and end entry, in the order found in the WAD file. + */ + +static animdef_t g_animdefs[] = +{ + {false, "NUKAGE3", "NUKAGE1", 8}, + {false, "FWATER4", "FWATER1", 8}, + {false, "SWATER4", "SWATER1", 8}, + {false, "LAVA4", "LAVA1", 8}, + {false, "BLOOD3", "BLOOD1", 8}, + {false, "RROCK08", "RROCK05", 8}, /* DOOM II flat animations. */ + {false, "SLIME04", "SLIME01", 8}, + {false, "SLIME08", "SLIME05", 8}, + {false, "SLIME12", "SLIME09", 8}, + {true, "BLODGR4", "BLODGR1", 8}, + {true, "SLADRIP3", "SLADRIP1", 8}, + {true, "BLODRIP4", "BLODRIP1", 8}, + {true, "FIREWALL", "FIREWALA", 8}, + {true, "GSTFONT3", "GSTFONT1", 8}, + {true, "FIRELAVA", "FIRELAV3", 8}, + {true, "FIREMAG3", "FIREMAG1", 8}, + {true, "FIREBLU2", "FIREBLU1", 8}, + {true, "ROCKRED3", "ROCKRED1", 8}, + {true, "BFALL4", "BFALL1", 8}, + {true, "SFALL4", "SFALL1", 8}, + {true, "WFALL4", "WFALL1", 8}, + {true, "DBRAIN4", "DBRAIN1", 8}, + { -1, "", "", 0}, +}; + +static anim_t g_anims[MAXANIMS]; +static anim_t *g_lastanim; + +static short g_numlinespecials; +static line_t *g_linespeciallist[MAXLINEANIMS1_2]; + +/* p_update_specials + * Animate planes, scroll walls, etc. + */ + +static boolean g_level_timer; +static int g_level_time_count; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void donut_overrun(fixed_t *s3_floorheight, short *s3_floorpic, + line_t *line, sector_t *pillar_sector) +{ + static int first = 1; + static int tmp_s3_floorheight; + static int tmp_s3_floorpic; + + if (first) + { + int p; + + /* This is the first time we have had an overrun. */ + + first = 0; + + /* Default values */ + + tmp_s3_floorheight = DONUT_FLOORHEIGHT_DEFAULT; + tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; + + /* @category compat + * @arg + * + * Use the specified magic values when emulating behavior caused + * by memory overruns from improperly constructed donuts. + * In Vanilla Doom this can differ depending on the operating + * system. The default (if this option is not specified) is to + * emulate the behavior when running under Windows 98. + */ + + p = m_check_parm_with_args("-donut", 2); + + if (p > 0) + { + /* Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008 + * + * C:\>debug + * -d 0:0 + * + * DOS 6.22: + * 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) + * DOS 7.1: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * Win98: + * 0000:0000 (00 00 00 00) 65 04 70 00-(16 00) + * DOSBox under XP: + * 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) + */ + + m_str_to_int(myargv[p + 1], &tmp_s3_floorheight); + m_str_to_int(myargv[p + 2], &tmp_s3_floorpic); + + if (tmp_s3_floorpic >= numflats) + { + fprintf(stderr, + "DonutOverrun: The second parameter for \"-donut\" " + "switch should be greater than 0 and less than number " + "of flats (%d). Using default value (%d) instead. \n", + numflats, DONUT_FLOORPIC_DEFAULT); + tmp_s3_floorpic = DONUT_FLOORPIC_DEFAULT; + } + } + } + + *s3_floorheight = (fixed_t)tmp_s3_floorheight; + *s3_floorpic = (short)tmp_s3_floorpic; +} + +/* p_spawn_specials + * After the map has been loaded, scan for specials that spawn thinkers + */ + +static unsigned int num_scrollers(void) +{ + unsigned int i; + unsigned int scrollers = 0; + + for (i = 0; i < numlines; i++) + { + if (48 == lines[i].special) + { + scrollers++; + } + } + + return scrollers; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_init_pic_anims(void) +{ + int i; + + /* Init animation */ + + g_lastanim = g_anims; + for (i = 0; g_animdefs[i].istexture != -1; i++) + { + const char *startname; + const char *endname; + + startname = (g_animdefs[i].startname); + endname = (g_animdefs[i].endname); + + if (g_animdefs[i].istexture) + { + /* different episode ? */ + + if (r_check_texture_num_for_name(startname) == -1) continue; + + g_lastanim->picnum = r_texture_num_for_name(endname); + g_lastanim->basepic = r_texture_num_for_name(startname); + } + else + { + if (w_check_num_for_name(startname) == -1) continue; + + g_lastanim->picnum = r_flat_num_for_name(endname); + g_lastanim->basepic = r_flat_num_for_name(startname); + } + + g_lastanim->istexture = g_animdefs[i].istexture; + g_lastanim->numpics = g_lastanim->picnum - g_lastanim->basepic + 1; + + if (g_lastanim->numpics < 2) + i_error("p_init_pic_anims: bad cycle from %s to %s", startname, + endname); + + g_lastanim->speed = g_animdefs[i].speed; + g_lastanim++; + } +} + +/* UTILITIES */ + +/* get_side() + * Will return a side_t* given the number of the current sector, the line + * number, and the side (0/1) that you want. + */ + +side_t *get_side(int current_sector, int line, int side) +{ + return &sides[(sectors[current_sector].lines[line])->sidenum[side]]; +} + +/* get_sector() + * Will return a sector_t* given the number of the current sector, the line + * number and the side (0/1) that you want. + */ + +sector_t *get_sector(int current_sector, int line, int side) +{ + return sides[(sectors[current_sector].lines[line])->sidenum[side]].sector; +} + +/* two_sided() + * Given the sector number and the line number, it will tell you whether the + * line is two-sided or not. + */ + +int two_sided(int sector, int line) +{ + return (sectors[sector].lines[line])->flags & ML_TWOSIDED; +} + +/* get_next_sector() + * Return sector_t * of sector next to current. NULL if not two-sided line + */ + +sector_t *get_next_sector(line_t *line, sector_t *sec) +{ + if (!(line->flags & ML_TWOSIDED)) return NULL; + + if (line->frontsector == sec) return line->backsector; + + return line->frontsector; +} + +/* p_find_lowest_floor_surrounding() + * FIND LOWEST FLOOR HEIGHT IN SURROUNDING SECTORS + */ + +fixed_t p_find_lowest_floor_surrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t floor = sec->floorheight; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight < floor) floor = other->floorheight; + } + + return floor; +} + +/* p_find_highest_floor_surrounding() + * FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS + */ + +fixed_t p_find_highest_floor_surrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t floor = -500 * FRACUNIT; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight > floor) floor = other->floorheight; + } + + return floor; +} + +/* p_find_next_highest_floor + * FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS + * Note: this should be doable w/o a fixed array. + */ + +fixed_t p_find_next_highest_floor(sector_t *sec, int currentheight) +{ + int i; + int h; + int min; + line_t *check; + sector_t *other; + fixed_t height = currentheight; + fixed_t heightlist[MAX_ADJOINING_SECTORS + 2]; + + for (i = 0, h = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->floorheight > height) + { + /* Emulation of memory (stack) overflow */ + + if (h == MAX_ADJOINING_SECTORS + 1) + { + height = other->floorheight; + } + else if (h == MAX_ADJOINING_SECTORS + 2) + { + /* Fatal overflow: game crashes at 22 sectors */ + + i_error("Sector with more than 22 adjoining sectors. " + "Vanilla will crash here"); + } + + heightlist[h++] = other->floorheight; + } + } + + /* Find lowest height in list */ + + if (!h) + { + return currentheight; + } + + min = heightlist[0]; + + /* Range checking? */ + + for (i = 1; i < h; i++) + { + if (heightlist[i] < min) + { + min = heightlist[i]; + } + } + + return min; +} + +/* FIND LOWEST CEILING IN THE SURROUNDING SECTORS */ + +fixed_t p_find_lowest_ceiling_surrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t height = INT_MAX; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->ceilingheight < height) height = other->ceilingheight; + } + + return height; +} + +/* FIND HIGHEST CEILING IN THE SURROUNDING SECTORS */ + +fixed_t p_find_heighest_ceiling_surrounding(sector_t *sec) +{ + int i; + line_t *check; + sector_t *other; + fixed_t height = 0; + + for (i = 0; i < sec->linecount; i++) + { + check = sec->lines[i]; + other = get_next_sector(check, sec); + + if (!other) continue; + + if (other->ceilingheight > height) height = other->ceilingheight; + } + + return height; +} + +/* RETURN NEXT SECTOR # THAT LINE TAG REFERS TO */ + +int p_find_sector_from_line_tag(line_t *line, int start) +{ + int i; + + for (i = start + 1; i < numsectors; i++) + { + if (sectors[i].tag == line->tag) + { + return i; + } + } + + return -1; +} + +/* Find minimum light from an adjacent sector */ + +int p_find_min_surrounding(sector_t *sector, int max) +{ + int i; + int min; + line_t *line; + sector_t *check; + + min = max; + for (i = 0; i < sector->linecount; i++) + { + line = sector->lines[i]; + check = get_next_sector(line, sector); + + if (!check) continue; + + if (check->lightlevel < min) min = check->lightlevel; + } + + return min; +} + +/* EVENTS + * Events are operations triggered by using, crossing, + * or shooting special lines, or by timed thinkers. + */ + +/* p_cross_special_line - TRIGGER + * Called every time a thing origin is about to cross a line with a non 0 + * special. + */ + +void p_cross_special_line(int linenum, int side, mobj_t *thing) +{ + line_t *line; + int is_ok; + + line = &lines[linenum]; + + if (gameversion <= exe_doom_1_2) + { + if (line->special > 98 && line->special != 104) + { + return; + } + } + else + { + /* Triggers that other things can activate */ + + if (!thing->player) + { + /* Things that should NOT trigger specials... */ + + switch (thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + + default: + break; + } + } + } + + if (!thing->player) + { + is_ok = 0; + switch (line->special) + { + case 39: /* TELEPORT TRIGGER */ + case 97: /* TELEPORT RETRIGGER */ + case 125: /* TELEPORT MONSTERONLY TRIGGER */ + case 126: /* TELEPORT MONSTERONLY RETRIGGER */ + case 4: /* RAISE DOOR */ + case 10: /* PLAT DOWN-WAIT-UP-STAY TRIGGER */ + case 88: /* PLAT DOWN-WAIT-UP-STAY RETRIGGER */ + is_ok = 1; + break; + } + + if (!is_ok) return; + } + + /* Note: could use some const's here. */ + + switch (line->special) + { + /* TRIGGERS. + * All from here to RETRIGGERS. + */ + + case 2: /* Open Door */ + ev_do_door(line, VLD_OPEN); + line->special = 0; + break; + + case 3: /* Close Door */ + ev_do_door(line, VLD_CLOSE); + line->special = 0; + break; + + case 4: /* Raise Door */ + ev_do_door(line, VLD_NORMAL); + line->special = 0; + break; + + case 5: /* Raise Floor */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + line->special = 0; + break; + + case 6: /* Fast Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_FASTCRUSHANDRAISE); + line->special = 0; + break; + + case 8: /* Build Stairs */ + ev_build_stairs(line, STAIR_BUILD8); + line->special = 0; + break; + + case 10: /* PlatDownWaitUp */ + ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0); + line->special = 0; + break; + + case 12: /* Light Turn On - brightest near */ + ev_light_turn_on(line, 0); + line->special = 0; + break; + + case 13: /* Light Turn On 255 */ + ev_light_turn_on(line, 255); + line->special = 0; + break; + + case 16: /* Close Door 30 */ + ev_do_door(line, VLD_CLOSE30THENOPEN); + line->special = 0; + break; + + case 17: /* Start Light Strobing */ + ev_start_light_strobing(line); + line->special = 0; + break; + + case 19: /* Lower Floor */ + ev_do_floor(line, FLOOR_LOWERFLOOR); + line->special = 0; + break; + + case 22: /* Raise floor to nearest height and change texture */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + line->special = 0; + break; + + case 25: /* Ceiling Crush and Raise */ + ev_do_ceiling(line, CEIL_CRUSHANDRAISE); + line->special = 0; + break; + + case 30: + + /* Raise floor to shortest texture height on either side of lines. */ + + ev_do_floor(line, FLOOR_RAISETOTEXTURE); + line->special = 0; + break; + + case 35: /* Lights Very Dark */ + ev_light_turn_on(line, 35); + line->special = 0; + break; + + case 36: /* Lower Floor (TURBO) */ + ev_do_floor(line, FLOOR_TURBOLOWER); + line->special = 0; + break; + + case 37: /* LowerAndChange */ + ev_do_floor(line, FLOOR_LOWERANDCHANGE); + line->special = 0; + break; + + case 38: /* Lower Floor To Lowest */ + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + line->special = 0; + break; + + case 39: /* TELEPORT! */ + ev_teleport(line, side, thing); + line->special = 0; + break; + + case 40: /* RaiseCeilingLowerFloor */ + ev_do_ceiling(line, CEIL_RAISETOHIGHEST); + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + line->special = 0; + break; + + case 44: /* Ceiling Crush */ + ev_do_ceiling(line, CEIL_LOWERANDCRUSH); + line->special = 0; + break; + + case 52: /* EXIT! */ + g_exit_level(); + break; + + case 53: /* Perpetual Platform Raise */ + ev_do_plat(line, PLAT_PERPETUALRAISE, 0); + line->special = 0; + break; + + case 54: /* Platform Stop */ + ev_stop_plat(line); + line->special = 0; + break; + + case 56: /* Raise Floor Crush */ + ev_do_floor(line, FLOOR_RAISEFLOORCRUSH); + line->special = 0; + break; + + case 57: /* Ceiling Crush Stop */ + ev_ceiling_crush_stop(line); + line->special = 0; + break; + + case 58: /* Raise Floor 24 */ + ev_do_floor(line, FLOOR_RAISEFLOOR24); + line->special = 0; + break; + + case 59: /* Raise Floor 24 And Change */ + ev_do_floor(line, FLOOR_RAISEFLOOR24ANDCHANGE); + line->special = 0; + break; + + case 104: /* Turn lights off in sector(tag) */ + ev_turn_tag_lights_off(line); + line->special = 0; + break; + + case 108: /* Blazing Door Raise (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZERAISE); + line->special = 0; + break; + + case 109: /* Blazing Door Open (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZEOPEN); + line->special = 0; + break; + + case 100: /* Build Stairs Turbo 16 */ + ev_build_stairs(line, STAIR_TURBO16); + line->special = 0; + break; + + case 110: /* Blazing Door Close (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZECLOSE); + line->special = 0; + break; + + case 119: /* Raise floor to nearest surr. floor */ + ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST); + line->special = 0; + break; + + case 121: /* Blazing PlatDownWaitUpStay */ + ev_do_plat(line, PLAT_BLAZEDWUS, 0); + line->special = 0; + break; + + case 124: /* Secret EXIT */ + g_secret_exit_level(); + break; + + case 125: /* TELEPORT MonsterONLY */ + if (!thing->player) + { + ev_teleport(line, side, thing); + line->special = 0; + } + break; + + case 130: /* Raise Floor Turbo */ + ev_do_floor(line, FLOOR_RAISEFLOORTURBO); + line->special = 0; + break; + + case 141: /* Silent Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_SILENTCRUSHANDRAISE); + line->special = 0; + break; + + /* RETRIGGERS. All from here till end. */ + + case 72: /* Ceiling Crush */ + ev_do_ceiling(line, CEIL_LOWERANDCRUSH); + break; + + case 73: /* Ceiling Crush and Raise */ + ev_do_ceiling(line, CEIL_CRUSHANDRAISE); + break; + + case 74: /* Ceiling Crush Stop */ + ev_ceiling_crush_stop(line); + break; + + case 75: /* Close Door */ + ev_do_door(line, VLD_CLOSE); + break; + + case 76: /* Close Door 30 */ + ev_do_door(line, VLD_CLOSE30THENOPEN); + break; + + case 77: /* Fast Ceiling Crush & Raise */ + ev_do_ceiling(line, CEIL_FASTCRUSHANDRAISE); + break; + + case 79: /* Lights Very Dark */ + ev_light_turn_on(line, 35); + break; + + case 80: /* Light Turn On - brightest near */ + ev_light_turn_on(line, 0); + break; + + case 81: /* Light Turn On 255 */ + ev_light_turn_on(line, 255); + break; + + case 82: /* Lower Floor To Lowest */ + ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST); + break; + + case 83: /* Lower Floor */ + ev_do_floor(line, FLOOR_LOWERFLOOR); + break; + + case 84: /* LowerAndChange */ + ev_do_floor(line, FLOOR_LOWERANDCHANGE); + break; + + case 86: /* Open Door */ + ev_do_door(line, VLD_OPEN); + break; + + case 87: /* Perpetual Platform Raise */ + ev_do_plat(line, PLAT_PERPETUALRAISE, 0); + break; + + case 88: /* PlatDownWaitUp */ + ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0); + break; + + case 89: /* Platform Stop */ + ev_stop_plat(line); + break; + + case 90: /* Raise Door */ + ev_do_door(line, VLD_NORMAL); + break; + + case 91: /* Raise Floor */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + break; + + case 92: /* Raise Floor 24 */ + ev_do_floor(line, FLOOR_RAISEFLOOR24); + break; + + case 93: /* Raise Floor 24 And Change */ + ev_do_floor(line, FLOOR_RAISEFLOOR24ANDCHANGE); + break; + + case 94: /* Raise Floor Crush */ + ev_do_floor(line, FLOOR_RAISEFLOORCRUSH); + break; + + case 95: /* Raise floor to nearest height and change texture. */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + break; + + case 96: + + /* Raise floor to shortest texture height on either side of lines. */ + + ev_do_floor(line, FLOOR_RAISETOTEXTURE); + break; + + case 97: /* TELEPORT! */ + ev_teleport(line, side, thing); + break; + + case 98: /* Lower Floor (TURBO) */ + ev_do_floor(line, FLOOR_TURBOLOWER); + break; + + case 105: /* Blazing Door Raise (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZERAISE); + break; + + case 106: /* Blazing Door Open (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZEOPEN); + break; + + case 107: /* Blazing Door Close (faster than TURBO!) */ + ev_do_door(line, VLD_BLAZECLOSE); + break; + + case 120: /* Blazing PlatDownWaitUpStay. */ + ev_do_plat(line, PLAT_BLAZEDWUS, 0); + break; + + case 126: /* TELEPORT MonsterONLY. */ + if (!thing->player) ev_teleport(line, side, thing); + break; + + case 128: /* Raise To Nearest Floor */ + ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST); + break; + + case 129: /* Raise Floor Turbo */ + ev_do_floor(line, FLOOR_RAISEFLOORTURBO); + break; + } +} + +/* p_shoot_special_line - IMPACT SPECIALS + * Called when a thing shoots a special line. + */ + +void p_shoot_special_line(mobj_t *thing, line_t *line) +{ + int is_ok; + + /* Impacts that other things can activate. */ + + if (!thing->player) + { + is_ok = 0; + switch (line->special) + { + case 46: /* OPEN DOOR IMPACT */ + is_ok = 1; + break; + } + + if (!is_ok) return; + } + + switch (line->special) + { + case 24: /* RAISE FLOOR */ + ev_do_floor(line, FLOOR_RAISEFLOOR); + p_change_switch_texture(line, 0); + break; + + case 46: /* OPEN DOOR */ + ev_do_door(line, VLD_OPEN); + p_change_switch_texture(line, 1); + break; + + case 47: /* RAISE FLOOR NEAR AND CHANGE */ + ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0); + p_change_switch_texture(line, 0); + break; + } +} + +/* p_player_in_special_sector + * Called every tic frame that the player origin is in a special sector + */ + +void p_player_in_special_sector(player_t *player) +{ + sector_t *sector; + + sector = player->mo->subsector->sector; + + /* Falling, not all the way down yet? */ + + if (player->mo->z != sector->floorheight) return; + + /* Has hitten ground. */ + + switch (sector->special) + { + case 5: /* HELLSLIME DAMAGE */ + if (!player->powers[pw_ironfeet]) + { + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 10); + } + + break; + + case 7: /* NUKAGE DAMAGE */ + if (!player->powers[pw_ironfeet]) + { + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 5); + } + + break; + + case 16: /* SUPER HELLSLIME DAMAGE */ + case 4: /* STROBE HURT */ + if (!player->powers[pw_ironfeet] || (p_random() < 5)) + { + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 20); + } + + break; + + case 9: /* SECRET SECTOR */ + player->secretcount++; + sector->special = 0; + break; + + case 11: /* EXIT SUPER DAMAGE! (for E1M8 finale) */ + player->cheats &= ~CF_GODMODE; + + if (!(leveltime & 0x1f)) p_damage_mobj(player->mo, NULL, NULL, 20); + + if (player->health <= 10) g_exit_level(); + break; + + default: + i_error("p_player_in_special_sector: " + "unknown special %i", + sector->special); + break; + }; +} + +void p_update_specials(void) +{ + anim_t *anim; + int pic; + int i; + line_t *line; + + /* LEVEL TIMER */ + + if (g_level_timer == true) + { + g_level_time_count--; + if (!g_level_time_count) g_exit_level(); + } + + /* ANIMATE FLATS AND TEXTURES GLOBALLY */ + + for (anim = g_anims; anim < g_lastanim; anim++) + { + for (i = anim->basepic; i < anim->basepic + anim->numpics; i++) + { + pic = anim->basepic + + ((leveltime / anim->speed + i) % anim->numpics); + + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + /* ANIMATE LINE SPECIALS */ + + for (i = 0; i < g_numlinespecials; i++) + { + line = g_linespeciallist[i]; + switch (line->special) + { + case 48: /* EFFECT FIRSTCOL SCROLL + */ + sides[line->sidenum[0]].textureoffset += FRACUNIT; + break; + } + } + + /* DO BUTTONS */ + + for (i = 0; i < MAXBUTTONS; i++) + if (buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if (!buttonlist[i].btimer) + { + switch (buttonlist[i].where) + { + case top: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + + case middle: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + + case bottom: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(&buttonlist[i].soundorg, SFX_SWTCHN); +#endif + memset(&buttonlist[i], 0, sizeof(button_t)); + } + } +} + +/* Special Stuff that can not be categorized */ + +int ev_do_donut(line_t *line) +{ + sector_t *s1; + sector_t *s2; + sector_t *s3; + int secnum; + int rtn; + int i; + floormove_t *floor; + fixed_t s3_floorheight; + short s3_floorpic; + + secnum = -1; + rtn = 0; + while ((secnum = p_find_sector_from_line_tag(line, secnum)) >= 0) + { + s1 = §ors[secnum]; + + /* ALREADY MOVING? IF SO, KEEP GOING... */ + + if (s1->specialdata) continue; + + rtn = 1; + s2 = get_next_sector(s1->lines[0], s1); + + /* Vanilla Doom does not check if the linedef is one sided. The + * game does not crash, but reads invalid memory and causes the + * sector floor to move "down" to some unknown height. + * DOSbox prints a warning about an invalid memory access. + * + * I'm not sure exactly what invalid memory is being read. This + * isn't something that should be done, anyway. + * Just print a warning and return. + */ + + if (s2 == NULL) + { + fprintf(stderr, + "ev_do_donut: linedef had no second sidedef! " + "Unexpected behavior may occur in Vanilla Doom. \n"); + break; + } + + for (i = 0; i < s2->linecount; i++) + { + s3 = s2->lines[i]->backsector; + + if (s3 == s1) continue; + + if (s3 == NULL) + { + /* e6y + * s3 is NULL, so + * s3->floorheight is an int at 0000:0000 + * s3->floorpic is a short at 0000:0008 + * Trying to emulate + */ + + fprintf(stderr, "ev_do_donut: WARNING: emulating buffer " + "overrun due to NULL back sector. Unexpected " + "behavior may occur in Vanilla Doom.\n"); + + donut_overrun(&s3_floorheight, &s3_floorpic, line, s1); + } + else + { + s3_floorheight = s3->floorheight; + s3_floorpic = s3->floorpic; + } + + /* Spawn rising slime */ + + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + p_add_thinker(&floor->thinker); + s2->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = FLOOR_DONUTRAISE; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3_floorpic; + floor->newspecial = 0; + floor->floordestheight = s3_floorheight; + + /* Spawn lowering donut-hole */ + + floor = z_malloc(sizeof(*floor), PU_LEVSPEC, 0); + p_add_thinker(&floor->thinker); + s1->specialdata = floor; + floor->thinker.function.acp1 = (actionf_p1)t_move_floor; + floor->type = FLOOR_LOWERFLOOR; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3_floorheight; + break; + } + } + + return rtn; +} + +/* SPECIAL SPAWNING */ + +/* Parses command line parameters. */ + +void p_spawn_specials(void) +{ + sector_t *sector; + int i; + short maxlineanims = + (gameversion <= exe_doom_1_2) ? MAXLINEANIMS1_2 : MAXLINEANIMS; + + /* See if -TIMER was specified. */ + + if (timelimit > 0 && deathmatch) + { + g_level_timer = true; + g_level_time_count = timelimit * 60 * TICRATE; + } + else + { + g_level_timer = false; + } + + /* Init special SECTORs. */ + + sector = sectors; + for (i = 0; i < numsectors; i++, sector++) + { + if (!sector->special) continue; + + switch (sector->special) + { + case 1: /* FLICKERING LIGHTS */ + p_spawn_light_flash(sector); + break; + + case 2: /* STROBE FAST */ + p_spawn_strobe_flash(sector, FASTDARK, 0); + break; + + case 3: /* STROBE SLOW */ + p_spawn_strobe_flash(sector, SLOWDARK, 0); + break; + + case 4: /* STROBE FAST/DEATH SLIME */ + p_spawn_strobe_flash(sector, FASTDARK, 0); + sector->special = 4; + break; + + case 8: /* GLOWING LIGHT */ + p_spawn_glowing_light(sector); + break; + + case 9: /* SECRET SECTOR */ + totalsecret++; + break; + + case 10: /* DOOR CLOSE IN 30 SECONDS */ + p_spawn_door_close_in30(sector); + break; + + case 12: /* SYNC STROBE SLOW */ + p_spawn_strobe_flash(sector, SLOWDARK, 1); + break; + + case 13: /* SYNC STROBE FAST */ + p_spawn_strobe_flash(sector, FASTDARK, 1); + break; + + case 14: /* DOOR RAISE IN 5 MINUTES */ + p_spawn_door_raise_in_5min(sector, i); + break; + + case 17: /* first introduced in official v1.4 beta */ + if (gameversion > exe_doom_1_2) + { + p_spawn_fire_flicker(sector); + } + break; + } + } + + /* Init line EFFECTs */ + + g_numlinespecials = 0; + + for (i = 0; i < numlines; i++) + { + switch (lines[i].special) + { + case 48: + if (g_numlinespecials >= maxlineanims) + { + i_error("p_spawn_specials: Too many scrolling wall linedefs " + "(%d)! (Vanilla limit is %d)", + num_scrollers(), maxlineanims); + } + + /* EFFECT FIRSTCOL SCROLL+ */ + + g_linespeciallist[g_numlinespecials] = &lines[i]; + g_numlinespecials++; + break; + } + } + + /* Init other misc stuff */ + + for (i = 0; i < MAXCEILINGS; i++) + activeceilings[i] = NULL; + + for (i = 0; i < MAXPLATS; i++) + activeplats[i] = NULL; + + for (i = 0; i < MAXBUTTONS; i++) + memset(&buttonlist[i], 0, sizeof(button_t)); + + /* UNUSED: no horizontal sliders. + * p_init_sliding_door_frames(); + */ +} diff --git a/games/NXDoom/src/doom/p_spec.h b/games/NXDoom/src/doom/p_spec.h new file mode 100644 index 000000000..f69c818b4 --- /dev/null +++ b/games/NXDoom/src/doom/p_spec.h @@ -0,0 +1,503 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_spec.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: none + * Implements special effects: + * Texture animation, height or lighting changes according to adjacent + * sectors, respective utility functions, etc. + * + ****************************************************************************/ + +#ifndef __P_SPEC__ +#define __P_SPEC__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Define values for map objects */ + +#define MO_TELEPORTMAN 14 + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +/* max # of wall switches in a level */ + +#define MAXSWITCHES 50 + +/* 4 players, 4 buttons each at once, max. */ + +#define MAXBUTTONS 16 + +/* 1 second, in ticks. */ + +#define BUTTONTIME 35 + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT +#define MAXPLATS 30 + +#define FLOORSPEED FRACUNIT + +#define VDOORSPEED FRACUNIT * 2 +#define VDOORWAIT 150 + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 +#define MAXCEILINGS 30 + +#if 0 /* UNUSED */ + +/* how many frames of animation */ + +#define SNUMFRAMES 4 + +#define SDOORWAIT 35 * 3 +#define SWAITTICS 4 + +/* how many diff. types of anims */ + +#define MAXSLIDEDOORS 5 +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* P_LIGHTS */ + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int count; + int maxlight; + int minlight; +} fireflicker_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; +} lightflash_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; +} strobe_t; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + int minlight; + int maxlight; + int direction; +} glow_t; + +/* P_SWITCH */ + +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} switchlist_t; + +typedef enum +{ + top, + middle, + bottom +} bwhere_e; + +typedef struct +{ + line_t *line; + bwhere_e where; + int btexture; + int btimer; + degenmobj_t *soundorg; +} button_t; + +/* P_PLATS */ + +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + +typedef enum +{ + PLAT_PERPETUALRAISE, + PLAT_DOWNWAITUPSTAY, + PLAT_RAISEANDCHANGE, + PLAT_RAISETONEARESTANDCHANGE, + PLAT_BLAZEDWUS +} plattype_e; + +typedef struct +{ + thinker_t thinker; + sector_t *sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + boolean crush; + int tag; + plattype_e type; +} plat_t; + +/* P_FLOOR */ + +typedef enum +{ + /* lower floor to highest surrounding floor */ + + FLOOR_LOWERFLOOR, + + /* lower floor to lowest surrounding floor */ + + FLOOR_LOWERFLOORTOLOWEST, + + /* lower floor to highest surrounding floor VERY FAST */ + + FLOOR_TURBOLOWER, + + /* raise floor to lowest surrounding CEILING */ + + FLOOR_RAISEFLOOR, + + /* raise floor to next highest surrounding floor */ + + FLOOR_RAISEFLOORTONEAREST, + + /* raise floor to shortest height texture around it */ + + FLOOR_RAISETOTEXTURE, + + /* lower floor to lowest surrounding floor and change floorpic */ + + FLOOR_LOWERANDCHANGE, + + FLOOR_RAISEFLOOR24, + FLOOR_RAISEFLOOR24ANDCHANGE, + FLOOR_RAISEFLOORCRUSH, + + /* raise to next highest floor, turbo-speed */ + + FLOOR_RAISEFLOORTURBO, + FLOOR_DONUTRAISE, + FLOOR_RAISEFLOOR512 +} floor_e; + +typedef enum +{ + STAIR_BUILD8, /* slowly build by 8 */ + STAIR_TURBO16 /* quickly build by 16 */ +} stair_e; + +typedef struct +{ + thinker_t thinker; + floor_e type; + boolean crush; + sector_t *sector; + int direction; + int newspecial; + short texture; + fixed_t floordestheight; + fixed_t speed; +} floormove_t; + +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + +typedef enum +{ + VLD_NORMAL, + VLD_CLOSE30THENOPEN, + VLD_CLOSE, + VLD_OPEN, + VLD_RAISEIN5MINS, + VLD_BLAZERAISE, + VLD_BLAZEOPEN, + VLD_BLAZECLOSE +} vldoor_e; + +typedef struct +{ + thinker_t thinker; + vldoor_e type; + sector_t *sector; + fixed_t topheight; + fixed_t speed; + + /* 1 = up, 0 = waiting at top, -1 = down */ + + int direction; + + /* tics to wait at the top */ + + int topwait; + + /* (keep in case a door going down is reset) + * when it reaches 0, start going down + */ + + int topcountdown; +} vldoor_t; + +/* P_CEILNG */ + +typedef enum +{ + CEIL_LOWERTOFLOOR, + CEIL_RAISETOHIGHEST, + CEIL_LOWERANDCRUSH, + CEIL_CRUSHANDRAISE, + CEIL_FASTCRUSHANDRAISE, + CEIL_SILENTCRUSHANDRAISE +} ceiling_e; + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t *sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + boolean crush; + + /* 1 = up, 0 = waiting, -1 = down */ + + int direction; + + /* ID */ + + int tag; + int olddirection; +} ceiling_t; + +#if 0 /* UNUSED */ + +/* Sliding doors... */ + +typedef enum +{ + sd_opening, + sd_waiting, + sd_closing +} sd_e; + +typedef enum +{ + SDT_OPENONLY, + SDT_CLOSEONLY, + SDT_OPENANDCLOSE +} sdt_e; + +typedef struct +{ + thinker_t thinker; + sdt_e type; + line_t *line; + int frame; + int which_door_index; + int timer; + sector_t *frontsector; + sector_t *backsector; + sd_e status; +} slidedoor_t; + +typedef struct +{ + char front_frame1[9]; + char front_frame2[9]; + char front_frame3[9]; + char front_frame4[9]; + char back_frame1[9]; + char back_frame2[9]; + char back_frame3[9]; + char back_frame4[9]; +} slidename_t; + +typedef struct +{ + int front_frames[4]; + int back_frames[4]; +} slideframe_t; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* End-level timer (-TIMER option) */ + +extern button_t buttonlist[MAXBUTTONS]; + +extern plat_t *activeplats[MAXPLATS]; + +extern ceiling_t *activeceilings[MAXCEILINGS]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* at game start */ + +void p_init_pic_anims(void); + +/* at map load */ + +void p_spawn_specials(void); + +/* every tic */ + +void p_update_specials(void); + +/* when needed */ + +boolean p_use_special_line(mobj_t *thing, line_t *line, int side); + +void p_shoot_special_line(mobj_t *thing, line_t *line); + +void p_cross_special_line(int linenum, int side, mobj_t *thing); + +void p_player_in_special_sector(player_t *player); + +int two_sided(int sector, int line); + +sector_t *get_sector(int current_sector, int line, int side); + +side_t *get_side(int current_sector, int line, int side); + +fixed_t p_find_lowest_floor_surrounding(sector_t *sec); +fixed_t p_find_highest_floor_surrounding(sector_t *sec); + +fixed_t p_find_next_highest_floor(sector_t *sec, int currentheight); + +fixed_t p_find_lowest_ceiling_surrounding(sector_t *sec); +fixed_t p_find_heighest_ceiling_surrounding(sector_t *sec); + +int p_find_sector_from_line_tag(line_t *line, int start); + +int p_find_min_surrounding(sector_t *sector, int max); + +sector_t *get_next_sector(line_t *line, sector_t *sec); + +/* SPECIAL */ + +int ev_do_donut(line_t *line); + +void p_spawn_fire_flicker(sector_t *sector); +void t_light_flash(lightflash_t *flash); +void p_spawn_light_flash(sector_t *sector); +void t_strobe_flash(strobe_t *flash); + +void p_spawn_strobe_flash(sector_t *sector, int fast_or_slow, int in_sync); + +void ev_start_light_strobing(line_t *line); +void ev_turn_tag_lights_off(line_t *line); + +void ev_light_turn_on(line_t *line, int bright); + +void t_glow(glow_t *g); +void p_spawn_glowing_light(sector_t *sector); + +void p_change_switch_texture(line_t *line, int use_again); + +void p_init_switch_list(void); + +void t_plat_raise(plat_t *plat); + +int ev_do_plat(line_t *line, plattype_e type, int amount); + +void p_add_active_plat(plat_t *plat); +void p_remove_active_plat(plat_t *plat); +void ev_stop_plat(line_t *line); +void p_activate_in_stasis(int tag); + +/* P_DOORS */ + +void ev_vertical_door(line_t *line, mobj_t *thing); + +int ev_do_door(line_t *line, vldoor_e type); + +int ev_do_locked_door(line_t *line, vldoor_e type, mobj_t *thing); + +void t_vertical_door(vldoor_t *door); +void p_spawn_door_close_in30(sector_t *sec); + +void p_spawn_door_raise_in_5min(sector_t *sec, int secnum); + +int ev_do_ceiling(line_t *line, ceiling_e type); + +void t_move_ceiling(ceiling_t *ceiling); +void p_add_active_ceiling(ceiling_t *c); +void p_remove_active_ceiling(ceiling_t *c); +int ev_ceiling_crush_stop(line_t *line); +void p_activate_in_stasis_ceiling(line_t *line); + +result_e t_move_plane(sector_t *sector, fixed_t speed, fixed_t dest, + boolean crush, int floor_or_ceiling, int direction); + +int ev_build_stairs(line_t *line, stair_e type); + +int ev_do_floor(line_t *line, floor_e floortype); + +void t_move_floor(floormove_t *floor); + +/* P_TELEPT */ + +int ev_teleport(line_t *line, int side, mobj_t *thing); + +#if 0 /* UNUSED */ +void p_init_sliding_door_frames(void); + +void ev_sliding_door(line_t *line, mobj_t *thing); +#endif + +#endif /* __P_SPEC__ */ diff --git a/games/NXDoom/src/doom/p_switch.c b/games/NXDoom/src/doom/p_switch.c new file mode 100644 index 000000000..27640d5ec --- /dev/null +++ b/games/NXDoom/src/doom/p_switch.c @@ -0,0 +1,596 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_switch.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * DESCRIPTION: + * Switches, buttons. Two-state animation. Exits. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" +#include "doomdef.h" +#include "i_system.h" +#include "p_local.h" + +#include "g_game.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* State. */ + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE */ + +static switchlist_t g_alph_switch_list[] = +{ + {"SW1BRCOM", "SW2BRCOM", 1}, /* Doom shareware episode 1 switches */ + {"SW1BRN1", "SW2BRN1", 1}, + {"SW1BRN2", "SW2BRN2", 1}, + {"SW1BRNGN", "SW2BRNGN", 1}, + {"SW1BROWN", "SW2BROWN", 1}, + {"SW1COMM", "SW2COMM", 1}, + {"SW1COMP", "SW2COMP", 1}, + {"SW1DIRT", "SW2DIRT", 1}, + {"SW1EXIT", "SW2EXIT", 1}, + {"SW1GRAY", "SW2GRAY", 1}, + {"SW1GRAY1", "SW2GRAY1", 1}, + {"SW1METAL", "SW2METAL", 1}, + {"SW1PIPE", "SW2PIPE", 1}, + {"SW1SLAD", "SW2SLAD", 1}, + {"SW1STARG", "SW2STARG", 1}, + {"SW1STON1", "SW2STON1", 1}, + {"SW1STON2", "SW2STON2", 1}, + {"SW1STONE", "SW2STONE", 1}, + {"SW1STRTN", "SW2STRTN", 1}, + {"SW1BLUE", "SW2BLUE", 2}, /* Doom registered episodes 2 & 3 switches */ + {"SW1CMT", "SW2CMT", 2}, + {"SW1GARG", "SW2GARG", 2}, + {"SW1GSTON", "SW2GSTON", 2}, + {"SW1HOT", "SW2HOT", 2}, + {"SW1LION", "SW2LION", 2}, + {"SW1SATYR", "SW2SATYR", 2}, + {"SW1SKIN", "SW2SKIN", 2}, + {"SW1VINE", "SW2VINE", 2}, + {"SW1WOOD", "SW2WOOD", 2}, + {"SW1PANEL", "SW2PANEL", 3}, /* Doom II switches */ + {"SW1ROCK", "SW2ROCK", 3}, + {"SW1MET2", "SW2MET2", 3}, + {"SW1WDMET", "SW2WDMET", 3}, + {"SW1BRIK", "SW2BRIK", 3}, + {"SW1MOD1", "SW2MOD1", 3}, + {"SW1ZIM", "SW2ZIM", 3}, + {"SW1STON6", "SW2STON6", 3}, + {"SW1TEK", "SW2TEK", 3}, + {"SW1MARB", "SW2MARB", 3}, + {"SW1SKULL", "SW2SKULL", 3}, +}; + +static int g_numswitches; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int switchlist[MAXSWITCHES * 2]; +button_t buttonlist[MAXBUTTONS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Start a button counting down till it turns off. */ + +static void p_start_button(line_t *line, bwhere_e w, int texture, int time) +{ + int i; + + /* See if button is already pressed */ + + for (i = 0; i < MAXBUTTONS; i++) + { + if (buttonlist[i].btimer && buttonlist[i].line == line) + { + return; + } + } + + for (i = 0; i < MAXBUTTONS; i++) + { + if (!buttonlist[i].btimer) + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + buttonlist[i].soundorg = &line->frontsector->soundorg; + return; + } + } + + i_error("P_StartButton: no button slots left!"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* p_init_switch_list + * Only called at game initialization. + */ + +void p_init_switch_list(void) +{ + int i; + int slindex; + int episode; + + /* Note that this is called "episode" here but it's actually something + * quite different. As we progress from Shareware->Registered->Doom II + * we support more switch textures. + */ + + switch (gamemode) + { + case registered: + case retail: + episode = 2; + break; + case commercial: + episode = 3; + break; + default: + episode = 1; + break; + } + + slindex = 0; + + for (i = 0; i < arrlen(g_alph_switch_list); i++) + { + if (g_alph_switch_list[i].episode <= episode) + { + switchlist[slindex++] = + r_texture_num_for_name((g_alph_switch_list[i].name1)); + switchlist[slindex++] = + r_texture_num_for_name((g_alph_switch_list[i].name2)); + } + } + + g_numswitches = slindex / 2; + switchlist[slindex] = -1; +} + +/* Function that changes wall texture. + * Tell it if switch is ok to use again (1=yes, it's a button). + */ + +void p_change_switch_texture(line_t *line, int use_again) +{ + int tex_top; + int tex_mid; + int tex_bot; + int i; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int sound; +#endif + + if (!use_again) line->special = 0; + + tex_top = sides[line->sidenum[0]].toptexture; + tex_mid = sides[line->sidenum[0]].midtexture; + tex_bot = sides[line->sidenum[0]].bottomtexture; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + sound = SFX_SWTCHN; + + /* EXIT SWITCH? */ + + if (line->special == 11) sound = SFX_SWTCHX; +#endif + + for (i = 0; i < g_numswitches * 2; i++) + { + if (switchlist[i] == tex_top) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].toptexture = switchlist[i ^ 1]; + + if (use_again) + { + p_start_button(line, top, switchlist[i], BUTTONTIME); + } + + return; + } + else + { + if (switchlist[i] == tex_mid) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].midtexture = switchlist[i ^ 1]; + + if (use_again) + p_start_button(line, middle, switchlist[i], BUTTONTIME); + + return; + } + else + { + if (switchlist[i] == tex_bot) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(buttonlist->soundorg, sound); +#endif + sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1]; + + if (use_again) + p_start_button(line, bottom, switchlist[i], BUTTONTIME); + + return; + } + } + } + } +} + +/* p_use_special_line + * Called when a thing uses a special line. + * Only the front sides of lines are usable. + */ + +boolean p_use_special_line(mobj_t *thing, line_t *line, int side) +{ + /* Err... + * Use the back sides of VERY SPECIAL lines... + */ + + if (side) + { + switch (line->special) + { + case 124: + + /* Sliding door open & close + * UNUSED? + */ + + break; + + default: + return false; + break; + } + } + + /* Switches that other things can activate. */ + + if (!thing->player) + { + /* never open secret doors */ + + if (line->flags & ML_SECRET) return false; + + switch (line->special) + { + case 1: /* MANUAL DOOR RAISE */ + case 32: /* MANUAL BLUE */ + case 33: /* MANUAL RED */ + case 34: /* MANUAL YELLOW */ + break; + + default: + return false; + break; + } + } + + /* do something */ + + switch (line->special) + { + /* MANUALS */ + + case 1: /* Vertical Door */ + case 26: /* Blue Door/Locked */ + case 27: /* Yellow Door /Locked */ + case 28: /* Red Door /Locked */ + + case 31: /* Manual door open */ + case 32: /* Blue locked door open */ + case 33: /* Red locked door open */ + case 34: /* Yellow locked door open */ + + case 117: /* Blazing door raise */ + case 118: /* Blazing door open */ + ev_vertical_door(line, thing); + break; + + /* UNUSED - Door Slide Open&Close + * case 124: + * ev_sliding_door (line, thing); + * break; + */ + + /* SWITCHES */ + + case 7: /* Build Stairs */ + if (ev_build_stairs(line, STAIR_BUILD8)) + p_change_switch_texture(line, 0); + break; + + case 9: /* Change Donut */ + if (ev_do_donut(line)) p_change_switch_texture(line, 0); + break; + + case 11: /* Exit level */ + p_change_switch_texture(line, 0); + g_exit_level(); + break; + + case 14: /* Raise Floor 32 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 32)) + p_change_switch_texture(line, 0); + break; + + case 15: /* Raise Floor 24 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 24)) + p_change_switch_texture(line, 0); + break; + + case 18: /* Raise Floor to next highest floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST)) + p_change_switch_texture(line, 0); + break; + + case 20: /* Raise Plat next highest floor and change texture */ + if (ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0)) + p_change_switch_texture(line, 0); + break; + + case 21: /* PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 0)) + p_change_switch_texture(line, 0); + break; + + case 23: /* Lower Floor to Lowest */ + if (ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST)) + p_change_switch_texture(line, 0); + break; + + case 29: /* Raise Door */ + if (ev_do_door(line, VLD_NORMAL)) p_change_switch_texture(line, 0); + break; + + case 41: /* Lower Ceiling to Floor */ + if (ev_do_ceiling(line, CEIL_LOWERTOFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 71: /* Turbo Lower Floor */ + if (ev_do_floor(line, FLOOR_TURBOLOWER)) + p_change_switch_texture(line, 0); + break; + + case 49: /* Ceiling Crush And Raise */ + if (ev_do_ceiling(line, CEIL_CRUSHANDRAISE)) + p_change_switch_texture(line, 0); + break; + + case 50: /* Close Door */ + if (ev_do_door(line, VLD_CLOSE)) p_change_switch_texture(line, 0); + break; + + case 51: /* Secret EXIT */ + p_change_switch_texture(line, 0); + g_secret_exit_level(); + break; + + case 55: /* Raise Floor Crush */ + if (ev_do_floor(line, FLOOR_RAISEFLOORCRUSH)) + p_change_switch_texture(line, 0); + break; + + case 101: /* Raise Floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 102: /* Lower Floor to Surrounding floor height */ + if (ev_do_floor(line, FLOOR_LOWERFLOOR)) + p_change_switch_texture(line, 0); + break; + + case 103: /* Open Door */ + if (ev_do_door(line, VLD_OPEN)) p_change_switch_texture(line, 0); + break; + + case 111: /* Blazing Door Raise (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZERAISE)) p_change_switch_texture(line, 0); + break; + + case 112: /* Blazing Door Open (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZEOPEN)) p_change_switch_texture(line, 0); + break; + + case 113: /* Blazing Door Close (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZECLOSE)) p_change_switch_texture(line, 0); + break; + + case 122: /* Blazing PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_BLAZEDWUS, 0)) + p_change_switch_texture(line, 0); + break; + + case 127: /* Build Stairs Turbo 16 */ + if (ev_build_stairs(line, STAIR_TURBO16)) + p_change_switch_texture(line, 0); + break; + + case 131: /* Raise Floor Turbo */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTURBO)) + p_change_switch_texture(line, 0); + break; + + case 133: /* BlzOpenDoor BLUE */ + case 135: /* BlzOpenDoor RED */ + case 137: /* BlzOpenDoor YELLOW */ + if (ev_do_locked_door(line, VLD_BLAZEOPEN, thing)) + p_change_switch_texture(line, 0); + break; + + case 140: /* Raise Floor 512 */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR512)) + p_change_switch_texture(line, 0); + break; + + /* BUTTONS */ + + case 42: /* Close Door */ + if (ev_do_door(line, VLD_CLOSE)) p_change_switch_texture(line, 1); + break; + + case 43: /* Lower Ceiling to Floor */ + if (ev_do_ceiling(line, CEIL_LOWERTOFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 45: /* Lower Floor to Surrounding floor height */ + if (ev_do_floor(line, FLOOR_LOWERFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 60: /* Lower Floor to Lowest */ + if (ev_do_floor(line, FLOOR_LOWERFLOORTOLOWEST)) + p_change_switch_texture(line, 1); + break; + + case 61: /* Open Door */ + if (ev_do_door(line, VLD_OPEN)) p_change_switch_texture(line, 1); + break; + + case 62: /* PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_DOWNWAITUPSTAY, 1)) + p_change_switch_texture(line, 1); + break; + + case 63: /* Raise Door */ + if (ev_do_door(line, VLD_NORMAL)) p_change_switch_texture(line, 1); + break; + + case 64: /* Raise Floor to ceiling */ + if (ev_do_floor(line, FLOOR_RAISEFLOOR)) + p_change_switch_texture(line, 1); + break; + + case 66: /* Raise Floor 24 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 24)) + p_change_switch_texture(line, 1); + break; + + case 67: /* Raise Floor 32 and change texture */ + if (ev_do_plat(line, PLAT_RAISEANDCHANGE, 32)) + p_change_switch_texture(line, 1); + break; + + case 65: /* Raise Floor Crush */ + if (ev_do_floor(line, FLOOR_RAISEFLOORCRUSH)) + p_change_switch_texture(line, 1); + break; + + case 68: /* Raise Plat to next highest floor and change texture */ + if (ev_do_plat(line, PLAT_RAISETONEARESTANDCHANGE, 0)) + p_change_switch_texture(line, 1); + break; + + case 69: /* Raise Floor to next highest floor */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTONEAREST)) + p_change_switch_texture(line, 1); + break; + + case 70: /* Turbo Lower Floor */ + if (ev_do_floor(line, FLOOR_TURBOLOWER)) + p_change_switch_texture(line, 1); + break; + + case 114: /* Blazing Door Raise (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZERAISE)) p_change_switch_texture(line, 1); + break; + + case 115: /* Blazing Door Open (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZEOPEN)) p_change_switch_texture(line, 1); + break; + + case 116: /* Blazing Door Close (faster than TURBO!) */ + if (ev_do_door(line, VLD_BLAZECLOSE)) p_change_switch_texture(line, 1); + break; + + case 123: /* Blazing PlatDownWaitUpStay */ + if (ev_do_plat(line, PLAT_BLAZEDWUS, 0)) + p_change_switch_texture(line, 1); + break; + + case 132: /* Raise Floor Turbo */ + if (ev_do_floor(line, FLOOR_RAISEFLOORTURBO)) + p_change_switch_texture(line, 1); + break; + + case 99: /* BlzOpenDoor BLUE */ + case 134: /* BlzOpenDoor RED */ + case 136: /* BlzOpenDoor YELLOW */ + if (ev_do_locked_door(line, VLD_BLAZEOPEN, thing)) + p_change_switch_texture(line, 1); + break; + + case 138: + + /* Light Turn On */ + + ev_light_turn_on(line, 255); + p_change_switch_texture(line, 1); + break; + + case 139: + + /* Light Turn Off */ + + ev_light_turn_on(line, 35); + p_change_switch_texture(line, 1); + break; + } + + return true; +} diff --git a/games/NXDoom/src/doom/p_telept.c b/games/NXDoom/src/doom/p_telept.c new file mode 100644 index 000000000..3986a0543 --- /dev/null +++ b/games/NXDoom/src/doom/p_telept.c @@ -0,0 +1,141 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_telept.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Teleportation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" +#include "doomstat.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "p_local.h" + +/* State. */ + +#include "r_state.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int ev_teleport(line_t *line, int side, mobj_t *thing) +{ + int i; + int tag; + mobj_t *m; + mobj_t *fog; + unsigned an; + thinker_t *thinker; + sector_t *sector; + fixed_t oldx; + fixed_t oldy; + fixed_t oldz; + + /* don't teleport missiles */ + + if (thing->flags & MF_MISSILE) return 0; + + /* Don't teleport if hit back of line, so you can get out of teleporter. */ + + if (side == 1) return 0; + + tag = line->tag; + for (i = 0; i < numsectors; i++) + { + if (sectors[i].tag == tag) + { + for (thinker = thinkercap.next; thinker != &thinkercap; + thinker = thinker->next) + { + /* not a mobj */ + + if (thinker->function.acp1 != (actionf_p1)p_mobj_thinker) + continue; + + m = (mobj_t *)thinker; + + /* not a teleportman */ + + if (m->type != MT_TELEPORTMAN) continue; + + sector = m->subsector->sector; + + /* wrong sector */ + + if (sector - sectors != i) continue; + + oldx = thing->x; + oldy = thing->y; + oldz = thing->z; + + if (!p_teleport_move(thing, m->x, m->y)) return 0; + + /* The first Final Doom executable does not set thing->z + * when teleporting. This quirk is unique to this + * particular version; the later version included in + * some versions of the Id Anthology fixed this. + */ + + if (gameversion != exe_final) thing->z = thing->floorz; + + if (thing->player) + thing->player->viewz = thing->z + thing->player->viewheight; + + /* spawn teleport fog at source and destination */ + + fog = p_spawn_mobj(oldx, oldy, oldz, MT_TFOG); +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(fog, SFX_TELEPT); +#endif + an = m->angle >> ANGLETOFINESHIFT; + fog = p_spawn_mobj(m->x + 20 * finecosine[an], + m->y + 20 * finesine[an], + thing->z, MT_TFOG); + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + + /* emit sound, where? */ + + s_start_sound(fog, SFX_TELEPT); +#else + UNUSED(fog); +#endif + + /* don't move for a bit */ + + if (thing->player) thing->reactiontime = 18; + + thing->angle = m->angle; + thing->momx = thing->momy = thing->momz = 0; + return 1; + } + } + } + + return 0; +} diff --git a/games/NXDoom/src/doom/p_tick.c b/games/NXDoom/src/doom/p_tick.c new file mode 100644 index 000000000..d2655dd18 --- /dev/null +++ b/games/NXDoom/src/doom/p_tick.c @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_tick.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * Thinker, Ticker. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "p_local.h" +#include "z_zone.h" + +#include "doomstat.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int leveltime; + +/* THINKERS + * All thinkers should be allocated by z_malloc + * so they can be operated on uniformly. + * The actual structures will vary in size, + * but the first element must be thinker_t. + */ + +/* Both the head and tail of the thinker list. */ + +thinker_t thinkercap; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void p_run_thinkers(void) +{ + thinker_t *currentthinker, *nextthinker; + + currentthinker = thinkercap.next; + while (currentthinker != &thinkercap) + { + if (currentthinker->function.acv == (actionf_v)(-1)) + { + /* time to remove it */ + + nextthinker = currentthinker->next; + currentthinker->next->prev = currentthinker->prev; + currentthinker->prev->next = currentthinker->next; + z_free(currentthinker); + } + else + { + if (currentthinker->function.acp1) + currentthinker->function.acp1(currentthinker); + nextthinker = currentthinker->next; + } + + currentthinker = nextthinker; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_init_thinkers(void) +{ + thinkercap.prev = thinkercap.next = &thinkercap; +} + +/* p_add_thinker + * Adds a new thinker at the end of the list. + */ + +void p_add_thinker(thinker_t *thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; +} + +/* p_remove_thinker Deallocation is lazy -- it will not actually be freed + * until its thinking turn comes up. + */ + +void p_remove_thinker(thinker_t *thinker) +{ + /* FIXME: NOP. */ + + thinker->function.acv = (actionf_v)(-1); +} + +void p_ticker(void) +{ + int i; + + /* run the tic */ + + if (paused) return; + + /* pause if in menu and at least one tic has been run */ + + if (!netgame && g_menuactive && !demoplayback && + players[consoleplayer].viewz != 1) + { + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + p_player_think(&players[i]); + } + } + + p_run_thinkers(); + p_update_specials(); + p_respawn_specials(); + + /* for par times */ + + leveltime++; +} diff --git a/games/NXDoom/src/doom/p_tick.h b/games/NXDoom/src/doom/p_tick.h new file mode 100644 index 000000000..0b2d5dee1 --- /dev/null +++ b/games/NXDoom/src/doom/p_tick.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_tick.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: ? + * + ****************************************************************************/ + +#ifndef __P_TICK__ +#define __P_TICK__ + +/* Called by C_Ticker, can call G_PlayerExited. + * Carries out all thinking of monsters and players. + */ + +void p_ticker(void); + +#endif /* __P_TICK__ */ diff --git a/games/NXDoom/src/doom/p_user.c b/games/NXDoom/src/doom/p_user.c new file mode 100644 index 000000000..0a5d0107b --- /dev/null +++ b/games/NXDoom/src/doom/p_user.c @@ -0,0 +1,371 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/p_user.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Player related stuff. + * Bobbing POV/weapon, movement. + * Pending weapon. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomdef.h" + +#include "p_local.h" + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Index of the special effects (INVUL inverse) map. */ + +#define INVERSECOLORMAP 32 + +/* Movement. */ + +/* 16 pixels of bob */ + +#define MAXBOB 0x100000 + +#define ANG5 (ANG90 / 18) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +boolean onground; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* P_Thrust + * Moves the given origin along a given angle. + */ + +static void p_thrust(player_t *player, angle_t angle, fixed_t move) +{ + angle >>= ANGLETOFINESHIFT; + + player->mo->momx += fixed_mul(move, finecosine[angle]); + player->mo->momy += fixed_mul(move, finesine[angle]); +} + +/* Calculate the walking / running height adjustment */ + +static void p_calc_height(player_t *player) +{ + int angle; + fixed_t bob; + + /* Regular movement bobbing + * (needs to be calculated for gun swing + * even if not on ground) + * OPTIMIZE: tablify angle + * Note: a LUT allows for effects + * like a ramp with low health. + */ + + player->bob = fixed_mul(player->mo->momx, player->mo->momx) + + fixed_mul(player->mo->momy, player->mo->momy); + + player->bob >>= 2; + + if (player->bob > MAXBOB) player->bob = MAXBOB; + + if ((player->cheats & CF_NOMOMENTUM) || !onground) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; + + player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES / 20 * leveltime) & FINEMASK; + bob = fixed_mul(player->bob / 2, finesine[angle]); + + /* move viewheight */ + + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT / 2) + { + player->viewheight = VIEWHEIGHT / 2; + if (player->deltaviewheight <= 0) player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT / 4; + if (!player->deltaviewheight) player->deltaviewheight = 1; + } + } + + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT) + player->viewz = player->mo->ceilingz - 4 * FRACUNIT; +} + +static void p_move_player(player_t *player) +{ + ticcmd_t *cmd; + + cmd = &player->cmd; + + player->mo->angle += (cmd->angleturn << FRACBITS); + + /* Do not let the player control movement if not onground. */ + + onground = (player->mo->z <= player->mo->floorz); + + if (cmd->forwardmove && onground) + p_thrust(player, player->mo->angle, cmd->forwardmove * 2048); + + if (cmd->sidemove && onground) + p_thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048); + + if ((cmd->forwardmove || cmd->sidemove) && + player->mo->state == &states[S_PLAY]) + { + p_set_mobj_state(player->mo, S_PLAY_RUN1); + } +} + +/* P_DeathThink + * Fall on your face when dying. + * Decrease POV height to floor height. + */ + +static void p_death_think(player_t *player) +{ + angle_t angle; + angle_t delta; + + p_move_psprites(player); + + /* fall to the ground */ + + if (player->viewheight > 6 * FRACUNIT) player->viewheight -= FRACUNIT; + + if (player->viewheight < 6 * FRACUNIT) player->viewheight = 6 * FRACUNIT; + + player->deltaviewheight = 0; + onground = (player->mo->z <= player->mo->floorz); + p_calc_height(player); + + if (player->attacker && player->attacker != player->mo) + { + angle = r_point_to_angle2(player->mo->x, player->mo->y, + player->attacker->x, player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta > (unsigned)-ANG5) + { + /* Looking at killer, so fade damage flash down. */ + + player->mo->angle = angle; + + if (player->damagecount) player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + if (player->cmd.buttons & BT_USE) player->playerstate = PST_REBORN; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void p_player_think(player_t *player) +{ + ticcmd_t *cmd; + weapontype_t newweapon; + + /* fixme: do this in the cheat code */ + + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + /* chain saw run forward */ + + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800 / 512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + if (player->playerstate == PST_DEAD) + { + p_death_think(player); + return; + } + + /* Move around. + * Reactiontime is used to prevent movement for a bit after a teleport. + */ + + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + p_move_player(player); + + p_calc_height(player); + + if (player->mo->subsector->sector->special) + { + p_player_in_special_sector(player); + } + + /* Check for weapon change. */ + + /* A special event has no other buttons. */ + + if (cmd->buttons & BT_SPECIAL) cmd->buttons = 0; + + if (cmd->buttons & BT_CHANGE) + { + /* The actual changing of the weapon is done + * when the weapon psprite can do it + * (read: not in the middle of an attack). + */ + + newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT; + + if (newweapon == wp_fist && player->weaponowned[wp_chainsaw] && + !(player->readyweapon == wp_chainsaw && + player->powers[pw_strength])) + { + newweapon = wp_chainsaw; + } + + if ((gamemode == commercial) && newweapon == wp_shotgun && + player->weaponowned[wp_supershotgun] && + player->readyweapon != wp_supershotgun) + { + newweapon = wp_supershotgun; + } + + if (player->weaponowned[newweapon] && newweapon != player->readyweapon) + { + /* Do not go to plasma or BFG in shareware, even if cheated. */ + + if ((newweapon != wp_plasma && newweapon != wp_bfg) || + (gamemode != shareware)) + { + player->pendingweapon = newweapon; + } + } + } + + /* check for use */ + + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + p_use_lines(player); + player->usedown = true; + } + } + else + player->usedown = false; + + /* cycle psprites */ + + p_move_psprites(player); + + /* Counters, time dependent power ups. */ + + /* Strength counts up to diminish fade. */ + + if (player->powers[pw_strength]) player->powers[pw_strength]++; + + if (player->powers[pw_invulnerability]) + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility]) + { + if (!--player->powers[pw_invisibility]) + { + player->mo->flags &= ~MF_SHADOW; + } + } + + if (player->powers[pw_infrared]) player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet]) player->powers[pw_ironfeet]--; + + if (player->damagecount) player->damagecount--; + + if (player->bonuscount) player->bonuscount--; + + /* Handling colormaps. */ + + if (player->powers[pw_invulnerability]) + { + if (player->powers[pw_invulnerability] > 4 * 32 || + (player->powers[pw_invulnerability] & 8)) + player->fixedcolormap = INVERSECOLORMAP; + else + player->fixedcolormap = 0; + } + else if (player->powers[pw_infrared]) + { + if (player->powers[pw_infrared] > 4 * 32 || + (player->powers[pw_infrared] & 8)) + { + /* almost full bright */ + + player->fixedcolormap = 1; + } + else + player->fixedcolormap = 0; + } + else + player->fixedcolormap = 0; +} diff --git a/games/NXDoom/src/doom/r_bsp.c b/games/NXDoom/src/doom/r_bsp.c new file mode 100644 index 000000000..605b21bea --- /dev/null +++ b/games/NXDoom/src/doom/r_bsp.c @@ -0,0 +1,684 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_bsp.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * BSP traversal, handling of LineSegs for rendering. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +#include "m_bbox.h" + +#include "i_system.h" + +#include "r_main.h" +#include "r_plane.h" +#include "r_things.h" + +#include "doomstat.h" +#include "r_state.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* We must expand MAXSEGS to the theoretical limit of the number of solidsegs + * that can be generated in a scene by the DOOM engine. This was determined + * by Lee Killough during BOOM development to be a function of the + * screensize. The simplest thing we can do, other than fix this bug, is to + * let the game render overage and then bomb out by detecting the overflow + * after the fact. -haleyjd + */ + +/* #define MAXSEGS 32 */ + +#define MAXSEGS (SCREENWIDTH / 2 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* ClipWallSegment + * Clips the given range of columns and includes it in the new clip list. + */ + +typedef struct +{ + int first; + int last; +} cliprange_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector; +sector_t *backsector; + +drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; +drawseg_t *ds_p; + +/* newend is one past the last valid seg */ + +cliprange_t *newend; +cliprange_t solidsegs[MAXSEGS]; + +int checkcoord[12][4] = +{ + { + 3, + 0, + 2, + 1, + }, + { + 3, + 0, + 2, + 0, + }, + { + 3, + 1, + 2, + 0, + }, + { + 0, + }, + { + 2, + 0, + 2, + 1, + }, + { + 0, + 0, + 0, + 0, + }, + { + 3, + 1, + 3, + 0, + }, + { + 0, + }, + { + 2, + 0, + 3, + 1, + }, + { + 2, + 1, + 3, + 1, + }, + { + 2, + 1, + 3, + 0, + }, +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_store_wall_range(int start, int stop); +void r_clip_solid_wall_segment(int first, int last); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_clip_pass_wall_segment + * + * Description: + * Clips the given range of columns, + * but does not includes it in the clip list. + * Does handle windows, + * e.g. LineDefs with upper and lower texture. + * + ****************************************************************************/ + +static void r_clip_pass_wall_segment(int first, int last) +{ + cliprange_t *start; + + /* Find the first range that touches the range + * (adjacent pixels are touching). + */ + + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + /* Post is entirely visible (above start). */ + + r_store_wall_range(first, last); + return; + } + + /* There is a fragment above *start. */ + + r_store_wall_range(first, start->first - 1); + } + + /* Bottom contained in start? */ + + if (last <= start->last) return; + + while (last >= (start + 1)->first - 1) + { + /* There is a fragment between two posts. */ + + r_store_wall_range(start->last + 1, (start + 1)->first - 1); + start++; + + if (last <= start->last) return; + } + + /* There is a fragment after *next. */ + + r_store_wall_range(start->last + 1, last); +} + +/* r_check_b_box + * Checks BSP node/subtree bounding box. + * Returns true if some part of the bbox might be visible. + */ + +static boolean r_check_b_box(fixed_t *bspcoord) +{ + int boxx; + int boxy; + int boxpos; + + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + cliprange_t *start; + + int sx1; + int sx2; + + /* Find the corners of the box + * that define the edges from current viewpoint. + */ + + if (viewx <= bspcoord[BOXLEFT]) + boxx = 0; + else if (viewx < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (viewy >= bspcoord[BOXTOP]) + boxy = 0; + else if (viewy > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) return true; + + x1 = bspcoord[checkcoord[boxpos][0]]; + y1 = bspcoord[checkcoord[boxpos][1]]; + x2 = bspcoord[checkcoord[boxpos][2]]; + y2 = bspcoord[checkcoord[boxpos][3]]; + + /* check clip list for an open space */ + + angle1 = r_point_to_angle(x1, y1) - viewangle; + angle2 = r_point_to_angle(x2, y2) - viewangle; + + span = angle1 - angle2; + + /* Sitting on a line? */ + + if (span >= ANG180) return true; + + tspan = angle1 + clipangle; + + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + /* Totally off the left edge? */ + + if (tspan >= span) return false; + + angle1 = clipangle; + } + + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + /* Totally off the left edge? */ + + if (tspan >= span) return false; + + angle2 = -clipangle; + } + + /* Find the first clippost + * that touches the source post + * (adjacent pixels are touching). + */ + + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + sx1 = viewangletox[angle1]; + sx2 = viewangletox[angle2]; + + /* Does not cross a pixel. */ + + if (sx1 == sx2) return false; + sx2--; + + start = solidsegs; + while (start->last < sx2) + start++; + + if (sx1 >= start->first && sx2 <= start->last) + { + /* The clippost contains the new span. */ + + return false; + } + + return true; +} + +/**************************************************************************** + * Name: r_add_line + * + * Description: + * Clips the given segment + * and adds any visible pieces to the line list. + * + ****************************************************************************/ + +static void r_add_line(seg_t *line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + + curline = line; + + /* OPTIMIZE: quickly reject orthogonal back sides. */ + + angle1 = r_point_to_angle(line->v1->x, line->v1->y); + angle2 = r_point_to_angle(line->v2->x, line->v2->y); + + /* Clip to view edges. + * OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW). + */ + + span = angle1 - angle2; + + /* Back side? I.e. backface culling? */ + + if (span >= ANG180) return; + + /* Global angle needed by segcalc. */ + + rw_angle1 = angle1; + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + /* Totally off the left edge? */ + + if (tspan >= span) return; + + angle1 = clipangle; + } + + tspan = clipangle - angle2; + if (tspan > 2 * clipangle) + { + tspan -= 2 * clipangle; + + /* Totally off the left edge? */ + + if (tspan >= span) return; + angle2 = -clipangle; + } + + /* The seg is in the view range, but not necessarily visible. */ + + angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT; + angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT; + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + + /* Does not cross a pixel? */ + + if (x1 == x2) return; + + backsector = line->backsector; + + /* Single sided line? */ + + if (!backsector) goto clipsolid; + + /* Closed door. */ + + if (backsector->ceilingheight <= frontsector->floorheight || + backsector->floorheight >= frontsector->ceilingheight) + { + goto clipsolid; + } + + /* Window. */ + + if (backsector->ceilingheight != frontsector->ceilingheight || + backsector->floorheight != frontsector->floorheight) + { + goto clippass; + } + + /* Reject empty lines used for triggers + * and special events. + * Identical floor and ceiling on both sides, + * identical light levels on both sides, + * and no middle texture. + */ + + if (backsector->ceilingpic == frontsector->ceilingpic && + backsector->floorpic == frontsector->floorpic && + backsector->lightlevel == frontsector->lightlevel && + curline->sidedef->midtexture == 0) + { + return; + } + +clippass: + r_clip_pass_wall_segment(x1, x2 - 1); + return; + +clipsolid: + r_clip_solid_wall_segment(x1, x2 - 1); +} + +/* r_subsector + * Determine floor/ceiling planes. + * Add sprites of things in sector. + * Draw one or more line segments. + */ + +static void r_subsector(int num) +{ + int count; + seg_t *line; + subsector_t *sub; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (num >= numsubsectors) + i_error("R_Subsector: ss %i with numss = %i", num, numsubsectors); +#endif + + sscount++; + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + if (frontsector->floorheight < viewz) + { + floorplane = + r_find_plane(frontsector->floorheight, frontsector->floorpic, + frontsector->lightlevel); + } + else + floorplane = NULL; + + if (frontsector->ceilingheight > viewz || + frontsector->ceilingpic == skyflatnum) + { + ceilingplane = + r_find_plane(frontsector->ceilingheight, frontsector->ceilingpic, + frontsector->lightlevel); + } + else + ceilingplane = NULL; + + r_add_sprites(frontsector); + + while (count--) + { + r_add_line(line); + line++; + } + + /* check for solidsegs overflow - extremely unsatisfactory! */ + + if (newend > &solidsegs[32]) + { + i_error("R_Subsector: solidsegs overflow (vanilla may crash here)\n"); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_clear_draw_segs + ****************************************************************************/ + +void r_clear_draw_segs(void) +{ + ds_p = drawsegs; +} + +/**************************************************************************** + * Name: r_clip_solid_wall_segment + * + * Description: + * Does handle solid walls, e.g. single sided LineDefs (middle texture) + * that entirely block the view. + * + ****************************************************************************/ + +void r_clip_solid_wall_segment(int first, int last) +{ + cliprange_t *next; + cliprange_t *start; + + /* Find the first range that touches the range + * (adjacent pixels are touching). + */ + + start = solidsegs; + while (start->last < first - 1) + start++; + + if (first < start->first) + { + if (last < start->first - 1) + { + /* Post is entirely visible (above start), + * so insert a new clippost. + */ + + r_store_wall_range(first, last); + next = newend; + newend++; + + while (next != start) + { + *next = *(next - 1); + next--; + } + + next->first = first; + next->last = last; + return; + } + + /* There is a fragment above *start. */ + + r_store_wall_range(first, start->first - 1); + + /* Now adjust the clip size. */ + + start->first = first; + } + + /* Bottom contained in start? */ + + if (last <= start->last) return; + + next = start; + while (last >= (next + 1)->first - 1) + { + /* There is a fragment between two posts. */ + + r_store_wall_range(next->last + 1, (next + 1)->first - 1); + next++; + + if (last <= next->last) + { + /* Bottom is contained in next. + * Adjust the clip size. + */ + + start->last = next->last; + goto crunch; + } + } + + /* There is a fragment after *next. */ + + r_store_wall_range(next->last + 1, last); + + /* Adjust the clip size. */ + + start->last = last; + + /* Remove start+1 to next from the clip list, + * because start now covers their area. + */ + +crunch: + if (next == start) + { + /* Post just extended past the bottom of one post. */ + + return; + } + + while (next++ != newend) + { + /* Remove a post. */ + + *++start = *next; + } + + newend = start + 1; +} + +/**************************************************************************** + * Name: r_clear_clip_segs + ****************************************************************************/ + +void r_clear_clip_segs(void) +{ + solidsegs[0].first = -0x7fffffff; + solidsegs[0].last = -1; + solidsegs[1].first = viewwidth; + solidsegs[1].last = 0x7fffffff; + newend = solidsegs + 2; +} + +/* RenderBSPNode + * Renders all subsectors below a given node, traversing subtree recursively. + * Just call with BSP root. + */ + +void r_render_bsp_node(int bspnum) +{ + node_t *bsp; + int side; + + /* Found a subsector? */ + + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + r_subsector(0); + else + r_subsector(bspnum & (~NF_SUBSECTOR)); + return; + } + + bsp = &nodes[bspnum]; + + /* Decide which side the view point is on. */ + + side = r_point_on_side(viewx, viewy, bsp); + + /* Recursively divide front space. */ + + r_render_bsp_node(bsp->children[side]); + + /* Possibly divide back space. */ + + if (r_check_b_box(bsp->bbox[side ^ 1])) + r_render_bsp_node(bsp->children[side ^ 1]); +} diff --git a/games/NXDoom/src/doom/r_bsp.h b/games/NXDoom/src/doom/r_bsp.h new file mode 100644 index 000000000..66c6d611f --- /dev/null +++ b/games/NXDoom/src/doom/r_bsp.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_bsp.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh module, BSP traversal and handling. + * + ****************************************************************************/ + +#ifndef __R_BSP__ +#define __R_BSP__ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*drawfunc_t)(int start, int stop); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern seg_t *curline; +extern side_t *sidedef; +extern line_t *linedef; +extern sector_t *frontsector; +extern sector_t *backsector; + +extern int rw_x; +extern int rw_stopx; + +extern boolean segtextured; + +/* false if the back side is the same plane */ + +extern boolean markfloor; +extern boolean markceiling; + +extern boolean skymap; + +extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; +extern drawseg_t *ds_p; + +extern lighttable_t **hscalelight; +extern lighttable_t **vscalelight; +extern lighttable_t **dscalelight; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* BSP? */ + +void r_clear_clip_segs(void); +void r_clear_draw_segs(void); + +void r_render_bsp_node(int bspnum); + +#endif /* __R_BSP__ */ diff --git a/games/NXDoom/src/doom/r_data.c b/games/NXDoom/src/doom/r_data.c new file mode 100644 index 000000000..2a35c2eda --- /dev/null +++ b/games/NXDoom/src/doom/r_data.c @@ -0,0 +1,944 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_data.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Preparation of data for rendering, + * generation of lookups, caching, retrieval by name. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" +#include "z_zone.h" + +#include "w_wad.h" + +#include "doomdef.h" +#include "m_misc.h" +#include "p_local.h" +#include "r_local.h" + +#include "doomstat.h" +#include "r_sky.h" + +#include "r_data.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Graphics. + * DOOM graphics for walls and sprites + * is stored in vertical runs of opaque pixels (posts). + * A column is composed of zero or more posts, + * a patch or sprite is composed of zero or more columns. + */ + +/* Texture definition. + * Each texture is composed of one or more patches, + * with patches being lumps stored in the WAD. + * The lumps are referenced by number, and patched + * into the rectangular texture space using origin + * and possibly other attributes. + */ + +begin_packed_struct struct mappatch_t +{ + short originx; + short originy; + short patch; + short stepdir; + short colormap; +} end_packed_struct; + +typedef struct mappatch_t mappatch_t; + +/* Texture definition. + * A DOOM wall texture is a list of patches + * which are to be combined in a predefined order. + */ + +begin_packed_struct struct maptexture_t +{ + char name[8]; + int masked; + short width; + short height; + int obsolete; + short patchcount; + mappatch_t patches[1]; +} end_packed_struct; + +typedef struct maptexture_t maptexture_t; + +/* A single patch from a texture definition, basically a rectangular area + * within the texture rectangle. + */ + +typedef struct +{ + /* Block origin (always UL), which has already accounted for the internal + * origin of the patch. + */ + + short originx; + short originy; + int patch; +} texpatch_t; + +/* A maptexturedef_t describes a rectangular texture, which is composed of + * one or more mappatch_t structures that arrange graphic patches. + */ + +typedef struct texture_s texture_t; + +struct texture_s +{ + /* Keep name for switch changing, etc. */ + + char name[8]; + short width; + short height; + + /* Index in textures list */ + + int index; + + /* Next in hash table chain */ + + texture_t *next; + + /* All the patches[patchcount] are drawn back to front into the cached + * texture. + */ + + short patchcount; + texpatch_t patches[1]; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int firstflat; +int lastflat; +int numflats; + +int firstpatch; +int lastpatch; +int numpatches; + +int firstspritelump; +int lastspritelump; +int numspritelumps; + +int numtextures; +texture_t **textures; +texture_t **textures_hashtable; + +int *texturewidthmask; + +/* needed for texture pegging */ + +fixed_t *textureheight; +int *texturecompositesize; +short **texturecolumnlump; +unsigned short **texturecolumnofs; +byte **texturecomposite; + +/* for global animation */ + +int *flattranslation; +int *texturetranslation; + +/* needed for pre rendering */ + +fixed_t *spritewidth; +fixed_t *spriteoffset; +fixed_t *spritetopoffset; + +lighttable_t *colormaps; + +int flatmemory; +int texturememory; +int spritememory; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void generate_texture_hashtable(void) +{ + texture_t **rover; + int i; + int key; + + textures_hashtable = + z_malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); + + memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); + + /* Add all textures to hash table */ + + for (i = 0; i < numtextures; ++i) + { + /* Store index */ + + textures[i]->index = i; + + /* Vanilla Doom does a linear search of the textures array + * and stops at the first entry it finds. If there are two + * entries with the same name, the first one in the array + * wins. The new entry must therefore be added at the end + * of the hash chain, so that earlier entries win. + */ + + key = w_lump_name_hash(textures[i]->name) % numtextures; + + rover = &textures_hashtable[key]; + + while (*rover != NULL) + { + rover = &(*rover)->next; + } + + /* Hook into hash table */ + + textures[i]->next = NULL; + *rover = textures[i]; + } +} + +/**************************************************************************** + * Name: r_generate_lookup + ****************************************************************************/ + +static void r_generate_lookup(int texnum) +{ + texture_t *texture; + byte *patchcount; /* patchcount[texture->width] */ + texpatch_t *patch; + patch_t *realpatch; + int x; + int x1; + int x2; + int i; + short *collump; + unsigned short *colofs; + + texture = textures[texnum]; + + /* Composited texture not created yet. */ + + texturecomposite[texnum] = 0; + + texturecompositesize[texnum] = 0; + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + /* Now count the number of columns that are covered by more than one patch. + * + * Fill in the lump / offset, so columns with only a single patch are all + * done. + */ + + patchcount = (byte *)z_malloc(texture->width, PU_STATIC, &patchcount); + memset(patchcount, 0, texture->width); + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = w_cache_lump_num(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) x2 = texture->width; + for (; x < x2; x++) + { + patchcount[x]++; + collump[x] = patch->patch; + colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; + } + } + + for (x = 0; x < texture->width; x++) + { + if (!patchcount[x]) + { + printf("r_generate_lookup: column without a patch (%s)\n", + texture->name); + return; + } + + if (patchcount[x] > 1) + { + /* Use the cached block. */ + + collump[x] = -1; + colofs[x] = texturecompositesize[texnum]; + + if (texturecompositesize[texnum] > 0x10000 - texture->height) + { + i_error("r_generate_lookup: texture %i is >64k", texnum); + } + + texturecompositesize[texnum] += texture->height; + } + } + + z_free(patchcount); +} + +/* MAPTEXTURE_T CACHING + * + * When a texture is first needed, it counts the number of composite columns + * required in the texture and allocates space for a column directory and any + * new columns. + * + * The directory will simply point inside other patches if there is only one + * patch in a given column, but any columns with multiple patches will have + * new column_ts generated. + */ + +/**************************************************************************** + * Name: r_init_textures + * + * Description: + * Initializes the texture list + * with the textures from the world map. + * + ****************************************************************************/ + +static void r_init_textures(void) +{ + maptexture_t *mtexture; + texture_t *texture; + mappatch_t *mpatch; + texpatch_t *patch; + + int i; + int j; + + int *maptex; + int *maptex2; + int *maptex1; + + char name[9]; + char *names; + char *name_p; + + int *patchlookup; + + int nummappatches; + int offset; + int maxoff; + int maxoff2; + int numtextures1; + int numtextures2; + + int *directory; + + int temp1; + int temp2; + int temp3; + + /* Load the patch names from pnames.lmp. */ + + name[8] = 0; + names = w_cache_lump_name(("PNAMES"), PU_STATIC); + nummappatches = LONG(*((int *)names)); + name_p = names + 4; + patchlookup = + z_malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); + + for (i = 0; i < nummappatches; i++) + { + m_str_copy(name, name_p + i * 8, sizeof(name)); + patchlookup[i] = w_check_num_for_name(name); + } + + w_release_lump_name(("PNAMES")); + + /* Load the map texture definitions from textures.lmp. + * The data is contained in one or two lumps, + * TEXTURE1 for shareware, plus TEXTURE2 for commercial. + */ + + maptex = maptex1 = w_cache_lump_name(("TEXTURE1"), PU_STATIC); + numtextures1 = LONG(*maptex); + maxoff = w_lump_length(w_get_num_for_name(("TEXTURE1"))); + directory = maptex + 1; + + if (w_check_num_for_name(("TEXTURE2")) != -1) + { + maptex2 = w_cache_lump_name(("TEXTURE2"), PU_STATIC); + numtextures2 = LONG(*maptex2); + maxoff2 = w_lump_length(w_get_num_for_name(("TEXTURE2"))); + } + else + { + maptex2 = NULL; + numtextures2 = 0; + maxoff2 = 0; + } + + numtextures = numtextures1 + numtextures2; + + textures = z_malloc(numtextures * sizeof(*textures), PU_STATIC, 0); + texturecolumnlump = + z_malloc(numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); + texturecolumnofs = + z_malloc(numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); + texturecomposite = + z_malloc(numtextures * sizeof(*texturecomposite), PU_STATIC, 0); + texturecompositesize = + z_malloc(numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); + texturewidthmask = + z_malloc(numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); + textureheight = + z_malloc(numtextures * sizeof(*textureheight), PU_STATIC, 0); + + /* Really complex printing shit... */ + + temp1 = w_get_num_for_name(("S_START")); /* P_??????? */ + temp2 = w_get_num_for_name(("S_END")) - 1; + temp3 = ((temp2 - temp1 + 63) / 64) + ((numtextures + 63) / 64); + + /* If stdout is a real console, use the classic vanilla "filling + * up the box" effect, which uses backspace to "step back" inside + * the box. If stdout is a file, don't draw the box. + */ + + if (i_console_stdout()) + { + printf("["); + for (i = 0; i < temp3 + 9; i++) + printf(" "); + printf("]"); + for (i = 0; i < temp3 + 10; i++) + printf("\b"); + } + + for (i = 0; i < numtextures; i++, directory++) + { + if (!(i & 63)) printf("."); + + if (i == numtextures1) + { + /* Start looking in second texture file. */ + + maptex = maptex2; + maxoff = maxoff2; + directory = maptex + 1; + } + + offset = LONG(*directory); + + if (offset > maxoff) i_error("r_init_textures: bad texture directory"); + + mtexture = (maptexture_t *)((byte *)maptex + offset); + + texture = textures[i] = + z_malloc(sizeof(texture_t) + + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), + PU_STATIC, 0); + + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + + memcpy(texture->name, mtexture->name, sizeof(texture->name)); + mpatch = &mtexture->patches[0]; + patch = &texture->patches[0]; + + for (j = 0; j < texture->patchcount; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + { + i_error("r_init_textures: Missing patch in texture %s", + texture->name); + } + } + + texturecolumnlump[i] = z_malloc( + texture->width * sizeof(**texturecolumnlump), PU_STATIC, 0); + texturecolumnofs[i] = + z_malloc(texture->width * sizeof(**texturecolumnofs), + PU_STATIC, 0); + + j = 1; + while (j * 2 <= texture->width) + j <<= 1; + + texturewidthmask[i] = j - 1; + textureheight[i] = texture->height << FRACBITS; + } + + z_free(patchlookup); + + w_release_lump_name(("TEXTURE1")); + if (maptex2) w_release_lump_name(("TEXTURE2")); + + /* Precalculate whatever possible. */ + + for (i = 0; i < numtextures; i++) + r_generate_lookup(i); + + /* Create translation table for global animation. */ + + texturetranslation = + z_malloc((numtextures + 1) * sizeof(*texturetranslation), + PU_STATIC, 0); + + for (i = 0; i < numtextures; i++) + texturetranslation[i] = i; + + generate_texture_hashtable(); +} + +/**************************************************************************** + * Name: r_init_flats + ****************************************************************************/ + +static void r_init_flats(void) +{ + int i; + + firstflat = w_get_num_for_name(("F_START")) + 1; + lastflat = w_get_num_for_name(("F_END")) - 1; + numflats = lastflat - firstflat + 1; + + /* Create translation table for global animation. */ + + flattranslation = + z_malloc((numflats + 1) * sizeof(*flattranslation), PU_STATIC, 0); + + for (i = 0; i < numflats; i++) + flattranslation[i] = i; +} + +/**************************************************************************** + * Name: r_init_sprite_lumps + * + * Description: + * Finds the width and hoffset of all sprites in the wad, + * so the sprite does not need to be cached completely + * just for having the header info ready during rendering. + * + ****************************************************************************/ + +static void r_init_sprite_lumps(void) +{ + int i; + patch_t *patch; + + firstspritelump = w_get_num_for_name(("S_START")) + 1; + lastspritelump = w_get_num_for_name(("S_END")) - 1; + + numspritelumps = lastspritelump - firstspritelump + 1; + spritewidth = + z_malloc(numspritelumps * sizeof(*spritewidth), PU_STATIC, 0); + spriteoffset = + z_malloc(numspritelumps * sizeof(*spriteoffset), PU_STATIC, 0); + spritetopoffset = + z_malloc(numspritelumps * sizeof(*spritetopoffset), PU_STATIC, 0); + + for (i = 0; i < numspritelumps; i++) + { + if (!(i & 63)) printf("."); + + patch = w_cache_lump_num(firstspritelump + i, PU_CACHE); + spritewidth[i] = SHORT(patch->width) << FRACBITS; + spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; + spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; + } +} + +/**************************************************************************** + * Name: r_init_colourmaps + ****************************************************************************/ + +static void r_init_colourmaps(void) +{ + int lump; + + /* Load in the light tables, 256 byte align tables. */ + + lump = w_get_num_for_name(("COLORMAP")); + colormaps = w_cache_lump_num(lump, PU_STATIC); +} + +/**************************************************************************** + * Name: r_draw_column_in_cache + * + * Description: + * Clip and draw a column from a patch into a cached post. + * + ****************************************************************************/ + +static void r_draw_column_in_cache(column_t *patch, byte *cache, int originy, + int cacheheight) +{ + int count; + int position; + byte *source; + + while (patch->topdelta != 0xff) + { + source = (byte *)patch + 3; + count = patch->length; + position = originy + patch->topdelta; + + if (position < 0) + { + count += position; + position = 0; + } + + if (position + count > cacheheight) count = cacheheight - position; + + if (count > 0) memcpy(cache + position, source, count); + + patch = (column_t *)((byte *)patch + patch->length + 4); + } +} + +/**************************************************************************** + * Name: r_generate_composite + * + * Description: + * Using the texture definition, the composite texture is created from the + * patches, and each column is cached. + * + ****************************************************************************/ + +static void r_generate_composite(int texnum) +{ + byte *block; + texture_t *texture; + texpatch_t *patch; + patch_t *realpatch; + int x; + int x1; + int x2; + int i; + column_t *patchcol; + short *collump; + unsigned short *colofs; + + texture = textures[texnum]; + + block = z_malloc(texturecompositesize[texnum], PU_STATIC, + &texturecomposite[texnum]); + + collump = texturecolumnlump[texnum]; + colofs = texturecolumnofs[texnum]; + + /* Composite the columns together. */ + + for (i = 0, patch = texture->patches; + i < texture->patchcount; + i++, patch++) + { + realpatch = w_cache_lump_num(patch->patch, PU_CACHE); + x1 = patch->originx; + x2 = x1 + SHORT(realpatch->width); + + if (x1 < 0) + x = 0; + else + x = x1; + + if (x2 > texture->width) x2 = texture->width; + + for (; x < x2; x++) + { + /* Column does not have multiple patches? */ + + if (collump[x] >= 0) continue; + + patchcol = (column_t *)((byte *)realpatch + + LONG(realpatch->columnofs[x - x1])); + r_draw_column_in_cache(patchcol, block + colofs[x], + patch->originy, texture->height); + } + } + + /* Now that the texture has been built in column cache, + * it is purgeable from zone memory. + */ + + z_change_tag(block, PU_CACHE); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_get_column + ****************************************************************************/ + +byte *r_get_column(int tex, int col) +{ + int lump; + int ofs; + + col &= texturewidthmask[tex]; + lump = texturecolumnlump[tex][col]; + ofs = texturecolumnofs[tex][col]; + + if (lump > 0) return (byte *)w_cache_lump_num(lump, PU_CACHE) + ofs; + + if (!texturecomposite[tex]) r_generate_composite(tex); + + return texturecomposite[tex] + ofs; +} + +/**************************************************************************** + * Name: r_init_data + * + * Description: + * Locates all the lumps that will be used by all views + * Must be called after W_Init. + * + ****************************************************************************/ + +void r_init_data(void) +{ + r_init_textures(); + printf("."); + r_init_flats(); + printf("."); + r_init_sprite_lumps(); + printf("."); + r_init_colourmaps(); +} + +/**************************************************************************** + * Name: r_flat_num_for_name + * + * Description: + * Retrieval, get a flat number for a flat name. + * + ****************************************************************************/ + +int r_flat_num_for_name(const char *name) +{ + int i; + char namet[9]; + + i = w_check_num_for_name(name); + + if (i == -1) + { + namet[8] = 0; + memcpy(namet, name, 8); + i_error("r_flat_num_for_name: %s not found", namet); + } + + return i - firstflat; +} + +/**************************************************************************** + * Name: r_check_texture_num_for_name + * + * Description: + * Check whether texture is available. + * Filter out NoTexture indicator. + * + ****************************************************************************/ + +int r_check_texture_num_for_name(const char *name) +{ + texture_t *texture; + int key; + + /* "NoTexture" marker. */ + + if (name[0] == '-') return 0; + + key = w_lump_name_hash(name) % numtextures; + + texture = textures_hashtable[key]; + + while (texture != NULL) + { + if (!strncasecmp(texture->name, name, 8)) return texture->index; + + texture = texture->next; + } + + return -1; +} + +/**************************************************************************** + * Name: r_texture_num_for_name + * + * Description: + * Calls r_check_texture_num_for_name, aborts with error message. + * + ****************************************************************************/ + +int r_texture_num_for_name(const char *name) +{ + int i; + + i = r_check_texture_num_for_name(name); + + if (i == -1) + { + i_error("r_texture_num_for_name: %s not found", name); + } + + return i; +} + +/**************************************************************************** + * Name: r_precache_level + * + * Description: + * Preloads all relevant graphics for the level. + * + ****************************************************************************/ + +void r_precache_level(void) +{ + char *flatpresent; + char *texturepresent; + char *spritepresent; + + int i; + int j; + int k; + int lump; + + texture_t *texture; + thinker_t *th; + spriteframe_t *sf; + + if (demoplayback) return; + + /* Precache flats. */ + + flatpresent = z_malloc(numflats, PU_STATIC, NULL); + memset(flatpresent, 0, numflats); + + for (i = 0; i < numsectors; i++) + { + flatpresent[sectors[i].floorpic] = 1; + flatpresent[sectors[i].ceilingpic] = 1; + } + + flatmemory = 0; + + for (i = 0; i < numflats; i++) + { + if (flatpresent[i]) + { + lump = firstflat + i; + flatmemory += lumpinfo[lump]->size; + w_cache_lump_num(lump, PU_CACHE); + } + } + + z_free(flatpresent); + + /* Precache textures. */ + + texturepresent = z_malloc(numtextures, PU_STATIC, NULL); + memset(texturepresent, 0, numtextures); + + for (i = 0; i < numsides; i++) + { + texturepresent[sides[i].toptexture] = 1; + texturepresent[sides[i].midtexture] = 1; + texturepresent[sides[i].bottomtexture] = 1; + } + + /* Sky texture is always present. + * Note that F_SKY1 is the name used to indicate a sky floor/ceiling as a + * flat, while the sky texture is stored like a wall texture, with an + * episode dependent name. + */ + + texturepresent[skytexture] = 1; + + texturememory = 0; + for (i = 0; i < numtextures; i++) + { + if (!texturepresent[i]) continue; + + texture = textures[i]; + + for (j = 0; j < texture->patchcount; j++) + { + lump = texture->patches[j].patch; + texturememory += lumpinfo[lump]->size; + w_cache_lump_num(lump, PU_CACHE); + } + } + + z_free(texturepresent); + + /* Precache sprites. */ + + spritepresent = z_malloc(numsprites, PU_STATIC, NULL); + memset(spritepresent, 0, numsprites); + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 == (actionf_p1)p_mobj_thinker) + spritepresent[((mobj_t *)th)->sprite] = 1; + } + + spritememory = 0; + for (i = 0; i < numsprites; i++) + { + if (!spritepresent[i]) continue; + + for (j = 0; j < sprites[i].numframes; j++) + { + sf = &sprites[i].spriteframes[j]; + for (k = 0; k < 8; k++) + { + lump = firstspritelump + sf->lump[k]; + spritememory += lumpinfo[lump]->size; + w_cache_lump_num(lump, PU_CACHE); + } + } + } + + z_free(spritepresent); +} diff --git a/games/NXDoom/src/doom/r_data.h b/games/NXDoom/src/doom/r_data.h new file mode 100644 index 000000000..f320ed0f7 --- /dev/null +++ b/games/NXDoom/src/doom/r_data.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_data.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh module, data I/O, caching, retrieval of graphics + * by name. + * + ****************************************************************************/ + +#ifndef __R_DATA__ +#define __R_DATA__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "r_defs.h" +#include "r_state.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int numflats; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Retrieve column data for span blitting. */ + +byte *r_get_column(int tex, int col); + +/* I/O, setting up the stuff. */ + +void r_init_data(void); +void r_precache_level(void); + +/* Retrieval. + * Floor/ceiling opaque texture tiles, + * lookup by name. For animation? + */ + +int r_flat_num_for_name(const char *name); + +/* Called by p_ticker for switches and animations, + * returns the texture number for the texture name. + */ + +int r_texture_num_for_name(const char *name); +int r_check_texture_num_for_name(const char *name); + +#endif /* __R_DATA__ */ diff --git a/games/NXDoom/src/doom/r_defs.h b/games/NXDoom/src/doom/r_defs.h new file mode 100644 index 000000000..ae84ae63e --- /dev/null +++ b/games/NXDoom/src/doom/r_defs.h @@ -0,0 +1,444 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + ****************************************************************************/ + +#ifndef __R_DEFS__ +#define __R_DEFS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Screenwidth. */ + +#include "doomdef.h" + +/* Some more or less basic data types we depend on. */ + +#include "m_fixed.h" + +/* We rely on the thinker data struct to handle sound origins in sectors. */ + +#include "d_think.h" + +/* SECTORS do store MObjs anyway. */ + +#include "p_mobj.h" + +#include "i_video.h" + +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Silhouette, needed for clipping Segs (mainly) and sprites representing + * things. + */ + +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* INTERNAL MAP TYPES used by play and refresh */ + +/* Your plain vanilla vertex. + * + * Note: transformed values not buffered locally, like some DOOM-alikes + * ("wt", "WebView") did. + */ + +typedef struct +{ + fixed_t x; + fixed_t y; +} vertex_t; + +/* Forward of LineDefs, for Sectors. */ + +struct line_s; + +/* Each sector has a degenmobj_t in its center for sound origin purposes. + * + * I suppose this does not handle sound from moving objects (doppler), + * because position is prolly just buffered, not updated. + */ + +typedef struct +{ + thinker_t thinker; /* not used for anything */ + fixed_t x; + fixed_t y; + fixed_t z; +} degenmobj_t; + +/* The SECTORS record, at runtime. Stores things/mobjs. */ + +typedef struct +{ + fixed_t floorheight; + fixed_t ceilingheight; + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short tag; + + /* 0 = untraversed, 1,2 = sndlines -1 */ + + int soundtraversed; + + /* thing that made a sound (or null) */ + + mobj_t *soundtarget; + + /* mapblock bounding box for height changes */ + + int blockbox[4]; + + /* origin for any sounds played by the sector */ + + degenmobj_t soundorg; + + /* if == validcount, already checked */ + + int validcount; + + /* list of mobjs in sector */ + + mobj_t *thinglist; + + /* thinker_t for reversible actions */ + + void *specialdata; + + int linecount; + struct line_s **lines; /* [linecount] size */ +} sector_t; + +/* The SideDef. */ + +typedef struct +{ + /* add this to the calculated texture column */ + + fixed_t textureoffset; + + /* add this to the calculated texture top */ + + fixed_t rowoffset; + + /* Texture indices. We do not maintain names here. */ + + short toptexture; + short bottomtexture; + short midtexture; + + /* Sector the SideDef is facing. */ + + sector_t *sector; +} side_t; + +/* Move clipping aid for LineDefs. */ + +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + +typedef struct line_s +{ + /* Vertices, from v1 to v2. */ + + vertex_t *v1; + vertex_t *v2; + + /* Precalculated v2 - v1 for side checking. */ + + fixed_t dx; + fixed_t dy; + + /* Animation related. */ + + short flags; + short special; + short tag; + + /* Visual appearance: SideDefs. sidenum[1] will be -1 if one sided */ + + short sidenum[2]; + + /* Neat. Another bounding box, for the extent of the LineDef. */ + + fixed_t bbox[4]; + + /* To aid move clipping. */ + + slopetype_t slopetype; + + /* Front and back sector. + * Note: redundant? Can be retrieved from SideDefs. + */ + + sector_t *frontsector; + sector_t *backsector; + + /* if == validcount, already checked */ + + int validcount; + + /* thinker_t for reversible actions */ + + void *specialdata; +} line_t; + +/* A SubSector. + * References a Sector. + * + * Basically, this is a list of LineSegs, indicating the visible walls that + * define (all or some) sides of a convex BSP leaf. + */ + +typedef struct subsector_s +{ + sector_t *sector; + short numlines; + short firstline; +} subsector_t; + +/* The LineSeg. */ + +typedef struct +{ + vertex_t *v1; + vertex_t *v2; + + fixed_t offset; + + angle_t angle; + + side_t *sidedef; + line_t *linedef; + + /* Sector references. + * Could be retrieved from linedef, too. + * backsector is NULL for one sided lines + */ + + sector_t *frontsector; + sector_t *backsector; +} seg_t; + +/* BSP node. */ + +typedef struct +{ + /* Partition line. */ + + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; + + /* Bounding box for each child. */ + + fixed_t bbox[2][4]; + + /* If NF_SUBSECTOR its a subsector. */ + + unsigned short children[2]; +} node_t; + +/* PC direct to screen pointers + * B UNUSED - keep till detailshift in r_draw.c resolved + * extern byte* destview; + * extern byte* destscreen; + */ + +/* OTHER TYPES */ + +/* This could be wider for >8 bit display. + * + * Indeed, true color support is possible precalculating 24bpp + * lightmap/colormap LUT. from darkening PLAYPAL to all black. Could even + * use more than 32 levels. + */ + +typedef pixel_t lighttable_t; + +/* ? */ + +typedef struct drawseg_s +{ + seg_t *curline; + int x1; + int x2; + + fixed_t scale1; + fixed_t scale2; + fixed_t scalestep; + + /* 0=none, 1=bottom, 2=top, 3=both */ + + int silhouette; + + /* do not clip sprites above this */ + + fixed_t bsilheight; + + /* do not clip sprites below this */ + + fixed_t tsilheight; + + /* Pointers to lists for sprite clipping, all three adjusted so [x1] is + * first value. + */ + + short *sprtopclip; + short *sprbottomclip; + short *maskedtexturecol; +} drawseg_t; + +/* A vissprite_t is a thing that will be drawn during a refresh. + * + * I.e. a sprite object that is partly visible. + */ + +typedef struct vissprite_s +{ + /* Doubly linked list. */ + + struct vissprite_s *prev; + struct vissprite_s *next; + + int x1; + int x2; + + /* for line side calculation */ + + fixed_t gx; + fixed_t gy; + + /* global bottom / top for silhouette clipping */ + + fixed_t gz; + fixed_t gzt; + + /* horizontal position of x1 */ + + fixed_t startfrac; + + fixed_t scale; + + /* negative if flipped */ + + fixed_t xiscale; + + fixed_t texturemid; + int patch; + + /* for color translation and shadow draw, maxbright frames as well */ + + lighttable_t *colormap; + + int mobjflags; +} vissprite_t; + +/* Sprites are patches with a special naming convention so they can be + * recognized by r_init_sprites. + * + * The base name is NNNNFx or NNNNFxFx, with x indicating the rotation, + * x = 0, 1-7. + * + * The sprite and frame specified by a thing_t is range checked at run time. + * + * A sprite is a patch_t that is assumed to represent a three dimensional + * object and may have multiple rotations pre drawn. + * + * Horizontal flipping is used to save space, thus NNNNF2F5 defines a + * mirrored patch. + * + * Some sprites will only have one picture used for all views: NNNNF0 + */ + +typedef struct +{ + /* If false use 0 for any position. + * Note: as eight entries are available, we might as well insert the same + * name eight times. + */ + + boolean rotate; + + /* Lump to use for view angles 0-7. */ + + short lump[8]; + + /* Flip bit (1 = flip) to use for view angles 0-7. */ + + byte flip[8]; +} spriteframe_t; + +/* A sprite definition: a number of animation frames. */ + +typedef struct +{ + int numframes; + spriteframe_t *spriteframes; +} spritedef_t; + +/* Now what is a visplane, anyway? */ + +typedef struct +{ + fixed_t height; + int picnum; + int lightlevel; + int minx; + int maxx; + + /* leave pads for [minx-1]/[maxx+1] */ + + byte pad1; + + /* Here lies the rub for all dynamic resize/change of resolution. */ + + byte top[SCREENWIDTH]; + byte pad2; + byte pad3; + + /* See above. */ + + byte bottom[SCREENWIDTH]; + byte pad4; +} visplane_t; + +#endif /* __R_DEFS__ */ diff --git a/games/NXDoom/src/doom/r_draw.c b/games/NXDoom/src/doom/r_draw.c new file mode 100644 index 000000000..ff243912a --- /dev/null +++ b/games/NXDoom/src/doom/r_draw.c @@ -0,0 +1,965 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_draw.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * The actual span/column drawing functions. + * Here find the main potential for optimization, e.g. inline assembly, + * different algorithms. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_main.h" +#include "doomdef.h" + +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +/* Needs access to LFB (guess what). */ + +#include "v_video.h" + +/* State. */ + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ? */ + +#define MAXWIDTH 1120 +#define MAXHEIGHT 832 + +/* status bar height at bottom of screen */ + +#define SBARHEIGHT 32 + +/* Spectre/Invisibility. */ + +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* All drawing to the view buffer is accomplished in this file. + * + * The other refresh files only know about coordinates, not the architecture + * of the frame buffer. + * + * Conveniently, the frame buffer is a linear one, and we need only the base + * address, and the total size == width*height*depth/8., + */ + +byte *viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; +int viewwindowx; +int viewwindowy; +pixel_t *ylookup[MAXHEIGHT]; +int columnofs[MAXWIDTH]; + +/* Color tables for different players, translate a limited part to another + * (color ramps used for suit colors). + */ + +byte translations[3][256]; + +lighttable_t *dc_colormap; +int dc_x; +int dc_yl; +int dc_yh; +fixed_t dc_iscale; +fixed_t dc_texturemid; + +/* first pixel in a column (possibly virtual) */ + +byte *dc_source; + +int fuzzoffset[FUZZTABLE] = +{ + FUZZOFF, -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, + FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, + FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, + FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, FUZZOFF, + -FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, + FUZZOFF, FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, -FUZZOFF, FUZZOFF, + FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, FUZZOFF, FUZZOFF, -FUZZOFF, + FUZZOFF, +}; + +int fuzzpos = 0; + +byte *dc_translation; +byte *translationtables; + +int ds_y; +int ds_x1; +int ds_x2; + +lighttable_t *ds_colormap; + +fixed_t ds_xfrac; +fixed_t ds_yfrac; +fixed_t ds_xstep; +fixed_t ds_ystep; + +/* start of a 64*64 tile image */ + +byte *ds_source; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Backing buffer containing the bezel drawn around the screen and + * surrounding background. + */ + +static pixel_t *background_buffer = NULL; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Source is the top of the column to scale. + * + * A column is a vertical slice/span from a wall texture that, given the DOOM + * style restrictions on the view orientation, will always have constant z + * depth. + * + * Thus a special case loop for very fast rendering can be used. It has also + * been used with Wolfenstein 3D. + */ + +void r_draw_column(void) +{ + int count; + pixel_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + + /* Zero length, column does not exceed a pixel. */ + + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); +#endif + + /* Framebuffer destination address. + * Use ylookup LUT to avoid multiply with ScreenWidth. + * Use columnofs LUT for subwindows? + */ + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + /* Determine scaling, which is the only mapping to be done. */ + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + /* Inner loop that does the actual texture mapping, + * e.g. a DDA-lile scaling. + * This is as fast as it gets. + */ + + do + { + /* Re-map color indices from wall texture column + * using a lighting/special effects LUT. + */ + + *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + + dest += SCREENWIDTH; + frac += fracstep; + } + while (count--); +} + +/* UNUSED. Loop unrolled. */ + +#if 0 +void r_draw_column(void) +{ + int count; + byte *source; + byte *dest; + byte *colormap; + + unsigned frac; + unsigned fracstep; + unsigned fracstep2; + unsigned fracstep3; + unsigned fracstep4; + + count = dc_yh - dc_yl + 1; + + source = dc_source; + colormap = dc_colormap; + dest = ylookup[dc_yl] + columnofs[dc_x]; + + fracstep = dc_iscale << 9; + frac = (dc_texturemid + (dc_yl - centery) * dc_iscale) << 9; + + fracstep2 = fracstep + fracstep; + fracstep3 = fracstep2 + fracstep; + fracstep4 = fracstep3 + fracstep; + + while (count >= 8) + { + dest[0] = colormap[source[frac >> 25]]; + dest[SCREENWIDTH] = colormap[source[(frac + fracstep) >> 25]]; + dest[SCREENWIDTH * 2] = colormap[source[(frac + fracstep2) >> 25]]; + dest[SCREENWIDTH * 3] = colormap[source[(frac + fracstep3) >> 25]]; + + frac += fracstep4; + + dest[SCREENWIDTH * 4] = colormap[source[frac >> 25]]; + dest[SCREENWIDTH * 5] = colormap[source[(frac + fracstep) >> 25]]; + dest[SCREENWIDTH * 6] = colormap[source[(frac + fracstep2) >> 25]]; + dest[SCREENWIDTH * 7] = colormap[source[(frac + fracstep3) >> 25]]; + + frac += fracstep4; + dest += SCREENWIDTH * 8; + count -= 8; + } + + while (count > 0) + { + *dest = colormap[source[frac >> 25]]; + dest += SCREENWIDTH; + frac += fracstep; + count--; + } +} +#endif + +void r_draw_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + fixed_t frac; + fixed_t fracstep; + int x; + + count = dc_yh - dc_yl; + + /* Zero length. */ + + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + /* Blocky mode, need to multiply by 2. */ + + x = dc_x << 1; + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + do + { + /* Hack. Does not work correctly. */ + + *dest2 = *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + frac += fracstep; + } + while (count--); +} + +/* Framebuffer postprocessing. + * + * Creates a fuzzy image by copying pixels from adjacent ones to left and + * right. + * + * Used with an all black colormap, this could create the SHADOW effect, i.e. + * spectres and invisible players. + */ + +void r_draw_fuzz_column(void) +{ + int count; + pixel_t *dest; + + /* Adjust borders. Low... */ + + if (!dc_yl) dc_yl = 1; + + /* .. and high. */ + + if (dc_yh == viewheight - 1) dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + + /* Zero length. */ + + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_fuzz_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + /* Looks like an attempt at dithering, + * using the colormap #6 (of 0-31, a bit + * brighter than average). + */ + + do + { + /* Lookup framebuffer, and retrieve a pixel that is either one column + * left or right of the current one. Add index from colormap to index. + */ + + *dest = colormaps[6 * 256 + dest[fuzzoffset[fuzzpos]]]; + + /* Clamp table lookup index. */ + + if (++fuzzpos == FUZZTABLE) fuzzpos = 0; + + dest += SCREENWIDTH; + } + while (count--); +} + +/* low detail mode version */ + +void r_draw_fuzz_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + int x; + + /* Adjust borders. Low... */ + + if (!dc_yl) dc_yl = 1; + + /* .. and high. */ + + if (dc_yh == viewheight - 1) dc_yh = viewheight - 2; + + count = dc_yh - dc_yl; + + /* Zero length. */ + + if (count < 0) return; + + /* low detail mode, need to multiply by 2 */ + + x = dc_x << 1; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_fuzz_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } +#endif + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + /* Looks like an attempt at dithering, using the colormap #6 + * (of 0-31, a bit brighter than average). + */ + + do + { + /* Lookup framebuffer, and retrieve a pixel that is either one column + * left or right of the current one. Add index from colormap to index. + */ + + *dest = colormaps[6 * 256 + dest[fuzzoffset[fuzzpos]]]; + *dest2 = colormaps[6 * 256 + dest2[fuzzoffset[fuzzpos]]]; + + /* Clamp table lookup index. */ + + if (++fuzzpos == FUZZTABLE) fuzzpos = 0; + + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + } + while (count--); +} + +/* r_draw_translated_column + * + * Used to draw player sprites with the green colorramp mapped to others. + * + * Could be used with different translation tables, e.g. the lighter colored + * version of the BaronOfHell, the HellKnight, uses identical sprites, kinda + * brightened up. + */ + +void r_draw_translated_column(void) +{ + int count; + pixel_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = dc_yh - dc_yl; + if (count < 0) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, dc_x); + } + +#endif + + dest = ylookup[dc_yl] + columnofs[dc_x]; + + /* Looks familiar. */ + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + /* Here we do an additional index re-mapping. */ + + do + { + /* Translation tables are used to map certain colorramps to other ones, + * used with PLAY sprites. + * + * Thus the "green" ramp of the player 0 sprite is mapped to gray, red, + * black/indigo. + */ + + *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + dest += SCREENWIDTH; + + frac += fracstep; + } + while (count--); +} + +void r_draw_translated_column_low(void) +{ + int count; + pixel_t *dest; + pixel_t *dest2; + fixed_t frac; + fixed_t fracstep; + int x; + + count = dc_yh - dc_yl; + if (count < 0) return; + + /* low detail, need to scale by 2 */ + + x = dc_x << 1; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT) + { + i_error("r_draw_column: %i to %i at %i", dc_yl, dc_yh, x); + } + +#endif + + dest = ylookup[dc_yl] + columnofs[x]; + dest2 = ylookup[dc_yl] + columnofs[x + 1]; + + /* Looks familiar. */ + + fracstep = dc_iscale; + frac = dc_texturemid + (dc_yl - centery) * fracstep; + + /* Here we do an additional index re-mapping. */ + + do + { + /* Translation tables are used to map certain colorramps to other ones, + * used with PLAY sprites. + * + * Thus the "green" ramp of the player 0 sprite is mapped to gray, red, + * black/indigo. + */ + + *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + *dest2 = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]]; + dest += SCREENWIDTH; + dest2 += SCREENWIDTH; + + frac += fracstep; + } + while (count--); +} + +/* r_init_translation_table + * + * Creates the translation tables to map the green color ramp to gray, brown, + * red. + * + * Assumes a given structure of the PLAYPAL. Could be read from a lump + * instead. + */ + +void r_init_translation_table(void) +{ + int i; + + translationtables = z_malloc(256 * 3, PU_STATIC, 0); + + /* translate just the 16 green colors */ + + for (i = 0; i < 256; i++) + { + if (i >= 0x70 && i <= 0x7f) + { + /* map green ramp to gray, brown, red */ + + translationtables[i] = 0x60 + (i & 0xf); + translationtables[i + 256] = 0x40 + (i & 0xf); + translationtables[i + 512] = 0x20 + (i & 0xf); + } + else + { + /* Keep all other colors as is. */ + + translationtables[i] = translationtables[i + 256] = + translationtables[i + 512] = i; + } + } +} + +/* r_draw_span + * + * With DOOM style restrictions on view orientation, the floors and ceilings + * consist of horizontal slices or spans with constant z depth. + * + * However, rotation around the world z axis is possible, thus this mapping, + * while simpler and faster than perspective correct texture mapping, has to + * traverse the texture at an angle in all but a few cases. + * + * In consequence, flats are not stored by column (like walls), and the inner + * loop has to step in texture space u and v. + * + * Draws the actual span. + */ + +void r_draw_span(void) +{ + unsigned int position; + unsigned int step; + pixel_t *dest; + int count; + int spot; + unsigned int xtemp; + unsigned int ytemp; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || + (unsigned)ds_y > SCREENHEIGHT) + { + i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); + } +#endif + + /* Pack position and step variables into a single 32-bit integer, + * with x in the top 16 bits and y in the bottom 16 bits. For + * each 16-bit part, the top 6 bits are the integer part and the + * bottom 10 bits are the fractional part of the pixel position. + */ + + position = ((ds_xfrac << 10) & 0xffff0000) | + ((ds_yfrac >> 6) & 0x0000ffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + /* We do not check for zero spans here? */ + + count = ds_x2 - ds_x1; + + do + { + /* Calculate current texture index in u,v. */ + + ytemp = (position >> 4) & 0x0fc0; + xtemp = (position >> 26); + spot = xtemp | ytemp; + + /* Lookup pixel from flat texture tile, re-index using light/colormap. + */ + + *dest++ = ds_colormap[ds_source[spot]]; + + position += step; + } + while (count--); +} + +/* UNUSED. Loop unrolled by 4. */ + +#if 0 +void r_draw_span(void) +{ + unsigned position; + unsigned step; + + byte *source; + byte *colormap; + pixel_t *dest; + + unsigned count; + usingned spot; + unsigned value; + unsigned temp; + unsigned xtemp; + unsigned ytemp; + + position = ((ds_xfrac << 10) & 0xffff0000) | ((ds_yfrac >> 6) & 0xffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0xffff); + + source = ds_source; + colormap = ds_colormap; + dest = ylookup[ds_y] + columnofs[ds_x1]; + count = ds_x2 - ds_x1 + 1; + + while (count >= 4) + { + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[0] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[1] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[2] = colormap[source[spot]]; + + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + dest[3] = colormap[source[spot]]; + + count -= 4; + dest += 4; + } + while (count > 0) + { + ytemp = position >> 4; + ytemp = ytemp & 4032; + xtemp = position >> 26; + spot = xtemp | ytemp; + position += step; + *dest++ = colormap[source[spot]]; + count--; + } +} +#endif + +/* Again.. */ + +void r_draw_span_low(void) +{ + unsigned int position; + unsigned int step; + unsigned int xtemp; + unsigned int ytemp; + pixel_t *dest; + int count; + int spot; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || + (unsigned)ds_y > SCREENHEIGHT) + { + i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); + } +#endif + + position = ((ds_xfrac << 10) & 0xffff0000) | + ((ds_yfrac >> 6) & 0x0000ffff); + step = ((ds_xstep << 10) & 0xffff0000) | ((ds_ystep >> 6) & 0x0000ffff); + + count = (ds_x2 - ds_x1); + + /* Blocky mode, need to multiply by 2. */ + + ds_x1 <<= 1; + ds_x2 <<= 1; + + dest = ylookup[ds_y] + columnofs[ds_x1]; + + do + { + /* Calculate current texture index in u,v. */ + + ytemp = (position >> 4) & 0x0fc0; + xtemp = (position >> 26); + spot = xtemp | ytemp; + + /* Lowres/blocky mode does it twice, while scale is adjusted + * appropriately. + */ + + *dest++ = ds_colormap[ds_source[spot]]; + *dest++ = ds_colormap[ds_source[spot]]; + + position += step; + } + while (count--); +} + +/* r_init_buffer + * + * Creates lookup tables that avoid multiplies and other hazzles for getting + * the framebuffer address of a pixel to draw. + */ + +void r_init_buffer(int width, int height) +{ + int i; + + /* Handle resize, + * e.g. smaller view windows + * with border and/or status bar. + */ + + viewwindowx = (SCREENWIDTH - width) >> 1; + + /* Column offset. For windows. */ + + for (i = 0; i < width; i++) + columnofs[i] = viewwindowx + i; + + /* Samw with base row offset. */ + + if (width == SCREENWIDTH) + viewwindowy = 0; + else + viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1; + + /* Preclaculate all row offsets. */ + + for (i = 0; i < height; i++) + ylookup[i] = i_video_buffer + (i + viewwindowy) * SCREENWIDTH; +} + +/* r_fill_back_screen + * + * Fills the back screen with a pattern for variable screen sizes + * Also draws a beveled edge. + */ + +void r_fill_back_screen(void) +{ + byte *src; + pixel_t *dest; + int x; + int y; + patch_t *patch; + + /* DOOM border patch. */ + + const char *name1 = ("FLOOR7_2"); + + /* DOOM II border patch. */ + + const char *name2 = ("GRNROCK"); + + const char *name; + + /* If we are running full screen, there is no need to do any of this, + * and the background buffer can be freed if it was previously in use. + */ + + if (scaledviewwidth == SCREENWIDTH) + { + if (background_buffer != NULL) + { + z_free(background_buffer); + background_buffer = NULL; + } + + return; + } + + /* Allocate the background buffer if necessary */ + + if (background_buffer == NULL) + { + background_buffer = z_malloc( + SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT) * + sizeof(*background_buffer), PU_STATIC, NULL); + } + + if (gamemode == commercial) + name = name2; + else + name = name1; + + src = w_cache_lump_name(name, PU_CACHE); + dest = background_buffer; + + for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH / 64; x++) + { + memcpy(dest, src + ((y & 63) << 6), 64); + dest += 64; + } + + if (SCREENWIDTH & 63) + { + memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63); + dest += (SCREENWIDTH & 63); + } + } + + /* Draw screen and bezel; this is done to a separate screen buffer. */ + + v_use_buffer(background_buffer); + + patch = w_cache_lump_name(("brdr_t"), PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + v_draw_patch(viewwindowx + x, viewwindowy - 8, patch); + patch = w_cache_lump_name(("brdr_b"), PU_CACHE); + + for (x = 0; x < scaledviewwidth; x += 8) + v_draw_patch(viewwindowx + x, viewwindowy + viewheight, patch); + patch = w_cache_lump_name(("brdr_l"), PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + v_draw_patch(viewwindowx - 8, viewwindowy + y, patch); + patch = w_cache_lump_name(("brdr_r"), PU_CACHE); + + for (y = 0; y < viewheight; y += 8) + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy + y, patch); + + /* Draw beveled edge. */ + + v_draw_patch(viewwindowx - 8, viewwindowy - 8, + w_cache_lump_name(("brdr_tl"), PU_CACHE)); + + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy - 8, + w_cache_lump_name(("brdr_tr"), PU_CACHE)); + + v_draw_patch(viewwindowx - 8, viewwindowy + viewheight, + w_cache_lump_name(("brdr_bl"), PU_CACHE)); + + v_draw_patch(viewwindowx + scaledviewwidth, viewwindowy + viewheight, + w_cache_lump_name(("brdr_br"), PU_CACHE)); + + v_restore_buffer(); +} + +/* Copy a screen buffer. */ + +void r_video_erase(unsigned ofs, int count) +{ + /* LFB copy. + * This might not be a good idea if memcpy is not optional, e.g. byte by + * byte on a 32bit CPU, as GNU GCC/Linux libc did at one point. + */ + + if (background_buffer != NULL) + { + memcpy(i_video_buffer + ofs, background_buffer + ofs, + count * sizeof(*i_video_buffer)); + } +} + +/* r_draw_view_border + * Draws the border around the view for different size windows? + */ + +void r_draw_view_border(void) +{ + int top; + int side; + int ofs; + int i; + + if (scaledviewwidth == SCREENWIDTH) return; + + top = ((SCREENHEIGHT - SBARHEIGHT) - viewheight) / 2; + side = (SCREENWIDTH - scaledviewwidth) / 2; + + /* copy top and one line of left side */ + + r_video_erase(0, top * SCREENWIDTH + side); + + /* copy one line of right side and bottom */ + + ofs = (viewheight + top) * SCREENWIDTH - side; + r_video_erase(ofs, top * SCREENWIDTH + side); + + /* copy sides using wraparound */ + + ofs = top * SCREENWIDTH + SCREENWIDTH - side; + side <<= 1; + + for (i = 1; i < viewheight; i++) + { + r_video_erase(ofs, side); + ofs += SCREENWIDTH; + } + + /* ? */ + + v_mark_rect(0, 0, SCREENWIDTH, SCREENHEIGHT - SBARHEIGHT); +} diff --git a/games/NXDoom/src/doom/r_draw.h b/games/NXDoom/src/doom/r_draw.h new file mode 100644 index 000000000..47e85c1ed --- /dev/null +++ b/games/NXDoom/src/doom/r_draw.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_draw.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __R_DRAW__ +#define __R_DRAW__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lighttable_t *dc_colormap; +extern int dc_x; +extern int dc_yl; +extern int dc_yh; +extern fixed_t dc_iscale; +extern fixed_t dc_texturemid; + +/* first pixel in a column */ + +extern byte *dc_source; + +extern int ds_y; +extern int ds_x1; +extern int ds_x2; + +extern lighttable_t *ds_colormap; + +extern fixed_t ds_xfrac; +extern fixed_t ds_yfrac; +extern fixed_t ds_xstep; +extern fixed_t ds_ystep; + +/* start of a 64*64 tile image */ + +extern byte *ds_source; + +extern byte *translationtables; +extern byte *dc_translation; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Span blitting for rows, floor/ceiling. No Spectre effect needed. */ + +void r_draw_span(void); + +/* Low resolution mode, 160x200? */ + +void r_draw_span_low(void); + +void r_init_buffer(int width, int height); + +/* Initialize color translation tables, for player rendering etc. */ + +void r_init_translation_table(void); + +/* Rendering function. */ + +void r_fill_back_screen(void); + +/* If the view size is not full screen, draws a border around it. */ + +void r_draw_view_border(void); + +/* The span blitting interface. + * Hook in assembler or system specific BLT here. + */ + +void r_draw_column(void); +void r_draw_column_low(void); + +/* The Spectre/Invisibility effect. */ + +void r_draw_fuzz_column(void); +void r_draw_fuzz_column_low(void); + +/* Draw with color translation tables, for player sprite rendering, + * Green/Red/Blue/Indigo shirts. + */ + +void r_draw_translated_column(void); +void r_draw_translated_column_low(void); + +void r_video_erase(unsigned ofs, int count); + +#endif /* __R_DRAW__ */ diff --git a/games/NXDoom/src/doom/r_local.h b/games/NXDoom/src/doom/r_local.h new file mode 100644 index 000000000..b1fd33e1c --- /dev/null +++ b/games/NXDoom/src/doom/r_local.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_local.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh (R_*) module, global header. + * All the rendering/drawing stuff is here. + * + ****************************************************************************/ + +#ifndef __R_LOCAL__ +#define __R_LOCAL__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Binary Angles, sine/cosine/atan lookups. */ + +#include "tables.h" + +/* Screen size related parameters. */ + +#include "doomdef.h" + +/* Include the refresh/render data structs. */ + +#include "r_data.h" + +/* Separate header file for each module. */ + +#include "r_bsp.h" +#include "r_data.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_plane.h" +#include "r_segs.h" +#include "r_things.h" + +#endif /* __R_LOCAL__ */ diff --git a/games/NXDoom/src/doom/r_main.c b/games/NXDoom/src/doom/r_main.c new file mode 100644 index 000000000..b50cdc337 --- /dev/null +++ b/games/NXDoom/src/doom/r_main.c @@ -0,0 +1,883 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Rendering main loop and setup functions, utility functions (BSP, + * geometry, trigonometry). See tables.c, too. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "d_loop.h" +#include "doomdef.h" + +#include "m_bbox.h" +#include "m_menu.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Fineangles in the SCREENWIDTH wide window. */ + +#define FIELDOFVIEW 2048 + +#define DISTMAP 2 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int viewangleoffset; + +/* increment every time a check is made */ + +int validcount = 1; + +lighttable_t *fixedcolormap; + +int centerx; +int centery; + +fixed_t centerxfrac; +fixed_t centeryfrac; +fixed_t projection; + +/* just for profiling purposes */ + +int framecount; + +int sscount; +int linecount; +int loopcount; + +fixed_t viewx; +fixed_t viewy; +fixed_t viewz; + +angle_t viewangle; + +fixed_t viewcos; +fixed_t viewsin; + +player_t *viewplayer; + +/* 0 = high, 1 = low */ + +int detailshift; + +/* precalculated math tables */ + +angle_t clipangle; + +/* The viewangletox[viewangle + FINEANGLES/4] lookup + * maps the visible view angles to screen X coordinates, + * flattening the arc to a flat projection plane. + * There will be many angles mapped to the same X. + */ + +int viewangletox[FINEANGLES / 2]; + +/* The xtoviewangleangle[] table maps a screen pixel + * to the lowest viewangle that maps back to x ranges + * from clipangle to -clipangle. + */ + +angle_t xtoviewangle[SCREENWIDTH + 1]; + +lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +/* bumped light from gun blasts */ + +int extralight; + +void (*colfunc)(void); +void (*basecolfunc)(void); +void (*fuzzcolfunc)(void); +void (*transcolfunc)(void); +void (*spanfunc)(void); + +boolean setsizeneeded; +int setblocks; +int setdetail; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_add_point_to_box + * + * Description: + * Expand a given bbox so that it encloses a given point. + * + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void r_add_point_to_box(int x, int y, fixed_t *box) +{ + if (x < box[BOXLEFT]) box[BOXLEFT] = x; + if (x > box[BOXRIGHT]) box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) box[BOXBOTTOM] = y; + if (y > box[BOXTOP]) box[BOXTOP] = y; +} +#endif + +/**************************************************************************** + * Name: r_setup_frame + ****************************************************************************/ + +static void r_setup_frame(player_t *player) +{ + int i; + + viewplayer = player; + viewx = player->mo->x; + viewy = player->mo->y; + viewangle = player->mo->angle + viewangleoffset; + extralight = player->extralight; + + viewz = player->viewz; + + viewsin = finesine[viewangle >> ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle >> ANGLETOFINESHIFT]; + + sscount = 0; + + if (player->fixedcolormap) + { + fixedcolormap = colormaps + player->fixedcolormap * 256; + + walllights = scalelightfixed; + + for (i = 0; i < MAXLIGHTSCALE; i++) + scalelightfixed[i] = fixedcolormap; + } + else + fixedcolormap = 0; + + framecount++; + validcount++; +} + +/**************************************************************************** + * Name: r_init_light_tables + * + * Description: + * Only inits the zlight table, because the scalelight table changes with + * view size. + * + ****************************************************************************/ + +static void r_init_light_tables(void) +{ + int i; + int j; + int level; + int startmap; + int scale; + + /* Calculate the light levels to use for each level / distance combination. + */ + + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTZ; j++) + { + scale = fixed_div((SCREENWIDTH / 2 * FRACUNIT), + (j + 1) << LIGHTZSHIFT); + scale >>= LIGHTSCALESHIFT; + level = startmap - scale / DISTMAP; + + if (level < 0) level = 0; + + if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; + + zlight[i][j] = colormaps + level * 256; + } + } +} + +/**************************************************************************** + * Name: r_init_tables + ****************************************************************************/ + +static void r_init_tables(void) +{ +#if 0 /* UNUSED: now getting from tables.c */ + int i; + float a; + float fv; + int t; + + /* viewangle tangent table */ + + for (i = 0; i < FINEANGLES / 2; i++) + { + a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES; + fv = FRACUNIT * tan(a); + t = fv; + finetangent[i] = t; + } + + /* finesine table */ + + for (i = 0; i < 5 * FINEANGLES / 4; i++) + { + /* OPTIMIZE: mirror... */ + + a = (i + 0.5) * PI * 2 / FINEANGLES; + t = FRACUNIT * sin(a); + finesine[i] = t; + } +#endif +} + +/**************************************************************************** + * Name: r_init_point_to_angle + ****************************************************************************/ + +static void r_init_point_to_angle(void) +{ +#if 0 /* UNUSED - now getting from tables.c */ + int i; + long t; + float f; + + /* slope (tangent) to angle lookup */ + + for (i = 0; i <= SLOPERANGE; i++) + { + f = atan((float)i / SLOPERANGE) / (3.141592657 * 2); + t = 0xffffffff * f; + tantoangle[i] = t; + } +#endif +} + +/**************************************************************************** + * Name: r_init_texture_mapping + ****************************************************************************/ + +static void r_init_texture_mapping(void) +{ + int i; + int x; + int t; + fixed_t focallength; + + /* Use tangent table to generate viewangletox: + * viewangletox will give the next greatest x + * after the view angle. + * + * Calc focallength + * so FIELDOFVIEW angles covers SCREENWIDTH. + */ + + focallength = + fixed_div(centerxfrac, finetangent[FINEANGLES / 4 + FIELDOFVIEW / 2]); + + for (i = 0; i < FINEANGLES / 2; i++) + { + if (finetangent[i] > FRACUNIT * 2) + t = -1; + else if (finetangent[i] < -FRACUNIT * 2) + t = viewwidth + 1; + else + { + t = fixed_mul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT - 1) >> FRACBITS; + + if (t < -1) + t = -1; + else if (t > viewwidth + 1) + t = viewwidth + 1; + } + + viewangletox[i] = t; + } + + /* Scan viewangletox[] to generate xtoviewangle[]: + * xtoviewangle will give the smallest view angle + * that maps to x. + */ + + for (x = 0; x <= viewwidth; x++) + { + i = 0; + while (viewangletox[i] > x) + i++; + xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90; + } + + /* Take out the fencepost cases from viewangletox. */ + + for (i = 0; i < FINEANGLES / 2; i++) + { + t = fixed_mul(finetangent[i], focallength); + t = centerx - t; + + if (viewangletox[i] == -1) + viewangletox[i] = 0; + else if (viewangletox[i] == viewwidth + 1) + viewangletox[i] = viewwidth; + } + + clipangle = xtoviewangle[0]; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_point_on_side + * + * Description: + * Traverse BSP (sub) tree, check point against partition plane. + * + * Returns: + * Side 0 (front) or 1 (back). + ****************************************************************************/ + +int r_point_on_side(fixed_t x, fixed_t y, node_t *node) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!node->dx) + { + if (x <= node->x) return node->dy > 0; + + return node->dy < 0; + } + + if (!node->dy) + { + if (y <= node->y) return node->dx < 0; + + return node->dx > 0; + } + + dx = (x - node->x); + dy = (y - node->y); + + /* Try to quickly decide by looking at sign bits. */ + + if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000) + { + if ((node->dy ^ dx) & 0x80000000) + { + return 1; /* (left is negative) */ + } + + return 0; + } + + left = fixed_mul(node->dy >> FRACBITS, dx); + right = fixed_mul(dy, node->dx >> FRACBITS); + + if (right < left) + { + return 0; /* front side */ + } + + return 1; /* back side */ +} + +/**************************************************************************** + * Name: r_point_on_seg_side + ****************************************************************************/ + +int r_point_on_seg_side(fixed_t x, fixed_t y, seg_t *line) +{ + fixed_t lx; + fixed_t ly; + fixed_t ldx; + fixed_t ldy; + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + lx = line->v1->x; + ly = line->v1->y; + + ldx = line->v2->x - lx; + ldy = line->v2->y - ly; + + if (!ldx) + { + if (x <= lx) return ldy > 0; + + return ldy < 0; + } + + if (!ldy) + { + if (y <= ly) return ldx < 0; + + return ldx > 0; + } + + dx = (x - lx); + dy = (y - ly); + + /* Try to quickly decide by looking at sign bits. */ + + if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000) + { + if ((ldy ^ dx) & 0x80000000) + { + return 1; /* (left is negative) */ + } + + return 0; + } + + left = fixed_mul(ldy >> FRACBITS, dx); + right = fixed_mul(dy, ldx >> FRACBITS); + + if (right < left) + { + return 0; /* front side */ + } + + return 1; /* back side */ +} + +/**************************************************************************** + * Name: r_point_to_angle + * + * Description: + * To get a global angle from cartesian coordinates, the coordinates are + * flipped until they are in the first octant of the coordinate system, then + * the y (<=x) is scaled and divided by x to get a tangent (slope) value + * which is looked up in the tantoangle[] table. + * + ****************************************************************************/ + +angle_t r_point_to_angle(fixed_t x, fixed_t y) +{ + x -= viewx; + y -= viewy; + + if ((!x) && (!y)) return 0; + + if (x >= 0) + { + /* x >=0 */ + + if (y >= 0) + { + /* y>= 0 */ + + if (x > y) + { + return tantoangle[slope_div(y, x)]; /* octant 0 */ + } + else + { + return ANG90 - 1 - tantoangle[slope_div(x, y)]; /* octant 1 */ + } + } + else + { + /* y<0 */ + + y = -y; + + if (x > y) + { + return -tantoangle[slope_div(y, x)]; /* octant 8 */ + } + else + { + return ANG270 + tantoangle[slope_div(x, y)]; /* octant 7 */ + } + } + } + else + { + /* x<0 */ + + x = -x; + + if (y >= 0) + { + /* y>= 0 */ + + if (x > y) + { + return ANG180 - 1 - tantoangle[slope_div(y, x)]; /* octant 3 */ + } + else + { + return ANG90 + tantoangle[slope_div(x, y)]; /* octant 2 */ + } + } + else + { + y = -y; /* y<0 */ + + if (x > y) + { + return ANG180 + tantoangle[slope_div(y, x)]; /* octant 4 */ + } + else + { + return ANG270 - 1 - tantoangle[slope_div(x, y)]; /* octant 5 */ + } + } + } + + return 0; +} + +/**************************************************************************** + * Name: r_point_to_angle2 + ****************************************************************************/ + +angle_t r_point_to_angle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + viewx = x1; + viewy = y1; + + return r_point_to_angle(x2, y2); +} + +/**************************************************************************** + * Name: r_point_to_dist + ****************************************************************************/ + +fixed_t r_point_to_dist(fixed_t x, fixed_t y) +{ + int angle; + fixed_t dx; + fixed_t dy; + fixed_t temp; + fixed_t dist; + fixed_t frac; + + dx = abs(x - viewx); + dy = abs(y - viewy); + + if (dy > dx) + { + temp = dx; + dx = dy; + dy = temp; + } + + /* Fix crashes in udm1.wad */ + + if (dx != 0) + { + frac = fixed_div(dy, dx); + } + else + { + frac = 0; + } + + angle = (tantoangle[frac >> DBITS] + ANG90) >> ANGLETOFINESHIFT; + + dist = fixed_div(dx, finesine[angle]); /* use as cosine */ + + return dist; +} + +/**************************************************************************** + * Name: r_scale_from_global_angle + * + * Description: + * Returns the texture mapping scale for the current line (horizontal span) + * at the given angle. rw_distance must be calculated first. + * + ****************************************************************************/ + +fixed_t r_scale_from_global_angle(angle_t visangle) +{ + fixed_t scale; + angle_t anglea; + angle_t angleb; + int sinea; + int sineb; + fixed_t num; + int den; + +#if 0 /* UNUSED */ + fixed_t dist; + fixed_t z; + fixed_t sinv; + fixed_t cosv; + + sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT]; + dist = fixed_div(rw_distance, sinv); + cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT]; + z = abs(fixed_mul(dist, cosv)); + scale = fixed_div(projection, z); + return scale; +#endif + + anglea = ANG90 + (visangle - viewangle); + angleb = ANG90 + (visangle - rw_normalangle); + + /* both sines are always positive */ + + sinea = finesine[anglea >> ANGLETOFINESHIFT]; + sineb = finesine[angleb >> ANGLETOFINESHIFT]; + num = fixed_mul(projection, sineb) << detailshift; + den = fixed_mul(rw_distance, sinea); + + if (den > num >> FRACBITS) + { + scale = fixed_div(num, den); + + if (scale > 64 * FRACUNIT) + scale = 64 * FRACUNIT; + else if (scale < 256) + scale = 256; + } + else + scale = 64 * FRACUNIT; + + return scale; +} + +/**************************************************************************** + * Name: r_set_view_size + * + * Description: + * Do not really change anything here, because it might be in the middle + * of a refresh. The change will take effect next refresh. + * + ****************************************************************************/ + +void r_set_view_size(int blocks, int detail) +{ + setsizeneeded = true; + setblocks = blocks; + setdetail = detail; +} + +/**************************************************************************** + * Name: r_execute_set_view_size + ****************************************************************************/ + +void r_execute_set_view_size(void) +{ + fixed_t cosadj; + fixed_t dy; + int i; + int j; + int level; + int startmap; + + setsizeneeded = false; + + if (setblocks == 11) + { + scaledviewwidth = SCREENWIDTH; + viewheight = SCREENHEIGHT; + } + else + { + scaledviewwidth = setblocks * 32; + viewheight = (setblocks * 168 / 10) & ~7; + } + + detailshift = setdetail; + viewwidth = scaledviewwidth >> detailshift; + + centery = viewheight / 2; + centerx = viewwidth / 2; + centerxfrac = centerx << FRACBITS; + centeryfrac = centery << FRACBITS; + projection = centerxfrac; + + if (!detailshift) + { + colfunc = basecolfunc = r_draw_column; + fuzzcolfunc = r_draw_fuzz_column; + transcolfunc = r_draw_translated_column; + spanfunc = r_draw_span; + } + else + { + colfunc = basecolfunc = r_draw_column_low; + fuzzcolfunc = r_draw_fuzz_column_low; + transcolfunc = r_draw_translated_column_low; + spanfunc = r_draw_span_low; + } + + r_init_buffer(scaledviewwidth, viewheight); + + r_init_texture_mapping(); + + /* psprite scales */ + + pspritescale = FRACUNIT * viewwidth / SCREENWIDTH; + pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth; + + /* thing clipping */ + + for (i = 0; i < viewwidth; i++) + screenheightarray[i] = viewheight; + + /* planes */ + + for (i = 0; i < viewheight; i++) + { + dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2; + dy = abs(dy); + yslope[i] = fixed_div((viewwidth << detailshift) / 2 * FRACUNIT, dy); + } + + for (i = 0; i < viewwidth; i++) + { + cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]); + distscale[i] = fixed_div(FRACUNIT, cosadj); + } + + /* Calculate the light levels to use for each level / scale combination. */ + + for (i = 0; i < LIGHTLEVELS; i++) + { + startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS; + for (j = 0; j < MAXLIGHTSCALE; j++) + { + level = startmap - + j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP; + + if (level < 0) level = 0; + + if (level >= NUMCOLORMAPS) level = NUMCOLORMAPS - 1; + + scalelight[i][j] = colormaps + level * 256; + } + } +} + +/**************************************************************************** + * Name: r_init + ****************************************************************************/ + +void r_init(void) +{ + r_init_data(); + printf("."); + r_init_point_to_angle(); + printf("."); + r_init_tables(); + + /* viewwidth / viewheight / g_detail_level are set by the defaults */ + + printf("."); + + r_set_view_size(screenblocks, g_detail_level); + r_init_planes(); + printf("."); + r_init_light_tables(); + printf("."); + r_init_sky_map(); + r_init_translation_table(); + printf("."); + + framecount = 0; +} + +/**************************************************************************** + * Name: r_point_in_subsector + ****************************************************************************/ + +subsector_t *r_point_in_subsector(fixed_t x, fixed_t y) +{ + node_t *node; + int side; + int nodenum; + + /* single subsector is a special case */ + + if (!numnodes) return subsectors; + + nodenum = numnodes - 1; + + while (!(nodenum & NF_SUBSECTOR)) + { + node = &nodes[nodenum]; + side = r_point_on_side(x, y, node); + nodenum = node->children[side]; + } + + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + +/**************************************************************************** + * Name: r_render_player_view + ****************************************************************************/ + +void r_render_player_view(player_t *player) +{ + r_setup_frame(player); + + /* Clear buffers. */ + + r_clear_clip_segs(); + r_clear_draw_segs(); + r_clear_planes(); + r_clear_sprites(); + + /* check for new console commands. */ + + net_update(); + + /* The head node is the last node output. */ + + r_render_bsp_node(numnodes - 1); + + /* Check for new console commands. */ + + net_update(); + + r_draw_planes(); + + /* Check for new console commands. */ + + net_update(); + + r_draw_masked(); + + /* Check for new console commands. */ + + net_update(); +} diff --git a/games/NXDoom/src/doom/r_main.h b/games/NXDoom/src/doom/r_main.h new file mode 100644 index 000000000..706ddb660 --- /dev/null +++ b/games/NXDoom/src/doom/r_main.h @@ -0,0 +1,150 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_player.h" +#include "r_data.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Lighting LUT. + * Used for z-depth cuing per column/row, and other lighting effects (sector + * ambient, flash). + */ + +/* Lighting constants. Now why not 32 levels here? */ + +#define LIGHTLEVELS 16 +#define LIGHTSEGSHIFT 4 + +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +/* Number of diminishing brightness levels. + * There a 0-31, i.e. 32 LUT in the COLORMAP lump. + */ + +#define NUMCOLORMAPS 32 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Function pointers to switch refresh/drawing functions. + * Used to select shadow mode etc. + */ + +extern void (*colfunc)(void); +extern void (*transcolfunc)(void); +extern void (*basecolfunc)(void); +extern void (*fuzzcolfunc)(void); + +/* No shadow effects on floors. */ + +extern void (*spanfunc)(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* POV related. */ + +extern fixed_t viewcos; +extern fixed_t viewsin; + +extern int viewwindowx; +extern int viewwindowy; + +extern int centerx; +extern int centery; + +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t projection; + +extern int validcount; + +extern int linecount; +extern int loopcount; + +extern boolean setsizeneeded; + +extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE]; +extern lighttable_t *scalelightfixed[MAXLIGHTSCALE]; +extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; + +extern int extralight; +extern lighttable_t *fixedcolormap; + +/* Blocky/low detail mode. B remove this? 0 = high, 1 = low + */ + +extern int detailshift; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Utility functions. */ + +int r_point_on_side(fixed_t x, fixed_t y, node_t *node); + +int r_point_on_seg_side(fixed_t x, fixed_t y, seg_t *line); + +angle_t r_point_to_angle(fixed_t x, fixed_t y); + +angle_t r_point_to_angle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); + +fixed_t r_point_to_dist(fixed_t x, fixed_t y); + +fixed_t r_scale_from_global_angle(angle_t visangle); + +subsector_t *r_point_in_subsector(fixed_t x, fixed_t y); + +/* REFRESH - the actual rendering functions. */ + +/* Called by G_Drawer. */ + +void r_render_player_view(player_t *player); + +/* Called by startup code. */ + +void r_init(void); + +/* Called by m_responder. */ + +void r_set_view_size(int blocks, int detail); + +void r_execute_set_view_size(void); + +#endif /* __R_MAIN__ */ diff --git a/games/NXDoom/src/doom/r_plane.c b/games/NXDoom/src/doom/r_plane.c new file mode 100644 index 000000000..65a2c2fa7 --- /dev/null +++ b/games/NXDoom/src/doom/r_plane.c @@ -0,0 +1,417 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_plane.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Here is a core component: drawing the floors and ceilings, while + * maintaining a per column clipping list only. Moreover, the sky areas + * have to be determined. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "doomdef.h" +#include "doomstat.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXOPENINGS (SCREENWIDTH * CONFIG_GAMES_NXDOOM_MAXOPENINGS) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +planefunction_t floorfunc; +planefunction_t ceilingfunc; + +/* opening */ + +/* Here comes the obnoxious "visplane". */ + +visplane_t visplanes[CONFIG_GAMES_NXDOOM_MAXVISPLANES]; +visplane_t *lastvisplane; +visplane_t *floorplane; +visplane_t *ceilingplane; + +short openings[MAXOPENINGS]; +short *lastopening; + +/* Clip values are the solid pixel bounding the range. floorclip starts out + * SCREENHEIGHT ceilingclip starts out -1 + */ + +short floorclip[SCREENWIDTH]; +short ceilingclip[SCREENWIDTH]; + +/* spanstart holds the start of a plane span initialized to 0 at start */ + +int spanstart[SCREENHEIGHT]; +int spanstop[SCREENHEIGHT]; + +/* texture mapping */ + +lighttable_t **planezlight; +fixed_t planeheight; + +fixed_t yslope[SCREENHEIGHT]; +fixed_t distscale[SCREENWIDTH]; +fixed_t basexscale; +fixed_t baseyscale; + +fixed_t cachedheight[SCREENHEIGHT]; +fixed_t cacheddistance[SCREENHEIGHT]; +fixed_t cachedxstep[SCREENHEIGHT]; +fixed_t cachedystep[SCREENHEIGHT]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Uses global vars: + * planeheight + * ds_source + * basexscale + * baseyscale + * viewx + * viewy + * + * BASIC PRIMITIVE + */ + +static void r_map_plane(int y, int x1, int x2) +{ + angle_t angle; + fixed_t distance; + fixed_t length; + unsigned index; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) + { + i_error("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = fixed_mul(planeheight, yslope[y]); + ds_xstep = cachedxstep[y] = fixed_mul(distance, basexscale); + ds_ystep = cachedystep[y] = fixed_mul(distance, baseyscale); + } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + length = fixed_mul(distance, distscale[x1]); + angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT; + ds_xfrac = viewx + fixed_mul(finecosine[angle], length); + ds_yfrac = -viewy - fixed_mul(finesine[angle], length); + + if (fixedcolormap) + ds_colormap = fixedcolormap; + else + { + index = distance >> LIGHTZSHIFT; + + if (index >= MAXLIGHTZ) index = MAXLIGHTZ - 1; + + ds_colormap = planezlight[index]; + } + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + + /* high or low detail */ + + spanfunc(); +} + +static void r_make_spans(int x, int t1, int b1, int t2, int b2) +{ + while (t1 < t2 && t1 <= b1) + { + r_map_plane(t1, spanstart[t1], x - 1); + t1++; + } + while (b1 > b2 && b1 >= t1) + { + r_map_plane(b1, spanstart[b1], x - 1); + b1--; + } + + while (t2 < t1 && t2 <= b2) + { + spanstart[t2] = x; + t2++; + } + while (b2 > b1 && b2 >= t2) + { + spanstart[b2] = x; + b2--; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* r_init_planes + * Only at game startup. + */ + +void r_init_planes(void) +{ + /* Doh! */ +} + +/* r_clear_planes + * At beginning of frame. + */ + +void r_clear_planes(void) +{ + int i; + angle_t angle; + + /* opening / clipping determination */ + + for (i = 0; i < viewwidth; i++) + { + floorclip[i] = viewheight; + ceilingclip[i] = -1; + } + + lastvisplane = visplanes; + lastopening = openings; + + /* texture calculation */ + + memset(cachedheight, 0, sizeof(cachedheight)); + + /* left to right mapping */ + + angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; + + /* scale will be unit scale at SCREENWIDTH/2 distance */ + + basexscale = fixed_div(finecosine[angle], centerxfrac); + baseyscale = -fixed_div(finesine[angle], centerxfrac); +} + +visplane_t *r_find_plane(fixed_t height, int picnum, int lightlevel) +{ + visplane_t *check; + + if (picnum == skyflatnum) + { + height = 0; /* all skys map together */ + lightlevel = 0; + } + + for (check = visplanes; check < lastvisplane; check++) + { + if (height == check->height && picnum == check->picnum && + lightlevel == check->lightlevel) + { + break; + } + } + + if (check < lastvisplane) return check; + + if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_find_plane: no more visplanes"); + + lastvisplane++; + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = SCREENWIDTH; + check->maxx = -1; + + memset(check->top, 0xff, sizeof(check->top)); + + return check; +} + +visplane_t *r_check_plane(visplane_t *pl, int start, int stop) +{ + int intrl; + int intrh; + int unionl; + int unionh; + int x; + + if (start < pl->minx) + { + intrl = pl->minx; + unionl = start; + } + else + { + unionl = pl->minx; + intrl = start; + } + + if (stop > pl->maxx) + { + intrh = pl->maxx; + unionh = stop; + } + else + { + unionh = pl->maxx; + intrh = stop; + } + + for (x = intrl; x <= intrh; x++) + { + if (pl->top[x] != 0xff) break; + } + + if (x > intrh) + { + pl->minx = unionl; + pl->maxx = unionh; + + return pl; /* use the same one */ + } + + /* make a new visplane */ + + lastvisplane->height = pl->height; + lastvisplane->picnum = pl->picnum; + lastvisplane->lightlevel = pl->lightlevel; + + if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_check_plane: no more visplanes"); + + pl = lastvisplane++; + pl->minx = start; + pl->maxx = stop; + + memset(pl->top, 0xff, sizeof(pl->top)); + + return pl; +} + +/* At the end of each frame. */ + +void r_draw_planes(void) +{ + visplane_t *pl; + int light; + int x; + int stop; + int angle; + int lumpnum; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (ds_p - drawsegs > CONFIG_GAMES_NXDOOM_MAXDRAWSEGS) + i_error("r_draw_planes: drawsegs overflow (%td)", ds_p - drawsegs); + + if (lastvisplane - visplanes > CONFIG_GAMES_NXDOOM_MAXVISPLANES) + i_error("r_draw_planes: visplane overflow (%td)", + lastvisplane - visplanes); + + if (lastopening - openings > MAXOPENINGS) + i_error("r_draw_planes: opening overflow (%td)", lastopening - openings); +#endif + + for (pl = visplanes; pl < lastvisplane; pl++) + { + if (pl->minx > pl->maxx) continue; + + /* sky flat */ + + if (pl->picnum == skyflatnum) + { + dc_iscale = pspriteiscale >> detailshift; + + /* Sky is always drawn full bright, i.e. colormaps[0] is used. + * Because of this hack, sky is not affected by INVUL inverse + * mapping. + */ + + dc_colormap = colormaps; + dc_texturemid = skytexturemid; + for (x = pl->minx; x <= pl->maxx; x++) + { + dc_yl = pl->top[x]; + dc_yh = pl->bottom[x]; + + if (dc_yl <= dc_yh) + { + angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT; + dc_x = x; + dc_source = r_get_column(skytexture, angle); + colfunc(); + } + } + + continue; + } + + /* regular flat */ + + lumpnum = firstflat + flattranslation[pl->picnum]; + ds_source = w_cache_lump_num(lumpnum, PU_STATIC); + + planeheight = abs(pl->height - viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (light >= LIGHTLEVELS) light = LIGHTLEVELS - 1; + + if (light < 0) light = 0; + + planezlight = zlight[light]; + + pl->top[pl->maxx + 1] = 0xff; + pl->top[pl->minx - 1] = 0xff; + + stop = pl->maxx + 1; + + for (x = pl->minx; x <= stop; x++) + { + r_make_spans(x, pl->top[x - 1], pl->bottom[x - 1], pl->top[x], + pl->bottom[x]); + } + + w_release_lump_num(lumpnum); + } +} diff --git a/games/NXDoom/src/doom/r_plane.h b/games/NXDoom/src/doom/r_plane.h new file mode 100644 index 000000000..1ae3b7925 --- /dev/null +++ b/games/NXDoom/src/doom/r_plane.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_plane.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh, visplane stuff (floor, ceilings). + * + ****************************************************************************/ + +#ifndef __R_PLANE__ +#define __R_PLANE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "r_data.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*planefunction_t)(int top, int bottom); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Visplane related. */ + +extern short *lastopening; + +extern planefunction_t floorfunc; +extern planefunction_t ceilingfunc_t; + +extern short floorclip[SCREENWIDTH]; +extern short ceilingclip[SCREENWIDTH]; + +extern fixed_t yslope[SCREENHEIGHT]; +extern fixed_t distscale[SCREENWIDTH]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_init_planes(void); +void r_clear_planes(void); + +void r_draw_planes(void); + +visplane_t *r_find_plane(fixed_t height, int picnum, int lightlevel); + +visplane_t *r_check_plane(visplane_t *pl, int start, int stop); + +#endif /* __R_PLANE__ */ diff --git a/games/NXDoom/src/doom/r_segs.c b/games/NXDoom/src/doom/r_segs.c new file mode 100644 index 000000000..83d9b026f --- /dev/null +++ b/games/NXDoom/src/doom/r_segs.c @@ -0,0 +1,787 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_segs.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * All the clipping: columns, horizontal spans, sky columns. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" + +#include "doomdef.h" +#include "doomstat.h" + +#include "r_local.h" +#include "r_sky.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1 << HEIGHTBITS) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* OPTIMIZE: closed two sided lines as single sided */ + +/* True if any of the segs textures might be visible. */ + +boolean segtextured; + +/* False if the back side is the same plane. */ + +boolean markfloor; +boolean markceiling; + +boolean maskedtexture; +int toptexture; +int bottomtexture; +int midtexture; + +angle_t rw_normalangle; + +/* angle to line origin */ + +int rw_angle1; + +/* regular wall */ + +int rw_x; +int rw_stopx; +angle_t rw_centerangle; +fixed_t rw_offset; +fixed_t rw_distance; +fixed_t rw_scale; +fixed_t rw_scalestep; +fixed_t rw_midtexturemid; +fixed_t rw_toptexturemid; +fixed_t rw_bottomtexturemid; + +int worldtop; +int worldbottom; +int worldhigh; +int worldlow; + +fixed_t pixhigh; +fixed_t pixlow; +fixed_t pixhighstep; +fixed_t pixlowstep; + +fixed_t topfrac; +fixed_t topstep; + +fixed_t bottomfrac; +fixed_t bottomstep; + +lighttable_t **walllights; + +short *maskedtexturecol; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Draws zero, one, or two textures (and possibly a masked texture) for + * walls. + * + * Can draw or mark the starting pixel of floor and ceiling textures. + * CALLED: CORE LOOPING ROUTINE. + */ + +static void r_render_seg_loop(void) +{ + angle_t angle; + unsigned index; + int yl; + int yh; + int mid; + fixed_t texturecolumn; + int top; + int bottom; + + for (; rw_x < rw_stopx; rw_x++) + { + /* mark floor / ceiling areas */ + + yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS; + + /* no space above wall? */ + + if (yl < ceilingclip[rw_x] + 1) yl = ceilingclip[rw_x] + 1; + + if (markceiling) + { + top = ceilingclip[rw_x] + 1; + bottom = yl - 1; + + if (bottom >= floorclip[rw_x]) bottom = floorclip[rw_x] - 1; + + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + } + + yh = bottomfrac >> HEIGHTBITS; + + if (yh >= floorclip[rw_x]) yh = floorclip[rw_x] - 1; + + if (markfloor) + { + top = yh + 1; + bottom = floorclip[rw_x] - 1; + if (top <= ceilingclip[rw_x]) top = ceilingclip[rw_x] + 1; + if (top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + } + + /* texturecolumn and lighting are independent of wall tiers */ + + if (segtextured) + { + /* calculate texture offset */ + + angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT; + texturecolumn = + rw_offset - fixed_mul(finetangent[angle], rw_distance); + texturecolumn >>= FRACBITS; + + /* calculate lighting */ + + index = rw_scale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + dc_x = rw_x; + dc_iscale = 0xffffffffu / (unsigned)rw_scale; + } + else + { + /* purely to shut up the compiler */ + + texturecolumn = 0; + } + + /* draw the wall tiers */ + + if (midtexture) + { + /* single sided line */ + + dc_yl = yl; + dc_yh = yh; + dc_texturemid = rw_midtexturemid; + dc_source = r_get_column(midtexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + /* two sided line */ + + if (toptexture) + { + /* top wall */ + + mid = pixhigh >> HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) mid = floorclip[rw_x] - 1; + + if (mid >= yl) + { + dc_yl = yl; + dc_yh = mid; + dc_texturemid = rw_toptexturemid; + dc_source = r_get_column(toptexture, texturecolumn); + colfunc(); + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl - 1; + } + else + { + /* no top wall */ + + if (markceiling) ceilingclip[rw_x] = yl - 1; + } + + if (bottomtexture) + { + /* bottom wall */ + + mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS; + pixlow += pixlowstep; + + /* no space above wall? */ + + if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x] + 1; + + if (mid <= yh) + { + dc_yl = mid; + dc_yh = yh; + dc_texturemid = rw_bottomtexturemid; + dc_source = r_get_column(bottomtexture, texturecolumn); + colfunc(); + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh + 1; + } + else + { + /* no bottom wall */ + + if (markfloor) floorclip[rw_x] = yh + 1; + } + + if (maskedtexture) + { + /* save texturecol + * for backdrawing of masked mid texture + */ + + maskedtexturecol[rw_x] = texturecolumn; + } + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void r_render_masked_seg_range(drawseg_t *ds, int x1, int x2) +{ + unsigned index; + column_t *col; + int lightnum; + int texnum; + + /* Calculate light table. + * Use different light tables for horizontal / vertical / diagonal. + * Diagonal? OPTIMIZE: get rid of LIGHTSEGSHIFT globally + */ + + curline = ds->curline; + frontsector = curline->frontsector; + backsector = curline->backsector; + texnum = texturetranslation[curline->sidedef->midtexture]; + + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1) * rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + + /* find positioning */ + + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dc_texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight + : backsector->floorheight; + dc_texturemid = dc_texturemid + textureheight[texnum] - viewz; + } + else + { + dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight + ? frontsector->ceilingheight + : backsector->ceilingheight; + dc_texturemid = dc_texturemid - viewz; + } + + dc_texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) dc_colormap = fixedcolormap; + + /* draw the columns */ + + for (dc_x = x1; dc_x <= x2; dc_x++) + { + /* calculate lighting */ + + if (maskedtexturecol[dc_x] != SHRT_MAX) + { + if (!fixedcolormap) + { + index = spryscale >> LIGHTSCALESHIFT; + + if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; + + dc_colormap = walllights[index]; + } + + sprtopscreen = centeryfrac - fixed_mul(dc_texturemid, spryscale); + dc_iscale = 0xffffffffu / (unsigned)spryscale; + + /* draw the texture */ + + col = (column_t *)((byte *)r_get_column(texnum, + maskedtexturecol[dc_x]) - + 3); + + r_draw_masked_column(col); + maskedtexturecol[dc_x] = SHRT_MAX; + } + + spryscale += rw_scalestep; + } +} + +/* r_store_wall_range + * A wall segment will be drawn between start and stop pixels (inclusive). + */ + +void r_store_wall_range(int start, int stop) +{ + fixed_t hyp; + fixed_t sineval; + angle_t distangle, offsetangle; + fixed_t vtop; + int lightnum; + + /* don't overflow and crash */ + + if (ds_p == &drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]) return; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (start >= viewwidth || start > stop) + i_error("Bad R_RenderWallRange: %i to %i", start, stop); +#endif + + sidedef = curline->sidedef; + linedef = curline->linedef; + + /* mark the segment as visible for auto map */ + + linedef->flags |= ML_MAPPED; + + /* calculate rw_distance for scale calculation */ + + rw_normalangle = curline->angle + ANG90; + offsetangle = abs((int)rw_normalangle - (int)rw_angle1); + + if (offsetangle > ANG90) offsetangle = ANG90; + + distangle = ANG90 - offsetangle; + hyp = r_point_to_dist(curline->v1->x, curline->v1->y); + sineval = finesine[distangle >> ANGLETOFINESHIFT]; + rw_distance = fixed_mul(hyp, sineval); + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop + 1; + + /* calculate scale at both ends and step */ + + ds_p->scale1 = rw_scale = + r_scale_from_global_angle(viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = + r_scale_from_global_angle(viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = + (ds_p->scale2 - rw_scale) / (stop - start); + } + else + { +#if 0 /* UNUSED: try to fix the stretched line bug */ + if (rw_distance < FRACUNIT / 2) + { + fixed_t trx, try; + fixed_t gxt, gyt; + + trx = curline->v1->x - viewx; + try = curline->v1->y - viewy; + + gxt = fixed_mul(trx, viewcos); + gyt = -fixed_mul(try, viewsin); + ds_p->scale1 = fixed_div(projection, gxt - gyt) << detailshift; + } + +#endif + ds_p->scale2 = ds_p->scale1; + } + + /* calculate texture boundaries and decide if floor / ceiling marks are + * needed + */ + + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = NULL; + + if (!backsector) + { + /* single sided line */ + + midtexture = texturetranslation[sidedef->midtexture]; + + /* a single sided line is terminal, so it must mark ends */ + + markfloor = markceiling = true; + if (linedef->flags & ML_DONTPEGBOTTOM) + { + vtop = + frontsector->floorheight + textureheight[sidedef->midtexture]; + + /* bottom of texture at bottom */ + + rw_midtexturemid = vtop - viewz; + } + else + { + /* top of texture at top */ + + rw_midtexturemid = worldtop; + } + + rw_midtexturemid += sidedef->rowoffset; + + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->tsilheight = INT_MIN; + } + else + { + /* two sided line */ + + ds_p->sprtopclip = ds_p->sprbottomclip = NULL; + ds_p->silhouette = 0; + + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + + /* ds_p->sprbottomclip = negonearray; */ + } + + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + + /* ds_p->sprtopclip = screenheightarray; */ + } + + if (backsector->ceilingheight <= frontsector->floorheight) + { + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->silhouette |= SIL_BOTTOM; + } + + if (backsector->floorheight >= frontsector->ceilingheight) + { + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = INT_MIN; + ds_p->silhouette |= SIL_TOP; + } + + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + /* hack to allow height changes in outdoor areas */ + + if (frontsector->ceilingpic == skyflatnum && + backsector->ceilingpic == skyflatnum) + { + worldtop = worldhigh; + } + + if (worldlow != worldbottom || + backsector->floorpic != frontsector->floorpic || + backsector->lightlevel != frontsector->lightlevel) + { + markfloor = true; + } + else + { + /* same plane on both sides */ + + markfloor = false; + } + + if (worldhigh != worldtop || + backsector->ceilingpic != frontsector->ceilingpic || + backsector->lightlevel != frontsector->lightlevel) + { + markceiling = true; + } + else + { + /* same plane on both sides */ + + markceiling = false; + } + + if (backsector->ceilingheight <= frontsector->floorheight || + backsector->floorheight >= frontsector->ceilingheight) + { + /* closed door */ + + markceiling = markfloor = true; + } + + if (worldhigh < worldtop) + { + /* top texture */ + + toptexture = texturetranslation[sidedef->toptexture]; + if (linedef->flags & ML_DONTPEGTOP) + { + /* top of texture at top */ + + rw_toptexturemid = worldtop; + } + else + { + vtop = backsector->ceilingheight + + textureheight[sidedef->toptexture]; + + /* bottom of texture */ + + rw_toptexturemid = vtop - viewz; + } + } + + if (worldlow > worldbottom) + { + /* bottom texture */ + + bottomtexture = texturetranslation[sidedef->bottomtexture]; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { + /* bottom of texture at bottom top of texture at top */ + + rw_bottomtexturemid = worldtop; + } + else /* top of texture at top */ + rw_bottomtexturemid = worldlow; + } + + rw_toptexturemid += sidedef->rowoffset; + rw_bottomtexturemid += sidedef->rowoffset; + + /* allocate space for masked texture tables */ + + if (sidedef->midtexture) + { + /* masked midtexture */ + + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + + /* calculate rw_offset (only needed for textured lines) */ + + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + offsetangle = rw_normalangle - rw_angle1; + + if (offsetangle > ANG180) offsetangle = -offsetangle; + + if (offsetangle > ANG90) offsetangle = ANG90; + + sineval = finesine[offsetangle >> ANGLETOFINESHIFT]; + rw_offset = fixed_mul(hyp, sineval); + + if (rw_normalangle - rw_angle1 < ANG180) rw_offset = -rw_offset; + + rw_offset += sidedef->textureoffset + curline->offset; + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + /* calculate light table use different light tables for horizontal / + * vertical / diagonal + * + * OPTIMIZE: get rid of LIGHTSEGSHIFT globally + */ + + if (!fixedcolormap) + { + lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (curline->v1->y == curline->v2->y) + lightnum--; + else if (curline->v1->x == curline->v2->x) + lightnum++; + + if (lightnum < 0) + walllights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + walllights = scalelight[LIGHTLEVELS - 1]; + else + walllights = scalelight[lightnum]; + } + } + + /* if a floor / ceiling plane is on the wrong side of the view plane, it is + * definitely invisible and doesn't need to be marked. + */ + + if (frontsector->floorheight >= viewz) + { + /* above view plane */ + + markfloor = false; + } + + if (frontsector->ceilingheight <= viewz && + frontsector->ceilingpic != skyflatnum) + { + /* below view plane */ + + markceiling = false; + } + + /* calculate incremental stepping values for texture edges */ + + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -fixed_mul(rw_scalestep, worldtop); + topfrac = (centeryfrac >> 4) - fixed_mul(worldtop, rw_scale); + + bottomstep = -fixed_mul(rw_scalestep, worldbottom); + bottomfrac = (centeryfrac >> 4) - fixed_mul(worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac >> 4) - fixed_mul(worldhigh, rw_scale); + pixhighstep = -fixed_mul(rw_scalestep, worldhigh); + } + + if (worldlow > worldbottom) + { + pixlow = (centeryfrac >> 4) - fixed_mul(worldlow, rw_scale); + pixlowstep = -fixed_mul(rw_scalestep, worldlow); + } + } + + /* render it */ + + if (markceiling) + { + ceilingplane = r_check_plane(ceilingplane, rw_x, rw_stopx - 1); + } + + if (markfloor) + { + floorplane = r_check_plane(floorplane, rw_x, rw_stopx - 1); + } + + r_render_seg_loop(); + + /* save sprite clipping info */ + + if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip) + { + memcpy(lastopening, ceilingclip + start, + sizeof(*lastopening) * (rw_stopx - start)); + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && + !ds_p->sprbottomclip) + { + memcpy(lastopening, floorclip + start, + sizeof(*lastopening) * (rw_stopx - start)); + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + + if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + } + + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + + ds_p++; +} diff --git a/games/NXDoom/src/doom/r_segs.h b/games/NXDoom/src/doom/r_segs.h new file mode 100644 index 000000000..8bd13275f --- /dev/null +++ b/games/NXDoom/src/doom/r_segs.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_segs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh module, drawing LineSegs from BSP. + * + ****************************************************************************/ + +#ifndef __R_SEGS__ +#define __R_SEGS__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lighttable_t **walllights; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_render_masked_seg_range(drawseg_t *ds, int x1, int x2); + +#endif /* __R_SEGS__ */ diff --git a/games/NXDoom/src/doom/r_sky.c b/games/NXDoom/src/doom/r_sky.c new file mode 100644 index 000000000..d84e6729b --- /dev/null +++ b/games/NXDoom/src/doom/r_sky.c @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_sky.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Sky rendering. The DOOM sky is a texture map like any + * wall, wrapping around. A 1024 columns equal 360 degrees. + * The default sky map is 256 columns and repeats 4 times + * on a 320 screen? + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Needed for FRACUNIT. */ + +#include "m_fixed.h" + +/* Needed for Flat retrieval. */ + +#include "r_data.h" + +#include "r_sky.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* sky mapping */ + +int skyflatnum; +int skytexture; +int skytexturemid; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: r_init_sky_map + * + * Description: + * Called whenever the view size changes. + * + ****************************************************************************/ + +void r_init_sky_map(void) +{ + /* skyflatnum = r_flat_num_for_name ( SKYFLATNAME ); */ + + skytexturemid = SCREENHEIGHT / 2 * FRACUNIT; +} diff --git a/games/NXDoom/src/doom/r_sky.h b/games/NXDoom/src/doom/r_sky.h new file mode 100644 index 000000000..e9455b72e --- /dev/null +++ b/games/NXDoom/src/doom/r_sky.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_sky.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Sky rendering. + * + ****************************************************************************/ + +#ifndef __R_SKY__ +#define __R_SKY__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SKY, store the number for name. */ + +#define SKYFLATNAME "F_SKY1" + +/* The sky map is 256*128*4 maps. */ + +#define ANGLETOSKYSHIFT 22 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int skytexture; +extern int skytexturemid; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called whenever the view size changes. */ + +void r_init_sky_map(void); + +#endif diff --git a/games/NXDoom/src/doom/r_state.h b/games/NXDoom/src/doom/r_state.h new file mode 100644 index 000000000..cab81830d --- /dev/null +++ b/games/NXDoom/src/doom/r_state.h @@ -0,0 +1,130 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_state.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh/render internal state variables (global). + * + ****************************************************************************/ + +#ifndef __R_STATE__ +#define __R_STATE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Need data structure definitions. */ + +#include "d_player.h" +#include "r_data.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Refresh internal data structures, for rendering. */ + +/* needed for texture pegging */ + +extern fixed_t *textureheight; + +/* needed for pre rendering (fracs) */ + +extern fixed_t *spritewidth; + +extern fixed_t *spriteoffset; +extern fixed_t *spritetopoffset; + +extern lighttable_t *colormaps; + +extern int viewwidth; +extern int scaledviewwidth; +extern int viewheight; + +extern int firstflat; + +/* for global animation */ + +extern int *flattranslation; +extern int *texturetranslation; + +/* Sprite.... */ + +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +/* Lookup tables for map data. */ + +extern int numsprites; +extern spritedef_t *sprites; + +extern int numvertices; +extern vertex_t *vertices; + +extern int numsegs; +extern seg_t *segs; + +extern int numsectors; +extern sector_t *sectors; + +extern int numsubsectors; +extern subsector_t *subsectors; + +extern int numnodes; +extern node_t *nodes; + +extern int numlines; +extern line_t *lines; + +extern int numsides; +extern side_t *sides; + +/* POV data. */ + +extern fixed_t viewx; +extern fixed_t viewy; +extern fixed_t viewz; + +extern angle_t viewangle; +extern player_t *viewplayer; + +/* ? */ + +extern angle_t clipangle; + +extern int viewangletox[FINEANGLES / 2]; +extern angle_t xtoviewangle[SCREENWIDTH + 1]; + +/* extern fixed_t finetangent[FINEANGLES/2]; */ + +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +/* angle to line origin */ + +extern int rw_angle1; + +/* Segs count? */ + +extern int sscount; + +extern visplane_t *floorplane; +extern visplane_t *ceilingplane; + +#endif /* __R_STATE__ */ diff --git a/games/NXDoom/src/doom/r_things.c b/games/NXDoom/src/doom/r_things.c new file mode 100644 index 000000000..019d83a70 --- /dev/null +++ b/games/NXDoom/src/doom/r_things.c @@ -0,0 +1,974 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_things.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh of things, i.e. objects represented by sprites. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "doomdef.h" + +#include "i_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "r_local.h" + +#include "doomstat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MINZ (FRACUNIT * 4) +#define BASEYCENTER (SCREENHEIGHT / 2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + int x1; + int x2; + + int column; + int topclip; + int bottomclip; +} maskdraw_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Sprite rotation 0 is facing the viewer, rotation 1 is one angle turn + * CLOCKWISE around the axis. + * + * This is not the same as the angle, which increases counter clockwise + * (protractor). There was a lot of stuff grabbed wrong, so I changed it... + */ + +fixed_t pspritescale; +fixed_t pspriteiscale; + +lighttable_t **spritelights; + +/* constant arrays used for psprite clipping and initializing clipping */ + +short negonearray[SCREENWIDTH]; +short screenheightarray[SCREENWIDTH]; + +/* INITIALIZATION FUNCTIONS */ + +/* variables used to look up and range check thing_t sprites patches */ + +spritedef_t *sprites; +int numsprites; + +spriteframe_t sprtemp[29]; +int maxframe; +const char *spritename; + +vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]; +vissprite_t *vissprite_p; +int newvissprite; + +vissprite_t overflowsprite; + +short *mfloorclip; +short *mceilingclip; + +fixed_t spryscale; +fixed_t sprtopscreen; + +vissprite_t vsprsortedhead; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* R_InstallSpriteLump + * Local function for r_init_sprites. + */ + +static void r_install_sprite_lump(int lump, unsigned frame, + unsigned rotation, boolean flipped) +{ + int r; + + if (frame >= 29 || rotation > 8) + i_error("R_InstallSpriteLump: " + "Bad frame characters in lump %i", + lump); + + if ((int)frame > maxframe) maxframe = frame; + + if (rotation == 0) + { + /* the lump should be used for all rotations */ + + if (sprtemp[frame].rotate == false) + i_error("r_init_sprites: Sprite %s frame %c has " + "multip rot=0 lump", + spritename, 'A' + frame); + + if (sprtemp[frame].rotate == true) + i_error("r_init_sprites: Sprite %s frame %c has rotations " + "and a rot=0 lump", + spritename, 'A' + frame); + + sprtemp[frame].rotate = false; + for (r = 0; r < 8; r++) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte)flipped; + } + + return; + } + + /* the lump is only used for one rotation */ + + if (sprtemp[frame].rotate == false) + i_error("r_init_sprites: Sprite %s frame %c has rotations " + "and a rot=0 lump", + spritename, 'A' + frame); + + sprtemp[frame].rotate = true; + + /* make 0 based */ + + rotation--; + if (sprtemp[frame].lump[rotation] != -1) + i_error("r_init_sprites: Sprite %s : %c : %c " + "has two lumps mapped to it", + spritename, 'A' + frame, '1' + rotation); + + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte)flipped; +} + +/* r_init_sprite_defs + * + * Pass a null terminated list of sprite names (4 chars exactly) to be used. + * + * Builds the sprite rotation matrixes to account for horizontally flipped + * sprites. + * + * Will report an error if the lumps are inconsistent. Only called at + * startup. + * + * Sprite lump names are 4 characters for the actor, a letter for the frame, + * and a number for the rotation. + * + * A sprite that is flippable will have an additional letter/number appended. + * + * The rotation character can be 0 to signify no rotations. + */ + +static void r_init_sprite_defs(const char **namelist) +{ + const char **check; + int i; + int l; + int frame; + int rotation; + int start; + int end; + int patched; + + /* count the number of sprite names */ + + check = namelist; + while (*check != NULL) + check++; + + numsprites = check - namelist; + + if (!numsprites) return; + + sprites = z_malloc(numsprites * sizeof(*sprites), PU_STATIC, NULL); + + start = firstspritelump - 1; + end = lastspritelump + 1; + + /* scan all the lump names for each of the names, noting the highest frame + * letter. Just compare 4 characters as ints + */ + + for (i = 0; i < numsprites; i++) + { + spritename = (namelist[i]); + memset(sprtemp, -1, sizeof(sprtemp)); + + maxframe = -1; + + /* scan the lumps, filling in the frames for whatever is found + */ + + for (l = start + 1; l < end; l++) + { + if (!strncasecmp(lumpinfo[l]->name, spritename, 4)) + { + frame = lumpinfo[l]->name[4] - 'A'; + rotation = lumpinfo[l]->name[5] - '0'; + + if (modifiedgame) + patched = w_get_num_for_name(lumpinfo[l]->name); + else + patched = l; + + r_install_sprite_lump(patched, frame, rotation, false); + + if (lumpinfo[l]->name[6]) + { + frame = lumpinfo[l]->name[6] - 'A'; + rotation = lumpinfo[l]->name[7] - '0'; + r_install_sprite_lump(l, frame, rotation, true); + } + } + } + + /* check the frames that were found for completeness */ + + if (maxframe == -1) + { + sprites[i].numframes = 0; + continue; + } + + maxframe++; + + for (frame = 0; frame < maxframe; frame++) + { + switch ((int)sprtemp[frame].rotate) + { + case -1: + + /* no rotations were found for that frame at all */ + + i_error("r_init_sprites: No patches found " + "for %s frame %c", + spritename, frame + 'A'); + break; + + case 0: + break; /* only the first rotation is needed */ + + case 1: + + /* must have all 8 frames */ + + for (rotation = 0; rotation < 8; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + i_error("r_init_sprites: Sprite %s frame %c " + "is missing rotations", + spritename, frame + 'A'); + break; + } + } + + /* allocate space for the frames present and copy sprtemp to it */ + + sprites[i].numframes = maxframe; + sprites[i].spriteframes = + z_malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); + memcpy(sprites[i].spriteframes, sprtemp, + maxframe * sizeof(spriteframe_t)); + } +} + +static vissprite_t *r_new_vis_sprite(void) +{ + if (vissprite_p == &vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]) + { + return &overflowsprite; + } + + vissprite_p++; + return vissprite_p - 1; +} + +/* R_DrawVisSprite + * mfloorclip and mceilingclip should also be set. + */ + +static void r_draw_vis_sprite(vissprite_t *vis, int x1, int x2) +{ + column_t *column; + int texturecolumn; + fixed_t frac; + patch_t *patch; + + patch = w_cache_lump_num(vis->patch + firstspritelump, PU_CACHE); + + dc_colormap = vis->colormap; + + if (!dc_colormap) + { + /* NULL colormap = shadow draw */ + + colfunc = fuzzcolfunc; + } + else if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = transcolfunc; + dc_translation = + translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8)); + } + + dc_iscale = abs(vis->xiscale) >> detailshift; + dc_texturemid = vis->texturemid; + frac = vis->startfrac; + spryscale = vis->scale; + sprtopscreen = centeryfrac - fixed_mul(dc_texturemid, spryscale); + + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) + { + texturecolumn = frac >> FRACBITS; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + { + i_error("R_DrawSpriteRange: bad texturecolumn"); + } + +#endif + + column = (column_t *)((byte *)patch + + LONG(patch->columnofs[texturecolumn])); + r_draw_masked_column(column); + } + + colfunc = basecolfunc; +} + +/* R_ProjectSprite + * Generates a vissprite for a thing if it might be visible. + */ + +static void r_project_sprite(mobj_t *thing) +{ + fixed_t tr_x; + fixed_t tr_y; + + fixed_t gxt; + fixed_t gyt; + + fixed_t tx; + fixed_t tz; + + fixed_t xscale; + + int x1; + int x2; + + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + + unsigned rot; + boolean flip; + + int index; + + vissprite_t *vis; + + angle_t ang; + fixed_t iscale; + + /* transform the origin point */ + + tr_x = thing->x - viewx; + tr_y = thing->y - viewy; + + gxt = fixed_mul(tr_x, viewcos); + gyt = -fixed_mul(tr_y, viewsin); + + tz = gxt - gyt; + + /* thing is behind view plane? */ + + if (tz < MINZ) return; + + xscale = fixed_div(projection, tz); + + gxt = -fixed_mul(tr_x, viewsin); + gyt = fixed_mul(tr_y, viewcos); + tx = -(gyt + gxt); + + /* too far off the side? */ + + if (abs(tx) > (tz << 2)) return; + + /* decide which patch to use for sprite relative to player */ + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned int)thing->sprite >= (unsigned int)numsprites) + i_error("R_ProjectSprite: invalid sprite number %i ", thing->sprite); +#endif + sprdef = &sprites[thing->sprite]; +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((thing->frame & FF_FRAMEMASK) >= sprdef->numframes) + i_error("R_ProjectSprite: invalid sprite frame %i : %i ", thing->sprite, + thing->frame); +#endif + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + /* choose a different rotation based on player view */ + + ang = r_point_to_angle(thing->x, thing->y); + rot = (ang - thing->angle + (unsigned)(ANG45 / 2) * 9) >> 29; + lump = sprframe->lump[rot]; + flip = (boolean)sprframe->flip[rot]; + } + else + { + /* use single rotation for all views */ + + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + } + + /* calculate edges of the shape */ + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + fixed_mul(tx, xscale)) >> FRACBITS; + + /* off the right side? */ + + if (x1 > viewwidth) return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + fixed_mul(tx, xscale)) >> FRACBITS) - 1; + + /* off the left side */ + + if (x2 < 0) return; + + /* store information in a vissprite */ + + vis = r_new_vis_sprite(); + vis->mobjflags = thing->flags; + vis->scale = xscale << detailshift; + vis->gx = thing->x; + vis->gy = thing->y; + vis->gz = thing->z; + vis->gzt = thing->z + spritetopoffset[lump]; + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + iscale = fixed_div(FRACUNIT, xscale); + + if (flip) + { + vis->startfrac = spritewidth[lump] - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); + vis->patch = lump; + + /* get light level */ + + if (thing->flags & MF_SHADOW) + { + /* shadow draw */ + + vis->colormap = NULL; + } + else if (fixedcolormap) + { + /* fixed map */ + + vis->colormap = fixedcolormap; + } + else if (thing->frame & FF_FULLBRIGHT) + { + /* full bright */ + + vis->colormap = colormaps; + } + + else + { + /* diminished light */ + + index = xscale >> (LIGHTSCALESHIFT - detailshift); + + if (index >= MAXLIGHTSCALE) index = MAXLIGHTSCALE - 1; + + vis->colormap = spritelights[index]; + } +} + +/* R_DrawPSprite */ + +static void r_draw_psprite(pspdef_t *psp) +{ + fixed_t tx; + int x1; + int x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + vissprite_t avis; + + /* decide which patch to use */ + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((unsigned)psp->state->sprite >= (unsigned int)numsprites) + { + i_error("R_ProjectSprite: invalid sprite number %i ", + psp->state->sprite); + } + +#endif + + sprdef = &sprites[psp->state->sprite]; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if ((psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes) + { + i_error("R_ProjectSprite: invalid sprite frame %i : %i ", + psp->state->sprite, psp->state->frame); + } + +#endif + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + + /* calculate edges of the shape */ + + tx = psp->sx - (SCREENWIDTH / 2) * FRACUNIT; + + tx -= spriteoffset[lump]; + x1 = (centerxfrac + fixed_mul(tx, pspritescale)) >> FRACBITS; + + /* off the right side */ + + if (x1 > viewwidth) return; + + tx += spritewidth[lump]; + x2 = ((centerxfrac + fixed_mul(tx, pspritescale)) >> FRACBITS) - 1; + + /* off the left side */ + + if (x2 < 0) return; + + /* store information in a vissprite */ + + vis = &avis; + vis->mobjflags = 0; + vis->texturemid = (BASEYCENTER << FRACBITS) + FRACUNIT / 2 - + (psp->sy - spritetopoffset[lump]); + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2; + vis->scale = pspritescale << detailshift; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = spritewidth[lump] - 1; + } + else + { + vis->xiscale = pspriteiscale; + vis->startfrac = 0; + } + + if (vis->x1 > x1) vis->startfrac += vis->xiscale * (vis->x1 - x1); + + vis->patch = lump; + + if (viewplayer->powers[pw_invisibility] > 4 * 32 || + viewplayer->powers[pw_invisibility] & 8) + { + vis->colormap = NULL; /* shadow draw */ + } + else if (fixedcolormap) + { + vis->colormap = fixedcolormap; /* fixed color */ + } + else if (psp->state->frame & FF_FULLBRIGHT) + { + vis->colormap = colormaps; /* full bright */ + } + else + { + vis->colormap = spritelights[MAXLIGHTSCALE - 1]; /* local light */ + } + + r_draw_vis_sprite(vis, vis->x1, vis->x2); +} + +static void r_draw_player_sprites(void) +{ + int i; + int lightnum; + pspdef_t *psp; + + /* get light level */ + + lightnum = + (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) + + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + /* clip to screen bounds */ + + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + /* add all active psprites */ + + for (i = 0, psp = viewplayer->psprites; i < NUMPSPRITES; i++, psp++) + { + if (psp->state) r_draw_psprite(psp); + } +} + +static void r_sort_vis_sprites(void) +{ + int i; + int count; + vissprite_t *ds; + vissprite_t *best; + static vissprite_t unsorted; + fixed_t bestscale; + + count = vissprite_p - vissprites; + + unsorted.next = unsorted.prev = &unsorted; + + if (!count) return; + + for (ds = vissprites; ds < vissprite_p; ds++) + { + ds->next = ds + 1; + ds->prev = ds - 1; + } + + vissprites[0].prev = &unsorted; + unsorted.next = &vissprites[0]; + (vissprite_p - 1)->next = &unsorted; + unsorted.prev = vissprite_p - 1; + + /* pull the vissprites out by scale */ + + vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead; + for (i = 0; i < count; i++) + { + bestscale = INT_MAX; + best = unsorted.next; + + for (ds = unsorted.next; ds != &unsorted; ds = ds->next) + { + if (ds->scale < bestscale) + { + bestscale = ds->scale; + best = ds; + } + } + + best->next->prev = best->prev; + best->prev->next = best->next; + best->next = &vsprsortedhead; + best->prev = vsprsortedhead.prev; + vsprsortedhead.prev->next = best; + vsprsortedhead.prev = best; + } +} + +static void r_draw_sprite(vissprite_t *spr) +{ + drawseg_t *ds; + short clipbot[SCREENWIDTH]; + short cliptop[SCREENWIDTH]; + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + int silhouette; + + for (x = spr->x1; x <= spr->x2; x++) + clipbot[x] = cliptop[x] = -2; + + /* Scan drawsegs from end to start for obscuring segs. + * The first drawseg that has a greater scale is the clip seg. + */ + + for (ds = ds_p - 1; ds >= drawsegs; ds--) + { + /* determine if the drawseg obscures the sprite */ + + if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + (!ds->silhouette && !ds->maskedtexturecol)) + { + continue; /* does not cover sprite */ + } + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale || + (lowscale < spr->scale && + !r_point_on_seg_side(spr->gx, spr->gy, ds->curline))) + { + /* masked mid texture? */ + + if (ds->maskedtexturecol) r_render_masked_seg_range(ds, r1, r2); + continue; /* seg is behind sprite */ + } + + /* clip this piece of the sprite */ + + silhouette = ds->silhouette; + + if (spr->gz >= ds->bsilheight) silhouette &= ~SIL_BOTTOM; + + if (spr->gzt <= ds->tsilheight) silhouette &= ~SIL_TOP; + + if (silhouette == 1) + { + /* bottom sil */ + + for (x = r1; x <= r2; x++) + { + if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; + } + } + else if (silhouette == 2) + { + /* top sil */ + + for (x = r1; x <= r2; x++) + { + if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; + } + } + else if (silhouette == 3) + { + /* both */ + + for (x = r1; x <= r2; x++) + { + if (clipbot[x] == -2) clipbot[x] = ds->sprbottomclip[x]; + if (cliptop[x] == -2) cliptop[x] = ds->sprtopclip[x]; + } + } + } + + /* all clipping has been performed, so draw the sprite */ + + /* check for unclipped columns */ + + for (x = spr->x1; x <= spr->x2; x++) + { + if (clipbot[x] == -2) clipbot[x] = viewheight; + + if (cliptop[x] == -2) cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + r_draw_vis_sprite(spr, spr->x1, spr->x2); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* r_init_sprites + * Called at program start. + */ + +void r_init_sprites(const char **namelist) +{ + int i; + + for (i = 0; i < SCREENWIDTH; i++) + { + negonearray[i] = -1; + } + + r_init_sprite_defs(namelist); +} + +/* Called at frame start. */ + +void r_clear_sprites(void) +{ + vissprite_p = vissprites; +} + +/* r_draw_masked_column + * Used for sprites and masked mid textures. + * Masked means: partly transparent, i.e. stored in posts/runs of opaque + * pixels. + */ + +void r_draw_masked_column(column_t *column) +{ + int topscreen; + int bottomscreen; + fixed_t basetexturemid; + + basetexturemid = dc_texturemid; + + for (; column->topdelta != 0xff; ) + { + /* calculate unclipped screen coordinates for post */ + + topscreen = sprtopscreen + spryscale * column->topdelta; + bottomscreen = topscreen + spryscale * column->length; + + dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS; + dc_yh = (bottomscreen - 1) >> FRACBITS; + + if (dc_yh >= mfloorclip[dc_x]) dc_yh = mfloorclip[dc_x] - 1; + if (dc_yl <= mceilingclip[dc_x]) dc_yl = mceilingclip[dc_x] + 1; + + if (dc_yl <= dc_yh) + { + dc_source = (byte *)column + 3; + dc_texturemid = basetexturemid - (column->topdelta << FRACBITS); + + /* Drawn by either r_draw_column or (SHADOW) r_draw_fuzz_column. + */ + + colfunc(); + } + + column = (column_t *)((byte *)column + column->length + 4); + } + + dc_texturemid = basetexturemid; +} + +/* r_add_sprites + * During BSP traversal, this adds sprites by sector. + */ + +void r_add_sprites(sector_t *sec) +{ + mobj_t *thing; + int lightnum; + + /* BSP is traversed by subsector. + * A sector might have been split into several subsectors during BSP + * building. Thus we check whether its already added. + */ + + if (sec->validcount == validcount) return; + + /* Well, now it will be done. */ + + sec->validcount = validcount; + + lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (lightnum < 0) + spritelights = scalelight[0]; + else if (lightnum >= LIGHTLEVELS) + spritelights = scalelight[LIGHTLEVELS - 1]; + else + spritelights = scalelight[lightnum]; + + /* Handle all things in sector. */ + + for (thing = sec->thinglist; thing; thing = thing->snext) + r_project_sprite(thing); +} + +void r_draw_masked(void) +{ + vissprite_t *spr; + drawseg_t *ds; + + r_sort_vis_sprites(); + + if (vissprite_p > vissprites) + { + /* draw all vissprites back to front */ + + for (spr = vsprsortedhead.next; + spr != &vsprsortedhead; + spr = spr->next) + { + r_draw_sprite(spr); + } + } + + /* render any remaining masked mid textures */ + + for (ds = ds_p - 1; ds >= drawsegs; ds--) + { + if (ds->maskedtexturecol) + { + r_render_masked_seg_range(ds, ds->x1, ds->x2); + } + } + + /* draw the psprites on top of everything but does not draw on side views + */ + + if (!viewangleoffset) + { + r_draw_player_sprites(); + } +} diff --git a/games/NXDoom/src/doom/r_things.h b/games/NXDoom/src/doom/r_things.h new file mode 100644 index 000000000..cdd299483 --- /dev/null +++ b/games/NXDoom/src/doom/r_things.h @@ -0,0 +1,61 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/r_things.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Rendering of moving objects, sprites. + * + ****************************************************************************/ + +#ifndef __R_THINGS__ +#define __R_THINGS__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]; +extern vissprite_t *vissprite_p; +extern vissprite_t vsprsortedhead; + +/* Constant arrays used for psprite clipping and initializing clipping. */ + +extern short negonearray[SCREENWIDTH]; +extern short screenheightarray[SCREENWIDTH]; + +/* vars for r_draw_masked_column */ + +extern short *mfloorclip; +extern short *mceilingclip; +extern fixed_t spryscale; +extern fixed_t sprtopscreen; + +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void r_draw_masked_column(column_t *column); + +void r_add_sprites(sector_t *sec); +void r_init_sprites(const char **namelist); +void r_clear_sprites(void); +void r_draw_masked(void); + +#endif /* __R_THINGS__ */ diff --git a/games/NXDoom/src/doom/s_sound.c b/games/NXDoom/src/doom/s_sound.c new file mode 100644 index 000000000..dbca08a57 --- /dev/null +++ b/games/NXDoom/src/doom/s_sound.c @@ -0,0 +1,771 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/s_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: none + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_sound.h" +#include "i_system.h" + +#include "deh_str.h" + +#include "doomstat.h" +#include "doomtype.h" + +#include "s_sound.h" +#include "sounds.h" + +#include "m_argv.h" +#include "m_misc.h" +#include "m_random.h" + +#include "p_local.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* when to clip out sounds + * Does not fit the large outdoor areas. + */ + +#define S_CLIPPING_DIST (1200 * FRACUNIT) + +/* Distance tp origin when sounds should be maxed out. + * This should relate to movement clipping resolution + * (see BLOCKMAP handling). + * In the source code release: (160*FRACUNIT). Changed back to the + * Vanilla value of 200 (why was this changed?) + */ + +#define S_CLOSE_DIST (200 * FRACUNIT) + +/* The range over which sound attenuates */ + +#define S_ATTENUATOR ((S_CLIPPING_DIST - S_CLOSE_DIST) >> FRACBITS) + +/* Stereo separation */ + +#define S_STEREO_SWING (96 * FRACUNIT) + +#define NORM_PRIORITY 64 +#define NORM_SEP 128 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + /* sound information (if null, channel avail.) */ + + sfxinfo_t *sfxinfo; + + /* origin of sound */ + + mobj_t *origin; + + /* handle of the sound being played */ + + int handle; + + int pitch; +} channel_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The set of channels available */ + +static channel_t *channels; + +/* Internal volume level, ranging from 0-127 */ + +static int g_snd_sfx_volume; + +/* Whether songs are mus_paused */ + +static boolean mus_paused; + +/* Music currently being played */ + +static musicinfo_t *mus_playing = NULL; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Maximum volume of a sound effect. + * Internal default is max out of 0-15. + */ + +int g_sfx_volume = 8; + +/* Maximum volume of music. */ + +int g_music_volume = 8; + +/* Number of channels to use */ + +int snd_channels = 8; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void s_stop_channel(int cnum) +{ + int i; + channel_t *c; + + c = &channels[cnum]; + + if (c->sfxinfo) + { + /* stop the sound playing */ + + if (i_sound_playing(c->handle)) + { + i_stop_sound(c->handle); + } + + /* check to see if other channels are playing the sound */ + + for (i = 0; i < snd_channels; i++) + { + if (cnum != i && c->sfxinfo == channels[i].sfxinfo) + { + break; + } + } + + /* degrade usefulness of sound data */ + + c->sfxinfo->usefulness--; + c->sfxinfo = NULL; + c->origin = NULL; + } +} + +/* S_GetChannel: If none available, return -1. Otherwise channel #. */ + +static int s_get_channel(mobj_t *origin, sfxinfo_t *sfxinfo) +{ + /* channel number to use */ + + int cnum; + + channel_t *c; + + /* Find an open channel */ + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (!channels[cnum].sfxinfo) + { + break; + } + else if (origin && channels[cnum].origin == origin) + { + s_stop_channel(cnum); + break; + } + } + + /* None available */ + + if (cnum == snd_channels) + { + /* Look for lower priority */ + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) + { + break; + } + } + + if (cnum == snd_channels) + { + /* FUCK! No lower priority. Sorry, Charlie. */ + + return -1; + } + else + { + /* Otherwise, kick out lower priority. */ + + s_stop_channel(cnum); + } + } + + c = &channels[cnum]; + + /* channel is decided to be cnum. */ + + c->sfxinfo = sfxinfo; + c->origin = origin; + + return cnum; +} + +/* Changes volume and stereo-separation variables from the norm of a sound + * effect to be played. If the sound is not audible, returns a 0. Otherwise, + * modifies parameters and returns 1. + */ + +static int s_adjust_sound_params(mobj_t *listener, mobj_t *source, int *vol, + int *sep) +{ + fixed_t approx_dist; + fixed_t adx; + fixed_t ady; + angle_t angle; + + /* calculate the distance to sound origin and clip it if necessary */ + + adx = abs(listener->x - source->x); + ady = abs(listener->y - source->y); + + /* From _GG1_ p.428. Appox. Euclidean distance fast. */ + + approx_dist = adx + ady - ((adx < ady ? adx : ady) >> 1); + + if (gamemap != 8 && approx_dist > S_CLIPPING_DIST) + { + return 0; + } + + /* angle of source to listener */ + + angle = r_point_to_angle2(listener->x, listener->y, source->x, source->y); + + if (angle > listener->angle) + { + angle = angle - listener->angle; + } + else + { + angle = angle + (0xffffffff - listener->angle); + } + + angle >>= ANGLETOFINESHIFT; + + /* stereo separation */ + + *sep = 128 - (fixed_mul(S_STEREO_SWING, finesine[angle]) >> FRACBITS); + + /* volume calculation */ + + if (approx_dist < S_CLOSE_DIST) + { + *vol = g_snd_sfx_volume; + } + else if (gamemap == 8) + { + if (approx_dist > S_CLIPPING_DIST) + { + approx_dist = S_CLIPPING_DIST; + } + + *vol = 15 + ((g_snd_sfx_volume - 15) * + ((S_CLIPPING_DIST - approx_dist) >> FRACBITS)) / + S_ATTENUATOR; + } + else + { + /* distance effect */ + + *vol = (g_snd_sfx_volume * + ((S_CLIPPING_DIST - approx_dist) >> FRACBITS) + ) / S_ATTENUATOR; + } + + return (*vol > 0); +} + +/* clamp supplied integer to the range 0 <= x <= 255. */ + +static int clamp(int x) +{ + if (x < 0) + { + return 0; + } + else if (x > 255) + { + return 255; + } + + return x; +} + +static void s_shutdown(void) +{ + i_shutdown_sound(); + i_shutdown_music(); +} + +static void s_stop_music(void) +{ + if (mus_playing) + { + if (mus_paused) + { + i_resume_song(); + } + + i_stop_song(); + i_unregister_song(mus_playing->handle); + w_release_lump_num(mus_playing->lumpnum); + mus_playing->data = NULL; + mus_playing = NULL; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initializes sound stuff, including volume + * + * Sets channels, SFX and music volume, allocates channel buffer, sets s_sfx + * lookup. + */ + +void s_init(int sfxvolume, int musicvolume) +{ + int i; + +#if 0 + if (gameversion == exe_doom_1_666) + { + if (logical_gamemission == doom) + { + i_set_opl_driver_ver(opl_doom1_1_666); + } + else + { + i_set_opl_driver_ver(opl_doom2_1_666); + } + } + else + { + i_set_opl_driver_ver(opl_doom_1_9); + } +#endif + + i_precache_sounds(s_sfx, SFX_NUMSFX); + + s_set_sfx_volume(sfxvolume); + s_set_music_volume(musicvolume); + + /* Allocating the internal channels for mixing + * (the maximum number of sounds rendered + * simultaneously) within zone memory. + */ + + channels = z_malloc(snd_channels * sizeof(channel_t), PU_STATIC, 0); + + /* Free all channels for use */ + + for (i = 0; i < snd_channels; i++) + { + channels[i].sfxinfo = 0; + } + + /* no sounds are playing, and they are not mus_paused */ + + mus_paused = 0; + + /* Note that sounds have not been cached (yet). */ + + for (i = 1; i < SFX_NUMSFX; i++) + { + s_sfx[i].lumpnum = s_sfx[i].usefulness = -1; + } + + /* Doom defaults to pitch-shifting off. */ + + if (snd_pitchshift == -1) + { + snd_pitchshift = 0; + } + + i_at_exit(s_shutdown, true); +} + +/* Per level startup code. + * Kills playing sounds at start of level, determines music if any, changes + * music. + */ + +void s_start(void) +{ + int cnum; + int mnum; + + /* kill all playing sounds at start of level (trust me - a good idea) + */ + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo) + { + s_stop_channel(cnum); + } + } + + /* start new music for the level */ + + mus_paused = 0; + + if (gamemode == commercial) + { + mnum = MUS_RUNNIN + gamemap - 1; + } + else + { + int spmus[] = { + /* Song - Who? - Where? */ + + MUS_E3M4, /* American e4m1 */ + MUS_E3M2, /* Romero e4m2 */ + MUS_E3M3, /* Shawn e4m3 */ + MUS_E1M5, /* American e4m4 */ + MUS_E2M7, /* Tim e4m5 */ + MUS_E2M4, /* Romero e4m6 */ + MUS_E2M6, /* J.Anderson e4m7 CHIRON.WAD */ + MUS_E2M5, /* Shawn e4m8 */ + MUS_E1M9, /* Tim e4m9 */ + }; + + if (gameepisode < 4) + { + mnum = MUS_E1M1 + (gameepisode - 1) * 9 + gamemap - 1; + } + else + { + mnum = spmus[gamemap - 1]; + } + } + + s_change_music(mnum, true); +} + +void s_stop_sound(mobj_t *origin) +{ + int cnum; + + for (cnum = 0; cnum < snd_channels; cnum++) + { + if (channels[cnum].sfxinfo && channels[cnum].origin == origin) + { + s_stop_channel(cnum); + break; + } + } +} + +void s_start_sound(void *origin_p, int sfx_id) +{ + sfxinfo_t *sfx; + mobj_t *origin; + int rc; + int sep; + int pitch; + int cnum; + int volume; + + origin = (mobj_t *)origin_p; + volume = g_snd_sfx_volume; + + /* check for bogus sound # */ + + if (sfx_id < 1 || sfx_id > SFX_NUMSFX) + { + i_error("Bad sfx #: %d", sfx_id); + } + + sfx = &s_sfx[sfx_id]; + + /* Initialize sound parameters */ + + pitch = NORM_PITCH; + if (sfx->link) + { + volume += sfx->volume; + pitch = sfx->pitch; + + if (volume < 1) + { + return; + } + + if (volume > g_snd_sfx_volume) + { + volume = g_snd_sfx_volume; + } + } + + /* Check to see if it is audible, and if not, modify the params */ + + if (origin && origin != players[consoleplayer].mo) + { + rc = s_adjust_sound_params(players[consoleplayer].mo, origin, &volume, + &sep); + + if (origin->x == players[consoleplayer].mo->x && + origin->y == players[consoleplayer].mo->y) + { + sep = NORM_SEP; + } + + if (!rc) + { + return; + } + } + else + { + sep = NORM_SEP; + } + + /* hacks to vary the sfx pitches */ + + if (sfx_id >= SFX_SAWUP && sfx_id <= SFX_SAWHIT) + { + pitch += 8 - (m_random() & 15); + } + else if (sfx_id != SFX_ITEMUP && sfx_id != SFX_TINK) + { + pitch += 16 - (m_random() & 31); + } + + pitch = clamp(pitch); + + s_stop_sound(origin); /* kill old sound */ + + /* try to find a channel */ + + cnum = s_get_channel(origin, sfx); + + if (cnum < 0) + { + return; + } + + /* increase the usefulness */ + + if (sfx->usefulness++ < 0) + { + sfx->usefulness = 1; + } + + if (sfx->lumpnum < 0) + { + sfx->lumpnum = i_get_sfx_lumpnum(sfx); + } + + channels[cnum].pitch = pitch; + channels[cnum].handle = + i_start_sound(sfx, cnum, volume, sep, channels[cnum].pitch); +} + +/* Stop and resume music, during game PAUSE. */ + +void s_pause_sound(void) +{ + if (mus_playing && !mus_paused) + { + i_pause_song(); + mus_paused = true; + } +} + +void s_resume_sound(void) +{ + if (mus_playing && mus_paused) + { + i_resume_song(); + mus_paused = false; + } +} + +/* Updates music & sounds */ + +void s_update_sounds(mobj_t *listener) +{ + int audible; + int cnum; + int volume; + int sep; + sfxinfo_t *sfx; + channel_t *c; + + i_update_sound(); + + for (cnum = 0; cnum < snd_channels; cnum++) + { + c = &channels[cnum]; + sfx = c->sfxinfo; + + if (c->sfxinfo) + { + if (i_sound_playing(c->handle)) + { + /* initialize parameters */ + + volume = g_snd_sfx_volume; + sep = NORM_SEP; + + if (sfx->link) + { + volume += sfx->volume; + if (volume < 1) + { + s_stop_channel(cnum); + continue; + } + else if (volume > g_snd_sfx_volume) + { + volume = g_snd_sfx_volume; + } + } + + /* check non-local sounds for distance clipping or modify their + * params + */ + + if (c->origin && listener != c->origin) + { + audible = s_adjust_sound_params(listener, c->origin, + &volume, &sep); + + if (!audible) + { + s_stop_channel(cnum); + } + else + { + i_update_sound_params(c->handle, volume, sep); + } + } + } + else + { + /* if channel is allocated but sound has stopped, free it + */ + + s_stop_channel(cnum); + } + } + } +} + +void s_set_music_volume(int volume) +{ + if (volume < 0 || volume > 127) + { + i_error("Attempt to set music volume at %d", volume); + } + + i_set_music_volume(volume); +} + +void s_set_sfx_volume(int volume) +{ + if (volume < 0 || volume > 127) + { + i_error("Attempt to set sfx volume at %d", volume); + } + + g_snd_sfx_volume = volume; +} + +/* Starts some music with the music id found in sounds.h. */ + +void s_start_music(int m_id) +{ + s_change_music(m_id, false); +} + +void s_change_music(int musicnum, int looping) +{ + musicinfo_t *music = NULL; + char namebuf[9]; + void *handle; + + /* The Doom IWAD file has two versions of the intro music: d_intro + * and d_introa. The latter is used for OPL playback. + */ + + if (musicnum == MUS_INTRO && + (snd_musicdevice == SNDDEVICE_ADLIB || + snd_musicdevice == SNDDEVICE_SB) && + w_check_num_for_name("D_INTROA") >= 0) + { + musicnum = MUS_INTROA; + } + + if (musicnum <= MUS_NONE || musicnum >= MUS_NUMMUSIC) + { + i_error("Bad music number %d", musicnum); + } + else + { + music = &s_music[musicnum]; + } + + if (mus_playing == music) + { + return; + } + + /* shutdown old music */ + + s_stop_music(); + + /* get lumpnum if necessary */ + + if (!music->lumpnum) + { + snprintf(namebuf, sizeof(namebuf), "d_%s", (music->name)); + music->lumpnum = w_get_num_for_name(namebuf); + } + + music->data = w_cache_lump_num(music->lumpnum, PU_STATIC); + + handle = i_register_song(music->data, w_lump_length(music->lumpnum)); + music->handle = handle; + i_play_song(handle, looping); + + mus_playing = music; +} diff --git a/games/NXDoom/src/doom/s_sound.h b/games/NXDoom/src/doom/s_sound.h new file mode 100644 index 000000000..21f569d9b --- /dev/null +++ b/games/NXDoom/src/doom/s_sound.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/s_sound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + ****************************************************************************/ + +#ifndef __S_SOUND__ +#define __S_SOUND__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "p_mobj.h" +#include "sounds.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int snd_channels; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initializes sound stuff, including volume + * Sets channels, SFX and music volume, allocates channel buffer, sets s_sfx + * lookup. + */ + +void s_init(int sfxvolume, int musicvolume); + +/* Per level startup code. + * Kills playing sounds at start of level, determines music if any, changes + * music. + */ + +void s_start(void); + +/* Start sound for thing at using from sounds.h */ + +void s_start_sound(void *origin, int sound_id); + +/* Stop sound for thing at */ + +void s_stop_sound(mobj_t *origin); + +/* Start music using from sounds.h */ + +void s_start_music(int music_id); + +/* Start music using from sounds.h, and set whether looping */ + +void s_change_music(int music_id, int looping); + +/* Stop and resume music, during game PAUSE. */ + +void s_pause_sound(void); +void s_resume_sound(void); + +/* Updates music & sounds */ + +void s_update_sounds(mobj_t *listener); + +void s_set_music_volume(int volume); +void s_set_sfx_volume(int volume); + +#endif /* __S_SOUND__ */ diff --git a/games/NXDoom/src/doom/sounds.c b/games/NXDoom/src/doom/sounds.c new file mode 100644 index 000000000..b1282cdb2 --- /dev/null +++ b/games/NXDoom/src/doom/sounds.c @@ -0,0 +1,189 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/sounds.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Created by a sound utility. + * Kept as a sample, DOOM2 sounds. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "sounds.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Information about all the music */ + +#define MUSIC(name) {name, 0, NULL, NULL} + +/* Information about all the sfx */ + +#define SOUND(name, priority) \ + {NULL, name, priority, NULL, -1, -1, 0, 0, -1, NULL} +#define SOUND_LINK(name, priority, link_id, pitch, volume) \ + {NULL, name, priority, &s_sfx[link_id], pitch, volume, 0, 0, -1, NULL} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +musicinfo_t s_music[] = +{ + MUSIC(NULL), MUSIC("e1m1"), MUSIC("e1m2"), MUSIC("e1m3"), + MUSIC("e1m4"), MUSIC("e1m5"), MUSIC("e1m6"), MUSIC("e1m7"), + MUSIC("e1m8"), MUSIC("e1m9"), MUSIC("e2m1"), MUSIC("e2m2"), + MUSIC("e2m3"), MUSIC("e2m4"), MUSIC("e2m5"), MUSIC("e2m6"), + MUSIC("e2m7"), MUSIC("e2m8"), MUSIC("e2m9"), MUSIC("e3m1"), + MUSIC("e3m2"), MUSIC("e3m3"), MUSIC("e3m4"), MUSIC("e3m5"), + MUSIC("e3m6"), MUSIC("e3m7"), MUSIC("e3m8"), MUSIC("e3m9"), + MUSIC("inter"), MUSIC("intro"), MUSIC("bunny"), MUSIC("victor"), + MUSIC("introa"), MUSIC("runnin"), MUSIC("stalks"), MUSIC("countd"), + MUSIC("betwee"), MUSIC("doom"), MUSIC("the_da"), MUSIC("shawn"), + MUSIC("ddtblu"), MUSIC("in_cit"), MUSIC("dead"), MUSIC("stlks2"), + MUSIC("theda2"), MUSIC("doom2"), MUSIC("ddtbl2"), MUSIC("runni2"), + MUSIC("dead2"), MUSIC("stlks3"), MUSIC("romero"), MUSIC("shawn2"), + MUSIC("messag"), MUSIC("count2"), MUSIC("ddtbl3"), MUSIC("ampie"), + MUSIC("theda3"), MUSIC("adrian"), MUSIC("messg2"), MUSIC("romer2"), + MUSIC("tense"), MUSIC("shawn3"), MUSIC("openin"), MUSIC("evil"), + MUSIC("ultima"), MUSIC("read_m"), MUSIC("dm2ttl"), MUSIC("dm2int"), +}; + +sfxinfo_t s_sfx[] = +{ + SOUND("none", 0), /* s_sfx[0] needs to be a dummy for odd reasons. */ + SOUND("pistol", 64), + SOUND("shotgn", 64), + SOUND("sgcock", 64), + SOUND("dshtgn", 64), + SOUND("dbopn", 64), + SOUND("dbcls", 64), + SOUND("dbload", 64), + SOUND("plasma", 64), + SOUND("bfg", 64), + SOUND("sawup", 64), + SOUND("sawidl", 118), + SOUND("sawful", 64), + SOUND("sawhit", 64), + SOUND("rlaunc", 64), + SOUND("rxplod", 70), + SOUND("firsht", 70), + SOUND("firxpl", 70), + SOUND("pstart", 100), + SOUND("pstop", 100), + SOUND("doropn", 100), + SOUND("dorcls", 100), + SOUND("stnmov", 119), + SOUND("swtchn", 78), + SOUND("swtchx", 78), + SOUND("plpain", 96), + SOUND("dmpain", 96), + SOUND("popain", 96), + SOUND("vipain", 96), + SOUND("mnpain", 96), + SOUND("pepain", 96), + SOUND("slop", 78), + SOUND("itemup", 78), + SOUND("wpnup", 78), + SOUND("oof", 96), + SOUND("telept", 32), + SOUND("posit1", 98), + SOUND("posit2", 98), + SOUND("posit3", 98), + SOUND("bgsit1", 98), + SOUND("bgsit2", 98), + SOUND("sgtsit", 98), + SOUND("cacsit", 98), + SOUND("brssit", 94), + SOUND("cybsit", 92), + SOUND("spisit", 90), + SOUND("bspsit", 90), + SOUND("kntsit", 90), + SOUND("vilsit", 90), + SOUND("mansit", 90), + SOUND("pesit", 90), + SOUND("sklatk", 70), + SOUND("sgtatk", 70), + SOUND("skepch", 70), + SOUND("vilatk", 70), + SOUND("claw", 70), + SOUND("skeswg", 70), + SOUND("pldeth", 32), + SOUND("pdiehi", 32), + SOUND("podth1", 70), + SOUND("podth2", 70), + SOUND("podth3", 70), + SOUND("bgdth1", 70), + SOUND("bgdth2", 70), + SOUND("sgtdth", 70), + SOUND("cacdth", 70), + SOUND("skldth", 70), + SOUND("brsdth", 32), + SOUND("cybdth", 32), + SOUND("spidth", 32), + SOUND("bspdth", 32), + SOUND("vildth", 32), + SOUND("kntdth", 32), + SOUND("pedth", 32), + SOUND("skedth", 32), + SOUND("posact", 120), + SOUND("bgact", 120), + SOUND("dmact", 120), + SOUND("bspact", 100), + SOUND("bspwlk", 100), + SOUND("vilact", 100), + SOUND("noway", 78), + SOUND("barexp", 60), + SOUND("punch", 64), + SOUND("hoof", 70), + SOUND("metal", 70), + SOUND_LINK("chgun", 64, SFX_PISTOL, 150, 0), + SOUND("tink", 60), + SOUND("bdopn", 100), + SOUND("bdcls", 100), + SOUND("itmbk", 100), + SOUND("flame", 32), + SOUND("flamst", 32), + SOUND("getpow", 60), + SOUND("bospit", 70), + SOUND("boscub", 70), + SOUND("bossit", 70), + SOUND("bospn", 70), + SOUND("bosdth", 70), + SOUND("manatk", 70), + SOUND("mandth", 70), + SOUND("sssit", 70), + SOUND("ssdth", 70), + SOUND("keenpn", 70), + SOUND("keendt", 70), + SOUND("skeact", 70), + SOUND("skesit", 70), + SOUND("skeatk", 70), + SOUND("radio", 60), +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/doom/sounds.h b/games/NXDoom/src/doom/sounds.h new file mode 100644 index 000000000..a2d9b371f --- /dev/null +++ b/games/NXDoom/src/doom/sounds.h @@ -0,0 +1,241 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/sounds.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Created by the sound utility written by Dave Taylor. + * Kept as a sample, DOOM2 sounds. Frozen. + * + ****************************************************************************/ + +#ifndef __SOUNDS__ +#define __SOUNDS__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "i_sound.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Identifiers for all music in game. */ + +typedef enum +{ + MUS_NONE, + MUS_E1M1, + MUS_E1M2, + MUS_E1M3, + MUS_E1M4, + MUS_E1M5, + MUS_E1M6, + MUS_E1M7, + MUS_E1M8, + MUS_E1M9, + MUS_E2M1, + MUS_E2M2, + MUS_E2M3, + MUS_E2M4, + MUS_E2M5, + MUS_E2M6, + MUS_E2M7, + MUS_E2M8, + MUS_E2M9, + MUS_E3M1, + MUS_E3M2, + MUS_E3M3, + MUS_E3M4, + MUS_E3M5, + MUS_E3M6, + MUS_E3M7, + MUS_E3M8, + MUS_E3M9, + MUS_INTER, + MUS_INTRO, + MUS_BUNNY, + MUS_VICTOR, + MUS_INTROA, + MUS_RUNNIN, + MUS_STALKS, + MUS_COUNTD, + MUS_BETWEE, + MUS_DOOM, + MUS_THE_DA, + MUS_SHAWN, + MUS_DDTBLU, + MUS_IN_CIT, + MUS_DEAD, + MUS_STLKS2, + MUS_THEDA2, + MUS_DOOM2, + MUS_DDTBL2, + MUS_RUNNI2, + MUS_DEAD2, + MUS_STLKS3, + MUS_ROMERO, + MUS_SHAWN2, + MUS_MESSAG, + MUS_COUNT2, + MUS_DDTBL3, + MUS_AMPIE, + MUS_THEDA3, + MUS_ADRIAN, + MUS_MESSG2, + MUS_ROMER2, + MUS_TENSE, + MUS_SHAWN3, + MUS_OPENIN, + MUS_EVIL, + MUS_ULTIMA, + MUS_READ_M, + MUS_DM2TTL, + MUS_DM2INT, + MUS_NUMMUSIC +} musicenum_t; + +/* Identifiers for all sfx in game. */ + +typedef enum +{ + SFX_NONE, + SFX_PISTOL, + SFX_SHOTGN, + SFX_SGCOCK, + SFX_DSHTGN, + SFX_DBOPN, + SFX_DBCLS, + SFX_DBLOAD, + SFX_PLASMA, + SFX_BFG, + SFX_SAWUP, + SFX_SAWIDL, + SFX_SAWFUL, + SFX_SAWHIT, + SFX_RLAUNC, + SFX_RXPLOD, + SFX_FIRSHT, + SFX_FIRXPL, + SFX_PSTART, + SFX_PSTOP, + SFX_DOROPN, + SFX_DORCLS, + SFX_STNMOV, + SFX_SWTCHN, + SFX_SWTCHX, + SFX_PLPAIN, + SFX_DMPAIN, + SFX_POPAIN, + SFX_VIPAIN, + SFX_MNPAIN, + SFX_PEPAIN, + SFX_SLOP, + SFX_ITEMUP, + SFX_WPNUP, + SFX_OOF, + SFX_TELEPT, + SFX_POSIT1, + SFX_POSIT2, + SFX_POSIT3, + SFX_BGSIT1, + SFX_BGSIT2, + SFX_SGTSIT, + SFX_CACSIT, + SFX_BRSSIT, + SFX_CYBSIT, + SFX_SPISIT, + SFX_BSPSIT, + SFX_KNTSIT, + SFX_VILSIT, + SFX_MANSIT, + SFX_PESIT, + SFX_SKLATK, + SFX_SGTATK, + SFX_SKEPCH, + SFX_VILATK, + SFX_CLAW, + SFX_SKESWG, + SFX_PLDETH, + SFX_PDIEHI, + SFX_PODTH1, + SFX_PODTH2, + SFX_PODTH3, + SFX_BGDTH1, + SFX_BGDTH2, + SFX_SGTDTH, + SFX_CACDTH, + SFX_SKLDTH, + SFX_BRSDTH, + SFX_CYBDTH, + SFX_SPIDTH, + SFX_BSPDTH, + SFX_VILDTH, + SFX_KNTDTH, + SFX_PEDTH, + SFX_SKEDTH, + SFX_POSACT, + SFX_BGACT, + SFX_DMACT, + SFX_BSPACT, + SFX_BSPWLK, + SFX_VILACT, + SFX_NOWAY, + SFX_BAREXP, + SFX_PUNCH, + SFX_HOOF, + SFX_METAL, + SFX_CHGUN, + SFX_TINK, + SFX_BDOPN, + SFX_BDCLS, + SFX_ITMBK, + SFX_FLAME, + SFX_FLAMST, + SFX_GETPOW, + SFX_BOSPIT, + SFX_BOSCUB, + SFX_BOSSIT, + SFX_BOSPN, + SFX_BOSDTH, + SFX_MANATK, + SFX_MANDTH, + SFX_SSSIT, + SFX_SSDTH, + SFX_KEENPN, + SFX_KEENDT, + SFX_SKEACT, + SFX_SKESIT, + SFX_SKEATK, + SFX_RADIO, + SFX_NUMSFX +} sfxenum_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* the complete set of sound effects */ + +extern sfxinfo_t s_sfx[]; + +/* the complete set of music */ + +extern musicinfo_t s_music[]; + +#endif /* __SOUNDS__ */ diff --git a/games/NXDoom/src/doom/st_lib.c b/games/NXDoom/src/doom/st_lib.c new file mode 100644 index 000000000..d8aeda710 --- /dev/null +++ b/games/NXDoom/src/doom/st_lib.c @@ -0,0 +1,238 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_lib.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * The status bar widget code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "deh_main.h" +#include "doomdef.h" + +#include "v_video.h" +#include "z_zone.h" + +#include "i_swap.h" +#include "i_system.h" + +#include "w_wad.h" + +#include "r_local.h" +#include "st_lib.h" +#include "st_stuff.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Hack display negative frags. Loads and store the stminus lump. */ + +patch_t *sttminus; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stlib_draw_num + * + * Description: + * A fairly efficient way to draw a number based on differences from the old + * number. Note: worth the trouble? + * + ****************************************************************************/ + +static void stlib_draw_num(st_number_t *n, boolean refresh) +{ + int numdigits = n->width; + int num = *n->num; + + int w = SHORT(n->p[0]->width); + int h = SHORT(n->p[0]->height); + int x = n->x; + + int neg; + + n->oldnum = *n->num; + + neg = num < 0; + + if (neg) + { + if (numdigits == 2 && num < -9) + num = -9; + else if (numdigits == 3 && num < -99) + num = -99; + + num = -num; + } + + /* clear the area */ + + x = n->x - numdigits * w; + + if (n->y - ST_Y < 0) i_error("drawNum: n->y - ST_Y < 0"); + + v_copy_rect(x, n->y - ST_Y, st_backing_screen, w * numdigits, h, x, n->y); + + if (num == 1994) return; /* if non-number, do not draw it */ + + x = n->x; + + /* in the special case of 0, you draw 0 */ + + if (!num) v_draw_patch(x - w, n->y, n->p[0]); + + /* draw the new number */ + + while (num && numdigits--) + { + x -= w; + v_draw_patch(x, n->y, n->p[num % 10]); + num /= 10; + } + + /* draw a minus sign if necessary */ + + if (neg && sttminus) v_draw_patch(x - 8, n->y, sttminus); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void stlib_init(void) +{ + if (w_check_num_for_name(("STTMINUS")) >= 0) + sttminus = (patch_t *)w_cache_lump_name(("STTMINUS"), PU_STATIC); + else + sttminus = NULL; +} + +/* ? */ + +void stlib_init_num(st_number_t *n, int x, int y, patch_t **pl, int *num, + boolean *on, int width) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +void stlib_update_num(st_number_t *n, boolean refresh) +{ + if (*n->on) stlib_draw_num(n, refresh); +} + +void stlib_init_percent(st_percent_t *p, int x, int y, patch_t **pl, + int *num, boolean *on, patch_t *percent) +{ + stlib_init_num(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +void stlib_update_percent(st_percent_t *per, int refresh) +{ + if (refresh && *per->n.on) v_draw_patch(per->n.x, per->n.y, per->p); + + stlib_update_num(&per->n, refresh); +} + +void stlib_init_mutl_icon(st_multicon_t *i, int x, int y, patch_t **il, + int *inum, boolean *on) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +void stlib_update_mult_icon(st_multicon_t *mi, boolean refresh) +{ + int w; + int h; + int x; + int y; + + if (*mi->on && (mi->oldinum != *mi->inum || refresh) && (*mi->inum != -1)) + { + if (mi->oldinum != -1) + { + x = mi->x - SHORT(mi->p[mi->oldinum]->leftoffset); + y = mi->y - SHORT(mi->p[mi->oldinum]->topoffset); + w = SHORT(mi->p[mi->oldinum]->width); + h = SHORT(mi->p[mi->oldinum]->height); + + if (y - ST_Y < 0) i_error("updateMultIcon: y - ST_Y < 0"); + + v_copy_rect(x, y - ST_Y, st_backing_screen, w, h, x, y); + } + + v_draw_patch(mi->x, mi->y, mi->p[*mi->inum]); + mi->oldinum = *mi->inum; + } +} + +void stlib_init_bin_icon(st_binicon_t *b, int x, int y, patch_t *i, + boolean *val, boolean *on) +{ + b->x = x; + b->y = y; + b->oldval = false; + b->val = val; + b->on = on; + b->p = i; +} + +void stlib_update_bin_icon(st_binicon_t *bi, boolean refresh) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - SHORT(bi->p->leftoffset); + y = bi->y - SHORT(bi->p->topoffset); + w = SHORT(bi->p->width); + h = SHORT(bi->p->height); + + if (y - ST_Y < 0) i_error("updateBinIcon: y - ST_Y < 0"); + + if (*bi->val) + v_draw_patch(bi->x, bi->y, bi->p); + else + v_copy_rect(x, y - ST_Y, st_backing_screen, w, h, x, y); + + bi->oldval = *bi->val; + } +} diff --git a/games/NXDoom/src/doom/st_lib.h b/games/NXDoom/src/doom/st_lib.h new file mode 100644 index 000000000..b1fa3867b --- /dev/null +++ b/games/NXDoom/src/doom/st_lib.h @@ -0,0 +1,187 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_lib.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * The status bar widget code. + * + ****************************************************************************/ + +#ifndef __STLIB__ +#define __STLIB__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* We are referring to patches. */ + +#include "r_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Typedefs of widgets */ + +/* Number widget */ + +typedef struct +{ + /* upper right-hand corner of the number (right-justified) + */ + + int x; + int y; + + /* max # of digits in number */ + + int width; + + /* last number value */ + + int oldnum; + + /* pointer to current value */ + + int *num; + + /* pointer to boolean stating whether to update number */ + + boolean *on; + + /* list of patches for 0-9 */ + + patch_t **p; + + /* user data */ + + int data; +} st_number_t; + +/* Percent widget ("child" of number widget, or, more precisely, contains a + * number widget.) + */ + +typedef struct +{ + /* number information */ + + st_number_t n; + + /* percent sign graphic */ + + patch_t *p; +} st_percent_t; + +/* Multiple Icon widget */ + +typedef struct +{ + /* center-justified location of icons */ + + int x; + int y; + + /* last icon number */ + + int oldinum; + + /* pointer to current icon */ + + int *inum; + + /* pointer to boolean stating whether to update icon */ + + boolean *on; + + /* list of icons */ + + patch_t **p; + + /* user data */ + + int data; +} st_multicon_t; + +/* Binary Icon widget */ + +typedef struct +{ + /* center-justified location of icon */ + + int x; + int y; + + /* last icon value */ + + boolean oldval; + + /* pointer to current icon status */ + + boolean *val; + + /* pointer to boolean stating whether to update icon */ + + boolean *on; + + patch_t *p; /* icon */ + int data; /* user data */ +} st_binicon_t; + +/* Widget creation, access, and update routines */ + +/* Initializes widget library. + * More precisely, initialize STMINUS, + * everything else is done somewhere else. + */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void stlib_init(void); + +/* Number widget routines */ + +void stlib_init_num(st_number_t *n, int x, int y, patch_t **pl, int *num, + boolean *on, int width); + +void stlib_update_num(st_number_t *n, boolean refresh); + +/* Percent widget routines */ + +void stlib_init_percent(st_percent_t *p, int x, int y, patch_t **pl, + int *num, boolean *on, patch_t *percent); + +void stlib_update_percent(st_percent_t *per, int refresh); + +/* Multiple Icon widget routines */ + +void stlib_init_mutl_icon(st_multicon_t *mi, int x, int y, patch_t **il, + int *inum, boolean *on); + +void stlib_update_mult_icon(st_multicon_t *mi, boolean refresh); + +/* Binary Icon widget routines */ + +void stlib_init_bin_icon(st_binicon_t *b, int x, int y, patch_t *i, + boolean *val, boolean *on); + +void stlib_update_bin_icon(st_binicon_t *bi, boolean refresh); + +#endif /* __STLIB__ */ diff --git a/games/NXDoom/src/doom/st_stuff.c b/games/NXDoom/src/doom/st_stuff.c new file mode 100644 index 000000000..c6e55ee67 --- /dev/null +++ b/games/NXDoom/src/doom/st_stuff.c @@ -0,0 +1,1418 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_stuff.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animating. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "i_system.h" +#include "i_video.h" +#include "m_misc.h" +#include "m_random.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "deh_main.h" +#include "deh_misc.h" +#include "doomdef.h" +#include "doomkeys.h" + +#include "g_game.h" + +#include "r_local.h" +#include "st_lib.h" +#include "st_stuff.h" + +#include "p_inter.h" +#include "p_local.h" + +#include "am_map.h" +#include "m_cheat.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +/* Needs access to LFB. */ + +#include "v_video.h" + +/* State. */ + +#include "doomstat.h" + +/* Data. */ + +#include "dstrings.h" + +/* STATUS BAR DATA */ + +/* Palette indices. + * For damage/bonus red-/gold-shifts + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 + +/* Radiation suit, green shift. */ + +#define RADIATIONPAL 13 + +/* Location of status bar */ + +#define ST_X 0 +#define ST_X2 104 + +#define ST_FX 143 +#define ST_FY 169 + +/* Number of status faces. */ + +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE \ + (ST_NUMSTRAIGHTFACES + ST_NUMTURNFACES + ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES (ST_FACESTRIDE * ST_NUMPAINFACES + ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES * ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE + 1) + +#define ST_FACESX 143 +#define ST_FACESY 168 + +#define ST_EVILGRINCOUNT (2 * TICRATE) +#define ST_STRAIGHTFACECOUNT (TICRATE / 2) +#define ST_TURNCOUNT (1 * TICRATE) +#define ST_OUCHCOUNT (1 * TICRATE) +#define ST_RAMPAGEDELAY (2 * TICRATE) + +#define ST_MUCHPAIN 20 + +/* Location and size of statistics, + * justified according to widget type. + * Problem is, within which space? STbar? Screen? + * Note: this could be read in by a lump. + * Problem is, is the stuff rendered + * into a buffer, + * or into the frame buffer? + */ + +/* AMMO number pos. */ + +#define ST_AMMOWIDTH 3 +#define ST_AMMOX 44 +#define ST_AMMOY 171 + +/* HEALTH number pos. */ + +#define ST_HEALTHWIDTH 3 +#define ST_HEALTHX 90 +#define ST_HEALTHY 171 + +/* Weapon pos. */ + +#define ST_ARMSX 111 +#define ST_ARMSY 172 +#define ST_ARMSBGX 104 +#define ST_ARMSBGY 168 +#define ST_ARMSXSPACE 12 +#define ST_ARMSYSPACE 10 + +/* Frags pos. */ + +#define ST_FRAGSX 138 +#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +/* ARMOR number pos. */ + +#define ST_ARMORWIDTH 3 +#define ST_ARMORX 221 +#define ST_ARMORY 171 + +/* Key icon positions. */ + +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +#define ST_KEY0X 239 +#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +#define ST_KEY1X 239 +#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +#define ST_KEY2X 239 +#define ST_KEY2Y 191 + +/* Ammunition counter. */ + +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +#define ST_AMMO0X 288 +#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +#define ST_AMMO1X 288 +#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +#define ST_AMMO2X 288 +#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +#define ST_AMMO3X 288 +#define ST_AMMO3Y 185 + +/* Indicate maximum ammunition. + * Only needed because backpack exists. + */ + +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +#define ST_MAXAMMO0X 314 +#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO1X 314 +#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO2X 314 +#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO3X 314 +#define ST_MAXAMMO3Y 185 + +/* Dimensions given in characters. */ + +#define ST_MSGWIDTH 52 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* main player in game */ + +static player_t *plyr; + +/* st_start() has just been called */ + +static boolean st_firsttime; + +/* lump number for PLAYPAL */ + +static int lu_palette; + +/* used for making messages go away */ + +static int st_msgcounter = 0; + +/* whether left-side main status bar is active */ + +static boolean st_statusbaron; + +/* whether status bar chat is active */ + +static boolean st_chat; + +/* value of st_chat before message popped up */ + +static boolean st_oldchat; + +/* !deathmatch */ + +static boolean st_notdeathmatch; + +/* !deathmatch && st_statusbaron */ + +static boolean st_armson; + +/* !deathmatch */ + +static boolean st_fragson; + +/* main bar left */ + +static patch_t *sbar; + +/* main bar right, for doom 1.0 */ + +static patch_t *sbarr; + +/* 0-9, tall numbers */ + +static patch_t *tallnum[10]; + +/* tall % sign */ + +static patch_t *tallpercent; + +/* 0-9, short, yellow (,different!) numbers */ + +static patch_t *shortnum[10]; + +/* 3 key-cards, 3 skulls */ + +static patch_t *keys[NUMCARDS]; + +/* face status patches */ + +static patch_t *faces[ST_NUMFACES]; + +/* face background */ + +static patch_t *faceback; + +/* main bar right */ + +static patch_t *armsbg; + +/* weapon ownership patches */ + +static patch_t *arms[6][2]; + +/* ready-weapon widget */ + +static st_number_t w_ready; + +/* in deathmatch only, summary of frags stats */ + +static st_number_t w_frags; + +/* health widget */ + +static st_percent_t w_health; + +/* arms background */ + +static st_binicon_t w_armsbg; + +/* weapon ownership widgets */ + +static st_multicon_t w_arms[6]; + +/* face status widget */ + +static st_multicon_t w_faces; + +/* keycard widgets */ + +static st_multicon_t w_keyboxes[3]; + +/* armor widget */ + +static st_percent_t w_armor; + +/* ammo widgets */ + +static st_number_t w_ammo[4]; + +/* max ammo widgets */ + +static st_number_t w_maxammo[4]; + +/* number of frags so far in deathmatch */ + +static int st_fragscount; + +/* used to use appropriately pained face */ + +static int st_oldhealth = -1; + +/* used for evil grin */ + +static boolean oldweaponsowned[NUMWEAPONS]; + +/* count until face changes */ + +static int st_facecount = 0; + +/* current face index, used by w_faces */ + +static int st_faceindex = 0; + +/* holds key-type for each key box on bar */ + +static int keyboxes[3]; + +/* a random number per tick */ + +static int st_randomnumber; + +static int st_palette = 0; + +static boolean st_stopped = true; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* graphics are drawn to a backing screen and blitted to the real screen */ + +pixel_t *st_backing_screen; + +cheatseq_t cheat_mus = CHEAT("idmus", 2); +cheatseq_t cheat_god = CHEAT("iddqd", 0); +cheatseq_t cheat_ammo = CHEAT("idkfa", 0); +cheatseq_t cheat_ammonokey = CHEAT("idfa", 0); +cheatseq_t cheat_noclip = CHEAT("idspispopd", 0); +cheatseq_t cheat_commercial_noclip = CHEAT("idclip", 0); + +cheatseq_t cheat_powerup[7] = +{ + CHEAT("idbeholdv", 0), CHEAT("idbeholds", 0), CHEAT("idbeholdi", 0), + CHEAT("idbeholdr", 0), CHEAT("idbeholda", 0), CHEAT("idbeholdl", 0), + CHEAT("idbehold", 0), +}; + +cheatseq_t cheat_choppers = CHEAT("idchoppers", 0); +cheatseq_t cheat_clev = CHEAT("idclev", 2); +cheatseq_t cheat_mypos = CHEAT("idmypos", 0); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void st_stop(void) +{ + if (st_stopped) return; + + i_set_palette(w_cache_lump_num(lu_palette, PU_CACHE)); + st_stopped = true; +} + +static void st_refresh_background(void) +{ + if (st_statusbaron) + { + v_use_buffer(st_backing_screen); + + v_draw_patch(ST_X, 0, sbar); + + /* draw right side of bar if needed (Doom 1.0) */ + + if (sbarr) v_draw_patch(ST_ARMSBGX, 0, sbarr); + + if (netgame) v_draw_patch(ST_FX, 0, faceback); + + v_restore_buffer(); + + v_copy_rect(ST_X, 0, st_backing_screen, ST_WIDTH, ST_HEIGHT, ST_X, + ST_Y); + } +} + +static int st_calc_paint_offset(void) +{ + int health; + static int lastcalc; + static int oldhealth = -1; + + health = plyr->health > 100 ? 100 : plyr->health; + + if (health != oldhealth) + { + lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); + oldhealth = health; + } + + return lastcalc; +} + +/* This is a not-very-pretty routine which handles the face states and their + * timing. the precedence of expressions is: dead > evil grin > turned head > + * straight ahead + */ + +static void st_update_face_widget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + boolean doevilgrin; + + if (priority < 10) + { + /* dead */ + + if (!plyr->health) + { + priority = 9; + st_faceindex = ST_DEADFACE; + st_facecount = 1; + } + } + + if (priority < 9) + { + if (plyr->bonuscount) + { + /* picking up bonus */ + + doevilgrin = false; + + for (i = 0; i < NUMWEAPONS; i++) + { + if (oldweaponsowned[i] != plyr->weaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + + if (doevilgrin) + { + /* evil grin if just picked up weapon */ + + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = st_calc_paint_offset() + ST_EVILGRINOFFSET; + } + } + } + + if (priority < 8) + { + if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) + { + /* being attacked */ + + priority = 7; + + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + st_facecount = ST_TURNCOUNT; + st_faceindex = st_calc_paint_offset() + ST_OUCHOFFSET; + } + else + { + badguyangle = + r_point_to_angle2(plyr->mo->x, plyr->mo->y, + plyr->attacker->x, plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + /* whether right or left */ + + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + /* whether left or right */ + + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } + + /* confusing, aint it? */ + + st_facecount = ST_TURNCOUNT; + st_faceindex = st_calc_paint_offset(); + + if (diffang < ANG45) + { + /* head-on */ + + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + /* turn face right */ + + st_faceindex += ST_TURNOFFSET; + } + else + { + /* turn face left */ + + st_faceindex += ST_TURNOFFSET + 1; + } + } + } + } + + if (priority < 7) + { + /* getting hurt because of your own damn stupidity */ + + if (plyr->damagecount) + { + if (plyr->health - st_oldhealth > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = st_calc_paint_offset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = st_calc_paint_offset() + ST_RAMPAGEOFFSET; + } + } + } + + if (priority < 6) + { + /* rapid firing */ + + if (plyr->attackdown) + { + if (lastattackdown == -1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = st_calc_paint_offset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + } + + if (priority < 5) + { + /* invulnerability */ + + if ((plyr->cheats & CF_GODMODE) || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + } + } + + /* look left or look right if the facecount has timed out */ + + if (!st_facecount) + { + st_faceindex = st_calc_paint_offset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; +} + +static void st_update_widgets(void) +{ + static int largeammo = 1994; /* means "n/a" */ + int i; + + /* must redirect the pointer if the ready weapon has changed. + * if (w_ready.data != plyr->readyweapon) + * { + */ + + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + + /* { + * static int tic=0; + * static int dir=-1; + * if (!(tic&15)) + * plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir; + * if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100) + * dir = 1; + * tic++; + * } + */ + + w_ready.data = plyr->readyweapon; + + /* if (*w_ready.on) + * stlib_update_num(&w_ready, true); + * refresh weapon change + * } + */ + + /* update keycard multiple widgets */ + + for (i = 0; i < 3; i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + if (plyr->cards[i + 3]) keyboxes[i] = i + 3; + } + + /* refresh everything if this is him coming back to life */ + + st_update_face_widget(); + + /* used by the w_armsbg widget */ + + st_notdeathmatch = !deathmatch; + + /* used by w_arms[] widgets */ + + st_armson = st_statusbaron && !deathmatch; + + /* used by w_frags widget */ + + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i != consoleplayer) + st_fragscount += plyr->frags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + /* get rid of chat window if up because of message */ + + if (!--st_msgcounter) st_chat = st_oldchat; +} + +static void st_do_palette_stuff(void) +{ + int palette; + byte *pal; + int cnt; + int bzc; + + cnt = plyr->damagecount; + + if (plyr->powers[pw_strength]) + { + /* slowly fade the berzerk out */ + + bzc = 12 - (plyr->powers[pw_strength] >> 6); + + if (bzc > cnt) cnt = bzc; + } + + if (cnt) + { + palette = (cnt + 7) >> 3; + + if (palette >= NUMREDPALS) palette = NUMREDPALS - 1; + + palette += STARTREDPALS; + } + + else if (plyr->bonuscount) + { + palette = (plyr->bonuscount + 7) >> 3; + + if (palette >= NUMBONUSPALS) palette = NUMBONUSPALS - 1; + + palette += STARTBONUSPALS; + } + + else if (plyr->powers[pw_ironfeet] > 4 * 32 || + plyr->powers[pw_ironfeet] & 8) + palette = RADIATIONPAL; + else + palette = 0; + + /* In Chex Quest, the player never sees red. Instead, the + * radiation suit palette is used to tint the screen green, + * as though the player is being covered in goo by an + * attacking flemoid. + */ + + if (gameversion == exe_chex && palette >= STARTREDPALS && + palette < STARTREDPALS + NUMREDPALS) + { + palette = RADIATIONPAL; + } + + if (palette != st_palette) + { + st_palette = palette; + pal = (byte *)w_cache_lump_num(lu_palette, PU_CACHE) + palette * 768; + i_set_palette(pal); + } +} + +static void st_draw_widgets(boolean refresh) +{ + int i; + + /* used by w_arms[] widgets */ + + st_armson = st_statusbaron && !deathmatch; + + /* used by w_frags widget */ + + st_fragson = deathmatch && st_statusbaron; + + stlib_update_num(&w_ready, refresh); + + for (i = 0; i < 4; i++) + { + stlib_update_num(&w_ammo[i], refresh); + stlib_update_num(&w_maxammo[i], refresh); + } + + stlib_update_percent(&w_health, refresh); + stlib_update_percent(&w_armor, refresh); + + stlib_update_bin_icon(&w_armsbg, refresh); + + for (i = 0; i < 6; i++) + stlib_update_mult_icon(&w_arms[i], refresh); + + stlib_update_mult_icon(&w_faces, refresh); + + for (i = 0; i < 3; i++) + stlib_update_mult_icon(&w_keyboxes[i], refresh); + + stlib_update_num(&w_frags, refresh); +} + +static void st_do_refresh(void) +{ + st_firsttime = false; + + /* draw status bar background to off-screen buff */ + + st_refresh_background(); + + /* and refresh all widgets */ + + st_draw_widgets(true); +} + +static void st_diff_draw(void) +{ + /* update all widgets */ + + st_draw_widgets(false); +} + +/* Iterates through all graphics to be loaded or unloaded, along with + * the variable they use, invoking the specified callback function. + */ + +static void st_load_unload_graphics(load_callback_t callback) +{ + int i; + int j; + int facenum; + + char namebuf[9]; + + /* Load the numbers, tall and short */ + + for (i = 0; i < 10; i++) + { + snprintf(namebuf, 9, "STTNUM%d", i); + callback(namebuf, &tallnum[i]); + + snprintf(namebuf, 9, "STYSNUM%d", i); + callback(namebuf, &shortnum[i]); + } + + /* Load percent key. + * Note: why not load STMINUS here, too? + */ + + callback(("STTPRCNT"), &tallpercent); + + /* key cards */ + + for (i = 0; i < NUMCARDS; i++) + { + snprintf(namebuf, 9, "STKEYS%d", i); + callback(namebuf, &keys[i]); + } + + /* arms background */ + + callback(("STARMS"), &armsbg); + + /* arms ownership widgets */ + + for (i = 0; i < 6; i++) + { + snprintf(namebuf, 9, "STGNUM%d", i + 2); + + /* gray # */ + + callback(namebuf, &arms[i][0]); + + /* yellow # */ + + arms[i][1] = shortnum[i + 2]; + } + + /* face backgrounds for different color players */ + + snprintf(namebuf, 9, "STFB%d", consoleplayer); + callback(namebuf, &faceback); + + /* status bar background bits */ + + if (w_check_num_for_name("STBAR") >= 0) + { + callback(("STBAR"), &sbar); + sbarr = NULL; + } + else + { + callback(("STMBARL"), &sbar); + callback(("STMBARR"), &sbarr); + } + + /* face states */ + + facenum = 0; + for (i = 0; i < ST_NUMPAINFACES; i++) + { + for (j = 0; j < ST_NUMSTRAIGHTFACES; j++) + { + snprintf(namebuf, 9, "STFST%d%d", i, j); + callback(namebuf, &faces[facenum]); + ++facenum; + } + + snprintf(namebuf, 9, "STFTR%d0", i); /* turn right */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFTL%d0", i); /* turn left */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFOUCH%d", i); /* ouch! */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFEVL%d", i); /* evil grin ;) */ + callback(namebuf, &faces[facenum]); + ++facenum; + snprintf(namebuf, 9, "STFKILL%d", i); /* pissed off */ + callback(namebuf, &faces[facenum]); + ++facenum; + } + + callback(("STFGOD0"), &faces[facenum]); + ++facenum; + callback(("STFDEAD0"), &faces[facenum]); + ++facenum; +} + +static void st_load_callback(const char *lumpname, patch_t **variable) +{ + *variable = w_cache_lump_name(lumpname, PU_STATIC); +} + +static void st_load_graphics(void) +{ + st_load_unload_graphics(st_load_callback); +} + +static void st_load_data(void) +{ + lu_palette = w_get_num_for_name(("PLAYPAL")); + st_load_graphics(); +} + +#if 0 /* UNUSED */ +static void st_unload_callback(const char *lumpname, patch_t **variable) +{ + w_release_lump_name(lumpname); + *variable = NULL; +} + +static void st_unload_graphics(void) +{ + st_load_unload_graphics(st_unload_callback); +} +#endif + +static void st_init_data(void) +{ + int i; + + st_firsttime = true; + plyr = &players[consoleplayer]; + + st_statusbaron = true; + st_oldchat = st_chat = false; + + st_faceindex = 0; + st_palette = -1; + + st_oldhealth = -1; + + for (i = 0; i < NUMWEAPONS; i++) + oldweaponsowned[i] = plyr->weaponowned[i]; + + for (i = 0; i < 3; i++) + keyboxes[i] = -1; + + stlib_init(); +} + +static void st_create_widgets(void) +{ + int i; + + /* ready weapon ammo */ + + stlib_init_num(&w_ready, ST_AMMOX, ST_AMMOY, tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, ST_AMMOWIDTH); + + /* the last weapon type */ + + w_ready.data = plyr->readyweapon; + + /* health percentage */ + + stlib_init_percent(&w_health, ST_HEALTHX, ST_HEALTHY, tallnum, + &plyr->health, &st_statusbaron, tallpercent); + + /* arms background */ + + stlib_init_bin_icon(&w_armsbg, ST_ARMSBGX, ST_ARMSBGY, armsbg, + &st_notdeathmatch, &st_statusbaron); + + /* weapons owned */ + + for (i = 0; i < 6; i++) + { + stlib_init_mutl_icon(&w_arms[i], ST_ARMSX + (i % 3) * ST_ARMSXSPACE, + ST_ARMSY + (i / 3) * ST_ARMSYSPACE, arms[i], + &plyr->weaponowned[i + 1], &st_armson); + } + + /* frags sum */ + + stlib_init_num(&w_frags, ST_FRAGSX, ST_FRAGSY, tallnum, &st_fragscount, + &st_fragson, ST_FRAGSWIDTH); + + /* faces */ + + stlib_init_mutl_icon(&w_faces, ST_FACESX, ST_FACESY, faces, &st_faceindex, + &st_statusbaron); + + /* armor percentage - should be colored later */ + + stlib_init_percent(&w_armor, ST_ARMORX, ST_ARMORY, tallnum, + &plyr->armorpoints, &st_statusbaron, tallpercent); + + /* keyboxes 0-2 */ + + stlib_init_mutl_icon(&w_keyboxes[0], ST_KEY0X, ST_KEY0Y, keys, + &keyboxes[0], &st_statusbaron); + + stlib_init_mutl_icon(&w_keyboxes[1], ST_KEY1X, ST_KEY1Y, keys, + &keyboxes[1], &st_statusbaron); + + stlib_init_mutl_icon(&w_keyboxes[2], ST_KEY2X, ST_KEY2Y, keys, + &keyboxes[2], &st_statusbaron); + + /* ammo count (all four kinds) */ + + stlib_init_num(&w_ammo[0], ST_AMMO0X, ST_AMMO0Y, shortnum, &plyr->ammo[0], + &st_statusbaron, ST_AMMO0WIDTH); + + stlib_init_num(&w_ammo[1], ST_AMMO1X, ST_AMMO1Y, shortnum, &plyr->ammo[1], + &st_statusbaron, ST_AMMO1WIDTH); + + stlib_init_num(&w_ammo[2], ST_AMMO2X, ST_AMMO2Y, shortnum, &plyr->ammo[2], + &st_statusbaron, ST_AMMO2WIDTH); + + stlib_init_num(&w_ammo[3], ST_AMMO3X, ST_AMMO3Y, shortnum, &plyr->ammo[3], + &st_statusbaron, ST_AMMO3WIDTH); + + /* max ammo count (all four kinds) */ + + stlib_init_num(&w_maxammo[0], ST_MAXAMMO0X, ST_MAXAMMO0Y, shortnum, + &plyr->maxammo[0], &st_statusbaron, ST_MAXAMMO0WIDTH); + + stlib_init_num(&w_maxammo[1], ST_MAXAMMO1X, ST_MAXAMMO1Y, shortnum, + &plyr->maxammo[1], &st_statusbaron, ST_MAXAMMO1WIDTH); + + stlib_init_num(&w_maxammo[2], ST_MAXAMMO2X, ST_MAXAMMO2Y, shortnum, + &plyr->maxammo[2], &st_statusbaron, ST_MAXAMMO2WIDTH); + + stlib_init_num(&w_maxammo[3], ST_MAXAMMO3X, ST_MAXAMMO3Y, shortnum, + &plyr->maxammo[3], &st_statusbaron, ST_MAXAMMO3WIDTH); +} + +/**************************************************************************** + * Public Function + ****************************************************************************/ + +/* Respond to keyboard input events, intercept cheats. */ + +boolean st_responder(event_t *ev) +{ + int i; + + /* Filter automap on/off. */ + + if (ev->type == ev_keyup && ((ev->data1 & 0xffff0000) == AM_MSGHEADER)) + { + switch (ev->data1) + { + case AM_MSGENTERED: + st_firsttime = true; + break; + + case AM_MSGEXITED: + break; + } + } + + /* if a user keypress... */ + + else if (ev->type == ev_keydown) + { + if (!netgame && gameskill != sk_nightmare) + { + /* 'dqd' cheat for toggleable god mode */ + + if (cht_check_cheat(&cheat_god, ev->data2)) + { + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) plyr->mo->health = deh_god_mode_health; + + plyr->health = deh_god_mode_health; + plyr->message = (STSTR_DQDON); + } + else + plyr->message = (STSTR_DQDOFF); + } + + /* 'fa' cheat for killer fucking arsenal */ + + else if (cht_check_cheat(&cheat_ammonokey, ev->data2)) + { + plyr->armorpoints = deh_idfa_armor; + plyr->armortype = deh_idfa_armor_class; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + plyr->message = (STSTR_FAADDED); + } + + /* 'kfa' cheat for key full ammo */ + + else if (cht_check_cheat(&cheat_ammo, ev->data2)) + { + plyr->armorpoints = deh_idkfa_armor; + plyr->armortype = deh_idkfa_armor_class; + + for (i = 0; i < NUMWEAPONS; i++) + plyr->weaponowned[i] = true; + + for (i = 0; i < NUMAMMO; i++) + plyr->ammo[i] = plyr->maxammo[i]; + + for (i = 0; i < NUMCARDS; i++) + plyr->cards[i] = true; + + plyr->message = (STSTR_KFAADDED); + } + + /* 'mus' cheat for changing music */ + + else if (cht_check_cheat(&cheat_mus, ev->data2)) + { + char buf[3]; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + int musnum; +#endif + + plyr->message = (STSTR_MUS); + ch_get_param(&cheat_mus, buf); + + /* Note: The original v1.9 had a bug that tried to play back + * the Doom II music regardless of gamemode. This was fixed + * in the Ultimate Doom executable so that it would work for + * the Doom 1 music as well. + */ + + if (gamemode == commercial || gameversion < exe_ultimate) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + musnum = + MUS_RUNNIN + (buf[0] - '0') * 10 + buf[1] - '0' - 1; +#endif + + if (((buf[0] - '0') * 10 + buf[1] - '0') > 35 && + gameversion >= exe_doom_1_8) + plyr->message = (STSTR_NOMUS); + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(musnum, 1); +#endif + } + } + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + musnum = MUS_E1M1 + (buf[0] - '1') * 9 + (buf[1] - '1'); +#endif + + if (((buf[0] - '1') * 9 + buf[1] - '1') > 31) + plyr->message = (STSTR_NOMUS); + else + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_change_music(musnum, 1); +#endif + } + } + } + else if ((logical_gamemission == doom && + cht_check_cheat(&cheat_noclip, ev->data2)) || + (logical_gamemission != doom && + cht_check_cheat(&cheat_commercial_noclip, ev->data2))) + { + /* Noclip cheat. + * For Doom 1, use the idspipsopd cheat; for all others, use + * idclip + */ + + plyr->cheats ^= CF_NOCLIP; + + if (plyr->cheats & CF_NOCLIP) + plyr->message = (STSTR_NCON); + else + plyr->message = (STSTR_NCOFF); + } + + /* 'behold?' power-up cheats */ + + for (i = 0; i < 6; i++) + { + if (cht_check_cheat(&cheat_powerup[i], ev->data2)) + { + if (!plyr->powers[i]) + p_give_power(plyr, i); + else if (i != pw_strength) + plyr->powers[i] = 1; + else + plyr->powers[i] = 0; + + plyr->message = (STSTR_BEHOLDX); + } + } + + /* 'behold' power-up menu */ + + if (cht_check_cheat(&cheat_powerup[6], ev->data2)) + { + plyr->message = (STSTR_BEHOLD); + } + + /* 'choppers' invulnerability & chainsaw */ + + else if (cht_check_cheat(&cheat_choppers, ev->data2)) + { + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = (STSTR_CHOPPERS); + } + + /* 'mypos' for player position */ + + else if (cht_check_cheat(&cheat_mypos, ev->data2)) + { + static char buf[ST_MSGWIDTH]; + snprintf(buf, sizeof(buf), "ang=0x%x;x,y=(0x%x,0x%x)", + players[consoleplayer].mo->angle, + players[consoleplayer].mo->x, + players[consoleplayer].mo->y); + plyr->message = buf; + } + } + + /* 'clev' change-level cheat */ + + if (!netgame && cht_check_cheat(&cheat_clev, ev->data2)) + { + char buf[3]; + int epsd; + int map; + + ch_get_param(&cheat_clev, buf); + + if (gamemode == commercial) + { + epsd = 0; + map = (buf[0] - '0') * 10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + + /* Chex.exe always warps to episode 1. */ + + if (gameversion == exe_chex) + { + if (epsd > 1) + { + epsd = 1; + } + + if (map > 5) + { + map = 5; + } + } + } + + /* Catch invalid maps. */ + + if (gamemode != commercial) + { + if (epsd < 1) + { + return false; + } + + if (epsd > 4) + { + return false; + } + + if (epsd == 4 && gameversion < exe_ultimate) + { + return false; + } + + if (map < 1) + { + return false; + } + + if (map > 9) + { + return false; + } + } + else + { + if (map < 1) + { + return false; + } + + if (map > 40) + { + return false; + } + } + + /* So be it. */ + + plyr->message = (STSTR_CLEV); + g_deferred_init_new(gameskill, epsd, map); + } + } + + return false; +} + +void st_ticker(void) +{ + st_randomnumber = m_random(); + st_update_widgets(); + st_oldhealth = plyr->health; +} + +void st_drawer(boolean p_fullscreen, boolean refresh) +{ + st_statusbaron = (!p_fullscreen) || automapactive; + st_firsttime = st_firsttime || refresh; + + /* Do red-/gold-shifts from damage/items */ + + st_do_palette_stuff(); + + /* If just after st_start(), refresh all */ + + if (st_firsttime) + { + st_do_refresh(); + } + else + { + /* Otherwise, update as little as possible */ + + st_diff_draw(); + } +} + +void st_start(void) +{ + if (!st_stopped) + { + st_stop(); + } + + st_init_data(); + st_create_widgets(); + st_stopped = false; +} + +void st_init(void) +{ + st_load_data(); + st_backing_screen = (pixel_t *)z_malloc( + ST_WIDTH * ST_HEIGHT * sizeof(*st_backing_screen), PU_STATIC, 0); +} diff --git a/games/NXDoom/src/doom/st_stuff.h b/games/NXDoom/src/doom/st_stuff.h new file mode 100644 index 000000000..f68279e66 --- /dev/null +++ b/games/NXDoom/src/doom/st_stuff.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/st_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animating. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + ****************************************************************************/ + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "doomtype.h" +#include "m_cheat.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Size of statusbar. + * Now sensitive for scaling. + */ + +#define ST_HEIGHT 32 +#define ST_WIDTH SCREENWIDTH +#define ST_Y (SCREENHEIGHT - ST_HEIGHT) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern pixel_t *st_backing_screen; +extern cheatseq_t cheat_mus; +extern cheatseq_t cheat_god; +extern cheatseq_t cheat_ammo; +extern cheatseq_t cheat_ammonokey; +extern cheatseq_t cheat_noclip; +extern cheatseq_t cheat_commercial_noclip; +extern cheatseq_t cheat_powerup[7]; +extern cheatseq_t cheat_choppers; +extern cheatseq_t cheat_clev; +extern cheatseq_t cheat_mypos; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* STATUS BAR */ + +/* Called by main loop. */ + +boolean st_responder(event_t *ev); + +/* Called by main loop. */ + +void st_ticker(void); + +/* Called by main loop. */ + +void st_drawer(boolean fullscreen, boolean refresh); + +/* Called when the console player is spawned on each level. */ + +void st_start(void); + +/* Called by startup code. */ + +void st_init(void); + +#endif /* __STSTUFF_H__ */ diff --git a/games/NXDoom/src/doom/statdump.c b/games/NXDoom/src/doom/statdump.c new file mode 100644 index 000000000..1db5657ab --- /dev/null +++ b/games/NXDoom/src/doom/statdump.c @@ -0,0 +1,388 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/statdump.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Functions for presenting the information captured from the statistics + * buffer to a file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "d_mode.h" +#include "d_player.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "statdump.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_CAPTURES 32 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Par times for E1M1-E1M9. */ + +static const int doom1_par_times[] = +{ + 30, 75, 120, 90, 165, 180, 180, 30, 165, +}; + +/* Par times for MAP01-MAP09. */ + +static const int doom2_par_times[] = +{ + 30, 90, 120, 120, 90, 150, 120, 120, 270, +}; + +/* Player colors. */ + +static const char *player_colors[] = +{ + "Green", + "Indigo", + "Brown", + "Red", +}; + +/* Array of end-of-level statistics that have been captured. */ + +static wbstartstruct_t captured_stats[MAX_CAPTURES]; +static int num_captured_stats = 0; + +static gamemission_t discovered_gamemission = none; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Try to work out whether this is a Doom 1 or Doom 2 game, by looking + * at the episode and map, and the par times. This is used to decide + * how to format the level name. Unfortunately, in some cases it is + * impossible to determine whether this is Doom 1 or Doom 2. + */ + +static void discover_game_mode(const wbstartstruct_t *stats, int num_stats) +{ + int partime; + int level; + int i; + + if (discovered_gamemission != none) + { + return; + } + + for (i = 0; i < num_stats; ++i) + { + level = stats[i].last; + + /* If episode 2, 3 or 4, this is Doom 1. */ + + if (stats[i].epsd > 0) + { + discovered_gamemission = doom; + return; + } + + /* This is episode 1. If this is level 10 or higher, it must be + * Doom 2. + */ + + if (level >= 9) + { + discovered_gamemission = doom2; + return; + } + + /* Try to work out if this is Doom 1 or Doom 2 by looking at the par + * time. + */ + + partime = stats[i].partime; + + if (partime == doom1_par_times[level] * TICRATE && + partime != doom2_par_times[level] * TICRATE) + { + discovered_gamemission = doom; + return; + } + + if (partime != doom1_par_times[level] * TICRATE && + partime == doom2_par_times[level] * TICRATE) + { + discovered_gamemission = doom2; + return; + } + } +} + +/* Returns the number of players active in the given stats buffer. */ + +static int get_num_players(const wbstartstruct_t *stats) +{ + int i; + int num_players = 0; + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (stats->plyr[i].in) + { + ++num_players; + } + } + + return num_players; +} + +static void print_banner(FILE *stream) +{ + fprintf(stream, "===========================================\n"); +} + +static void print_percentage(FILE *stream, int amount, int total) +{ + if (total == 0) + { + fprintf(stream, "0"); + } + else + { + fprintf(stream, "%i / %i", amount, total); + + /* statdump.exe is a 16-bit program, so very occasionally an + * integer overflow can occur when doing this calculation with + * a large value. Therefore, cast to short to give the same + * output. + */ + + fprintf(stream, " (%i%%)", (short)(amount * 100) / total); + } +} + +/* Display statistics for a single player. */ + +static void print_player_stats(FILE *stream, const wbstartstruct_t *stats, + int player_num) +{ + const wbplayerstruct_t *player = &stats->plyr[player_num]; + + fprintf(stream, "Player %i (%s):\n", player_num + 1, + player_colors[player_num]); + + /* Kills percentage */ + + fprintf(stream, "\tKills: "); + print_percentage(stream, player->skills, stats->maxkills); + fprintf(stream, "\n"); + + /* Items percentage */ + + fprintf(stream, "\tItems: "); + print_percentage(stream, player->sitems, stats->maxitems); + fprintf(stream, "\n"); + + /* Secrets percentage */ + + fprintf(stream, "\tSecrets: "); + print_percentage(stream, player->ssecret, stats->maxsecret); + fprintf(stream, "\n"); +} + +/* Frags table for multiplayer games. */ + +static void print_frags_table(FILE *stream, const wbstartstruct_t *stats) +{ + int x; + int y; + + fprintf(stream, "Frags:\n"); + + /* Print header */ + + fprintf(stream, "\t\t"); + + for (x = 0; x < MAXPLAYERS; ++x) + { + if (!stats->plyr[x].in) + { + continue; + } + + fprintf(stream, "%s\t", player_colors[x]); + } + + fprintf(stream, "\n"); + + fprintf(stream, "\t\t-------------------------------- VICTIMS\n"); + + /* Print table */ + + for (y = 0; y < MAXPLAYERS; ++y) + { + if (!stats->plyr[y].in) + { + continue; + } + + fprintf(stream, "\t%s\t|", player_colors[y]); + + for (x = 0; x < MAXPLAYERS; ++x) + { + if (!stats->plyr[x].in) + { + continue; + } + + fprintf(stream, "%i\t", stats->plyr[y].frags[x]); + } + + fprintf(stream, "\n"); + } + + fprintf(stream, "\t\t|\n"); + fprintf(stream, "\t KILLERS\n"); +} + +/* Displays the level name: MAPxy or ExMy, depending on game mode. */ + +static void print_level_name(FILE *stream, int episode, int level) +{ + print_banner(stream); + + switch (discovered_gamemission) + { + case doom: + fprintf(stream, "E%iM%i\n", episode + 1, level + 1); + break; + case doom2: + fprintf(stream, "MAP%02i\n", level + 1); + break; + default: + case none: + fprintf(stream, "E%iM%i / MAP%02i\n", episode + 1, level + 1, + level + 1); + break; + } + + print_banner(stream); +} + +/* Print details of a statistics buffer to the given file. */ + +static void print_stats(FILE *stream, const wbstartstruct_t *stats) +{ + short leveltime; + short partime; + int i; + + print_level_name(stream, stats->epsd, stats->last); + fprintf(stream, "\n"); + + leveltime = stats->plyr[0].stime / TICRATE; + partime = stats->partime / TICRATE; + fprintf(stream, "Time: %i:%02i", leveltime / 60, leveltime % 60); + fprintf(stream, " (par: %i:%02i)\n", partime / 60, partime % 60); + fprintf(stream, "\n"); + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (stats->plyr[i].in) + { + print_player_stats(stream, stats, i); + } + } + + if (get_num_players(stats) >= 2) + { + print_frags_table(stream, stats); + } + + fprintf(stream, "\n"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void stat_copy(const wbstartstruct_t *stats) +{ + if (m_parm_exists("-statdump") && num_captured_stats < MAX_CAPTURES) + { + memcpy(&captured_stats[num_captured_stats], stats, + sizeof(wbstartstruct_t)); + ++num_captured_stats; + } +} + +void stat_dump(void) +{ + FILE *dumpfile; + int i; + + /* @category compat + * @arg + * + * Dump statistics information to the specified file on the levels + * that were played. The output from this option matches the output + * from statdump.exe (see ctrlapi.zip in the /idgames archive). + */ + + i = m_check_parm_with_args("-statdump", 1); + + if (i > 0) + { + printf("Statistics captured for %i level(s)\n", num_captured_stats); + + /* We actually know what the real gamemission is, but this has + * to match the output from statdump.exe. + */ + + discover_game_mode(captured_stats, num_captured_stats); + + /* Allow "-" as output file, for stdout. */ + + if (strcmp(myargv[i + 1], "-") != 0) + { + dumpfile = fopen(myargv[i + 1], "w"); + } + else + { + dumpfile = stdout; + } + + for (i = 0; i < num_captured_stats; ++i) + { + print_stats(dumpfile, &captured_stats[i]); + } + + if (dumpfile != stdout) + { + fclose(dumpfile); + } + } +} diff --git a/games/NXDoom/src/doom/statdump.h b/games/NXDoom/src/doom/statdump.h new file mode 100644 index 000000000..57073aa08 --- /dev/null +++ b/games/NXDoom/src/doom/statdump.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/statdump.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef DOOM_STATDUMP_H +#define DOOM_STATDUMP_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void stat_copy(const wbstartstruct_t *stats); +void stat_dump(void); + +#endif /* DOOM_STATDUMP_H */ diff --git a/games/NXDoom/src/doom/wi_stuff.c b/games/NXDoom/src/doom/wi_stuff.c new file mode 100644 index 000000000..fdb5964ad --- /dev/null +++ b/games/NXDoom/src/doom/wi_stuff.c @@ -0,0 +1,1893 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/wi_stuff.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Intermission screens. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "z_zone.h" + +#include "m_misc.h" +#include "m_random.h" + +#include "deh_main.h" +#include "i_swap.h" +#include "i_system.h" + +#include "w_wad.h" + +#include "g_game.h" + +#include "r_local.h" + +#ifdef CONFIG_GAMES_NXDOOM_SOUND +#include "s_sound.h" +#include "sounds.h" +#endif + +#include "doomstat.h" + +/* Needs access to LFB. */ + +#include "v_video.h" + +#include "wi_stuff.h" + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* Data needed to add patches to full screen intermission pics. + * Patches are statistics messages, and animations. + * Loads of by-pixel layout and placement, offsets etc. + */ + +/* Different between registered DOOM (1994) and + * Ultimate DOOM - Final edition (retail, 1995?). + * This is supposedly ignored for commercial + * release (aka DOOM II), which had 34 maps + * in one episode. So there. + */ + +#define NUMEPISODES 4 +#define NUMMAPS 9 + +/* in tics + * U #define PAUSELEN (TICRATE*2) + * U #define SCORESTEP 100 + * U #define ANIMPERIOD 32 + * pixel distance from "(YOU)" to "PLAYER N" + * U #define STARDIST 10 + * U #define WK 1 + */ + +/* GLOBAL LOCATIONS */ + +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +/* SINGLE-PLAYER STUFF */ + +#define SP_STATSX 50 +#define SP_STATSY 50 + +#define SP_TIMEX 16 +#define SP_TIMEY (SCREENHEIGHT - 32) + +/* NET GAME STUFF */ + +#define NG_STATSY 50 +#define NG_STATSX (32 + SHORT(star->width) / 2 + 32 * !dofrags) + +#define NG_SPACINGX 64 + +/* DEATHMATCH STUFF */ + +#define DM_MATRIXX 42 +#define DM_MATRIXY 68 + +#define DM_SPACINGX 40 + +#define DM_TOTALSX 269 + +#define DM_KILLERSX 10 +#define DM_KILLERSY 100 +#define DM_VICTIMSX 5 +#define DM_VICTIMSY 50 + +/* Animation locations for episode 0 (1). + * Using patches saves a lot of space, + * as they replace 320x200 full screen frames. + */ + +#define ANIM(type, period, nanims, x, y, nexttic) \ + {(type), (period), (nanims), {(x), (y)}, (nexttic), 0, {NULL, NULL, NULL}, \ + 0, 0, 0, 0} + +/* GENERAL DATA */ + +/* Locally used stuff. */ + +/* States for single-player */ + +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +/* in seconds */ + +#define SHOWNEXTLOCDELAY 4 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + ANIM_ALWAYS, + ANIM_RANDOM, + ANIM_LEVEL +} animenum_t; + +/* States for the intermission */ + +typedef enum +{ + NO_STATE = -1, + STAT_COUNT, + SHOW_NEXT_LOC, +} stateenum_t; + +typedef struct +{ + int x; + int y; +} point_t; + +/* Animation. + * There is another anim_t used in p_spec. + */ + +typedef struct +{ + animenum_t type; + + /* period in tics between animations */ + + int period; + + /* number of animation frames */ + + int nanims; + + /* location of animation */ + + point_t loc; + + /* ALWAYS: n/a, + * RANDOM: period deviation (<256), + * LEVEL: level + */ + + int data1; + + /* ALWAYS: n/a, + * RANDOM: random base period, + * LEVEL: n/a + */ + + int data2; + + /* actual graphics for frames of animations */ + + patch_t *p[3]; + + /* following must be initialized to zero before use! */ + + /* next value of bcnt (used in conjunction with period) */ + + int nexttic; + + /* last drawn animation frame */ + + int lastdrawn; + + /* next frame number to animate */ + + int ctr; + + /* used by RANDOM and LEVEL when animating */ + + int state; +} anim_t; + +typedef void (*load_callback_t)(const char *lumpname, patch_t **variable); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + /* Episode 0 World Map */ + + { + {185, 164}, /* location of level 0 (CJ) */ + {148, 143}, /* location of level 1 (CJ) */ + {69, 122}, /* location of level 2 (CJ) */ + {209, 102}, /* location of level 3 (CJ) */ + {116, 89}, /* location of level 4 (CJ) */ + {166, 55}, /* location of level 5 (CJ) */ + {71, 56}, /* location of level 6 (CJ) */ + {135, 29}, /* location of level 7 (CJ) */ + {71, 24}, /* location of level 8 (CJ) */ + }, + + /* Episode 1 World Map should go here */ + + { + {254, 25}, /* location of level 0 (CJ) */ + {97, 50}, /* location of level 1 (CJ) */ + {188, 64}, /* location of level 2 (CJ) */ + {128, 78}, /* location of level 3 (CJ) */ + {214, 92}, /* location of level 4 (CJ) */ + {133, 130}, /* location of level 5 (CJ) */ + {208, 136}, /* location of level 6 (CJ) */ + {148, 140}, /* location of level 7 (CJ) */ + {235, 158}, /* location of level 8 (CJ) */ + }, + + /* Episode 2 World Map should go here */ + + { + {156, 168}, /* location of level 0 (CJ) */ + {48, 154}, /* location of level 1 (CJ) */ + {174, 95}, /* location of level 2 (CJ) */ + {265, 75}, /* location of level 3 (CJ) */ + {130, 48}, /* location of level 4 (CJ) */ + {279, 23}, /* location of level 5 (CJ) */ + {198, 48}, /* location of level 6 (CJ) */ + {140, 25}, /* location of level 7 (CJ) */ + {281, 136}, /* location of level 8 (CJ) */ + }, +}; + +static anim_t epsd0animinfo[] = +{ + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 224, 104, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 184, 160, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 112, 136, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 72, 112, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 88, 96, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 64, 48, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 192, 40, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 136, 16, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 80, 16, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 64, 24, 0), +}; + +static anim_t epsd1animinfo[] = +{ + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 1), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 2), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 3), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 4), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 5), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 6), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 7), + ANIM(ANIM_LEVEL, TICRATE / 3, 3, 192, 144, 8), + ANIM(ANIM_LEVEL, TICRATE / 3, 1, 128, 136, 8), +}; + +static anim_t epsd2animinfo[] = +{ + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 104, 168, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 40, 136, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 160, 96, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 104, 80, 0), + ANIM(ANIM_ALWAYS, TICRATE / 3, 3, 120, 32, 0), + ANIM(ANIM_ALWAYS, TICRATE / 4, 3, 40, 0, 0), +}; + +static int NUMANIMS[NUMEPISODES] = +{ + arrlen(epsd0animinfo), + arrlen(epsd1animinfo), + arrlen(epsd2animinfo), +}; + +static anim_t *anims[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo, +}; + +/* used to accelerate or skip a stage */ + +static int acceleratestage; + +/* wbs->pnum */ + +static int me; + +/* specifies current state */ + +static stateenum_t state; + +/* contains information passed into intermission */ + +static wbstartstruct_t *wbs; + +static wbplayerstruct_t *plrs; /* wbs->plyr[] */ + +/* used for general timing */ + +static int cnt; + +/* used for timing of background animation */ + +static int bcnt; + +/* signals to refresh everything for one frame */ + +static int firstrefresh; + +static int cnt_kills[MAXPLAYERS]; +static int cnt_items[MAXPLAYERS]; +static int cnt_secret[MAXPLAYERS]; +static int cnt_time; +static int cnt_par; +static int cnt_pause; + +/* # of commercial levels */ + +static int NUMCMAPS; + +/* GRAPHICS */ + +/* You Are Here graphic */ + +static patch_t *yah[3] = +{ + NULL, + NULL, + NULL, +}; + +/* splat */ + +static patch_t *splat[2] = +{ + NULL, + NULL, +}; + +/* %, : graphics */ + +static patch_t *percent; +static patch_t *colon; + +/* 0-9 graphic */ + +static patch_t *num[10]; + +/* minus sign */ + +static patch_t *wiminus; + +/* "Finished!" graphics */ + +static patch_t *finished; + +/* "Entering" graphic */ + +static patch_t *entering; + +/* "secret" */ + +static patch_t *sp_secret; + +/* "Kills", "Scrt", "Items", "Frags" */ + +static patch_t *kills; +static patch_t *secret; +static patch_t *items; +static patch_t *frags; + +/* Time sucks. */ + +static patch_t *timepatch; +static patch_t *par; +static patch_t *sucks; + +/* "killers", "victims" */ + +static patch_t *killers; +static patch_t *victims; + +/* "Total", your face, your dead face */ + +static patch_t *total; +static patch_t *star; +static patch_t *bstar; + +/* "red P[1..MAXPLAYERS]" */ + +static patch_t *p[MAXPLAYERS]; + +/* "gray P[1..MAXPLAYERS]" */ + +static patch_t *bp[MAXPLAYERS]; + +/* Name graphics of each level (centered) */ + +static patch_t **lnames; + +/* Buffer storing the backdrop */ + +static patch_t *background; + +static int dm_state; +static int dm_frags[MAXPLAYERS][MAXPLAYERS]; +static int dm_totals[MAXPLAYERS]; + +static int cnt_frags[MAXPLAYERS]; +static int dofrags; +static int ng_state; + +static int sp_state; + +static boolean snl_pointeron = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Common load/unload function. Iterates over all the graphics + * lumps to be loaded/unloaded into memory. + */ + +static void wi_load_unload_data(load_callback_t callback) +{ + int8_t i; + int8_t j; + char name[23]; + anim_t *a; + + if (gamemode == commercial) + { + for (i = 0; i < NUMCMAPS; i++) + { + snprintf(name, sizeof(name), "CWILV%2.2d", i); + callback(name, &lnames[i]); + } + } + else + { + for (i = 0; i < NUMMAPS; i++) + { + snprintf(name, sizeof(name), "WILV%d%d", wbs->epsd, i); + callback(name, &lnames[i]); + } + + /* you are here */ + + callback(("WIURH0"), &yah[0]); + + /* you are here (alt.) */ + + callback(("WIURH1"), &yah[1]); + + /* splat */ + + callback(("WISPLAT"), &splat[0]); + + if (wbs->epsd < 3) + { + for (j = 0; j < NUMANIMS[wbs->epsd]; j++) + { + a = &anims[wbs->epsd][j]; + for (i = 0; i < a->nanims; i++) + { + /* MONDO HACK! */ + + if (wbs->epsd != 1 || j != 8) + { + /* animations */ + + snprintf(name, sizeof(name), "WIA%d%.2d%.2d", + wbs->epsd, j, i); + callback(name, &a->p[i]); + } + else + { + /* HACK ALERT! */ + + a->p[i] = anims[1][4].p[i]; + } + } + } + } + } + + /* More hacks on minus sign. */ + + if (w_check_num_for_name(("WIMINUS")) > 0) + callback(("WIMINUS"), &wiminus); + else + wiminus = NULL; + + for (i = 0; i < 10; i++) + { + /* numbers 0-9 */ + + snprintf(name, sizeof(name), "WINUM%d", i); + callback(name, &num[i]); + } + + /* percent sign */ + + callback(("WIPCNT"), &percent); + + /* "finished" */ + + callback(("WIF"), &finished); + + /* "entering" */ + + callback(("WIENTER"), &entering); + + /* "kills" */ + + callback(("WIOSTK"), &kills); + + /* "scrt" */ + + callback(("WIOSTS"), &secret); + + /* "secret" */ + + callback(("WISCRT2"), &sp_secret); + + /* french wad uses WIOBJ (?) */ + + if (w_check_num_for_name(("WIOBJ")) >= 0) + { + /* "items" */ + + if (netgame && !deathmatch) + callback(("WIOBJ"), &items); + else + callback(("WIOSTI"), &items); + } + else + { + callback(("WIOSTI"), &items); + } + + /* "frgs" */ + + callback(("WIFRGS"), &frags); + + /* ":" */ + + callback(("WICOLON"), &colon); + + /* "time" */ + + callback(("WITIME"), &timepatch); + + /* "sucks" */ + + callback(("WISUCKS"), &sucks); + + /* "par" */ + + callback(("WIPAR"), &par); + + /* "killers" (vertical) */ + + callback(("WIKILRS"), &killers); + + /* "victims" (horiz) */ + + callback(("WIVCTMS"), &victims); + + /* "total" */ + + callback(("WIMSTT"), &total); + + for (i = 0; i < MAXPLAYERS; i++) + { + /* "1,2,3,4" */ + + snprintf(name, sizeof(name), "STPB%d", i); + callback(name, &p[i]); + + /* "1,2,3,4" */ + + snprintf(name, sizeof(name), "WIBP%d", i + 1); + callback(name, &bp[i]); + } + + /* Background image */ + + if (gamemode == commercial) + { + m_str_copy(name, ("INTERPIC"), sizeof(name)); + } + else if (gameversion >= exe_ultimate && wbs->epsd == 3) + { + m_str_copy(name, ("INTERPIC"), sizeof(name)); + } + else + { + snprintf(name, sizeof(name), "WIMAP%d", wbs->epsd); + } + + /* Draw backdrop and save to a temporary buffer */ + + callback(name, &background); +} + +static void wi_load_callback(const char *name, patch_t **variable) +{ + *variable = w_cache_lump_name(name, PU_STATIC); +} + +static void wi_unload_callback(const char *name, patch_t **variable) +{ + w_release_lump_name(name); + *variable = NULL; +} + +static void wi_load_data(void) +{ + if (gamemode == commercial) + { + NUMCMAPS = 32; + lnames = (patch_t **)z_malloc(sizeof(patch_t *) * NUMCMAPS, + PU_STATIC, NULL); + } + else + { + lnames = + (patch_t **)z_malloc(sizeof(patch_t *) * NUMMAPS, PU_STATIC, NULL); + } + + wi_load_unload_data(wi_load_callback); + + /* These two graphics are special cased because we're sharing them with the + * status bar code + */ + + /* your face */ + + star = w_cache_lump_name(("STFST01"), PU_STATIC); + + /* dead face */ + + bstar = w_cache_lump_name(("STFDEAD0"), PU_STATIC); +} + +static void wi_unload_data(void) +{ + wi_load_unload_data(wi_unload_callback); + + /* We do not free these lumps as they are shared with the status + * bar code. + */ + + /* w_release_lump_name("STFST01"); + * w_release_lump_name("STFDEAD0"); + */ +} + +/* slam background */ + +static void wi_slam_background(void) +{ + v_draw_patch(0, 0, background); +} + +/* Draws " Finished!" */ + +static void wi_draw_lf(void) +{ + int y = WI_TITLEY; + + if (gamemode != commercial || wbs->last < NUMCMAPS) + { + /* draw */ + + v_draw_patch((SCREENWIDTH - SHORT(lnames[wbs->last]->width)) / 2, y, + lnames[wbs->last]); + + /* draw "Finished!" */ + + y += (5 * SHORT(lnames[wbs->last]->height)) / 4; + + v_draw_patch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); + } + else if (wbs->last == NUMCMAPS) + { + /* MAP33 - draw "Finished!" only */ + + v_draw_patch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished); + } + else if (wbs->last > NUMCMAPS) + { + /* > MAP33. Doom bombs out here with a Bad v_draw_patch error. + * I'm pretty sure that doom2.exe is just reading into random + * bits of memory at this point, but let's try to be accurate + * anyway. This deliberately triggers a v_draw_patch error. + */ + + patch_t tmp = + { + SCREENWIDTH, + SCREENHEIGHT, + 1, + 1, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + }; + + v_draw_patch(0, y, &tmp); + } +} + +/* Draws "Entering " */ + +static void wi_draw_e(void) +{ + int y = WI_TITLEY; + + /* draw "Entering" */ + + v_draw_patch((SCREENWIDTH - SHORT(entering->width)) / 2, y, entering); + + /* draw level */ + + y += (5 * SHORT(lnames[wbs->next]->height)) / 4; + + v_draw_patch((SCREENWIDTH - SHORT(lnames[wbs->next]->width)) / 2, y, + lnames[wbs->next]); +} + +static void wi_draw_on_lnode(int n, patch_t *c[]) +{ + int i; + int left; + int top; + int right; + int bottom; + boolean fits = false; + + i = 0; + do + { + left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset); + top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset); + right = left + SHORT(c[i]->width); + bottom = top + SHORT(c[i]->height); + + if (left >= 0 && right < SCREENWIDTH && top >= 0 && + bottom < SCREENHEIGHT) + { + fits = true; + } + else + { + i++; + } + } + while (!fits && i != 2 && c[i] != NULL); + + if (fits && i < 2) + { + v_draw_patch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, c[i]); + } + else + { + printf("Could not place patch on level %d", n + 1); /* DEBUG */ + } +} + +static void wi_init_animate_back(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[wbs->epsd][i]; + + /* init variables */ + + a->ctr = -1; + + /* specify the next time to draw it */ + + if (a->type == ANIM_ALWAYS) + a->nexttic = bcnt + 1 + (m_random() % a->period); + else if (a->type == ANIM_RANDOM) + a->nexttic = bcnt + 1 + a->data2 + (m_random() % a->data1); + else if (a->type == ANIM_LEVEL) + a->nexttic = bcnt + 1; + } +} + +static void wi_update_animated_block(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[wbs->epsd][i]; + + if (bcnt == a->nexttic) + { + switch (a->type) + { + case ANIM_ALWAYS: + if (++a->ctr >= a->nanims) a->ctr = 0; + a->nexttic = bcnt + a->period; + break; + + case ANIM_RANDOM: + a->ctr++; + if (a->ctr == a->nanims) + { + a->ctr = -1; + a->nexttic = bcnt + a->data2 + (m_random() % a->data1); + } + else + a->nexttic = bcnt + a->period; + break; + + case ANIM_LEVEL: + + /* gawd-awful hack for level anims */ + + if (!(state == STAT_COUNT && i == 7) && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + +static void wi_draw_animated_back(void) +{ + int i; + anim_t *a; + + if (gamemode == commercial) return; + + if (wbs->epsd > 2) return; + + for (i = 0; i < NUMANIMS[wbs->epsd]; i++) + { + a = &anims[wbs->epsd][i]; + + if (a->ctr >= 0) v_draw_patch(a->loc.x, a->loc.y, a->p[a->ctr]); + } +} + +/* Draws a number. + * If digits > 0, then use that many digits minimum, otherwise only use as + * many as necessary. Returns new x position. + */ + +static int wi_draw_num(int x, int y, int n, int digits) +{ + int fontwidth = SHORT(num[0]->width); + int neg; + int temp; + + if (digits < 0) + { + if (!n) + { + /* make variable-length zeros 1 digit long */ + + digits = 1; + } + else + { + /* figure out # of digits in # */ + + digits = 0; + temp = n; + + while (temp) + { + temp /= 10; + digits++; + } + } + } + + neg = n < 0; + if (neg) n = -n; + + /* if non-number, do not draw it */ + + if (n == 1994) return 0; + + /* draw the new number */ + + while (digits--) + { + x -= fontwidth; + v_draw_patch(x, y, num[n % 10]); + n /= 10; + } + + /* draw a minus sign if necessary */ + + if (neg && wiminus) v_draw_patch(x -= 8, y, wiminus); + + return x; +} + +static void wi_draw_percent(int x, int y, int p_p) +{ + if (p_p < 0) return; + + v_draw_patch(x, y, percent); + wi_draw_num(x, y, p_p, -1); +} + +/* Display level completion time and par, or "sucks" message if overflow. + */ + +static void wi_draw_time(int x, int y, int t) +{ + int div; + int n; + + if (t < 0) return; + + if (t <= 61 * 59) + { + div = 1; + + do + { + n = (t / div) % 60; + x = wi_draw_num(x, y, n, 2) - SHORT(colon->width); + div *= 60; + + /* draw */ + + if (div == 60 || t / div) v_draw_patch(x, y, colon); + } + while (t / div); + } + else + { + /* "sucks" */ + + v_draw_patch(x - SHORT(sucks->width), y, sucks); + } +} + +static void wi_init_nostate(void) +{ + state = NO_STATE; + acceleratestage = 0; + cnt = 10; +} + +static void wi_update_no_state(void) +{ + wi_update_animated_block(); + + if (!--cnt) + { + /* Don't call wi_end yet. g_world_done doesn't immediately + * change gamestate, so wi_drawer is still going to get + * run until that happens. If we do that after wi_end + * (which unloads all the graphics), we're in trouble. + * wi_end(); + */ + + g_world_done(); + } +} + +static void wi_init_show_next_loc(void) +{ + state = SHOW_NEXT_LOC; + acceleratestage = 0; + cnt = SHOWNEXTLOCDELAY * TICRATE; + + wi_init_animate_back(); +} + +static void wi_update_show_next_loc(void) +{ + wi_update_animated_block(); + + if (!--cnt || acceleratestage) + wi_init_nostate(); + else + snl_pointeron = (cnt & 31) < 20; +} + +static void wi_draw_show_next_loc(void) +{ + int i; + int last; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + if (gamemode != commercial) + { + if (wbs->epsd > 2) + { + wi_draw_e(); + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + /* draw a splat on taken cities. */ + + for (i = 0; i <= last; i++) + wi_draw_on_lnode(i, splat); + + /* splat the secret level? */ + + if (wbs->didsecret) wi_draw_on_lnode(8, splat); + + /* draw flashing ptr */ + + if (snl_pointeron) wi_draw_on_lnode(wbs->next, yah); + } + + /* draws which level you are entering.. */ + + if ((gamemode != commercial) || wbs->next != 30) wi_draw_e(); +} + +static void wi_draw_no_state(void) +{ + snl_pointeron = true; + wi_draw_show_next_loc(); +} + +static int wi_frag_sum(int playernum) +{ + int i; + int l_frags = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && i != playernum) + { + l_frags += plrs[playernum].frags[i]; + } + } + + /* JDC hack - negative frags. */ + + l_frags -= plrs[playernum].frags[playernum]; + + /* UNUSED if (frags < 0) + * frags = 0; + */ + + return l_frags; +} + +static void wi_init_deathmatch_stats(void) +{ + int i; + int j; + + state = STAT_COUNT; + acceleratestage = 0; + dm_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j]) dm_frags[i][j] = 0; + } + + dm_totals[i] = 0; + } + } + + wi_init_animate_back(); +} + +static void wi_update_deathmatch_stats(void) +{ + int i; + int j; + + boolean stillticking; + + wi_update_animated_block(); + + if (acceleratestage && dm_state != 4) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j]) dm_frags[i][j] = plrs[i].frags[j]; + } + + dm_totals[i] = wi_frag_sum(i); + } + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + dm_state = 4; + } + + if (dm_state == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j] && dm_frags[i][j] != plrs[i].frags[j]) + { + if (plrs[i].frags[j] < 0) + dm_frags[i][j]--; + else + dm_frags[i][j]++; + + if (dm_frags[i][j] > 99) dm_frags[i][j] = 99; + + if (dm_frags[i][j] < -99) dm_frags[i][j] = -99; + + stillticking = true; + } + } + + dm_totals[i] = wi_frag_sum(i); + + if (dm_totals[i] > 99) dm_totals[i] = 99; + + if (dm_totals[i] < -99) dm_totals[i] = -99; + } + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + dm_state++; + } + } + else if (dm_state == 4) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SLOP); +#endif + + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_draw_deathmatch_stats(void) +{ + int i; + int j; + int x; + int y; + int w; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + wi_draw_lf(); + + /* draw stat titles (top line) */ + + v_draw_patch(DM_TOTALSX - SHORT(total->width) / 2, + DM_MATRIXY - WI_SPACINGY + 10, total); + + v_draw_patch(DM_KILLERSX, DM_KILLERSY, killers); + v_draw_patch(DM_VICTIMSX, DM_VICTIMSY, victims); + + /* draw P? */ + + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + v_draw_patch(x - SHORT(p[i]->width) / 2, DM_MATRIXY - WI_SPACINGY, + p[i]); + + v_draw_patch(DM_MATRIXX - SHORT(p[i]->width) / 2, y, p[i]); + + if (i == me) + { + v_draw_patch(x - SHORT(p[i]->width) / 2, + DM_MATRIXY - WI_SPACINGY, bstar); + + v_draw_patch(DM_MATRIXX - SHORT(p[i]->width) / 2, y, star); + } + } + else + { + /* v_draw_patch(x-SHORT(bp[i]->width)/2, + * DM_MATRIXY - WI_SPACINGY, bp[i]); + * v_draw_patch(DM_MATRIXX-SHORT(bp[i]->width)/2, + * y, bp[i]); + */ + } + + x += DM_SPACINGX; + y += WI_SPACINGY; + } + + /* draw stats */ + + y = DM_MATRIXY + 10; + w = SHORT(num[0]->width); + + for (i = 0; i < MAXPLAYERS; i++) + { + x = DM_MATRIXX + DM_SPACINGX; + + if (playeringame[i]) + { + for (j = 0; j < MAXPLAYERS; j++) + { + if (playeringame[j]) wi_draw_num(x + w, y, dm_frags[i][j], 2); + + x += DM_SPACINGX; + } + + wi_draw_num(DM_TOTALSX + w, y, dm_totals[i], 2); + } + + y += WI_SPACINGY; + } +} + +static void wi_init_netgame_stats(void) +{ + int i; + + state = STAT_COUNT; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = TICRATE; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0; + + dofrags += wi_frag_sum(i); + } + + dofrags = !!dofrags; + + wi_init_animate_back(); +} + +static void wi_update_netgame_stats(void) +{ + int i; + int fsum; + + boolean stillticking; + + wi_update_animated_block(); + + if (acceleratestage && ng_state != 10) + { + acceleratestage = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + + if (dofrags) cnt_frags[i] = wi_frag_sum(i); + } + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state = 10; + } + + if (ng_state == 2) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_kills[i] += 2; + + if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state++; + } + } + else if (ng_state == 4) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_items[i] += 2; + if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state++; + } + } + else if (ng_state == 6) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_secret[i] += 2; + + if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret) + cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret; + else + stillticking = true; + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + ng_state += 1 + 2 * !dofrags; + } + } + else if (ng_state == 8) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + stillticking = false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + cnt_frags[i] += 1; + + if (cnt_frags[i] >= (fsum = wi_frag_sum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_PLDETH); +#endif + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SGCOCK); +#endif + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_init_stats(void) +{ + state = STAT_COUNT; + acceleratestage = 0; + sp_state = 1; + cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1; + cnt_time = cnt_par = -1; + cnt_pause = TICRATE; + + wi_init_animate_back(); +} + +static void wi_update_stats(void) +{ + wi_update_animated_block(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret) + { + cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret; +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + + else if (sp_state == 8) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + if (!(bcnt & 3)) s_start_sound(0, SFX_PISTOL); +#endif + + cnt_time += 3; + + if (cnt_time >= plrs[me].stime / TICRATE) + cnt_time = plrs[me].stime / TICRATE; + + cnt_par += 3; + + if (cnt_par >= wbs->partime / TICRATE) + { + cnt_par = wbs->partime / TICRATE; + + if (cnt_time >= plrs[me].stime / TICRATE) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_BAREXP); +#endif + sp_state++; + } + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + s_start_sound(0, SFX_SGCOCK); +#endif + + if (gamemode == commercial) + wi_init_nostate(); + else + wi_init_show_next_loc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + +static void wi_draw_netgame_stats(void) +{ + int i; + int x; + int y; + int pwidth = SHORT(percent->width); + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + wi_draw_lf(); + + /* draw stat titles (top line) */ + + v_draw_patch(NG_STATSX + NG_SPACINGX - SHORT(kills->width), NG_STATSY, + kills); + + v_draw_patch(NG_STATSX + 2 * NG_SPACINGX - SHORT(items->width), NG_STATSY, + items); + + v_draw_patch(NG_STATSX + 3 * NG_SPACINGX - SHORT(secret->width), NG_STATSY, + secret); + + if (dofrags) + { + v_draw_patch(NG_STATSX + 4 * NG_SPACINGX - SHORT(frags->width), + NG_STATSY, frags); + } + + /* draw stats */ + + y = NG_STATSY + SHORT(kills->height); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + + x = NG_STATSX; + v_draw_patch(x - SHORT(p[i]->width), y, p[i]); + + if (i == me) v_draw_patch(x - SHORT(p[i]->width), y, star); + + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_kills[i]); + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_items[i]); + x += NG_SPACINGX; + wi_draw_percent(x - pwidth, y + 10, cnt_secret[i]); + x += NG_SPACINGX; + + if (dofrags) wi_draw_num(x, y + 10, cnt_frags[i], -1); + + y += WI_SPACINGY; + } +} + +static void wi_draw_stats(void) +{ + int lh; /* line height */ + + lh = (3 * SHORT(num[0]->height)) / 2; + + wi_slam_background(); + + /* draw animated background */ + + wi_draw_animated_back(); + + wi_draw_lf(); + + v_draw_patch(SP_STATSX, SP_STATSY, kills); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]); + + v_draw_patch(SP_STATSX, SP_STATSY + lh, items); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY + lh, cnt_items[0]); + + v_draw_patch(SP_STATSX, SP_STATSY + 2 * lh, sp_secret); + wi_draw_percent(SCREENWIDTH - SP_STATSX, SP_STATSY + 2 * lh, + cnt_secret[0]); + + v_draw_patch(SP_TIMEX, SP_TIMEY, timepatch); + wi_draw_time(SCREENWIDTH / 2 - SP_TIMEX, SP_TIMEY, cnt_time); + + if (wbs->epsd < 3) + { + v_draw_patch(SCREENWIDTH / 2 + SP_TIMEX, SP_TIMEY, par); + wi_draw_time(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par); + } +} + +static void wi_check_for_accelerate(void) +{ + int i; + player_t *player; + + /* check for button presses to skip delays */ + + for (i = 0, player = players; i < MAXPLAYERS; i++, player++) + { + if (playeringame[i]) + { + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown) acceleratestage = 1; + player->attackdown = true; + } + else + player->attackdown = false; + if (player->cmd.buttons & BT_USE) + { + if (!player->usedown) acceleratestage = 1; + player->usedown = true; + } + else + player->usedown = false; + } + } +} + +static void wi_init_variables(wbstartstruct_t *wbstartstruct) +{ + wbs = wbstartstruct; + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) wbs->maxkills = 1; + + if (!wbs->maxitems) wbs->maxitems = 1; + + if (!wbs->maxsecret) wbs->maxsecret = 1; + + if (gameversion < exe_ultimate) + { + if (wbs->epsd > 2) wbs->epsd -= 3; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void wi_end(void) +{ + wi_unload_data(); +} + +/* Updates stuff each tick */ + +void wi_ticker(void) +{ + /* counter for general background animation */ + + bcnt++; + + if (bcnt == 1) + { +#ifdef CONFIG_GAMES_NXDOOM_SOUND + /* intermission music */ + + if (gamemode == commercial) + s_change_music(MUS_DM2INT, true); + else + s_change_music(MUS_INTER, true); +#endif + } + + wi_check_for_accelerate(); + + switch (state) + { + case STAT_COUNT: + if (deathmatch) + wi_update_deathmatch_stats(); + else if (netgame) + wi_update_netgame_stats(); + else + wi_update_stats(); + break; + + case SHOW_NEXT_LOC: + wi_update_show_next_loc(); + break; + + case NO_STATE: + wi_update_no_state(); + break; + } +} + +void wi_drawer(void) +{ + switch (state) + { + case STAT_COUNT: + if (deathmatch) + wi_draw_deathmatch_stats(); + else if (netgame) + wi_draw_netgame_stats(); + else + wi_draw_stats(); + break; + + case SHOW_NEXT_LOC: + wi_draw_show_next_loc(); + break; + + case NO_STATE: + wi_draw_no_state(); + break; + } +} + +void wi_start(wbstartstruct_t *wbstartstruct) +{ + wi_init_variables(wbstartstruct); + wi_load_data(); + + if (deathmatch) + wi_init_deathmatch_stats(); + else if (netgame) + wi_init_netgame_stats(); + else + wi_init_stats(); +} diff --git a/games/NXDoom/src/doom/wi_stuff.h b/games/NXDoom/src/doom/wi_stuff.h new file mode 100644 index 000000000..78c689d78 --- /dev/null +++ b/games/NXDoom/src/doom/wi_stuff.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doom/wi_stuff.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Intermission. + * + ****************************************************************************/ + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomdef.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: wi_ticker + * + * Description: + * Called by main loop, animate the intermission. + * + ****************************************************************************/ + +void wi_ticker(void); + +/**************************************************************************** + * Name: wi_drawer + * + * Description: + * Called by main loop, draws the intermission directly into the screen + * buffer. + * + ****************************************************************************/ + +void wi_drawer(void); + +/**************************************************************************** + * Name: wi_start + * + * Description: + * Setup for an intermission screen. + * + ****************************************************************************/ + +void wi_start(wbstartstruct_t *wbstartstruct); + +/**************************************************************************** + * Name: wi_end + * + * Description: + * Shut down the intermission screen + * + ****************************************************************************/ + +void wi_end(void); + +#endif /* __WI_STUFF__ */ diff --git a/games/NXDoom/src/doomkeys.h b/games/NXDoom/src/doomkeys.h new file mode 100644 index 000000000..f29a2b782 --- /dev/null +++ b/games/NXDoom/src/doomkeys.h @@ -0,0 +1,302 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doomkeys.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Key definitions + * + * + ****************************************************************************/ + +#ifndef __DOOMKEYS__ +#define __DOOMKEYS__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* DOOM keyboard definition. + * This is the stuff configured by Setup.Exe. + * Most key data are simple ascii (uppercased). + */ + +#define KEY_RIGHTARROW 0xae +#define KEY_LEFTARROW 0xac +#define KEY_UPARROW 0xad +#define KEY_DOWNARROW 0xaf +#define KEY_ESCAPE 27 +#define KEY_ENTER 13 +#define KEY_TAB 9 +#define KEY_F1 (0x80 + 0x3b) +#define KEY_F2 (0x80 + 0x3c) +#define KEY_F3 (0x80 + 0x3d) +#define KEY_F4 (0x80 + 0x3e) +#define KEY_F5 (0x80 + 0x3f) +#define KEY_F6 (0x80 + 0x40) +#define KEY_F7 (0x80 + 0x41) +#define KEY_F8 (0x80 + 0x42) +#define KEY_F9 (0x80 + 0x43) +#define KEY_F10 (0x80 + 0x44) +#define KEY_F11 (0x80 + 0x57) +#define KEY_F12 (0x80 + 0x58) + +#define KEY_BACKSPACE 0x7f +#define KEY_PAUSE 0xff + +#define KEY_EQUALS 0x3d +#define KEY_MINUS 0x2d + +#define KEY_RSHIFT (0x80 + 0x36) +#define KEY_RCTRL (0x80 + 0x1d) +#define KEY_RALT (0x80 + 0x38) + +#define KEY_LALT KEY_RALT + +/* new keys: */ + +#define KEY_CAPSLOCK (0x80 + 0x3a) +#define KEY_NUMLOCK (0x80 + 0x45) +#define KEY_SCRLCK (0x80 + 0x46) +#define KEY_PRTSCR (0x80 + 0x59) + +#define KEY_HOME (0x80 + 0x47) +#define KEY_END (0x80 + 0x4f) +#define KEY_PGUP (0x80 + 0x49) +#define KEY_PGDN (0x80 + 0x51) +#define KEY_INS (0x80 + 0x52) +#define KEY_DEL (0x80 + 0x53) + +#define KEYP_0 KEY_INS +#define KEYP_1 KEY_END +#define KEYP_2 KEY_DOWNARROW +#define KEYP_3 KEY_PGDN +#define KEYP_4 KEY_LEFTARROW +#define KEYP_5 (0x80 + 0x4c) +#define KEYP_6 KEY_RIGHTARROW +#define KEYP_7 KEY_HOME +#define KEYP_8 KEY_UPARROW +#define KEYP_9 KEY_PGUP + +#define KEYP_DIVIDE '/' +#define KEYP_PLUS '+' +#define KEYP_MINUS '-' +#define KEYP_MULTIPLY '*' +#define KEYP_PERIOD 0 +#define KEYP_EQUALS KEY_EQUALS +#define KEYP_ENTER KEY_ENTER + +#define KEY_NONUSBACKSLASH '\\' + +#define SCANCODE_TO_KEYS_ARRAY \ + { \ + 0, \ + 0, \ + 0, \ + 0, \ + 'a', /* 0-9 */ \ + 'b', \ + 'c', \ + 'd', \ + 'e', \ + 'f', \ + 'g', \ + 'h', \ + 'i', \ + 'j', \ + 'k', /* 10-19 */ \ + 'l', \ + 'm', \ + 'n', \ + 'o', \ + 'p', \ + 'q', \ + 'r', \ + 's', \ + 't', \ + 'u', /* 20-29 */ \ + 'v', \ + 'w', \ + 'x', \ + 'y', \ + 'z', \ + '1', \ + '2', \ + '3', \ + '4', \ + '5', /* 30-39 */ \ + '6', \ + '7', \ + '8', \ + '9', \ + '0', \ + KEY_ENTER, \ + KEY_ESCAPE, \ + KEY_BACKSPACE, \ + KEY_TAB, \ + ' ', /* 40-49 */ \ + KEY_MINUS, \ + KEY_EQUALS, \ + '[', \ + ']', \ + '\\', \ + 0, \ + ';', \ + '\'', \ + '`', \ + ',', /* 50-59 */ \ + '.', \ + '/', \ + KEY_CAPSLOCK, \ + KEY_F1, \ + KEY_F2, \ + KEY_F3, \ + KEY_F4, \ + KEY_F5, \ + KEY_F6, \ + KEY_F7, /* 60-69 */ \ + KEY_F8, \ + KEY_F9, \ + KEY_F10, \ + KEY_F11, \ + KEY_F12, \ + KEY_PRTSCR, \ + KEY_SCRLCK, \ + KEY_PAUSE, \ + KEY_INS, \ + KEY_HOME, /* 70-79 */ \ + KEY_PGUP, \ + KEY_DEL, \ + KEY_END, \ + KEY_PGDN, \ + KEY_RIGHTARROW, \ + KEY_LEFTARROW, \ + KEY_DOWNARROW, \ + KEY_UPARROW, /* 80-89 */ \ + KEY_NUMLOCK, \ + KEYP_DIVIDE, \ + KEYP_MULTIPLY, \ + KEYP_MINUS, \ + KEYP_PLUS, \ + KEYP_ENTER, \ + KEYP_1, \ + KEYP_2, \ + KEYP_3, \ + KEYP_4, \ + KEYP_5, \ + KEYP_6, /* 90-99 */ \ + KEYP_7, \ + KEYP_8, \ + KEYP_9, \ + KEYP_0, \ + KEYP_PERIOD, \ + KEY_NONUSBACKSLASH, \ + 0, \ + 0, \ + KEYP_EQUALS, /* 100-103 */ \ + } + +/* Default names for keys, to use in English or as fallback. */ + +#define KEY_NAMES_ARRAY \ + { \ + {KEY_BACKSPACE, "BACKSP"}, \ + {KEY_TAB, "TAB"}, \ + {KEY_INS, "INS"}, \ + {KEY_DEL, "DEL"}, \ + {KEY_PGUP, "PGUP"}, \ + {KEY_PGDN, "PGDN"}, \ + {KEY_ENTER, "ENTER"}, \ + {KEY_ESCAPE, "ESC"}, \ + {KEY_F1, "F1"}, \ + {KEY_F2, "F2"}, \ + {KEY_F3, "F3"}, \ + {KEY_F4, "F4"}, \ + {KEY_F5, "F5"}, \ + {KEY_F6, "F6"}, \ + {KEY_F7, "F7"}, \ + {KEY_F8, "F8"}, \ + {KEY_F9, "F9"}, \ + {KEY_F10, "F10"}, \ + {KEY_F11, "F11"}, \ + {KEY_F12, "F12"}, \ + {KEY_HOME, "HOME"}, \ + {KEY_END, "END"}, \ + {KEY_MINUS, "-"}, \ + {KEY_EQUALS, "="}, \ + {KEY_NUMLOCK, "NUMLCK"}, \ + {KEY_SCRLCK, "SCRLCK"}, \ + {KEY_PAUSE, "PAUSE"}, \ + {KEY_PRTSCR, "PRTSC"}, \ + {KEY_UPARROW, "UP"}, \ + {KEY_DOWNARROW, "DOWN"}, \ + {KEY_LEFTARROW, "LEFT"}, \ + {KEY_RIGHTARROW, "RIGHT"}, \ + {KEY_RALT, "ALT"}, \ + {KEY_LALT, "ALT"}, \ + {KEY_RSHIFT, "SHIFT"}, \ + {KEY_CAPSLOCK, "CAPS"}, \ + {KEY_RCTRL, "CTRL"}, \ + {KEYP_5, "NUM5"}, \ + {' ', "SPACE"}, \ + {'a', "A"}, \ + {'b', "B"}, \ + {'c', "C"}, \ + {'d', "D"}, \ + {'e', "E"}, \ + {'f', "F"}, \ + {'g', "G"}, \ + {'h', "H"}, \ + {'i', "I"}, \ + {'j', "J"}, \ + {'k', "K"}, \ + {'l', "L"}, \ + {'m', "M"}, \ + {'n', "N"}, \ + {'o', "O"}, \ + {'p', "P"}, \ + {'q', "Q"}, \ + {'r', "R"}, \ + {'s', "S"}, \ + {'t', "T"}, \ + {'u', "U"}, \ + {'v', "V"}, \ + {'w', "W"}, \ + {'x', "X"}, \ + {'y', "Y"}, \ + {'z', "Z"}, \ + {'0', "0"}, \ + {'1', "1"}, \ + {'2', "2"}, \ + {'3', "3"}, \ + {'4', "4"}, \ + {'5', "5"}, \ + {'6', "6"}, \ + {'7', "7"}, \ + {'8', "8"}, \ + {'9', "9"}, \ + {'[', "["}, \ + {']', "]"}, \ + {';', ";"}, \ + {'`', "`"}, \ + {',', ","}, \ + {'.', "."}, \ + {'/', "/"}, \ + {'\\', "\\"}, \ + {'\'', "\'"}, \ + } + +#endif /* __DOOMKEYS__ */ diff --git a/games/NXDoom/src/doomtype.h b/games/NXDoom/src/doomtype.h new file mode 100644 index 000000000..2029723ad --- /dev/null +++ b/games/NXDoom/src/doomtype.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * apps/games/NXDoom/src/doomtype.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Simple basic typedefs, isolated here to make it easier + * separating modules. + * + ****************************************************************************/ + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/* What is really wanted here is stdint.h; however, some old versions + * of Solaris don't have stdint.h and only have inttypes.h (the + * pre-standardisation version). inttypes.h is also in the C99 + * standard and defined to include stdint.h, so include this. + */ + +#include +#include + +#include + +#include "config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DIR_SEPARATOR '/' +#define DIR_SEPARATOR_S "/" +#define PATH_SEPARATOR ':' + +#define arrlen(array) (sizeof(array) / sizeof(*array)) + +/* The packed attribute forces structures to be packed into the minimum + * space necessary. If this is not done, the compiler may align structure + * fields differently to optimize memory access, inflating the overall + * structure size. It is important to use the packed attribute on certain + * structures where alignment is important, particularly data read/written + * to disk. + */ + +#ifdef __GNUC__ + +#define PACKEDATTR __attribute__((packed)) + +#define PRINTF_ATTR(fmt, first) __attribute__((format(printf, fmt, first))) +#define PRINTF_ARG_ATTR(x) __attribute__((format_arg(x))) +#define NORETURN __attribute__((noreturn)) + +#else +#if defined(_MSC_VER) +#define PACKEDATTR __pragma(pack(pop)) +#else +#define PACKEDATTR +#endif +#define PRINTF_ATTR(fmt, first) +#define PRINTF_ARG_ATTR(x) +#define NORETURN +#endif /* __GNUC__ */ + +#define PACKEDPREFIX + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* C99 integer types; with gcc we just use this. Other compilers + * should add conditional statements that define the C99 types. + */ + +#if defined(__cplusplus) || defined(__bool_true_false_are_defined) + +/* The C++/C99 bool type (or _Bool that is) can only have two values: + * 0 or 1. However, the Doom source code assumes any non-zero value + * to evaluate to true, so we have to use an int type here. + */ + +typedef int boolean; + +#else + +typedef enum +{ + false, + true +} boolean; + +#endif + +typedef uint8_t byte; +typedef uint8_t pixel_t; +typedef int16_t dpixel_t; + +#endif /* __DOOMTYPE__ */ diff --git a/games/NXDoom/src/gusconf.c b/games/NXDoom/src/gusconf.c new file mode 100644 index 000000000..01f6606c5 --- /dev/null +++ b/games/NXDoom/src/gusconf.c @@ -0,0 +1,337 @@ +/**************************************************************************** + * apps/games/NXDoom/src/gusconf.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * GUS emulation code. + * + * Actually emulating a GUS is far too much work; fortunately + * GUS "emulation" already exists in the form of Timidity, which + * supports GUS patch files. This code therefore converts Doom's + * DMXGUS lump into an equivalent Timidity configuration file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_INSTRUMENTS 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *patch_names[MAX_INSTRUMENTS]; + int used[MAX_INSTRUMENTS]; + int mapping[MAX_INSTRUMENTS]; + unsigned int count; +} gus_config_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *gus_patch_path = ""; +int gus_ram_kb = 1024; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static unsigned int mapping_index(void) +{ + unsigned int result = gus_ram_kb / 256; + + if (result < 1) + { + return 1; + } + else if (result > 4) + { + return 4; + } + else + { + return result; + } +} + +static int split_line(char *line, char **fields, unsigned int max_fields) +{ + unsigned int num_fields; + char *p; + + fields[0] = line; + num_fields = 1; + + for (p = line; *p != '\0'; ++p) + { + if (*p == ',') + { + *p = '\0'; + + /* Skip spaces following the comma. */ + + do + { + ++p; + } + while (*p != '\0' && isspace(*p)); + + fields[num_fields] = p; + ++num_fields; + --p; + + if (num_fields >= max_fields) + { + break; + } + } + else if (*p == '#') + { + *p = '\0'; + break; + } + } + + /* Strip off trailing whitespace from the end of the line. */ + + p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); + while (p > fields[num_fields - 1] && isspace(*(p - 1))) + { + --p; + *p = '\0'; + } + + return num_fields; +} + +static void parse_line(gus_config_t *config, char *line) +{ + char *fields[6]; + unsigned int i; + unsigned int num_fields; + unsigned int instr_id; + unsigned int mapped_id; + + num_fields = split_line(line, fields, 6); + + if (num_fields < 6) + { + return; + } + + instr_id = atoi(fields[0]); + + /* Skip non GM percussions. */ + + if ((instr_id >= 128 && instr_id < 128 + 35) || instr_id > 128 + 81) + { + return; + } + + mapped_id = atoi(fields[mapping_index()]); + + for (i = 0; i < config->count; i++) + { + if (config->used[i] == mapped_id) + { + break; + } + } + + if (i == config->count) + { + /* DMX uses wrong patch name (we should use name of 'mapped_id' + * instrument, but DMX uses name of 'instr_id' instead). + */ + + free(config->patch_names[i]); + config->patch_names[i] = m_string_duplicate(fields[5]); + config->used[i] = mapped_id; + config->count++; + } + + config->mapping[instr_id] = i; +} + +static void parse_dmx_config(char *dmxconf, gus_config_t *config) +{ + char *p; + char *newline; + unsigned int i; + + memset(config, 0, sizeof(gus_config_t)); + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + config->mapping[i] = -1; + config->used[i] = -1; + } + + config->count = 0; + + p = dmxconf; + + for (; ; ) + { + newline = strchr(p, '\n'); + + if (newline != NULL) + { + *newline = '\0'; + } + + parse_line(config, p); + + if (newline == NULL) + { + break; + } + else + { + p = newline + 1; + } + } +} + +static void free_dmx_config(gus_config_t *config) +{ + unsigned int i; + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + free(config->patch_names[i]); + } +} + +static char *read_dmx_config(void) +{ + int lumpnum; + unsigned int len; + char *data; + + /* TODO: This should be chosen based on gamemode == commercial: */ + + lumpnum = w_check_num_for_name("DMXGUS"); + + if (lumpnum < 0) + { + lumpnum = w_get_num_for_name("DMXGUSC"); + } + + len = w_lump_length(lumpnum); + data = z_malloc(len + 1, PU_STATIC, NULL); + w_read_lump(lumpnum, data); + + data[len] = '\0'; + return data; +} + +static boolean write_timidity_config(char *path, gus_config_t *config) +{ + FILE *fstream; + unsigned int i; + + fstream = fopen(path, "w"); + + if (fstream == NULL) + { + return false; + } + + fprintf(fstream, "# Autogenerated Timidity config.\n\n"); + + fprintf(fstream, "dir \"%s\"\n", gus_patch_path); + + fprintf(fstream, "\nbank 0\n\n"); + + for (i = 0; i < 128; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && + config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%u %s\n", i, + config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\ndrumset 0\n\n"); + + for (i = 128 + 35; i <= 128 + 81; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS && + config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%u %s\n", i - 128, + config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\n"); + + fclose(fstream); + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +boolean gus_write_config(char *path) +{ + boolean result; + char *dmxconf; + gus_config_t config; + + if (!strcmp(gus_patch_path, "")) + { + printf("You haven't configured gus_patch_path.\n"); + printf("gus_patch_path needs to point to the location of " + "your GUS patch set.\n" + "To get a copy of the \"standard\" GUS patches, " + "download a copy of dgguspat.zip.\n"); + + return false; + } + + dmxconf = read_dmx_config(); + parse_dmx_config(dmxconf, &config); + + result = write_timidity_config(path, &config); + + free_dmx_config(&config); + z_free(dmxconf); + + return result; +} diff --git a/games/NXDoom/src/gusconf.h b/games/NXDoom/src/gusconf.h new file mode 100644 index 000000000..aef259d5e --- /dev/null +++ b/games/NXDoom/src/gusconf.h @@ -0,0 +1,45 @@ +/**************************************************************************** + * apps/games/NXDoom/src/gusconf.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * GUS emulation code. + * + ****************************************************************************/ + +#ifndef __GUSCONF_H__ +#define __GUSCONF_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern char *gus_patch_path; +extern int gus_ram_kb; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean gus_write_config(char *path); + +#endif /* __GUSCONF_H__ */ diff --git a/games/NXDoom/src/i_endoom.c b/games/NXDoom/src/i_endoom.c new file mode 100644 index 000000000..97a797ee8 --- /dev/null +++ b/games/NXDoom/src/i_endoom.c @@ -0,0 +1,99 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_endoom.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Exit text-mode ENDOOM screen. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "config.h" +#include "doomtype.h" +#include "i_video.h" + +#include "txt_main.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ENDOOM_W 80 +#define ENDOOM_H 25 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i_endoom + * + * Description: + * Displays the text mode ending screen after the game quits + * + ****************************************************************************/ + +void i_endoom(byte *endoom_data) +{ + unsigned char *screendata; + int y; + int indent; + + /* Set up text mode screen */ + + txt_init(); + + txt_set_window_title(PACKAGE_STRING); + + /* SDL2-TODO i_init_window_title(); + */ + + /* Write the data to the screen memory */ + + screendata = txt_get_screen_data(); + + indent = (ENDOOM_W - TXT_SCREEN_W) / 2; + + for (y = 0; y < TXT_SCREEN_H; ++y) + { + memcpy(screendata + (y * TXT_SCREEN_W * 2), + endoom_data + (y * ENDOOM_W + indent) * 2, TXT_SCREEN_W * 2); + } + + /* Wait for a keypress */ + + while (true) + { + txt_update_screen(); + + if (txt_getchar() > 0) + { + break; + } + + txt_sleep(0); + } + + /* Shut down text mode screen */ + + txt_shutdown(); +} diff --git a/games/NXDoom/src/i_endoom.h b/games/NXDoom/src/i_endoom.h new file mode 100644 index 000000000..22a773dc4 --- /dev/null +++ b/games/NXDoom/src/i_endoom.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_endoom.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Exit text-mode ENDOOM screen. + * + ****************************************************************************/ + +#ifndef __I_ENDOOM__ +#define __I_ENDOOM__ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_ENDOOM + +/* Display the Endoom screen on shutdown. Pass a pointer to the + * ENDOOM lump. + */ + +void i_endoom(byte *data); +#endif + +#endif /* __I_ENDOOM__ */ diff --git a/games/NXDoom/src/i_glob.c b/games/NXDoom/src/i_glob.c new file mode 100644 index 000000000..c60158937 --- /dev/null +++ b/games/NXDoom/src/i_glob.c @@ -0,0 +1,397 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_glob.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2018 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * File globbing API. This allows the contents of the filesystem + * to be interrogated. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" +#include "i_glob.h" +#include "m_misc.h" + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct glob_s +{ + char **globs; + int num_globs; + int flags; + DIR *dir; + char *directory; + char *last_filename; + + /* These fields are only used when the GLOB_FLAG_SORTED flag is set: + */ + + char **filenames; + int filenames_len; + int next_index; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Only the fields d_name and (as an XSI extension) d_ino are specified + * in POSIX.1. Other than Linux, the d_type field is available mainly + * only on BSD systems. The remaining fields are available on many, but + * not all systems. + */ + +static boolean is_directory(char *dir, struct dirent *de) +{ +#if defined(_DIRENT_HAVE_D_TYPE) + if (de->d_type != DT_UNKNOWN && de->d_type != DT_LNK) + { + return de->d_type == DT_DIR; + } + else +#endif + { + char *filename; + struct stat sb; + int result; + + filename = m_string_join(dir, DIR_SEPARATOR_S, de->d_name, NULL); + result = stat(filename, &sb); + free(filename); + + if (result != 0) + { + return false; + } + + return S_ISDIR(sb.st_mode); + } +} + +static void free_string_list(char **globs, int num_globs) +{ + int i; + for (i = 0; i < num_globs; ++i) + { + free(globs[i]); + } + + free(globs); +} + +static boolean matches_glob(const char *name, const char *glob, int flags) +{ + int n; + int g; + + while (*glob != '\0') + { + n = *name; + g = *glob; + + if ((flags & GLOB_FLAG_NOCASE) != 0) + { + n = tolower(n); + g = tolower(g); + } + + if (g == '*') + { + /* To handle *-matching we skip past the * and recurse + * to check each subsequent character in turn. If none + * match then the whole match is a failure. + */ + + while (*name != '\0') + { + if (matches_glob(name, glob + 1, flags)) + { + return true; + } + + ++name; + } + + return glob[1] == '\0'; + } + else if (g != '?' && n != g) + { + /* For normal characters the name must match the glob, + * but for ? we don't care what the character is. + */ + + return false; + } + + ++name; + ++glob; + } + + /* Match successful when glob and name end at the same time. */ + + return *name == '\0'; +} + +static boolean matches_any_glob(const char *name, glob_t *glob) +{ + int i; + + for (i = 0; i < glob->num_globs; ++i) + { + if (matches_glob(name, glob->globs[i], glob->flags)) + { + return true; + } + } + + return false; +} + +static char *next_glob(glob_t *glob) +{ + struct dirent *de; + + do + { + de = readdir(glob->dir); + if (de == NULL) + { + return NULL; + } + } + while (is_directory(glob->directory, de) || + !matches_any_glob(de->d_name, glob)); + + /* Return the fully-qualified path, not just the bare filename. */ + + return m_string_join(glob->directory, DIR_SEPARATOR_S, de->d_name, NULL); +} + +static void read_all_filenames(glob_t *glob) +{ + char *name; + + glob->filenames = NULL; + glob->filenames_len = 0; + glob->next_index = 0; + + for (; ; ) + { + name = next_glob(glob); + if (name == NULL) + { + break; + } + + glob->filenames = realloc(glob->filenames, + (glob->filenames_len + 1) * sizeof(char *)); + glob->filenames[glob->filenames_len] = name; + ++glob->filenames_len; + } +} + +static void sort_filenames(char **filenames, int len, int flags) +{ + char *pivot; + char *tmp; + int i; + int left_len; + int cmp; + + if (len <= 1) + { + return; + } + + pivot = filenames[len - 1]; + left_len = 0; + for (i = 0; i < len - 1; ++i) + { + if ((flags & GLOB_FLAG_NOCASE) != 0) + { + cmp = strcasecmp(filenames[i], pivot); + } + else + { + cmp = strcmp(filenames[i], pivot); + } + + if (cmp < 0) + { + tmp = filenames[i]; + filenames[i] = filenames[left_len]; + filenames[left_len] = tmp; + ++left_len; + } + } + + filenames[len - 1] = filenames[left_len]; + filenames[left_len] = pivot; + + sort_filenames(filenames, left_len, flags); + sort_filenames(&filenames[left_len + 1], len - left_len - 1, flags); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +glob_t *i_start_multi_glob(const char *directory, int flags, + const char *glob, ...) +{ + char **globs; + int num_globs; + glob_t *result; + va_list args; + + globs = malloc(sizeof(char *)); + if (globs == NULL) + { + return NULL; + } + + globs[0] = m_string_duplicate(glob); + num_globs = 1; + + va_start(args, glob); + for (; ; ) + { + const char *arg = va_arg(args, const char *); + char **new_globs; + + if (arg == NULL) + { + break; + } + + new_globs = realloc(globs, sizeof(char *) * (num_globs + 1)); + if (new_globs == NULL) + { + free_string_list(globs, num_globs); + } + + globs = new_globs; + globs[num_globs] = m_string_duplicate(arg); + ++num_globs; + } + + va_end(args); + + result = malloc(sizeof(glob_t)); + if (result == NULL) + { + free_string_list(globs, num_globs); + return NULL; + } + + result->dir = opendir(directory); + if (result->dir == NULL) + { + free_string_list(globs, num_globs); + free(result); + return NULL; + } + + result->directory = m_string_duplicate(directory); + result->globs = globs; + result->num_globs = num_globs; + result->flags = flags; + result->last_filename = NULL; + result->filenames = NULL; + result->filenames_len = 0; + result->next_index = -1; + return result; +} + +glob_t *i_start_glob(const char *directory, const char *glob, int flags) +{ + return i_start_multi_glob(directory, flags, glob, NULL); +} + +void i_end_glob(glob_t *glob) +{ + if (glob == NULL) + { + return; + } + + free_string_list(glob->globs, glob->num_globs); + free_string_list(glob->filenames, glob->filenames_len); + + free(glob->directory); + free(glob->last_filename); + (void)closedir(glob->dir); + free(glob); +} + +const char *i_next_glob(glob_t *glob) +{ + const char *result; + + if (glob == NULL) + { + return NULL; + } + + /* In unsorted mode we just return the filenames as we read + * them back from the system API. + */ + + if ((glob->flags & GLOB_FLAG_SORTED) == 0) + { + free(glob->last_filename); + glob->last_filename = next_glob(glob); + return glob->last_filename; + } + + /* In sorted mode we read the whole list of filenames into memory, + * sort them and return them one at a time. + */ + + if (glob->next_index < 0) + { + read_all_filenames(glob); + sort_filenames(glob->filenames, glob->filenames_len, glob->flags); + } + + if (glob->next_index >= glob->filenames_len) + { + return NULL; + } + + result = glob->filenames[glob->next_index]; + ++glob->next_index; + return result; +} diff --git a/games/NXDoom/src/i_glob.h b/games/NXDoom/src/i_glob.h new file mode 100644 index 000000000..a151795ad --- /dev/null +++ b/games/NXDoom/src/i_glob.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_glob.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2018 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific file globbing interface. + * + ****************************************************************************/ + +#ifndef __I_GLOB__ +#define __I_GLOB__ + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define GLOB_FLAG_NOCASE 0x01 +#define GLOB_FLAG_SORTED 0x02 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct glob_s glob_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_start_glob + * + * Description: + * Start reading a list of file paths from the given directory which match + * the given glob pattern. i_end_glob() must be called on completion. + * + ****************************************************************************/ + +glob_t *i_start_glob(const char *directory, const char *glob, int flags); + +/**************************************************************************** + * Name: i_start_multi_glob + * + * Description: + * Same as i_start_glob but multiple glob patterns can be provided. The list + * of patterns must be terminated with NULL. + * + ****************************************************************************/ + +glob_t *i_start_multi_glob(const char *directory, int flags, + const char *glob, ...); + +/**************************************************************************** + * Name: i_end_glob + * + * Description: + * Finish reading file list. + * + ****************************************************************************/ + +void i_end_glob(glob_t *glob); + +/**************************************************************************** + * Name: i_next_glob + * + * Description: + * Read the name of the next globbed filename. NULL is returned if there + * are no more found. + * + ****************************************************************************/ + +const char *i_next_glob(glob_t *glob); + +#endif /* __I_GLOB__ */ diff --git a/games/NXDoom/src/i_input.c b/games/NXDoom/src/i_input.c new file mode 100644 index 000000000..a7e61b8f8 --- /dev/null +++ b/games/NXDoom/src/i_input.c @@ -0,0 +1,522 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_input.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * SDL implementation of system-specific input interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +#include +#endif + +#include "d_event.h" +#include "doomkeys.h" +#include "doomtype.h" +#include "i_input.h" +#include "m_argv.h" +#include "m_config.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct keyboard_dev +{ + int fd; + bool inited; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +struct keyboard_dev g_kbd_dev = +{ + .fd = -1, + .inited = false, +}; +#endif + +#if 0 +static const int g_scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; + +/* Lookup table for mapping ASCII characters to their equivalent when + * shift is pressed on a US layout keyboard. This is the original table + * as found in the Doom sources, comments and all. + */ + +static const char shiftxform[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, ' ', '!', '"', '#', '$', '%', '&', + '"', /* shift-' */ + '(', ')', '*', '+', + '<', /* shift-, */ + '_', /* shift-- */ + '>', /* shift-. */ + '?', /* shift-/ */ + ')', /* shift-0 */ + '!', /* shift-1 */ + '@', /* shift-2 */ + '#', /* shift-3 */ + '$', /* shift-4 */ + '%', /* shift-5 */ + '^', /* shift-6 */ + '&', /* shift-7 */ + '*', /* shift-8 */ + '(', /* shift-9 */ + ':', + ':', /* shift-; */ + '<', + '+', /* shift-= */ + '>', '?', '@', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '[', /* shift-[ */ + '!', /* shift-backslash - OH MY GOD DOES WATCOM SUCK */ + ']', /* shift-] */ + '"', '_', + '\'', /* shift-` */ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '{', '|', '}', '~', 127 +}; + +/* If true, i_start_text_input() has been called, and we are populating + * the data3 field of ev_keydown events. + */ + +static boolean text_input_enabled = true; + +/* Bit mask of mouse button state. */ + +static unsigned int mouse_button_state = 0; +#endif + +/* Disallow mouse and joystick movement to cause forward/backward + * motion. Specified with the '-novert' command line parameter. + * This is an int to allow saving to config file + */ + +int novert = 0; + +/* If true, keyboard mapping is ignored, like in Vanilla Doom. + * The sensible thing to do is to disable this if you have a non-US + * keyboard. + */ + +int vanilla_keyboard_mapping = true; + +/* Mouse acceleration + * + * This emulates some of the behavior of DOS mouse drivers by increasing + * the speed when the mouse is moved fast. + * + * The mouse input values are input directly to the game, but when + * the values exceed the value of mouse_threshold, they are multiplied + * by mouse_acceleration to increase the speed. + */ + +float mouse_acceleration = 2.0; +int mouse_threshold = 10; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: init_kbd_dev + * + * Description: + * Set up the keyboard device for getting keyboard events. + * + * Return: + * 0 on success, error code otherwise. + * + ****************************************************************************/ + +static int init_kbd_dev(struct keyboard_dev *dev) +{ + if (dev->inited) + { + return 0; + } + + dev->fd = open(CONFIG_GAMES_NXDOOM_KBDPATH, O_RDONLY | O_NONBLOCK); + if (dev->fd < 0) + { + return errno; + } + + dev->inited = true; + return 0; +} + +/**************************************************************************** + * Name: translate_key + * + * Description: + * Translates a NuttX key code into one from doomkeys.h. + * + * Return: + * The translated key code. + * + ****************************************************************************/ + +static int translate_key(uint32_t keycode) +{ + /* A keyboard driver reports Enter as the character that it produces, so + * it never arrives as KEYCODE_ENTER. The game binds the menu to + * KEY_ENTER, which is the carriage return, so the line feed that a + * driver actually sends has to be folded onto it. + */ + + if (keycode == '\n') + { + return KEY_ENTER; + } + + return keycode; +} + +/**************************************************************************** + * Name: translate_speckey + * + * Description: + * Translates a value from enum kbd_keycode_e into one from doomkeys.h. + * + * A special key arrives with a KEYBOARD_SPECPRESS or KEYBOARD_SPECREL + * event rather than as a character: the keycodes overlap the character + * range, so the event type is what tells an arrow key from the character + * that shares its value. + * + * Return: + * The translated key code, or zero if the key has no meaning to the game. + * + ****************************************************************************/ + +static int translate_speckey(uint32_t keycode) +{ + switch (keycode) + { + case KEYCODE_LEFT: + return KEY_LEFTARROW; + case KEYCODE_RIGHT: + return KEY_RIGHTARROW; + case KEYCODE_UP: + return KEY_UPARROW; + case KEYCODE_DOWN: + return KEY_DOWNARROW; + + /* Drivers disagree on Enter: a USB HID keyboard reports the line feed + * that the key produces, while the simulator reports KEYCODE_ENTER. + * Both have to land on KEY_ENTER, so both are handled, here and in + * translate_key(). + */ + + case KEYCODE_ENTER: + return KEY_ENTER; + + case KEYCODE_BACKDEL: + return KEY_BACKSPACE; + case KEYCODE_FWDDEL: + return KEY_DEL; + case KEYCODE_INSERT: + return KEY_INS; + case KEYCODE_HOME: + return KEY_HOME; + case KEYCODE_END: + return KEY_END; + case KEYCODE_PAGEUP: + return KEY_PGUP; + case KEYCODE_PAGEDOWN: + return KEY_PGDN; + case KEYCODE_PAUSE: + return KEY_PAUSE; + case KEYCODE_CAPSLOCK: + return KEY_CAPSLOCK; + + /* Fire, run and strafe. The game binds these by default, so a + * keyboard that does not report its modifiers cannot play it. + */ + + case KEYCODE_LCTRL: + case KEYCODE_RCTRL: + return KEY_RCTRL; + case KEYCODE_LSHIFT: + case KEYCODE_RSHIFT: + return KEY_RSHIFT; + case KEYCODE_LALT: + case KEYCODE_RALT: + return KEY_RALT; + + case KEYCODE_F1: + return KEY_F1; + case KEYCODE_F2: + return KEY_F2; + case KEYCODE_F3: + return KEY_F3; + case KEYCODE_F4: + return KEY_F4; + case KEYCODE_F5: + return KEY_F5; + case KEYCODE_F6: + return KEY_F6; + case KEYCODE_F7: + return KEY_F7; + case KEYCODE_F8: + return KEY_F8; + case KEYCODE_F9: + return KEY_F9; + case KEYCODE_F10: + return KEY_F10; + case KEYCODE_F11: + return KEY_F11; + case KEYCODE_F12: + return KEY_F12; + + default: + return 0; + } +} + +/**************************************************************************** + * Name: get_localized_key + * + * Description: + * Get the localized version of the key press. This takes into account the + * keyboard layout, but does not apply any changes due to modifiers, (eg. + * shift-, alt-, etc.) + * + ****************************************************************************/ + +static int get_localized_key(uint32_t sym) +{ + /* NOTE: Argument was SDL_Keysym *sym */ + + /* When using Vanilla mapping, we just base everything off the scancode + * and always pretend the user is using a US layout keyboard. + */ + + if (vanilla_keyboard_mapping) + { + return translate_key(sym); + } + else + { + int result = sym; + + if (result < 0 || result >= 128) + { + result = 0; + } + + return result; + } +} + +/**************************************************************************** + * Name: get_typed_char + * + * Description: + * Get the equivalent ASCII (Unicode?) character for a keypress. + * + ****************************************************************************/ + +static int get_typed_char(uint32_t sym) +{ + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD + +/**************************************************************************** + * Name: get_kbd_event + * + * Description: + * Read a single keyboard event from the keyboard device. + * + * Return: + * 0 on success, error code otherwise. + * + ****************************************************************************/ + +int get_kbd_event(struct keyboard_event_s *sample) +{ + int err; + ssize_t nbytes; + + /* Initialize the keyboard device if it isn't already */ + + err = init_kbd_dev(&g_kbd_dev); + if (err) + { + return err; + } + + /* Read events until we're out of them */ + + nbytes = read(g_kbd_dev.fd, sample, sizeof(*sample)); + if (nbytes < 0) + { + err = errno; + if (err != EINTR) + { + return err; + } + } + else if (nbytes != sizeof(*sample)) + { + return EIO; + } + else if (nbytes == 0) + { + return EAGAIN; /* No event */ + } + + return 0; +} +#endif + +void i_handle_keyboard_event(struct keyboard_event_s *kevent) +{ + /* XXX: passing pointers to event for access after this function + * has terminated is undefined behaviour + */ + + event_t event; + bool press; + + switch (kevent->type) + { + case KEYBOARD_PRESS: + case KEYBOARD_RELEASE: + press = (kevent->type == KEYBOARD_PRESS); + event.data1 = translate_key(kevent->code); + break; + + case KEYBOARD_SPECPRESS: + case KEYBOARD_SPECREL: + press = (kevent->type == KEYBOARD_SPECPRESS); + event.data1 = translate_speckey(kevent->code); + break; + + default: + return; + } + + if (event.data1 == 0) + { + return; + } + + if (press) + { + event.type = ev_keydown; + + /* A special key has no printable character, so it contributes + * nothing to data2 and data3. + */ + + if (kevent->type == KEYBOARD_PRESS) + { + event.data2 = get_localized_key(kevent->code); + event.data3 = get_typed_char(kevent->code); + } + else + { + event.data2 = 0; + event.data3 = 0; + } + } + else + { + event.type = ev_keyup; + + /* data2/data3 are initialized to zero for ev_keyup. + * For ev_keydown it's the shifted Unicode character + * that was typed, but if something wants to detect + * key releases it should do so based on data1 + * (key ID), not the printable char. + */ + + event.data2 = 0; + event.data3 = 0; + } + + d_post_event(&event); +} + +void i_start_text_input(int x1, int y1, int x2, int y2) +{ +} + +void i_stop_text_input(void) +{ +} + +void i_handle_mouse_event(void) +{ + /* Argument was SDL_Event *sdlevent */ +} + +/**************************************************************************** + * Name: i_read_mouse + * + * Description: + * Read the change in mouse state to generate mouse motion events. This is + * to combine all mouse movement for a tic into one mouse motion event. + * + ****************************************************************************/ + +void i_read_mouse(void) +{ +} + +/**************************************************************************** + * Name: i_bind_input_variables + * + * Description: + * Bind all variables controlling input options. + * + ****************************************************************************/ + +void i_bind_input_variables(void) +{ + m_bind_float_variable("mouse_acceleration", &mouse_acceleration); + m_bind_int_variable("mouse_threshold", &mouse_threshold); + m_bind_int_variable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping); + m_bind_int_variable("novert", &novert); +} diff --git a/games/NXDoom/src/i_input.h b/games/NXDoom/src/i_input.h new file mode 100644 index 000000000..97fc81bf7 --- /dev/null +++ b/games/NXDoom/src/i_input.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_input.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System-specific keyboard/mouse input. + * + ****************************************************************************/ + +#ifndef __I_INPUT__ +#define __I_INPUT__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +#include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_MOUSE_BUTTONS 8 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern float mouse_acceleration; +extern int mouse_threshold; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_bind_input_variables(void); +void i_read_mouse(void); + +/* i_start_text_input begins text input, activating the on-screen keyboard + * (if one is used). The caller indicates that any entered text will be + * displayed in the rectangle given by the provided set of coordinates. + */ + +void i_start_text_input(int x1, int y1, int x2, int y2); + +/* i_stop_text_input finishes text input, deactivating the on-screen keyboard + * (if one is used). + */ + +void i_stop_text_input(void); + +void i_handle_keyboard_event(struct keyboard_event_s *kevent); +void i_handle_mouse_event(void); + +#ifdef CONFIG_GAMES_NXDOOM_KEYBOARD +int get_kbd_event(struct keyboard_event_s *sample); +#endif + +#endif /* __I_INPUT__ */ diff --git a/games/NXDoom/src/i_joystick.c b/games/NXDoom/src/i_joystick.c new file mode 100644 index 000000000..4ae0b3a32 --- /dev/null +++ b/games/NXDoom/src/i_joystick.c @@ -0,0 +1,181 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_joystick.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * SDL Joystick code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "d_event.h" +#include "doomtype.h" +#include "i_joystick.h" +#include "i_system.h" + +#include "m_config.h" +#include "m_fixed.h" +#include "m_misc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DIRECTION_DEADZONE (50 * 32768 / 100) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Configuration variables: */ + +/* Standard default.cfg Joystick enable/disable */ + +static int usejoystick = 0; + +/* Use SDL_gamecontroller interface for the selected device */ + +static int use_gamepad = 0; + +/* SDL_GameControllerType of gamepad */ + +static int gamepad_type = 0; + +/* SDL GUID and index of the joystick to use. */ + +static char *joystick_guid = ""; +static int joystick_index = -1; + +/* Which joystick axis to use for horizontal movement, and whether to + * invert the direction: + */ + +static int joystick_x_axis = 0; +static int joystick_x_invert = 0; + +/* Which joystick axis to use for vertical movement, and whether to + * invert the direction: + */ + +static int joystick_y_axis = 1; +static int joystick_y_invert = 0; + +/* Which joystick axis to use for strafing? */ + +static int joystick_strafe_axis = -1; +static int joystick_strafe_invert = 0; + +/* Which joystick axis to use for looking? */ + +static int joystick_look_axis = -1; +static int joystick_look_invert = 0; + +/* Configurable dead zone for each axis, specified as a percentage of the + * axis max value. + */ + +static int joystick_x_dead_zone = 33; +static int joystick_y_dead_zone = 33; +static int joystick_strafe_dead_zone = 33; +static int joystick_look_dead_zone = 33; + +/* Virtual to physical button joystick button mapping. By default this + * is a straight mapping. + */ + +static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int use_analog = 0; + +int joystick_turn_sensitivity = 10; +int joystick_move_sensitivity = 10; +int joystick_look_sensitivity = 10; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_init_joystick(void) +{ +} + +void i_update_gamepad(void) +{ +} + +void i_shutdown_joystick(void) +{ +} + +void i_update_joystick(void) +{ +} + +void i_bind_joystick_variables(void) +{ + int i; + + m_bind_int_variable("use_joystick", &usejoystick); + m_bind_int_variable("use_gamepad", &use_gamepad); + m_bind_int_variable("gamepad_type", &gamepad_type); + m_bind_string_variable("joystick_guid", &joystick_guid); + m_bind_int_variable("joystick_index", &joystick_index); + m_bind_int_variable("joystick_x_axis", &joystick_x_axis); + m_bind_int_variable("joystick_y_axis", &joystick_y_axis); + m_bind_int_variable("joystick_strafe_axis", &joystick_strafe_axis); + m_bind_int_variable("joystick_x_invert", &joystick_x_invert); + m_bind_int_variable("joystick_y_invert", &joystick_y_invert); + m_bind_int_variable("joystick_strafe_invert", &joystick_strafe_invert); + m_bind_int_variable("joystick_look_axis", &joystick_look_axis); + m_bind_int_variable("joystick_look_invert", &joystick_look_invert); + m_bind_int_variable("joystick_x_dead_zone", &joystick_x_dead_zone); + m_bind_int_variable("joystick_y_dead_zone", &joystick_y_dead_zone); + m_bind_int_variable("joystick_strafe_dead_zone", + &joystick_strafe_dead_zone); + m_bind_int_variable("joystick_look_dead_zone", &joystick_look_dead_zone); + m_bind_int_variable("use_analog", &use_analog); + m_bind_int_variable("joystick_turn_sensitivity", + &joystick_turn_sensitivity); + m_bind_int_variable("joystick_move_sensitivity", + &joystick_move_sensitivity); + m_bind_int_variable("joystick_look_sensitivity", + &joystick_look_sensitivity); + + for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i) + { + char name[32]; + snprintf(name, sizeof(name), "joystick_physical_button%i", i); + m_bind_int_variable(name, &joystick_physical_buttons[i]); + } +} diff --git a/games/NXDoom/src/i_joystick.h b/games/NXDoom/src/i_joystick.h new file mode 100644 index 000000000..6c1ff3df5 --- /dev/null +++ b/games/NXDoom/src/i_joystick.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_joystick.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System-specific joystick interface. + * + ****************************************************************************/ + +#ifndef __I_JOYSTICK__ +#define __I_JOYSTICK__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Number of "virtual" joystick buttons defined in configuration files. + * This needs to be at least as large as the number of different key + * bindings supported by the higher-level game code (joyb* variables). + */ + +#define NUM_VIRTUAL_BUTTONS 17 + +/* Max allowed number of virtual mappings. Chosen to be less than joybspeed + * autorun value. + */ + +#define MAX_VIRTUAL_BUTTONS 20 + +/* If this bit is set in a configuration file axis value, the axis is + * not actually a joystick axis, but instead is a "button axis". This + * means that instead of reading an SDL joystick axis, we read the + * state of two buttons to get the axis value. This is needed for eg. + * the PS3 SIXAXIS controller, where the D-pad buttons register as + * buttons, not as two axes. + */ + +#define BUTTON_AXIS 0x10000 + +/* Query whether a given axis value describes a button axis. */ + +#define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0) + +/* Get the individual buttons from a button axis value. */ + +#define BUTTON_AXIS_NEG(axis) ((axis) & 0xff) +#define BUTTON_AXIS_POS(axis) (((axis) >> 8) & 0xff) + +/* Create a button axis value from two button values. */ + +#define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8)) + +/* If this bit is set in an axis value, the axis is not actually a + * joystick axis, but is a "hat" axis. This means that we read (one of) + * the hats on the joystick. + */ + +#define HAT_AXIS 0x20000 + +#define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0) + +/* Get the hat number from a hat axis value. */ + +#define HAT_AXIS_HAT(axis) ((axis) & 0xff) + +/* Which axis of the hat? (horizontal or vertical) */ + +#define HAT_AXIS_DIRECTION(axis) (((axis) >> 8) & 0xff) + +#define CREATE_HAT_AXIS(hat, direction) \ + (HAT_AXIS | (hat) | ((direction) << 8)) + +#define HAT_AXIS_HORIZONTAL 1 +#define HAT_AXIS_VERTICAL 2 + +/* When a trigger reads greater than this, consider it to be pressed. 30 + * comes from XINPUT_GAMEPAD_TRIGGER_THRESHOLD in xinput.h, and is scaled + * here for the SDL_GameController trigger max value. + */ + +#define TRIGGER_THRESHOLD (30 * 32767 / 255) + +/* To be used with SDL_JoystickGetGUIDString; see SDL_joystick.h */ + +#define GUID_STRING_BUF_SIZE 33 + +/* Helper macros for bitpacked directional data from gamepad inputs. */ + +#define DPAD_SHIFT 0 +#define LSTICK_SHIFT 4 +#define RSTICK_SHIFT 8 +#define JOY_GET_DPAD(x) (((x) >> DPAD_SHIFT) & 0xf) +#define JOY_GET_LSTICK(x) (((x) >> LSTICK_SHIFT) & 0xf) +#define JOY_GET_RSTICK(x) (((x) >> RSTICK_SHIFT) & 0xf) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* 4-way direction data for gamepad directional inputs. */ + +enum +{ + JOY_DIR_NONE = 0x0, + JOY_DIR_UP = 0x1, + JOY_DIR_DOWN = 0x2, + JOY_DIR_LEFT = 0x4, + JOY_DIR_RIGHT = 0x8 +}; + +/* Extend the SDL_GameControllerButton enum to include the triggers. */ + +enum +{ + GAMEPAD_BUTTON_TRIGGERLEFT, /* = SDL_CONTROLLER_BUTTON_MAX, */ + GAMEPAD_BUTTON_TRIGGERRIGHT, + GAMEPAD_BUTTON_MAX +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int use_analog; +extern int joystick_turn_sensitivity; +extern int joystick_move_sensitivity; +extern int joystick_look_sensitivity; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_init_joystick(void); +void i_shutdown_joystick(void); +void i_update_joystick(void); + +void i_bind_joystick_variables(void); + +#endif /* __I_JOYSTICK__ */ diff --git a/games/NXDoom/src/i_main.c b/games/NXDoom/src/i_main.c new file mode 100644 index 000000000..bd9dab609 --- /dev/null +++ b/games/NXDoom/src/i_main.c @@ -0,0 +1,87 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_main.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Main program, simply calls d_doom_main high level loop. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: d_doom_main() + * + * Description: + * Not a globally visible function, just included for source reference, + * calls all startup code, parses command line options. + * + ****************************************************************************/ + +void d_doom_main(void); + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv) +{ + /* save arguments */ + + myargc = argc; + myargv = malloc(argc * sizeof(char *)); + assert(myargv != NULL); + + for (int i = 0; i < argc; i++) + { + myargv[i] = m_string_duplicate(argv[i]); + } + + /* Print the program version and exit. */ + + if (m_parm_exists("-version") || m_parm_exists("--version")) + { + puts(PACKAGE_STRING); + exit(0); + } + + m_find_response_file(); + m_set_exe_dir(); + + /* start doom */ + + d_doom_main(); + + return 0; +} diff --git a/games/NXDoom/src/i_musicpack.c b/games/NXDoom/src/i_musicpack.c new file mode 100644 index 000000000..733a45c7c --- /dev/null +++ b/games/NXDoom/src/i_musicpack.c @@ -0,0 +1,152 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_musicpack.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System interface for music. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_glob.h" + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean i_null_init_music(void); +static void i_null_shutdown_music(void); +static void i_null_set_music_volume(int volume); +static void i_null_pause_song(void); +static void i_null_resume_song(void); +static void *i_null_register_song(void *data, int len); +static void i_null_unregister_song(void *handle); +static void i_null_play_song(void *handle, boolean looping); +static void i_null_stop_song(void); +static boolean i_null_musicisplaying(void); +static void i_null_pollmusic(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *music_pack_path = ""; + +const music_module_t music_pack_module = +{ + NULL, + 0, + i_null_init_music, + i_null_shutdown_music, + i_null_set_music_volume, + i_null_pause_song, + i_null_resume_song, + i_null_register_song, + i_null_unregister_song, + i_null_play_song, + i_null_stop_song, + i_null_musicisplaying, + i_null_pollmusic, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean i_null_init_music(void) +{ + return false; +} + +static void i_null_shutdown_music(void) +{ + return; +} + +static void i_null_set_music_volume(int volume) +{ + return; +} + +static void i_null_pause_song(void) +{ + return; +} + +static void i_null_resume_song(void) +{ + return; +} + +static void *i_null_register_song(void *data, int len) +{ + return NULL; +} + +static void i_null_unregister_song(void *handle) +{ + return; +} + +static void i_null_play_song(void *handle, boolean looping) +{ + return; +} + +static void i_null_stop_song(void) +{ + return; +} + +static boolean i_null_musicisplaying(void) +{ + return false; +} + +static void i_null_pollmusic(void) +{ + return; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/i_pcsound.c b/games/NXDoom/src/i_pcsound.c new file mode 100644 index 000000000..b6251c0e2 --- /dev/null +++ b/games/NXDoom/src/i_pcsound.c @@ -0,0 +1,256 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_pcsound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System interface for PC speaker sound. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" + +#include "deh_str.h" +#include "i_sound.h" +#include "m_misc.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "pcsound.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TIMER_FREQ 1193181 /* Hz */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int i_pcs_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, + int sep, int pitch); +static void i_pcs_stop_sound(int handle); +static int i_pcs_get_sfx_lumpnum(sfxinfo_t *sfx); +static boolean i_pcs_sound_is_playing(int handle); +static boolean i_pcs_init_sound(gamemission_t mission); +static void i_pcs_shutdown_sound(void); +static void i_pcs_update_sound(void); +static void i_pcs_update_sound_params(int channel, int vol, int sep); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean g_pcs_initialized = false; + +static gamemission_t g_gamemission; + +static uint8_t *g_current_sound_lump = NULL; +#if 0 /* UNUSED */ +static uint8_t *g_current_sound_pos = NULL; +#endif +static unsigned int g_current_sound_remaining = 0; +static int g_current_sound_handle = 0; +#if 0 /* UNUSED */ +static int g_current_sound_lump_num = -1; + +static const uint16_t g_divisors[] = +{ + 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, + 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, + 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, + 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, + 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, + 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, + 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, + 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452, + 439, 427, 415, 403, 391, 380, 369, 359, 348, 339, 329, 319, + 310, 302, 293, 285, 276, 269, 261, 253, 246, 239, 232, 226, + 219, 213, 207, 201, 195, 190, 184, 179, +}; +#endif + +static const snddevice_t g_sound_pcsound_devices[] = +{ + SNDDEVICE_PCSPEAKER, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const sound_module_t sound_pcsound_module = +{ + .sound_devices = g_sound_pcsound_devices, + .num_sound_devices = arrlen(g_sound_pcsound_devices), + .init = i_pcs_init_sound, + .shutdown = i_pcs_shutdown_sound, + .get_sfx_lumpnum = i_pcs_get_sfx_lumpnum, + .update = i_pcs_update_sound, + .update_sound_params = i_pcs_update_sound_params, + .start_sound = i_pcs_start_sound, + .stop_sound = i_pcs_stop_sound, + .sound_is_playing = i_pcs_sound_is_playing, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void pcs_callback_func(int *duration, int *freq) +{ +} + +static boolean cache_pcs_lump(sfxinfo_t *sfxinfo) +{ + int lumplen; + int headerlen; + + /* Free the current sound lump back to the cache */ + + if (g_current_sound_lump != NULL) + { + w_release_lump_num(g_current_sound_lump_num); + g_current_sound_lump = NULL; + } + + /* Load from WAD */ + + g_current_sound_lump = w_cache_lump_num(sfxinfo->lumpnum, PU_STATIC); + lumplen = w_lump_length(sfxinfo->lumpnum); + + /* Read header */ + + if (g_current_sound_lump[0] != 0x00 || g_current_sound_lump[1] != 0x00) + { + return false; + } + + headerlen = (g_current_sound_lump[3] << 8) | g_current_sound_lump[2]; + + if (headerlen > lumplen - 4) + { + return false; + } + + /* Header checks out ok */ + + g_current_sound_remaining = headerlen; + g_current_sound_pos = g_current_sound_lump + 4; + g_current_sound_lump_num = sfxinfo->lumpnum; + + return true; +} + +/* These Doom PC speaker sounds are not played - this can be seen in the + * Heretic source code, where there are remnants of this left over + * from Doom. + */ + +static boolean is_disabled_sound(sfxinfo_t *sfxinfo) +{ + int i; + const char *disabled_sounds[] = { + "posact", "bgact", "dmact", "dmpain", "popain", "sawidl", "rifle", + }; + + for (i = 0; i < arrlen(disabled_sounds); ++i) + { + if (!strcmp(sfxinfo->name, disabled_sounds[i])) + { + return true; + } + } + + return false; +} +#endif /* UNUSED */ + +static int i_pcs_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, + int sep, int pitch) +{ + return 0; +} + +static void i_pcs_stop_sound(int handle) +{ +} + +/* Retrieve the raw data lump index for a given SFX name. */ + +static int i_pcs_get_sfx_lumpnum(sfxinfo_t *sfx) +{ + char namebuf[16]; + + if (g_gamemission == doom || g_gamemission == strife) + { + snprintf(namebuf, sizeof(namebuf), "dp%s", (sfx->name)); + + if (g_gamemission == strife && w_check_num_for_name(namebuf) == -1) + { + /* Missing sounds replaced with DPRIFLE. */ + + snprintf(namebuf, sizeof(namebuf), "dp%s", ("rifle")); + } + } + else + { + m_str_copy(namebuf, (sfx->name), sizeof(namebuf)); + } + + return w_get_num_for_name(namebuf); +} + +static boolean i_pcs_sound_is_playing(int handle) +{ + if (!g_pcs_initialized) + { + return false; + } + + if (handle != g_current_sound_handle) + { + return false; + } + + return g_current_sound_lump != NULL && g_current_sound_remaining > 0; +} + +static boolean i_pcs_init_sound(gamemission_t mission) +{ + return false; +} + +static void i_pcs_shutdown_sound(void) +{ +} + +static void i_pcs_update_sound(void) +{ + /* no-op. */ +} + +static void i_pcs_update_sound_params(int channel, int vol, int sep) +{ + /* no-op. */ +} diff --git a/games/NXDoom/src/i_rtttl_music.c b/games/NXDoom/src/i_rtttl_music.c new file mode 100644 index 000000000..78dd7122c --- /dev/null +++ b/games/NXDoom/src/i_rtttl_music.c @@ -0,0 +1,522 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_rtttl_music.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System interface for music. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i_glob.h" + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +#ifdef CONFIG_PWM +#include +#endif + +#include + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean i_rtttl_init_music(void); +static void i_rtttl_shutdown_music(void); +static void i_rtttl_set_music_volume(int volume); +static void i_rtttl_pause_song(void); +static void i_rtttl_resume_song(void); +static void *i_rtttl_register_song(void *data, int len); +static void i_rtttl_unregister_song(void *handle); +static void i_rtttl_play_song(void *handle, boolean looping); +static void i_rtttl_stop_song(void); +static boolean i_rtttl_musicisplaying(void); +static void i_rtttl_pollmusic(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +const char g_doom_rtttl[] = + "Doom:d=32,o=5,b=56:f,f,f6,f,f,d#6,f,f,c#6,f,f,b,f,f,c6,c#6,f,f,f6,f,f,d#" + "6,f,f,c#6,f,f,8b.,f,f,f6,f,f,d#6,f,f,c#6,f,f,b,f,f,c6,c#6,f,f,f6,f,f,d#" + "6,f,f,c#6,f,f,8b.,a#,a#,a#6,a#,a#,g#6,a#,a#,f#6,a#,a#,e6,a#,a#,f6,f#6,a#" + ",a#,a#6,a#,a#,g#6,a#,a#,f#6,a#,a#,8e6"; + +/* List of sound devices that this sound module is used for. + * It appears in i_sound.c that the default music device is 'SNDDEVICE_SB', + * so I have just put that here. In reality, we may either wish to add an + * additional sound device for RTTL, or put this as something else + * (PCSPEAKER?). I don't know what most of the snddevice_t enum options + * refer to. + */ + +static const snddevice_t g_rtttl_sound_devices[] = +{ + SNDDEVICE_SB, +}; + +/* Player state */ + +static void (*g_player)(struct rtttl_tone); +static int g_fd = -1; +static bool g_playing = false; +static bool g_loop = true; + +/* Player thread state */ + +static pthread_t g_thread; +static pthread_mutex_t g_player_lock; +static pthread_cond_t g_pausecond; +static pthread_cond_t g_playingcond; +static bool g_paused; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const music_module_t g_rtttl_music_module = +{ + .sound_devices = g_rtttl_sound_devices, + .num_sound_devices = arrlen(g_rtttl_sound_devices), + .init = i_rtttl_init_music, + .shutdown = i_rtttl_shutdown_music, + .set_music_volume = i_rtttl_set_music_volume, + .pause_music = i_rtttl_pause_song, + .resume_music = i_rtttl_resume_song, + .register_song = i_rtttl_register_song, + .unregister_song = i_rtttl_unregister_song, + .play_song = i_rtttl_play_song, + .stop_song = i_rtttl_stop_song, + .music_is_playing = i_rtttl_musicisplaying, + .poll = i_rtttl_pollmusic, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: wait_while_paused + * + * Description: + * Wait while the music is paused. + * + ****************************************************************************/ + +static void wait_while_paused(void) +{ + pthread_mutex_lock(&g_player_lock); + + while (g_paused) + { + pthread_cond_wait(&g_pausecond, &g_player_lock); + } + + pthread_mutex_unlock(&g_player_lock); +} + +/**************************************************************************** + * Name: wait_while_stopped + * + * Description: + * Wait until we are told that a song is started (i.e. while song stopped + * and nothing is playing). + * + ****************************************************************************/ + +static void wait_while_stopped(void) +{ + pthread_mutex_lock(&g_player_lock); + + while (!g_playing) + { + pthread_cond_wait(&g_playingcond, &g_player_lock); + } + + pthread_mutex_unlock(&g_player_lock); +} + +/**************************************************************************** + * Name: play_default + * + * Description: + * Default player which does nothing. + * + * Input Parameters: + * sound - The sound to play + * + ****************************************************************************/ + +static void play_default(struct rtttl_tone sound) +{ + wait_while_paused(); + return; +} + +#ifdef CONFIG_PWM + +/**************************************************************************** + * Name: play_pwm + * + * Description: + * RTTTL player for audio sinks using PWM devices + * + * Input Parameters: + * sound - The sound to play + * + ****************************************************************************/ + +static void play_pwm(struct rtttl_tone sound) +{ + int err; + int i; + struct pwm_info_s info; + + wait_while_paused(); /* Wait until unpaused */ + + /* If we are not meant to be playing (i.e. song was stopped), we should + * return from this function. + */ + + if (!g_playing) + { + return; + } + + info.channels[0].channel = 1; + info.channels[0].duty = b16divi(uitoub16(50) - 1, 100); /* 50% duty cycle */ + info.channels[0].cpol = PWM_CPOL_NDEF; /* Default */ + info.channels[0].dcpol = PWM_DCPOL_NDEF; /* Default */ + info.frequency = sound.frequency_100hz / 100; + + /* Copy settings to all channels */ + + for (i = 1; i < CONFIG_PWM_NCHANNELS; i++) + { + info.channels[i] = info.channels[0]; + info.channels[i].channel = i + 1; + } + + err = ioctl(g_fd, PWMIOC_SETCHARACTERISTICS, &info); + if (err < 0) + { + fprintf(stderr, "Failed to set PWM characteristics: %d\n", errno); + return; + } + + err = ioctl(g_fd, PWMIOC_START, 0); + if (err < 0) + { + fprintf(stderr, "Failed to start PWM: %d\n", errno); + return; + } + + usleep(sound.duration_us); /* Wait */ + + err = ioctl(g_fd, PWMIOC_STOP, 0); + if (err < 0) + { + fprintf(stderr, "Failed to stop PWM: %d\n", errno); + } +} +#endif + +static void *player_thread(void *arg) +{ + for (; ; ) + { + /* If there is nothing playing (i.e. song stopped or done), block here + * until the main DOOM player logic tells us it wants to play a song. + */ + + wait_while_stopped(); + + rtttl_play(g_doom_rtttl, g_player); + + /* If we are not meant to loop, then after one play we will indicate + * that we are 'not playing'. This will causes the thread to block. + * Otherwise, this will continue forever. + */ + + if (!g_loop) + { + pthread_mutex_lock(&g_player_lock); + g_playing = false; + g_paused = false; + pthread_mutex_unlock(&g_player_lock); + } + } + + return NULL; +} + +/**************************************************************************** + * Name: i_rtttl_init_music + * + * Description: + * Choose the appropriate player implementation for RTTTl based on the + * configured music device. + * + * Returned Value: + * True on success, false otherwise. + * + ****************************************************************************/ + +static boolean i_rtttl_init_music(void) +{ + int err; + g_player = play_default; +#ifdef CONFIG_PWM + if (strstr(CONFIG_GAMES_NXDOOM_RTTTL_DEVICE, "pwm")) + { + g_player = play_pwm; + } +#endif + + /* Initialize thread state */ + + pthread_mutex_init(&g_player_lock, NULL); + pthread_cond_init(&g_pausecond, NULL); + pthread_cond_init(&g_playingcond, NULL); + g_paused = true; + g_loop = false; + g_playing = false; + + /* Start the music playing thread */ + + err = pthread_create(&g_thread, NULL, player_thread, NULL); + if (err) + { + return false; + } + + /* We only managed to match with the default player. */ + + if (g_player == play_default) + { + return true; + } + + /* If we're not using the default player, we need to open a file + * descriptor. + */ + + g_fd = open(CONFIG_GAMES_NXDOOM_RTTTL_DEVICE, O_RDWR); + if (g_fd < 0) + { + fprintf(stderr, + "Couldn't open RTTL device '" CONFIG_GAMES_NXDOOM_RTTTL_DEVICE + "': %d", + errno); + return false; + } + + return true; +} + +/**************************************************************************** + * Name: i_rtttl_shutdown_music + * + * Description: + * Turns off the music and de-initializes the module. + * + ****************************************************************************/ + +static void i_rtttl_shutdown_music(void) +{ + i_rtttl_stop_song(); /* Stop music */ + pthread_cancel(g_thread); /* Kill thread */ + close(g_fd); /* Clean up device */ +} + +static void i_rtttl_set_music_volume(int volume) +{ + return; /* Currently not possible */ +} + +/**************************************************************************** + * Name: i_rtttl_pause_song + * + * Description: + * Pauses the currently playing song. Right now, this is implemented by + * causing the RTTL `play` function to block on a condition variable. + * + ****************************************************************************/ + +static void i_rtttl_pause_song(void) +{ + pthread_mutex_lock(&g_player_lock); + g_paused = true; + pthread_mutex_unlock(&g_player_lock); +} + +/**************************************************************************** + * Name: i_rtttl_resume_song + * + * Description: + * Resume the currently paused song. Unblocks the RTTL `play` function by + * signalling the condition variable. + * + ****************************************************************************/ + +static void i_rtttl_resume_song(void) +{ + pthread_mutex_lock(&g_player_lock); + g_paused = false; + pthread_cond_signal(&g_pausecond); + pthread_mutex_unlock(&g_player_lock); +} + +/**************************************************************************** + * Name: i_rtttl_register_song + * + * Description: + * Appears to be used to register song data from the DOOM lump. Right now, + * we are using a statically stored RTTL string so we just ignore this. It + * would be good to have RTTL data substituted for lump data in the future + * so we can play the different songs provided by DOOM WADS. + * + * Input Parameters: + * data - Song data + * len - The length of the song data in bytes + * + * Returns: In theory, a handle representing the registered song. However, in + * practice, we return NULL to indicate this function isn't doing anything. + * + ****************************************************************************/ + +static void *i_rtttl_register_song(void *data, int len) +{ + /* TODO: no idea what this would be for; right now we play only the theme */ + + return NULL; +} + +/**************************************************************************** + * Name: i_rtttl_unregister_song + * + * Description: + * Unregisters one of the registered songs from `i_rtttl_register_song`. + * Currently unimplemented since we register nothing. + * + * Input Parameters: + * handle - The returned handle to the registered song + * + ****************************************************************************/ + +static void i_rtttl_unregister_song(void *handle) +{ + /* TODO: no idea what this would be for */ + + return; +} + +/**************************************************************************** + * Name: i_rtttl_play_song + * + * Description: + * Plays a previously registered song via the RTTTL library. + * + * Input Parameters: + * handle - The handle to a previously registered song + * looping - True if the song should loop, false otherwise + * + ****************************************************************************/ + +static void i_rtttl_play_song(void *handle, boolean looping) +{ + pthread_mutex_lock(&g_player_lock); + g_playing = true; + g_paused = false; + g_loop = looping; + pthread_cond_signal(&g_playingcond); + pthread_mutex_unlock(&g_player_lock); +} + +/**************************************************************************** + * Name: i_rtttl_stop_song + * + * Description: + * Stops playing the currently playing song (fully, not paused). + * + ****************************************************************************/ + +static void i_rtttl_stop_song(void) +{ + pthread_mutex_lock(&g_player_lock); + g_playing = false; + + /* Song not paused, allows play function to return early by also unblocking + * it. Then control is returned to the thread's main loop instead of the + * loop inside the RTTL library thread, where the thread can then block + * until a new song is played. + */ + + if (g_paused) + { + g_paused = false; + pthread_cond_signal(&g_pausecond); + } + + g_loop = false; + pthread_mutex_unlock(&g_player_lock); +} + +static boolean i_rtttl_musicisplaying(void) +{ + return g_playing; +} + +static void i_rtttl_pollmusic(void) +{ + /* Unsure what this function does */ + + return; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ diff --git a/games/NXDoom/src/i_sdlmusic.c b/games/NXDoom/src/i_sdlmusic.c new file mode 100644 index 000000000..29af65a4e --- /dev/null +++ b/games/NXDoom/src/i_sdlmusic.c @@ -0,0 +1,149 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sdlmusic.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System interface for music. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "doomtype.h" +#include "memio.h" +#include "mus2mid.h" + +#include "deh_str.h" +#include "gusconf.h" +#include "i_sound.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *temp_timidity_cfg = NULL; + +/* putenv requires a non-const string whose lifetime is the whole program + * so can't use a string directly, have to do this silliness + */ + +static char sdl_mixer_disable_fluidsynth[] = + "SDL_MIXER_DISABLE_FLUIDSYNTH=1"; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +char *timidity_cfg_path = ""; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* If the temp_timidity_cfg config variable is set, generate a "wrapper" + * config file for Timidity to point to the actual config file. This + * is needed to inject a "dir" command so that the patches are read + * relative to the actual config file. + */ + +static boolean write_wrapper_timidity_config(char *write_path) +{ + char *path; + FILE *fstream; + + if (!strcmp(timidity_cfg_path, "")) + { + return false; + } + + fstream = fopen(write_path, "w"); + + if (fstream == NULL) + { + return false; + } + + path = m_dir_name(timidity_cfg_path); + fprintf(fstream, "dir %s\n", path); + free(path); + + fprintf(fstream, "source %s\n", timidity_cfg_path); + fclose(fstream); + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_init_timidity_config(void) +{ + char *env_string; + boolean success; + + temp_timidity_cfg = m_temp_file("timidity.cfg"); + + if (snd_musicdevice == SNDDEVICE_GUS) + { + success = gus_write_config(temp_timidity_cfg); + } + else + { + success = write_wrapper_timidity_config(temp_timidity_cfg); + } + + /* Set the TIMIDITY_CFG environment variable to point to the temporary + * config file. + */ + + if (success) + { + env_string = m_string_join("TIMIDITY_CFG=", temp_timidity_cfg, NULL); + putenv(env_string); + + /* env_string deliberately not freed; see putenv manpage + * If we're explicitly configured to use Timidity (either through + * timidity_cfg_path or GUS mode), then disable Fluidsynth, because + * SDL_mixer considers Fluidsynth a higher priority than Timidity and + * therefore can end up circumventing Timidity entirely. + */ + + putenv(sdl_mixer_disable_fluidsynth); + } + else + { + free(temp_timidity_cfg); + temp_timidity_cfg = NULL; + } +} diff --git a/games/NXDoom/src/i_sound.c b/games/NXDoom/src/i_sound.c new file mode 100644 index 000000000..8c8cafaed --- /dev/null +++ b/games/NXDoom/src/i_sound.c @@ -0,0 +1,525 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sound.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "config.h" +#include "doomtype.h" + +#include "gusconf.h" +#include "i_sound.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Sound sample rate to use for digital output (Hz) */ + +int snd_samplerate = 44100; + +/* Maximum number of bytes to dedicate to allocated sound effects. + * (Default: 64MB) + */ + +int snd_cachesize = 64 * 1024 * 1024; + +/* Config variable that controls the sound buffer size. + * We default to 28ms (1000 / 35fps = 1 buffer per tic). + */ + +int snd_maxslicetime_ms = 28; + +/* External command to invoke to play back music. */ + +char *snd_musiccmd = ""; + +/* Whether to vary the pitch of sound effects + * Each game will set the default differently + */ + +int snd_pitchshift = -1; + +int snd_musicdevice = SNDDEVICE_SB; +int snd_sfxdevice = SNDDEVICE_SB; + +/* Scale factor used when converting libsamplerate floating point numbers + * to integers. Too high means the sounds can clip; too low means they + * will be too quiet. This is an amount that should avoid clipping most + * of the time: with all the Doom IWAD sound effects, at least. If a PWAD + * is used, clipping might occur. + * + * NOTE: originally from i_sdlsound.c + */ + +float libsamplerate_scale = 0.65f; +int use_libsamplerate = 0; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Low-level sound and music modules we are using */ + +static const sound_module_t *sound_module; +static const music_module_t *music_module; + +/* If true, the music pack module was successfully initialized. */ + +static boolean music_packs_active = false; + +/* This is either equal to music_module or &music_pack_module, + * depending on whether the current track is substituted. + */ + +static const music_module_t *active_music_module; + +/* DOS-specific options: These are unused but should be maintained + * so that the config file can be shared between chocolate + * doom and doom.exe + */ + +static int snd_sbport = 0; +static int snd_sbirq = 0; +static int snd_sbdma = 0; +static int snd_mport = 0; + +/* Compiled-in sound modules: */ + +static const sound_module_t *sound_modules[] = +{ + &sound_pcsound_module, + NULL, +}; + +/* Compiled-in music modules: */ + +static const music_module_t *g_music_modules[] = +{ +#if 0 + &music_opl_module, +#endif +#ifdef CONFIG_GAMES_NXDOOM_RTTTL_MUSIC + &g_rtttl_music_module, +#endif + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check if a sound device is in the given list of devices */ + +static boolean snd_device_in_list(snddevice_t device, + const snddevice_t *list, int len) +{ + int i; + + for (i = 0; i < len; ++i) + { + if (device == list[i]) + { + return true; + } + } + + return false; +} + +/* Find and initialize a sound_module_t appropriate for the setting + * in snd_sfxdevice. + */ + +static void init_sfx_module(gamemission_t mission) +{ + int i; + + sound_module = NULL; + + for (i = 0; sound_modules[i] != NULL; ++i) + { + /* Is the sfx device in the list of devices supported by + * this module? + */ + + if (snd_device_in_list(snd_sfxdevice, sound_modules[i]->sound_devices, + sound_modules[i]->num_sound_devices)) + { + /* initialize the module */ + + if (sound_modules[i]->init(mission)) + { + sound_module = sound_modules[i]; + return; + } + } + } +} + +/* initialize music according to snd_musicdevice. */ + +static void init_music_module(void) +{ + int i; + + music_module = NULL; + + for (i = 0; g_music_modules[i] != NULL; ++i) + { + /* Is the music device in the list of devices supported + * by this module? + */ + + if (snd_device_in_list(snd_musicdevice, + g_music_modules[i]->sound_devices, + g_music_modules[i]->num_sound_devices)) + { + /* initialize the module */ + + if (g_music_modules[i]->init()) + { + music_module = g_music_modules[i]; + return; + } + } + } +} + +static void check_volume_separation(int *vol, int *sep) +{ + if (*sep < 0) + { + *sep = 0; + } + else if (*sep > 254) + { + *sep = 254; + } + + if (*vol < 0) + { + *vol = 0; + } + else if (*vol > 127) + { + *vol = 127; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* initializes sound stuff, including volume + * Sets channels, SFX and music volume, allocates channel buffer, sets s_sfx + * lookup. + */ + +void i_init_sound(gamemission_t mission) +{ + boolean nosound; + boolean nosfx; + boolean nomusic; + boolean nomusicpacks; + + /* @vanilla + * + * Disable all sound output. + */ + + nosound = m_check_parm("-nosound") > 0; + + /* @vanilla + * + * Disable sound effects. + */ + + nosfx = m_check_parm("-nosfx") > 0; + + /* @vanilla + * + * Disable music. + */ + + nomusic = m_check_parm("-nomusic") > 0; + + /* Disable substitution music packs. */ + + nomusicpacks = m_parm_exists("-nomusicpacks"); + + /* Auto configure the music pack directory. */ + + m_set_music_pack_dir(); + + /* initialize the sound and music subsystems. */ + + if (!nosound && !screensaver_mode) + { + /* This is kind of a hack. If native MIDI is enabled, set up + * the TIMIDITY_CFG environment variable here before SDL_mixer + * is opened. + */ + + if (!nomusic && (snd_musicdevice == SNDDEVICE_GENMIDI || + snd_musicdevice == SNDDEVICE_GUS)) + { + i_init_timidity_config(); + } + + if (!nosfx) + { + init_sfx_module(mission); + } + + if (!nomusic) + { + init_music_module(); + active_music_module = music_module; + } + + /* We may also have substitute MIDIs we can load. */ + + if (!nomusicpacks && music_module != NULL) + { + music_packs_active = music_pack_module.init(); + } + } +} + +void i_shutdown_sound(void) +{ + if (sound_module != NULL) + { + sound_module->shutdown(); + } + + if (music_packs_active) + { + music_pack_module.shutdown(); + } + + if (music_module != NULL) + { + music_module->shutdown(); + } +} + +int i_get_sfx_lumpnum(sfxinfo_t *sfxinfo) +{ + if (sound_module != NULL) + { + return sound_module->get_sfx_lumpnum(sfxinfo); + } + else + { + return 0; + } +} + +void i_update_sound(void) +{ + if (sound_module != NULL) + { + sound_module->update(); + } + + if (active_music_module != NULL && active_music_module->poll != NULL) + { + active_music_module->poll(); + } +} + +void i_update_sound_params(int channel, int vol, int sep) +{ + if (sound_module != NULL) + { + check_volume_separation(&vol, &sep); + sound_module->update_sound_params(channel, vol, sep); + } +} + +int i_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch) +{ + if (sound_module != NULL) + { + check_volume_separation(&vol, &sep); + return sound_module->start_sound(sfxinfo, channel, vol, sep, pitch); + } + else + { + return 0; + } +} + +void i_stop_sound(int channel) +{ + if (sound_module != NULL) + { + sound_module->stop_sound(channel); + } +} + +boolean i_sound_playing(int channel) +{ + if (sound_module != NULL) + { + return sound_module->sound_is_playing(channel); + } + else + { + return false; + } +} + +void i_precache_sounds(sfxinfo_t *sounds, int num_sounds) +{ + if (sound_module != NULL && sound_module->cache_sounds != NULL) + { + sound_module->cache_sounds(sounds, num_sounds); + } +} + +void i_init_music(void) +{ + printf("i_init_music: Music initialized\n"); +} + +void i_shutdown_music(void) +{ +} + +void i_set_music_volume(int volume) +{ + if (music_module != NULL) + { + music_module->set_music_volume(volume); + + if (music_packs_active && music_module != &music_pack_module) + { + music_pack_module.set_music_volume(volume); + } + } +} + +void i_pause_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->pause_music(); + } +} + +void i_resume_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->resume_music(); + } +} + +void *i_register_song(void *data, int len) +{ + /* If the music pack module is active, check to see if there is a + * valid substitution for this track. If there is, we set the + * active_music_module pointer to the music pack module for the + * duration of this particular track. + */ + + if (music_packs_active) + { + void *handle; + + handle = music_pack_module.register_song(data, len); + if (handle != NULL) + { + active_music_module = &music_pack_module; + return handle; + } + } + + /* No substitution for this track, so use the main module. */ + + active_music_module = music_module; + if (active_music_module != NULL) + { + return active_music_module->register_song(data, len); + } + else + { + return NULL; + } +} + +void i_unregister_song(void *handle) +{ + if (active_music_module != NULL) + { + active_music_module->unregister_song(handle); + } +} + +void i_play_song(void *handle, boolean looping) +{ + printf("i_play_song: Playing on %p\n", active_music_module); + if (active_music_module != NULL) + { + active_music_module->play_song(handle, looping); + } +} + +void i_stop_song(void) +{ + if (active_music_module != NULL) + { + active_music_module->stop_song(); + } +} + +void i_bind_sound_variables(void) +{ + m_bind_int_variable("snd_musicdevice", &snd_musicdevice); + m_bind_int_variable("snd_sfxdevice", &snd_sfxdevice); + m_bind_int_variable("snd_sbport", &snd_sbport); + m_bind_int_variable("snd_sbirq", &snd_sbirq); + m_bind_int_variable("snd_sbdma", &snd_sbdma); + m_bind_int_variable("snd_mport", &snd_mport); + m_bind_int_variable("snd_maxslicetime_ms", &snd_maxslicetime_ms); + m_bind_string_variable("snd_musiccmd", &snd_musiccmd); + m_bind_int_variable("snd_samplerate", &snd_samplerate); + m_bind_int_variable("snd_cachesize", &snd_cachesize); + m_bind_int_variable("snd_pitchshift", &snd_pitchshift); + + m_bind_string_variable("music_pack_path", &music_pack_path); + m_bind_string_variable("timidity_cfg_path", &timidity_cfg_path); + m_bind_string_variable("gus_patch_path", &gus_patch_path); + m_bind_int_variable("gus_ram_kb", &gus_ram_kb); + + m_bind_int_variable("use_libsamplerate", &use_libsamplerate); + m_bind_float_variable("libsamplerate_scale", &libsamplerate_scale); +} diff --git a/games/NXDoom/src/i_sound.h b/games/NXDoom/src/i_sound.h new file mode 100644 index 000000000..6827df992 --- /dev/null +++ b/games/NXDoom/src/i_sound.h @@ -0,0 +1,318 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_sound.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + ****************************************************************************/ + +#ifndef __I_SOUND__ +#define __I_SOUND__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* so that the individual game logic and sound driver code agree */ + +#define NORM_PITCH 127 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* SoundFX struct. */ + +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct +{ + /* tag name, used for hexen. */ + + const char *tagname; + + /* lump name. If we are running with use_sfx_prefix=true, a + * 'DS' (or 'DP' for PC speaker sounds) is prepended to this. + */ + + char name[9]; + + /* Sfx priority */ + + int priority; + + /* referenced sound if a link */ + + sfxinfo_t *link; + + /* pitch if a link (Doom), whether to pitch-shift (Hexen) */ + + int pitch; + + /* volume if a link */ + + int volume; + + /* this is checked every second to see if sound + * can be thrown out (if 0, then decrement, if -1, + * then throw out, if > 0, then it is in use) + */ + + int usefulness; + + /* lump number of sfx */ + + int lumpnum; + + /* Maximum number of channels that the sound can be played on + * (Heretic) + */ + + int numchannels; + + /* data used by the low level code */ + + void *driver_data; +}; + +/* MusicInfo struct. */ + +typedef struct +{ + /* up to 6-character name */ + + const char *name; + + /* lump number of music */ + + int lumpnum; + + /* music data */ + + void *data; + + /* music handle once registered */ + + void *handle; +} musicinfo_t; + +typedef enum +{ + SNDDEVICE_NONE = 0, + SNDDEVICE_PCSPEAKER = 1, + SNDDEVICE_ADLIB = 2, + SNDDEVICE_SB = 3, + SNDDEVICE_PAS = 4, + SNDDEVICE_GUS = 5, + SNDDEVICE_WAVEBLASTER = 6, + SNDDEVICE_SOUNDCANVAS = 7, + SNDDEVICE_GENMIDI = 8, + SNDDEVICE_AWE32 = 9, + SNDDEVICE_CD = 10, + SNDDEVICE_FSYNTH = 11, +} snddevice_t; + +/* Interface for sound modules */ + +typedef struct +{ + /* List of sound devices that this sound module is used for. */ + + const snddevice_t *sound_devices; + int num_sound_devices; + + /* initialise sound module + * Returns true if successfully initialised + */ + + boolean (*init)(gamemission_t mission); + + /* shutdown sound module */ + + void (*shutdown)(void); + + /* Returns the lump index of the given sound. */ + + int (*get_sfx_lumpnum)(sfxinfo_t *sfxinfo); + + /* Called periodically to update the subsystem. */ + + void (*update)(void); + + /* Update the sound settings on the given channel. */ + + void (*update_sound_params)(int channel, int vol, int sep); + + /* Start a sound on a given channel. Returns the channel id + * or -1 on failure. + */ + + int (*start_sound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch); + + /* Stop the sound playing on the given channel. */ + + void (*stop_sound)(int channel); + + /* Query if a sound is playing on the given channel */ + + boolean (*sound_is_playing)(int channel); + + /* Called on startup to precache sound effects (if necessary) */ + + void (*cache_sounds)(sfxinfo_t *sounds, int num_sounds); +} sound_module_t; + +/* Interface for music modules */ + +typedef struct +{ + /* List of sound devices that this music module is used for. */ + + const snddevice_t *sound_devices; + int num_sound_devices; + + /* initialise the music subsystem */ + + boolean (*init)(void); + + /* shutdown the music subsystem */ + + void (*shutdown)(void); + + /* Set music volume - range 0-127 */ + + void (*set_music_volume)(int volume); + + /* Pause music */ + + void (*pause_music)(void); + + /* Un-pause music */ + + void (*resume_music)(void); + + /* Register a song handle from data + * Returns a handle that can be used to play the song + */ + + void *(*register_song)(void *data, int len); + + /* Un-register (free) song data */ + + void (*unregister_song)(void *handle); + + /* Play the song */ + + void (*play_song)(void *handle, boolean looping); + + /* Stop playing the current song. */ + + void (*stop_song)(void); + + /* Query if music is playing. */ + + boolean (*music_is_playing)(void); + + /* Invoked periodically to poll. */ + + void (*poll)(void); +} music_module_t; + +#if 0 +/* DMX version to emulate for OPL emulation: */ + +typedef enum +{ + opl_doom1_1_666, /* Doom 1 v1.666 */ + opl_doom2_1_666, /* Doom 2 v1.666, Hexen, Heretic */ + opl_doom_1_9 /* Doom v1.9, Strife */ +} opl_driver_ver_t; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int snd_sfxdevice; +extern int snd_musicdevice; +extern int snd_samplerate; +extern int snd_cachesize; +extern int snd_maxslicetime_ms; +extern char *snd_musiccmd; +extern int snd_pitchshift; +extern int use_libsamplerate; +extern float libsamplerate_scale; + +extern const sound_module_t sound_pcsound_module; + +extern const music_module_t music_pack_module; + +#ifdef CONFIG_GAMES_NXDOOM_RTTTL_MUSIC +extern const music_module_t g_rtttl_music_module; +#endif + +/* For native music module: */ + +extern char *music_pack_path; +extern char *timidity_cfg_path; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void i_init_sound(gamemission_t mission); +void i_shutdown_sound(void); +int i_get_sfx_lumpnum(sfxinfo_t *sfxinfo); +void i_update_sound(void); +void i_update_sound_params(int channel, int vol, int sep); +int i_start_sound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, + int pitch); +void i_stop_sound(int channel); +boolean i_sound_playing(int channel); +void i_precache_sounds(sfxinfo_t *sounds, int num_sounds); + +void i_init_music(void); +void i_shutdown_music(void); +void i_set_music_volume(int volume); +void i_pause_song(void); +void i_resume_song(void); +void *i_register_song(void *data, int len); +void i_unregister_song(void *handle); +void i_play_song(void *handle, boolean looping); +void i_stop_song(void); + +void i_bind_sound_variables(void); + +#if 0 +void i_set_opl_driver_ver(opl_driver_ver_t ver); +void i_opl_dev_messages(char *, size_t); +#endif + +/* Sound modules */ + +void i_init_timidity_config(void); + +#endif /* __I_SOUND__ */ diff --git a/games/NXDoom/src/i_swap.h b/games/NXDoom/src/i_swap.h new file mode 100644 index 000000000..72b64236f --- /dev/null +++ b/games/NXDoom/src/i_swap.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_swap.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Endianness handling, swapping 16bit and 32bit. + * + ****************************************************************************/ + +#ifndef __I_SWAP__ +#define __I_SWAP__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Endianness handling. + * WAD files are stored little endian. + */ + +/* These are deliberately cast to signed values; this is the behaviour + * of the macros in the original source and some code relies on it. + */ + +#define SHORT(x) ((signed short)le16toh(x)) +#define LONG(x) ((signed int)le32toh(x)) + +/* Defines for checking the endianness of the system. */ + +#if BYTE_ORDER == BIG_ENDIAN +#define SYS_BIG_ENDIAN +#endif + +#endif /* __I_SWAP__ */ diff --git a/games/NXDoom/src/i_system.c b/games/NXDoom/src/i_system.c new file mode 100644 index 000000000..b3867e0ad --- /dev/null +++ b/games/NXDoom/src/i_system.c @@ -0,0 +1,482 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_system.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "config.h" + +#include "deh_str.h" +#include "doomtype.h" +#include "i_joystick.h" +#include "i_sound.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" + +#include "i_system.h" + +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DEFAULT_RAM 16 /* MiB */ +#define MIN_RAM 4 /* MiB */ + +#define DOS_MEM_DUMP_SIZE 10 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct atexit_listentry_s atexit_listentry_t; + +struct atexit_listentry_s +{ + atexit_func_t func; + boolean run_on_error; + atexit_listentry_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static atexit_listentry_t *exit_funcs = NULL; + +static boolean already_quitting = false; + +/* Read Access Violation emulation. + * + * From PrBoom+, by entryway. + */ + +/* C:\>debug + * -d 0:0 + * + * DOS 6.22: + * 0000:0000 (57 92 19 00) F4 06 70 00-(16 00) + * DOS 7.1: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * Win98: + * 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00) + * DOSBox under XP: + * 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00) + */ + +static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = +{ + 0x57, 0x92, 0x19, 0x00, 0xf4, 0x06, 0x70, 0x00, 0x16, 0x00, +}; + +static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = +{ + 0x9e, 0x0f, 0xc9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00, +}; + +static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = +{ + 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, +}; + +static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE]; + +static const unsigned char *dos_mem_dump = mem_dump_dos622; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Zone memory auto-allocation function that allocates the zone size + * by trying progressively smaller zone sizes until one is found that + * works. + */ + +static byte *auto_alloc_memory(int *size, int default_ram, int min_ram) +{ + byte *zonemem; + + /* Allocate the zone memory. This loop tries progressively smaller + * zone sizes until a size is found that can be allocated. + * If we used the -mb command line parameter, only the parameter + * provided is accepted. + */ + + zonemem = NULL; + + while (zonemem == NULL) + { + /* We need a reasonable minimum amount of RAM to start. */ + + if (default_ram < min_ram) + { + i_error("Unable to allocate %i MiB of RAM for zone", default_ram); + } + + /* Try to allocate the zone memory. */ + + *size = default_ram * 1024 * 1024; + + zonemem = malloc(*size); + + /* Failed to allocate? Reduce zone size until we reach a size + * that is acceptable. + */ + + if (zonemem == NULL) + { + default_ram -= 1; + } + } + + return zonemem; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_at_exit(atexit_func_t func, boolean run_on_error) +{ + atexit_listentry_t *entry; + + entry = malloc(sizeof(*entry)); + + entry->func = func; + entry->run_on_error = run_on_error; + entry->next = exit_funcs; + exit_funcs = entry; +} + +/* Tactile feedback function, probably used for the Logitech Cyberman */ + +void i_tactile(int on, int off, int total) +{ +} + +byte *i_zone_base(int *size) +{ + byte *zonemem; + int min_ram; + int default_ram; + int p; + + /* @category obscure + * @arg + * + * Specify the heap size, in MiB. + */ + + p = m_check_parm_with_args("-mb", 1); + + if (p > 0) + { + default_ram = atoi(myargv[p + 1]); + min_ram = default_ram; + } + else + { + /* Because of the 8-byte pointer size in a 64-bit build, the default + * heap size (16 MiB) is insufficient compared to a 32-bit build. For + * example, the Alien Vendetta avm62402.lmp demo completes successfully + * on a 32-bit build, but terminates with an out of memory error on a + * 64-bit build. Therefore, to maintain consistency with a 32-bit + * build, the heap size should be increased. + */ + + if (sizeof(void *) == 8) + { + default_ram = DEFAULT_RAM * 2; + } + else + { + default_ram = DEFAULT_RAM; + } + + min_ram = MIN_RAM; + } + + zonemem = auto_alloc_memory(size, default_ram, min_ram); + + printf("zone memory: %p, %x allocated for zone\n", zonemem, *size); + + return zonemem; +} + +void i_print_banner(const char *msg) +{ + int i; + int spaces = 35 - (strlen(msg) / 2); + + for (i = 0; i < spaces; ++i) + putchar(' '); + + puts(msg); +} + +void i_print_divider(void) +{ + int i; + + for (i = 0; i < 75; ++i) + { + putchar('='); + } + + putchar('\n'); +} + +void i_print_startup_banner(const char *gamedescription) +{ + i_print_divider(); + i_print_banner(gamedescription); + i_print_divider(); + + printf( + " " PACKAGE_NAME + " is free software, covered by the GNU General Public\n" + " License. There is NO warranty; not even for MERCHANTABILITY or " + "FITNESS\n" + " FOR A PARTICULAR PURPOSE. You are welcome to change and distribute\n" + " copies under certain conditions. See the source for more " + "information.\n"); + + i_print_divider(); +} + +/* i_console_stdout + * + * Returns true if stdout is a real console, false if it is a file + */ + +boolean i_console_stdout(void) +{ + return isatty(fileno(stdout)); +} + +/* i_init */ + +#if 0 +void i_init(void) +{ + i_check_is_screensaver(); + i_init_timer(); + i_init_joystick(); +} + +void i_bind_variables(void) +{ + i_bind_video_variables(); + i_bind_joystick_variables(); + i_bind_sound_variables(); +} +#endif + +/* i_quit */ + +void i_quit(void) +{ + atexit_listentry_t *entry; + + /* Run through all exit functions */ + + entry = exit_funcs; + + while (entry != NULL) + { + entry->func(); + entry = entry->next; + } + + exit(0); +} + +void i_error(const char *error, ...) +{ + char msgbuf[512]; + va_list argptr; + atexit_listentry_t *entry; + boolean exit_gui_popup; + + if (already_quitting) + { + fprintf(stderr, "Warning: recursive call to i_error detected.\n"); + exit(-1); + } + else + { + already_quitting = true; + } + + /* Message first. */ + + va_start(argptr, error); + + vfprintf(stderr, error, argptr); + fprintf(stderr, "\n\n"); + va_end(argptr); + fflush(stderr); + + /* Write a copy of the message into buffer. */ + + va_start(argptr, error); + memset(msgbuf, 0, sizeof(msgbuf)); + vsnprintf(msgbuf, sizeof(msgbuf), error, argptr); + va_end(argptr); + + /* Shutdown. Here might be other errors. */ + + entry = exit_funcs; + + while (entry != NULL) + { + if (entry->run_on_error) + { + entry->func(); + } + + entry = entry->next; + } + + /* @category obscure + * + * If specified, don't show a GUI window for error messages when the + * game exits with an error. + */ + + exit_gui_popup = !m_parm_exists("-nogui"); + + /* Pop up a GUI dialog box to show the error message, if the + * game was not run from the console (and the user will + * therefore be unable to otherwise see the message). + */ + + if (exit_gui_popup && !i_console_stdout()) + { + } + + /* abort(); */ + + exit(-1); +} + +void *i_realloc(void *ptr, size_t size) +{ + void *new_ptr; + + new_ptr = realloc(ptr, size); + + if (size != 0 && new_ptr == NULL) + { + i_error("i_realloc: failed on reallocation of %zu bytes", size); + } + + return new_ptr; +} + +boolean i_get_memory_value(unsigned int offset, void *value, int size) +{ + static boolean firsttime = true; + + if (firsttime) + { + int p; + int i; + int val; + + firsttime = false; + i = 0; + + /* @category compat + * @arg + * + * Specify DOS version to emulate for NULL pointer dereference + * emulation. Supported versions are: dos622, dos71, dosbox. + * The default is to emulate DOS 7.1 (Windows 98). + */ + + p = m_check_parm_with_args("-setmem", 1); + + if (p > 0) + { + if (!strcasecmp(myargv[p + 1], "dos622")) + { + dos_mem_dump = mem_dump_dos622; + } + + if (!strcasecmp(myargv[p + 1], "dos71")) + { + dos_mem_dump = mem_dump_win98; + } + else if (!strcasecmp(myargv[p + 1], "dosbox")) + { + dos_mem_dump = mem_dump_dosbox; + } + else + { + for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i) + { + ++p; + + if (p >= myargc || myargv[p][0] == '-') + { + break; + } + + m_str_to_int(myargv[p], &val); + mem_dump_custom[i++] = (unsigned char)val; + } + + dos_mem_dump = mem_dump_custom; + } + } + } + + switch (size) + { + case 1: + *((unsigned char *)value) = dos_mem_dump[offset]; + return true; + case 2: + *((unsigned short *)value) = + dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8); + return true; + case 4: + *((unsigned int *)value) = + dos_mem_dump[offset] | (dos_mem_dump[offset + 1] << 8) | + (dos_mem_dump[offset + 2] << 16) | + (dos_mem_dump[offset + 3] << 24); + return true; + } + + return false; +} diff --git a/games/NXDoom/src/i_system.h b/games/NXDoom/src/i_system.h new file mode 100644 index 000000000..ae1c56806 --- /dev/null +++ b/games/NXDoom/src/i_system.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_system.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_event.h" +#include "d_ticcmd.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*atexit_func_t)(void); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Called by DoomMain. */ + +void i_init(void); + +/* Called by startup code to get the amount of memory to malloc for the zone + * management. + */ + +byte *i_zone_base(int *size); + +boolean i_console_stdout(void); + +/* Asynchronous interrupt functions should maintain private queues + * that are read by the synchronous functions + * to be converted into events. + */ + +#if 0 /* Unused */ + +/* Either returns a null ticcmd, or calls a loadable driver to build it. + * This ticcmd will then be modified by the gameloop for normal input. + */ + +ticcmd_t *i_base_ticcmd(void); +#endif + +/* Called by m_responder when quit is selected. + * Clean exit, displays sell blurb. + */ + +void i_quit(void) NORETURN; + +void i_error(const char *error, ...) NORETURN PRINTF_ATTR(1, 2); + +void i_tactile(int on, int off, int total); + +void *i_realloc(void *ptr, size_t size); + +boolean i_get_memory_value(unsigned int offset, void *value, int size); + +/* Schedule a function to be called when the program exits. + * If run_if_error is true, the function is called if the exit + * is due to an error (i_error) + */ + +void i_at_exit(atexit_func_t func, boolean run_if_error); + +/* Add all system-specific config file variable bindings. */ + +void i_bind_variables(void); + +/* Print startup banner copyright message. */ + +void i_print_startup_banner(const char *gamedescription); + +/* Print a centered text banner displaying the given string. */ + +void i_print_banner(const char *text); + +/* Print a dividing line for startup banners. */ + +void i_print_divider(void); + +#endif /* __I_SYSTEM__ */ diff --git a/games/NXDoom/src/i_timer.c b/games/NXDoom/src/i_timer.c new file mode 100644 index 000000000..abde6d4c9 --- /dev/null +++ b/games/NXDoom/src/i_timer.c @@ -0,0 +1,107 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_timer.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Timer functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_timer.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The start time of the game used as the base for tic calculations. */ + +static struct timespec basetime = +{ + .tv_nsec = 0, .tv_sec = 0 +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i_get_time + * + * Description: + * Called by D_DoomLoop. + * + * Returns: + * The current time in 1/35th second tics. + ****************************************************************************/ + +int i_get_time(void) +{ + return (i_get_time_ms() * TICRATE) / 1000; +} + +/**************************************************************************** + * Name: i_get_time_ms + * + * Returns: + * The current time in ms. + ****************************************************************************/ + +int i_get_time_ms(void) +{ + struct timespec curtime; + + /* NOTE: we ignore any possible error here */ + + clock_gettime(CLOCK_MONOTONIC, &curtime); + if (basetime.tv_sec == 0 && basetime.tv_nsec == 0) + { + clock_gettime(CLOCK_MONOTONIC, &basetime); + } + + return (clock_time2usec(&curtime) - clock_time2usec(&basetime)) / 1000; +} + +/**************************************************************************** + * Name: i_init_timer + * + * Description: + * Initialize timer. + ****************************************************************************/ + +void i_init_timer(void) +{ +} + +/**************************************************************************** + * Name: i_wait_vbl + * + * Description: + * Wait for vertical retrace or pause a bit. + ****************************************************************************/ + +void i_wait_vbl(int count) +{ + usleep((count * 1000000) / 70); +} diff --git a/games/NXDoom/src/i_timer.h b/games/NXDoom/src/i_timer.h new file mode 100644 index 000000000..eb9d7756c --- /dev/null +++ b/games/NXDoom/src/i_timer.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_timer.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System-specific timer interface + * + ****************************************************************************/ + +#ifndef __I_TIMER__ +#define __I_TIMER__ + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define TICRATE 35 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_get_time + * + * Description: + * Called by d_doomloop. + * + * Returns: + * The current time in tics. + ****************************************************************************/ + +int i_get_time(void); + +/**************************************************************************** + * Name: i_get_time_ms + * + * Returns: + * The current time in ms. + ****************************************************************************/ + +int i_get_time_ms(void); + +/**************************************************************************** + * Name: i_init_timer + * + * Description: + * Initialize timer. + ****************************************************************************/ + +void i_init_timer(void); + +/**************************************************************************** + * Name: i_wait_vbl + * + * Description: + * Wait for vertical retrace or pause a bit. + ****************************************************************************/ + +void i_wait_vbl(int count); + +#endif /* __I_TIMER__ */ diff --git a/games/NXDoom/src/i_video.c b/games/NXDoom/src/i_video.c new file mode 100644 index 000000000..d9a76a651 --- /dev/null +++ b/games/NXDoom/src/i_video.c @@ -0,0 +1,1069 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_video.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * DOOM graphics stuff for SDL. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "config.h" +#include "d_loop.h" +#include "deh_str.h" +#include "doomtype.h" +#include "i_input.h" +#include "i_joystick.h" +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" +#include "tables.h" +#include "v_diskicon.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#define RESIZE_DELAY 500 + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct graphics_state_s +{ + int fd; /* File descriptor handle to frame buffer */ + + /* The 320x200x32 RGBA intermediate buffer is what we blit the former + * buffer to. On NuttX, this is the frame buffer memory `fbmem`. It may not + * have 32-bit depth, but if it doesn't, the code is adjusted accordingly. + */ + + FAR void *fbmem; + + /* 8-bit depth screen buffer (320x200x8) that we draw to (i.e. the one that + * holds i_video_buffer) + */ + + pixel_t *scrnbuf; + + /* Information about the frame buffer needed for rendering. */ + + struct fb_videoinfo_s vinfo; + struct fb_planeinfo_s pinfo; + + /* Scale multiplier for rendering large image */ + + uint8_t scale; + + /* Byte offset into the frame buffer of the top left corner of the scaled + * image, so that it is centred on displays it does not fill. + */ + + size_t origin; + + /* Size of the scaled image in pixels. This is an integer multiple of the + * DOOM resolution unless the image is stretched to fill the display. + */ + + unsigned outw; + unsigned outh; + + /* Maps an output column onto the source column it is drawn from, so that + * the inner loop needs neither a division nor a separate case for + * fractional scaling. + */ + + FAR uint16_t *colmap; + + bool inited; /* Track initialization */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* NuttX graphics state */ + +static struct graphics_state_s g_graphics_state = +{ + 0 +}; + +#ifdef CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF +/* The buffer DOOM renders into. Keeping it in .bss rather than on the heap + * puts it in internal RAM on boards whose heap is mostly external memory. + */ + +static pixel_t g_scrnbuf[SCREENWIDTH * SCREENHEIGHT]; +#endif + +#ifdef CONFIG_GAMES_NXDOOM_ROWSTAGE +/* Staging area for one output row. Expanding a row here and then copying it + * out keeps the per-pixel writes in internal RAM and turns every frame + * buffer access into a burst-friendly copy, which is what an external frame + * buffer wants. Declared as uint32_t for alignment; it holds one row at the + * largest scale and pixel size it can serve. + */ + +#define BLIT_MAX_STAGED_SCALE 4 + +static uint32_t g_rowbuf[SCREENWIDTH * BLIT_MAX_STAGED_SCALE]; +#endif + +/* Window title */ + +static const char *g_window_title = ""; + +/* Colour palette map from 8-bit colour to 32-bit */ + +static struct argbcolor_s g_palette[256]; + +/* The same palette, pre-converted to the pixel format of the frame buffer. + * Doing the conversion once per palette change instead of once per pixel + * keeps blit_screen() down to a lookup and a store. + */ + +static uint32_t g_palette_native[256]; + +static boolean palette_to_set; + +/* disable mouse? */ + +static boolean nomouse = false; + +/* Maximum number of pixels to use for intermediate scale buffer. */ + +static int max_scaling_buffer_pixels = 16000000; + +/* Time to wait for the screen to settle on startup before starting the game + * (ms) + */ + +static int startup_delay = 1000; + +/* Grab the mouse? (int type for config code). nograbmouse_override allows + * this to be temporarily disabled via the command line. + */ + +static int grabmouse = true; +static boolean nograbmouse_override = false; + +/* If true, we display dots at the bottom of the screen to + * indicate FPS. + */ + +static boolean display_fps_dots; + +/* If this is true, the screen is rendered but not blitted to the + * video buffer. + */ + +static boolean noblit; + +/* Callback function to invoke to determine whether to grab the + * mouse pointer. + */ + +static grabmouse_callback_t grabmouse_callback = NULL; + +/* Does the window currently have focus? */ + +static boolean window_focused = true; + +/* Window resize state. */ + +#if 0 +static boolean need_resize = false; +static unsigned int last_resize_time; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int usemouse = 1; + +/* Save screenshots in PNG format. */ + +int png_screenshots = 0; + +/* SDL video driver name */ + +char *video_driver = ""; + +/* Window position: */ + +char *window_position = "center"; + +/* SDL display number on which to run. */ + +int video_display = 0; + +/* Screen width and height, from configuration file. */ + +int window_width = 320; +int window_height = 200; + +/* Fullscreen mode, 0x0 for SDL_WINDOW_FULLSCREEN_DESKTOP. */ + +int fullscreen_width = 0; +int fullscreen_height = 0; + +/* Run in full screen mode? (int type for config code) */ + +int fullscreen = true; + +/* Smooth pixel scaling */ + +int smooth_pixel_scaling = true; + +/* Force integer scales for resolution-independent rendering */ + +int integer_scaling = false; + +/* VGA Porch palette change emulation */ + +int vga_porch_flash = false; + +/* Force software rendering, for systems which lack effective hardware + * acceleration + */ + +int force_software_renderer = false; + +/* The screen buffer; this is modified to draw things to the screen */ + +pixel_t *i_video_buffer = NULL; + +/* If true, game is running as a screensaver */ + +boolean screensaver_mode = false; + +/* Flag indicating whether the screen is currently visible: + * when the screen isn't visible, don't render the screen + */ + +boolean screenvisible = true; + +/* Gamma correction level to use */ + +int usegamma = 0; + +/* Joystick/gamepad hysteresis */ + +unsigned int joywait = 0; + +/* TODO: I'm sure more of the variables above can be private */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: update_native_palette + * + * Description: + * Convert the ARGB palette into the pixel format of the frame buffer so + * that blitting is a plain table lookup. + * + ****************************************************************************/ + +static void update_native_palette(void) +{ + int i; + +#ifdef CONFIG_GAMES_NXDOOM_FB_CMAP + uint8_t red[256]; + uint8_t green[256]; + uint8_t blue[256]; +#ifdef CONFIG_FB_TRANSPARENCY + uint8_t transp[256]; +#endif + struct fb_cmap_s cmap; + + /* Hand the palette to the display and leave the lookup an identity, so + * that blitting writes the palette index and the hardware converts it + * while it scans the display out. + */ + + for (i = 0; i < 256; i++) + { + red[i] = g_palette[i].r; + green[i] = g_palette[i].g; + blue[i] = g_palette[i].b; +#ifdef CONFIG_FB_TRANSPARENCY + transp[i] = 0xff; +#endif + g_palette_native[i] = i; + } + + cmap.first = 0; + cmap.len = 256; + cmap.red = red; + cmap.green = green; + cmap.blue = blue; +#ifdef CONFIG_FB_TRANSPARENCY + cmap.transp = transp; +#endif + + if (ioctl(g_graphics_state.fd, FBIOPUT_CMAP, + (unsigned long)((uintptr_t)&cmap)) < 0) + { + i_error("ioctl(FBIOPUT_CMAP) failed: %d\n", errno); + } +#else + for (i = 0; i < 256; i++) + { + switch (g_graphics_state.pinfo.bpp) + { + case 8: + g_palette_native[i] = RGBTO8(g_palette[i].r, g_palette[i].g, + g_palette[i].b); + break; + + case 16: + g_palette_native[i] = RGBTO16(g_palette[i].r, g_palette[i].g, + g_palette[i].b); + break; + + case 24: + g_palette_native[i] = RGBTO24(g_palette[i].r, g_palette[i].g, + g_palette[i].b); + break; + + default: + g_palette_native[i] = ARGBTO32(g_palette[i].a, g_palette[i].r, + g_palette[i].g, g_palette[i].b); + break; + } + } +#endif +} + +/**************************************************************************** + * Name: blit_screen + * + * Description: + * Blit the 8-bit depth buffer that DOOM renders to onto the frame buffer, + * converting to the pixel format of the frame buffer and scaling up by an + * integer factor. + * + ****************************************************************************/ + +static void blit_screen(void) +{ + FAR uint8_t *fbbase = (FAR uint8_t *)g_graphics_state.fbmem + + g_graphics_state.origin; + FAR const uint16_t *colmap = g_graphics_state.colmap; + unsigned stride = g_graphics_state.pinfo.stride; + unsigned pixlen = g_graphics_state.pinfo.bpp >> 3; + unsigned outw = g_graphics_state.outw; + unsigned outh = g_graphics_state.outh; + unsigned rowbytes = outw * pixlen; +#ifdef CONFIG_GAMES_NXDOOM_ROWSTAGE + bool staged = rowbytes <= sizeof(g_rowbuf); +#else + const bool staged = false; +#endif + FAR uint8_t *prevrow = NULL; + unsigned prevsy = SCREENHEIGHT; + unsigned x; + unsigned oy; + unsigned sy; + + for (oy = 0; oy < outh; oy++) + { + FAR uint8_t *dst = fbbase + oy * stride; +#ifdef CONFIG_GAMES_NXDOOM_ROWSTAGE + FAR uint8_t *out = staged ? (FAR uint8_t *)g_rowbuf : dst; +#else + FAR uint8_t *out = dst; +#endif + FAR const pixel_t *src; + FAR uint8_t *dest8 = out; + FAR uint16_t *dest16 = (FAR uint16_t *)out; + FAR uint32_t *dest32 = (FAR uint32_t *)out; + uint32_t pixel; + + /* Which source row this output row comes from. Several output rows + * map to the same source row whenever the image is scaled up. + */ + + sy = oy * SCREENHEIGHT / outh; + if (sy == prevsy) + { + /* Identical to the row just built, so copy it rather than + * converting the same pixels again. + */ + + memcpy(dst, prevrow, rowbytes); + continue; + } + + /* Expand one source row. The column map turns this into a lookup per + * output pixel, with no division and no special case for a scale + * factor that is not a whole number. + */ + + src = &g_graphics_state.scrnbuf[sy * SCREENWIDTH]; + + switch (g_graphics_state.pinfo.bpp) + { + case 8: + for (x = 0; x < outw; x++) + { + *dest8++ = g_palette_native[src[colmap[x]]]; + } + break; + + case 16: + for (x = 0; x < outw; x++) + { + *dest16++ = g_palette_native[src[colmap[x]]]; + } + break; + + case 24: + for (x = 0; x < outw; x++) + { + pixel = g_palette_native[src[colmap[x]]]; + + *dest8++ = pixel; + *dest8++ = pixel >> 8; + *dest8++ = pixel >> 16; + } + break; + + default: + for (x = 0; x < outw; x++) + { + *dest32++ = g_palette_native[src[colmap[x]]]; + } + break; + } + + if (staged) + { + memcpy(dst, out, rowbytes); + } + + prevsy = sy; + prevrow = out; + } +} + +static void update_grab(void) +{ +} + +static void set_video_mode(void) +{ +} + +static void i_get_event(void) +{ + int err; +#if CONFIG_GAMES_NXDOOM_KEYBOARD + struct keyboard_event_s kbdevent; + + while ((err = get_kbd_event(&kbdevent)) == 0) + { + /* All four event types are handled: dropping the special ones here + * would silently lose the arrow keys. + */ + + i_handle_keyboard_event(&kbdevent); + } +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_set_grab_mouse_callback(grabmouse_callback_t func) +{ + grabmouse_callback = func; +} + +/* Set the variable controlling FPS dots. */ + +void i_display_fps_dots(boolean dots_on) +{ + display_fps_dots = dots_on; +} + +void i_shutdown_graphics(void) +{ + if (!g_graphics_state.inited) + { + return; + } + + close(g_graphics_state.fd); + munmap(g_graphics_state.fbmem, g_graphics_state.pinfo.fblen); +#ifndef CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF + free(g_graphics_state.scrnbuf); +#endif + free(g_graphics_state.colmap); + g_graphics_state.inited = false; +} + +void i_start_frame(void) +{ + /* er? */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void i_start_tic(void) +{ + if (!g_graphics_state.inited) + { + return; + } + + i_get_event(); + + if (usemouse && !nomouse && window_focused) + { + i_read_mouse(); + } + + if (joywait < i_get_time()) + { + i_update_joystick(); + } +} + +void i_update_no_blit(void) +{ + /* what is this? */ +} + +void i_finish_update(void) +{ + static int lasttic; + int tics; + int i; + + if (!g_graphics_state.inited) return; + + if (noblit) return; + + /* draws little dots on the bottom of the screen */ + + if (display_fps_dots) + { + i = i_get_time(); + tics = i - lasttic; + lasttic = i; + if (tics > 20) tics = 20; + + for (i = 0; i < tics * 4; i += 4) + i_video_buffer[(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0xff; + for (; i < 20 * 4; i += 4) + i_video_buffer[(SCREENHEIGHT - 1) * SCREENWIDTH + i] = 0x0; + } + + /* Draw disk icon before blit, if necessary. */ + + v_draw_disk_icon(); + + if (palette_to_set) + { + palette_to_set = false; + update_native_palette(); + } + + blit_screen(); + + /* Draw! */ + + /* Restore background and undo the disk indicator, if it was drawn. */ + + v_restore_disk_background(); +} + +void i_read_screen(pixel_t *scr) +{ + memcpy(scr, i_video_buffer, SCREENWIDTH * SCREENHEIGHT * sizeof(*scr)); +} + +/**************************************************************************** + * Name: i_set_palette + ****************************************************************************/ + +void i_set_palette(byte *doompalette) +{ + for (int i = 0; i < 256; ++i) + { + /* Zero out the bottom two bits of each channel - the PC VGA + * controller only supports 6 bits of accuracy. + */ + + g_palette[i].a = 0xffu; + g_palette[i].r = gammatable[usegamma][*doompalette++] & ~3; + g_palette[i].g = gammatable[usegamma][*doompalette++] & ~3; + g_palette[i].b = gammatable[usegamma][*doompalette++] & ~3; + } + + palette_to_set = true; +} + +/**************************************************************************** + * Name: i_get_palette_index + * + * Description: + * Given an RGB value, find the closest matching palette index. + * + * Return: + * An index into the palette lookup table for the best match. + * + ****************************************************************************/ + +int i_get_palette_index(int r, int g, int b) +{ + int best = 0; + int best_diff = INT_MAX; + int diff; + + for (int i = 0; i < 256; ++i) + { + diff = (r - g_palette[i].r) * (r - g_palette[i].r) + + (g - g_palette[i].g) * (g - g_palette[i].g) + + (b - g_palette[i].b) * (b - g_palette[i].b); + + if (diff < best_diff) + { + best = i; + best_diff = diff; + } + + if (diff == 0) + { + break; + } + } + + return best; +} + +/**************************************************************************** + * Name: i_set_window_title + * + * Description: + * Set the window title internally. + * + ****************************************************************************/ + +void i_set_window_title(const char *title) +{ + g_window_title = title; +} + +/**************************************************************************** + * Name: i_init_window_title + * + * Description: + * Actually cause the window title to update with whatever window title was + * last set via i_set_window_title. + * + ****************************************************************************/ + +void i_init_window_title(void) +{ +} + +/**************************************************************************** + * Name: i_set_window_title + ****************************************************************************/ + +void i_graphics_check_commandline(void) +{ + int i; + + /* @category video + * @vanilla + * + * Disable blitting the screen. + */ + + noblit = m_check_parm("-noblit"); + + /* @category video + * + * Don't grab the mouse when running in windowed mode. + */ + + nograbmouse_override = m_parm_exists("-nograbmouse"); + + /* default to fullscreen mode, allow override with command line + * nofullscreen because we love prboom + */ + + /* @category video + * + * Run in a window. + */ + + if (m_check_parm("-window") || m_check_parm("-nofullscreen")) + { + fullscreen = false; + } + + /* @category video + * + * Run in fullscreen mode. + */ + + if (m_check_parm("-fullscreen")) + { + fullscreen = true; + } + + /* @category video + * + * Disable the mouse. + */ + + nomouse = m_check_parm("-nomouse") > 0; + + /* @category video + * @arg + * + * Specify the screen width, in pixels. Implies -window. + */ + + i = m_check_parm_with_args("-width", 1); + + if (i > 0) + { + window_width = atoi(myargv[i + 1]); + fullscreen = false; + } + + /* @category video + * @arg + * + * Specify the screen height, in pixels. Implies -window. + */ + + i = m_check_parm_with_args("-height", 1); + + if (i > 0) + { + window_height = atoi(myargv[i + 1]); + fullscreen = false; + } + + /* @category video + * @arg + * + * Specify the dimensions of the window. Implies -window. + */ + + i = m_check_parm_with_args("-geometry", 1); + + if (i > 0) + { + int w; + int h; + int s; + + s = sscanf(myargv[i + 1], "%ix%i", &w, &h); + if (s == 2) + { + window_width = w; + window_height = h; + fullscreen = false; + } + } + + /* @category video + * @arg + * + * Specify the display number on which to show the screen. + */ + + i = m_check_parm_with_args("-display", 1); + + if (i > 0) + { + int display = atoi(myargv[i + 1]); + if (display >= 0) + { + video_display = display; + } + } +} + +/* Check if we have been invoked as a screensaver by xscreensaver. */ + +void i_check_is_screensaver(void) +{ + char *env; + + env = getenv("XSCREENSAVER_WINDOW"); + + if (env != NULL) + { + screensaver_mode = true; + } +} + +/* Check the display bounds of the display referred to by 'video_display' and + * set x and y to a location that places the window in the center of that + * display. + */ + +static void center_window(int *x, int *y, int w, int h) +{ + *x = MAX((g_graphics_state.vinfo.xres - w) / 2, 0); + *y = MAX((g_graphics_state.vinfo.yres - h) / 2, 0); +} + +void i_get_window_position(int *x, int *y, int w, int h) +{ + /* in fullscreen mode, the window "position" still matters, because + * we use it to control which display we run fullscreen on. + */ + + if (fullscreen) + { + center_window(x, y, w, h); + return; + } +} + +void i_init_graphics(void) +{ + uint8_t xscale; + uint8_t yscale; + unsigned i; + int err; + byte *doompal; + + /* Open frame buffer */ + + g_graphics_state.fd = open(CONFIG_GAMES_NXDOOM_FBPATH, O_RDWR | O_CLOEXEC); + if (g_graphics_state.fd < 0) + { + i_error("Failed to open frame buffer: %d", errno); + } + + /* Get frame buffer characteristics */ + + err = ioctl(g_graphics_state.fd, FBIOGET_VIDEOINFO, + (unsigned long)(uintptr_t)&g_graphics_state.vinfo); + if (err < 0) + { + close(g_graphics_state.fd); + i_error("Failed to get video info: %d", errno); + } + + /* Here, we check the dimensions of the frame buffer. If we have enough + * space to scale up the rendered image in both width and height, record + * that so we can make use of it elsewhere. + * + * If we don't have enough frame buffer space for the game, quit! + */ + + if (g_graphics_state.vinfo.xres < SCREENWIDTH) + { + i_error("Resolution width of %u px < minimum of %u px\n", + g_graphics_state.vinfo.xres, SCREENWIDTH); + } + + if (g_graphics_state.vinfo.yres < SCREENHEIGHT) + { + i_error("Resolution height of %u px < minimum of %u px\n", + g_graphics_state.vinfo.yres, SCREENHEIGHT); + } + + xscale = g_graphics_state.vinfo.xres / SCREENWIDTH; + yscale = g_graphics_state.vinfo.yres / SCREENHEIGHT; + g_graphics_state.scale = xscale > yscale ? yscale : xscale; + +#ifdef CONFIG_GAMES_NXDOOM_FILLSCREEN + /* Stretch to the whole display. The scale factor is then generally not an + * integer, which the column map below takes care of. + */ + + g_graphics_state.outw = g_graphics_state.vinfo.xres; + g_graphics_state.outh = g_graphics_state.vinfo.yres; +#else + g_graphics_state.outw = SCREENWIDTH * g_graphics_state.scale; + g_graphics_state.outh = SCREENHEIGHT * g_graphics_state.scale; +#endif + + /* Centre the scaled image in the frame buffer */ + + g_graphics_state.origin = + (g_graphics_state.vinfo.yres - g_graphics_state.outh) / 2 * + g_graphics_state.pinfo.stride + + (g_graphics_state.vinfo.xres - g_graphics_state.outw) / 2 * + (g_graphics_state.pinfo.bpp >> 3); + + /* Build the output column to source column map once */ + + g_graphics_state.colmap = malloc(g_graphics_state.outw * sizeof(uint16_t)); + if (g_graphics_state.colmap == NULL) + { + i_error("Couldn't allocate column map: %d\n", errno); + } + + for (i = 0; i < g_graphics_state.outw; i++) + { + g_graphics_state.colmap[i] = i * SCREENWIDTH / g_graphics_state.outw; + } + + /* Get frame buffer plane info */ + + if (ioctl(g_graphics_state.fd, FBIOGET_PLANEINFO, + (unsigned long)((uintptr_t)&g_graphics_state.pinfo)) < 0) + { + i_error("ioctl(FBIOGET_PLANEINFO) failed: %d\n", errno); + } + + /* Initialize frame buffer memory for actual rendering */ + + g_graphics_state.fbmem = + mmap(NULL, g_graphics_state.pinfo.fblen, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FILE, g_graphics_state.fd, 0); + if (g_graphics_state.fbmem == MAP_FAILED) + { + i_error("mmap() of frame buffer failed: %d\n", errno); + } + + /* Create an 8-bit depth screen buffer for DOOM to render to */ + +#ifdef CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF + g_graphics_state.scrnbuf = g_scrnbuf; +#else + g_graphics_state.scrnbuf = malloc(SCREENWIDTH * SCREENHEIGHT); +#endif + if (g_graphics_state.scrnbuf == NULL) + { + i_error("Couldn't allocate screen buffer: %d\n", errno); + } + + /* Create the game window; this may switch graphic modes depending + * on configuration. + * AdjustWindowSize(); + */ + + set_video_mode(); + + /* Start with a clear black screen + * (screen will be flipped after we set the palette) + */ + + memset(g_graphics_state.scrnbuf, 0, SCREENHEIGHT * SCREENWIDTH); + + /* Set the palette */ + + doompal = w_cache_lump_name(("PLAYPAL"), PU_CACHE); + i_set_palette(doompal); + + update_grab(); + + /* On some systems, it takes a second or so for the screen to settle + * after changing modes. We include the option to add a delay when + * setting the screen mode, so that the game doesn't start immediately + * with the player unable to see anything. + */ + + if (fullscreen && !screensaver_mode) + { + usleep(startup_delay * 1000); + } + + /* The actual 320x200 canvas that we draw to. This is the pixel buffer of + * the 8-bit paletted screen buffer that gets blit on an intermediate + * 32-bit RGBA screen buffer that gets loaded into a texture that gets + * finally rendered into our window or full screen in i_finish_update(). + */ + + i_video_buffer = g_graphics_state.scrnbuf; + v_restore_buffer(); + + /* Clear the screen to black. */ + + memset(i_video_buffer, 0, + SCREENWIDTH * SCREENHEIGHT * sizeof(*i_video_buffer)); + + /* clear out any events waiting at the start and center the mouse */ + + g_graphics_state.inited = true; + + /* Call i_shutdown_graphics on quit */ + + i_at_exit(i_shutdown_graphics, true); +} + +/* Bind all variables controlling video options into the configuration + * file system. + */ + +void i_bind_video_variables(void) +{ + m_bind_int_variable("use_mouse", &usemouse); + m_bind_int_variable("fullscreen", &fullscreen); + m_bind_int_variable("video_display", &video_display); + m_bind_int_variable("integer_scaling", &integer_scaling); + m_bind_int_variable("smooth_pixel_scaling", &smooth_pixel_scaling); + m_bind_int_variable("vga_porch_flash", &vga_porch_flash); + m_bind_int_variable("startup_delay", &startup_delay); + m_bind_int_variable("fullscreen_width", &fullscreen_width); + m_bind_int_variable("fullscreen_height", &fullscreen_height); + m_bind_int_variable("force_software_renderer", &force_software_renderer); + m_bind_int_variable("max_scaling_buffer_pixels", + &max_scaling_buffer_pixels); + m_bind_int_variable("window_width", &window_width); + m_bind_int_variable("window_height", &window_height); + m_bind_int_variable("grabmouse", &grabmouse); + m_bind_string_variable("video_driver", &video_driver); + m_bind_string_variable("window_position", &window_position); + m_bind_int_variable("usegamma", &usegamma); + m_bind_int_variable("png_screenshots", &png_screenshots); +} diff --git a/games/NXDoom/src/i_video.h b/games/NXDoom/src/i_video.h new file mode 100644 index 000000000..322160f9b --- /dev/null +++ b/games/NXDoom/src/i_video.h @@ -0,0 +1,154 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_video.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Screen width and height. */ + +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 + +/* Screen height used when aspect_ratio_correct=true. */ + +#define SCREENHEIGHT_4_3 240 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef boolean (*grabmouse_callback_t)(void); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern char *video_driver; +extern boolean screenvisible; + +extern int vanilla_keyboard_mapping; +extern boolean screensaver_mode; +extern int usegamma; +extern pixel_t *i_video_buffer; + +extern int screen_width; +extern int screen_height; +extern int fullscreen; +extern int aspect_ratio_correct; +extern int integer_scaling; +extern int smooth_pixel_scaling; +extern int vga_porch_flash; +extern int force_software_renderer; + +extern int png_screenshots; + +extern char *window_position; + +/* Joystic/gamepad hysteresis */ + +extern unsigned int joywait; + +extern int usemouse; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: i_init_graphics + * + * Description: + * Called by d_doom_main, determines the hardware configuration and sets up + * the video mode. + * + ****************************************************************************/ + +void i_init_graphics(void); + +void i_graphics_check_commandline(void); + +void i_shutdown_graphics(void); + +/**************************************************************************** + * Name: i_set_palette + * + * Description: + * Takes full 8 bit values. + * + ****************************************************************************/ + +void i_set_palette(byte *palette); + +int i_get_palette_index(int r, int g, int b); + +void i_update_no_blit(void); +void i_finish_update(void); + +void i_read_screen(pixel_t *scr); + +void i_set_window_title(const char *title); + +void i_check_is_screensaver(void); +void i_set_grab_mouse_callback(grabmouse_callback_t func); + +void i_display_fps_dots(boolean dots_on); +void i_bind_video_variables(void); + +void i_init_window_title(void); + +/**************************************************************************** + * Name: i_start_frame + * + * Description: + * Called before processing any tics in a frame (just after displaying a + * frame). Time consuming synchronous operations are performed here + * (joystick reading). + * + ****************************************************************************/ + +void i_start_frame(void); + +/**************************************************************************** + * Name: i_start_tic + * + * Description: + * Called before processing each tic in a frame. Quick synchronous + * operations are performed here. + * + ****************************************************************************/ + +void i_start_tic(void); + +void i_get_window_position(int *x, int *y, int w, int h); + +#endif /* __I_VIDEO__ */ diff --git a/games/NXDoom/src/m_argv.c b/games/NXDoom/src/m_argv.c new file mode 100644 index 000000000..a9b0ccd14 --- /dev/null +++ b/games/NXDoom/src/m_argv.c @@ -0,0 +1,349 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_argv.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_iwad.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAXARGVS 100 + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int myargc; +char **myargv; + +char *exedir = NULL; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void load_response_file(int argv_index, const char *filename) +{ + FILE *handle; + int size; + char *infile; + char *file; + char **newargv; + int newargc; + int i; + int k; + + /* Read the response file into memory */ + + handle = fopen(filename, "rb"); + + if (handle == NULL) + { + printf("\nNo such response file!"); + exit(1); + } + + printf("Found response file %s!\n", filename); + + size = m_file_length(handle); + + /* Read in the entire file + * Allocate one byte extra - this is in case there is an argument + * at the end of the response file, in which case a '\0' will be + * needed. + */ + + file = malloc(size + 1); + + i = 0; + + while (i < size) + { + k = fread(file + i, 1, size - i, handle); + + if (k < 0) + { + i_error("Failed to read full contents of '%s'", filename); + } + + i += k; + } + + fclose(handle); + + /* Create new arguments list array */ + + newargv = malloc(sizeof(char *) * MAXARGVS); + newargc = 0; + memset(newargv, 0, sizeof(char *) * MAXARGVS); + + /* Copy all the arguments in the list up to the response file */ + + if (argv_index >= MAXARGVS) + { + i_error("Too many arguments up to the response file!"); + } + + for (i = 0; i < argv_index; ++i) + { + newargv[i] = myargv[i]; + myargv[i] = NULL; + ++newargc; + } + + infile = file; + k = 0; + + while (k < size) + { + /* Skip past space characters to the next argument */ + + while (k < size && isspace(infile[k])) + { + ++k; + } + + if (k >= size) + { + break; + } + + /* If the next argument is enclosed in quote marks, treat + * the contents as a single argument. This allows long filenames + * to be specified. + */ + + if (infile[k] == '\"') + { + char *argstart; + + /* Skip the first character(") */ + + ++k; + + argstart = &infile[k]; + + /* Read all characters between quotes */ + + while (k < size && infile[k] != '\"' && infile[k] != '\n') + { + ++k; + } + + if (k >= size || infile[k] == '\n') + { + i_error("Quotes unclosed in response file '%s'", filename); + } + + /* Cut off the string at the closing quote */ + + infile[k] = '\0'; + ++k; + + if (newargc >= MAXARGVS) + { + i_error("Too many arguments in the response file!"); + } + + newargv[newargc++] = m_string_duplicate(argstart); + } + else + { + char *argstart; + + /* Read in the next argument until a space is reached */ + + argstart = &infile[k]; + + while (k < size && !isspace(infile[k])) + { + ++k; + } + + /* Cut off the end of the argument at the first space */ + + infile[k] = '\0'; + ++k; + + if (newargc >= MAXARGVS) + { + i_error("Too many arguments in the response file!"); + } + + newargv[newargc++] = m_string_duplicate(argstart); + } + } + + /* Add arguments following the response file argument */ + + if (newargc + myargc - (argv_index + 1) >= MAXARGVS) + { + i_error("Too many arguments following the response file!"); + } + + for (i = argv_index + 1; i < myargc; ++i) + { + newargv[newargc] = myargv[i]; + myargv[i] = NULL; + ++newargc; + } + + /* Free any old strings in myargv which were not moved to newargv */ + + for (i = 0; i < myargc; ++i) + { + if (myargv[i] != NULL) + { + free(myargv[i]); + myargv[i] = NULL; + } + } + + free(myargv); + myargv = newargv; + myargc = newargc; + + free(file); + +#if 0 + /* Disabled - Vanilla Doom does not do this. + * Display arguments + */ + + printf("%d command-line args:\n", myargc); + + for (k = 1; k < myargc; k++) + { + printf("'%s'\n", myargv[k]); + } +#endif +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* m_check_parm + * Checks for the given parameter in the program's command line arguments. + * Returns the argument number (1 to argc-1) or 0 if not present + */ + +int m_check_parm_with_args(const char *check, int num_args) +{ + int i; + + /* Check if myargv[i] has been set to NULL in load_response_file(), + * which may call i_error(), which in turn calls m_parm_exists("-nogui"). + */ + + for (i = 1; i < myargc - num_args && myargv[i]; i++) + { + if (!strcasecmp(check, myargv[i])) return i; + } + + return 0; +} + +/* m_parm_exists + * + * Returns true if the given parameter exists in the program's command + * line arguments, false if not. + */ + +boolean m_parm_exists(const char *check) +{ + return m_check_parm(check) != 0; +} + +int m_check_parm(const char *check) +{ + return m_check_parm_with_args(check, 0); +} + +/* Find a Response File */ + +void m_find_response_file(void) +{ + int i; + + for (i = 1; i < myargc; i++) + { + if (myargv[i][0] == '@') + { + load_response_file(i, myargv[i] + 1); + } + } + + for (; ; ) + { + /* @arg + * + * Load extra command-line arguments from the given response + * file. Arguments read from the file are inserted into the + * command line, replacing this argument. A response file can + * also be loaded using the abbreviated syntax '@file.rsp'. + */ + + i = m_check_parm_with_args("-response", 1); + if (i <= 0) + { + break; + } + + /* Replace the -response argument so that the next time through + * the loop we'll ignore it. Since some parameters stop reading when + * an argument beginning with a '-' is encountered, we keep something + * that starts with a '-'. + */ + + free(myargv[i]); + myargv[i] = m_string_duplicate("-_"); + load_response_file(i + 1, myargv[i + 1]); + } +} + +/* Return the name of the executable used to start the program: */ + +const char *m_get_executable_name(void) +{ + return m_base_name(myargv[0]); +} + +void m_set_exe_dir(void) +{ + char *dirname; + + dirname = m_dir_name(myargv[0]); + exedir = m_string_join(dirname, DIR_SEPARATOR_S, NULL); + free(dirname); +} diff --git a/games/NXDoom/src/m_argv.h b/games/NXDoom/src/m_argv.h new file mode 100644 index 000000000..bb7f6a8cb --- /dev/null +++ b/games/NXDoom/src/m_argv.h @@ -0,0 +1,92 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_argv.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef __M_ARGV__ +#define __M_ARGV__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int myargc; +extern char **myargv; + +extern char *exedir; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_set_exe_dir(void); + +/**************************************************************************** + * Name: m_check_parm + * + * Description: + * Returns the position of the given parameter in the arg list. + * + * Returns: + * The position of the given parameter in the arg list (0 if not found). + * + ****************************************************************************/ + +int m_check_parm(const char *check); + +/**************************************************************************** + * Name: m_check_parm_with_args + * + * Description: + * Same as m_check_parm, but checks that num_args arguments are available + * following the specified argument. + * + ****************************************************************************/ + +int m_check_parm_with_args(const char *check, int num_args); + +void m_find_response_file(void); +void m_add_loose_files(void); + +/**************************************************************************** + * Name: m_parm_exists + * + * Description: + * Parameter has been specified? + * + ****************************************************************************/ + +boolean m_parm_exists(const char *check); + +/**************************************************************************** + * Name: m_get_executable_name + * + * Description: + * Get name of executable used to run this program. + * + ****************************************************************************/ + +const char *m_get_executable_name(void); + +#endif diff --git a/games/NXDoom/src/m_bbox.c b/games/NXDoom/src/m_bbox.c new file mode 100644 index 000000000..226237fbb --- /dev/null +++ b/games/NXDoom/src/m_bbox.c @@ -0,0 +1,53 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_bbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Main loop menu stuff. + * Random number LUT. + * Default Config File. + * PCX Screenshots. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "m_bbox.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void m_clear_box(fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = INT_MIN; + box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX; +} + +void m_add_to_box(fixed_t *box, fixed_t x, fixed_t y) +{ + if (x < box[BOXLEFT]) + box[BOXLEFT] = x; + else if (x > box[BOXRIGHT]) + box[BOXRIGHT] = x; + if (y < box[BOXBOTTOM]) + box[BOXBOTTOM] = y; + else if (y > box[BOXTOP]) + box[BOXTOP] = y; +} diff --git a/games/NXDoom/src/m_bbox.h b/games/NXDoom/src/m_bbox.h new file mode 100644 index 000000000..a36d545d5 --- /dev/null +++ b/games/NXDoom/src/m_bbox.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_bbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Nil. + * + ****************************************************************************/ + +#ifndef __M_BBOX__ +#define __M_BBOX__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_fixed.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Bounding box coordinate storage. */ + +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; /* bbox coordinates */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Bounding box functions. */ + +void m_clear_box(fixed_t *box); + +void m_add_to_box(fixed_t *box, fixed_t x, fixed_t y); + +#endif /* __M_BBOX__ */ diff --git a/games/NXDoom/src/m_cheat.c b/games/NXDoom/src/m_cheat.c new file mode 100644 index 000000000..038d77107 --- /dev/null +++ b/games/NXDoom/src/m_cheat.c @@ -0,0 +1,95 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_cheat.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Cheat sequence checking. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "m_cheat.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* CHEAT SEQUENCE PACKAGE */ + +/* Called in st_stuff module, which handles the input. + * Returns a 1 if the cheat was successful, 0 if failed. + */ + +int cht_check_cheat(cheatseq_t *cht, char key) +{ + /* if we make a short sequence on a cheat with parameters, this + * will not work in vanilla doom. behave the same. + */ + + if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len) + { + return false; + } + + if (cht->chars_read < strlen(cht->sequence)) + { + /* still reading characters from the cheat code + * and verifying. reset back to the beginning + * if a key is wrong + */ + + if (key == cht->sequence[cht->chars_read]) + ++cht->chars_read; + else + cht->chars_read = 0; + + cht->param_chars_read = 0; + } + else if (cht->param_chars_read < cht->parameter_chars) + { + /* we have passed the end of the cheat sequence and are + * entering parameters now + */ + + cht->parameter_buf[cht->param_chars_read] = key; + + ++cht->param_chars_read; + } + + if (cht->chars_read >= strlen(cht->sequence) && + cht->param_chars_read >= cht->parameter_chars) + { + cht->chars_read = cht->param_chars_read = 0; + + return true; + } + + /* cheat not matched yet */ + + return false; +} + +void ch_get_param(cheatseq_t *cht, char *buffer) +{ + memcpy(buffer, cht->parameter_buf, cht->parameter_chars); +} diff --git a/games/NXDoom/src/m_cheat.h b/games/NXDoom/src/m_cheat.h new file mode 100644 index 000000000..8fa745615 --- /dev/null +++ b/games/NXDoom/src/m_cheat.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_cheat.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Cheat code checking. + * + ****************************************************************************/ + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* CHEAT SEQUENCE PACKAGE */ + +/* declaring a cheat */ + +#define CHEAT(value, parameters) \ + {value, sizeof(value) - 1, parameters, 0, 0, ""} + +#define MAX_CHEAT_LEN 25 +#define MAX_CHEAT_PARAMS 5 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + /* settings for this cheat */ + + char sequence[MAX_CHEAT_LEN]; + size_t sequence_len; + int parameter_chars; + + /* state used during the game */ + + size_t chars_read; + int param_chars_read; + char parameter_buf[MAX_CHEAT_PARAMS]; +} cheatseq_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int cht_check_cheat(cheatseq_t *cht, char key); + +void ch_get_param(cheatseq_t *cht, char *buffer); + +#endif /* __M_CHEAT__ */ diff --git a/games/NXDoom/src/m_config.c b/games/NXDoom/src/m_config.c new file mode 100644 index 000000000..d4441f5ea --- /dev/null +++ b/games/NXDoom/src/m_config.c @@ -0,0 +1,2494 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_config.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Configuration file interface. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "doomkeys.h" +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_misc.h" + +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CONFIG_VARIABLE_GENERIC(name, type) {#name, {NULL}, type, 0, 0, false} + +#define CONFIG_VARIABLE_KEY(name) CONFIG_VARIABLE_GENERIC(name, DEFAULT_KEY) +#define CONFIG_VARIABLE_INT(name) CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT) +#define CONFIG_VARIABLE_INT_HEX(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_INT_HEX) +#define CONFIG_VARIABLE_FLOAT(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_FLOAT) +#define CONFIG_VARIABLE_STRING(name) \ + CONFIG_VARIABLE_GENERIC(name, DEFAULT_STRING) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + DEFAULT_INT, + DEFAULT_INT_HEX, + DEFAULT_STRING, + DEFAULT_FLOAT, + DEFAULT_KEY, +} default_type_t; + +typedef struct +{ + /* Name of the variable */ + + const char *name; + + /* Pointer to the location in memory of the variable */ + + union + { + int *i; + char **s; + float *f; + } location; + + /* Type of the variable */ + + default_type_t type; + + /* If this is a key value, the original integer scancode we read from + * the config file before translating it to the internal key value. + * If zero, we didn't read this value from a config file. + */ + + int untranslated; + + /* The value we translated the scancode into when we read the + * config file on startup. If the variable value is different from + * this, it has been changed and needs to be converted; otherwise, + * use the 'untranslated' value. + */ + + int original_translated; + + /* If true, this config variable has been bound to a variable + * and is being used. + */ + + boolean bound; +} default_t; + +typedef struct +{ + default_t *defaults; + int numdefaults; + const char *filename; +} default_collection_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *autoload_path = ""; + +/* Default filenames for configuration files. */ + +static const char *default_main_config; +static const char *default_extra_config; + +/* @begin_config_file default */ + +static default_t doom_defaults_list[] = +{ + /* Mouse sensitivity. This value is used to multiply input mouse + * movement to control the effect of moving the mouse. + * + * The "normal" maximum value available for this through the + * in-game options menu is 9. A value of 31 or greater will cause + * the game to crash when entering the options menu. + * + */ + + CONFIG_VARIABLE_INT(mouse_sensitivity), + + /* Volume of sound effects, range 0-15. */ + + CONFIG_VARIABLE_INT(sfx_volume), + + /* Volume of in-game music, range 0-15. */ + + CONFIG_VARIABLE_INT(music_volume), + + /* @game strife + * + * If non-zero, dialogue text is displayed over characters' pictures + * when engaging actors who have voices. + */ + + CONFIG_VARIABLE_INT(show_talk), + + /* @game strife + * + * Volume of voice sound effects, range 0-15. + */ + + CONFIG_VARIABLE_INT(voice_volume), + + /* @game doom + * + * If non-zero, messages are displayed on the heads-up display + * in the game ("picked up a clip", etc). If zero, these messages + * are not displayed. + */ + + CONFIG_VARIABLE_INT(show_messages), + + /* Keyboard key to turn right. */ + + CONFIG_VARIABLE_KEY(key_right), + + /* Keyboard key to turn left. */ + + CONFIG_VARIABLE_KEY(key_left), + + /* Keyboard key to move forward. */ + + CONFIG_VARIABLE_KEY(key_up), + + /* Keyboard key to move backward. */ + + CONFIG_VARIABLE_KEY(key_down), + + /* Keyboard key to strafe left. */ + + CONFIG_VARIABLE_KEY(key_strafeleft), + + /* Keyboard key to strafe right. */ + + CONFIG_VARIABLE_KEY(key_straferight), + + /* @game strife + * + * Keyboard key to use health. + */ + + CONFIG_VARIABLE_KEY(key_use_health), + + /* @game hexen + * + * Keyboard key to jump. + */ + + CONFIG_VARIABLE_KEY(key_jump), + + /* @game heretic hexen + * + * Keyboard key to fly upward. + */ + + CONFIG_VARIABLE_KEY(key_flyup), + + /* @game heretic hexen + * + * Keyboard key to fly downwards. + */ + + CONFIG_VARIABLE_KEY(key_flydown), + + /* @game heretic hexen + * + * Keyboard key to center flying. + */ + + CONFIG_VARIABLE_KEY(key_flycenter), + + /* @game heretic hexen + * + * Keyboard key to look up. + */ + + CONFIG_VARIABLE_KEY(key_lookup), + + /* @game heretic hexen + * + * Keyboard key to look down. + */ + + CONFIG_VARIABLE_KEY(key_lookdown), + + /* @game heretic hexen + * + * Keyboard key to center the view. + */ + + CONFIG_VARIABLE_KEY(key_lookcenter), + + /* @game strife + * + * Keyboard key to query inventory. + */ + + CONFIG_VARIABLE_KEY(key_invquery), + + /* @game strife + * + * Keyboard key to display mission objective. + */ + + CONFIG_VARIABLE_KEY(key_mission), + + /* @game strife + * + * Keyboard key to display inventory popup. + */ + + CONFIG_VARIABLE_KEY(key_inv_pop), + + /* @game strife + * + * Keyboard key to display keys popup. + */ + + CONFIG_VARIABLE_KEY(key_inv_key), + + /* @game strife + * + * Keyboard key to jump to start of inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_home), + + /* @game strife + * + * Keyboard key to jump to end of inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_end), + + /* @game heretic hexen + * + * Keyboard key to scroll left in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_invleft), + + /* @game heretic hexen + * + * Keyboard key to scroll right in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_invright), + + /* @game strife + * + * Keyboard key to scroll left in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_left), + + /* @game strife + * + * Keyboard key to scroll right in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_inv_right), + + /* @game heretic hexen + * + * Keyboard key to use the current item in the inventory. + */ + + CONFIG_VARIABLE_KEY(key_useartifact), + + /* @game strife + * + * Keyboard key to use inventory item. + */ + + CONFIG_VARIABLE_KEY(key_inv_use), + + /* @game strife + * + * Keyboard key to drop an inventory item. + */ + + CONFIG_VARIABLE_KEY(key_inv_drop), + + /* @game strife + * + * Keyboard key to look up. + */ + + CONFIG_VARIABLE_KEY(key_look_up), + + /* @game strife + * + * Keyboard key to look down. + */ + + CONFIG_VARIABLE_KEY(key_look_down), + + /* Keyboard key to fire the currently selected weapon. */ + + CONFIG_VARIABLE_KEY(key_fire), + + /* Keyboard key to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_KEY(key_use), + + /* Keyboard key to turn on strafing. When held down, pressing the + * key to turn left or right causes the player to strafe left or + * right instead. + */ + + CONFIG_VARIABLE_KEY(key_strafe), + + /* Keyboard key to make the player run. */ + + CONFIG_VARIABLE_KEY(key_speed), + + /* If non-zero, mouse input is enabled. If zero, mouse input is + * disabled. + */ + + CONFIG_VARIABLE_INT(use_mouse), + + /* Mouse button to fire the currently selected weapon. */ + + CONFIG_VARIABLE_INT(mouseb_fire), + + /* Mouse button to turn on strafing. When held down, the player + * will strafe left and right instead of turning left and right. + */ + + CONFIG_VARIABLE_INT(mouseb_strafe), + + /* Mouse button to move forward. */ + + CONFIG_VARIABLE_INT(mouseb_forward), + + /* Mouse button to turn on running. When held down, the player + * will run while moving. + */ + + CONFIG_VARIABLE_INT(mouseb_speed), + + /* @game hexen strife + * + * Mouse button to jump. + */ + + CONFIG_VARIABLE_INT(mouseb_jump), + + /* If non-zero, joystick input is enabled. */ + + CONFIG_VARIABLE_INT(use_joystick), + + /* Joystick virtual button that fires the current weapon. */ + + CONFIG_VARIABLE_INT(joyb_fire), + + /* Joystick virtual button that makes the player strafe while + * held down. + */ + + CONFIG_VARIABLE_INT(joyb_strafe), + + /* Joystick virtual button to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_INT(joyb_use), + + /* Joystick virtual button that makes the player run while held + * down. + * + * If this has a value of 20 or greater, the player will always run, + * even if use_joystick is 0. + */ + + CONFIG_VARIABLE_INT(joyb_speed), + + /* @game hexen strife + * + * Joystick virtual button that makes the player jump. + */ + + CONFIG_VARIABLE_INT(joyb_jump), + + /* @game doom heretic hexen + * + * Screen size, range 3-11. + * + * A value of 11 gives a full-screen view with the status bar not + * displayed. A value of 10 gives a full-screen view with the + * status bar displayed. + */ + + CONFIG_VARIABLE_INT(screenblocks), + + /* @game strife + * + * Screen size, range 3-11. + * + * A value of 11 gives a full-screen view with the status bar not + * displayed. A value of 10 gives a full-screen view with the + * status bar displayed. + */ + + CONFIG_VARIABLE_INT(screensize), + + /* @game doom + * + * Screen detail. Zero gives normal "high detail" mode, while + * a non-zero value gives "low detail" mode. + */ + + CONFIG_VARIABLE_INT(detaillevel), + + /* Number of sounds that will be played simultaneously. */ + + CONFIG_VARIABLE_INT(snd_channels), + + /* Music output device. A non-zero value gives MIDI sound output, + * while a value of zero disables music. + */ + + CONFIG_VARIABLE_INT(snd_musicdevice), + + /* Sound effects device. A value of zero disables in-game sound + * effects, a value of 1 enables PC speaker sound effects, while + * a value in the range 2-9 enables the "normal" digital sound + * effects. + */ + + CONFIG_VARIABLE_INT(snd_sfxdevice), + + /* SoundBlaster I/O port. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbport), + + /* SoundBlaster IRQ. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbirq), + + /* SoundBlaster DMA channel. Unused. */ + + CONFIG_VARIABLE_INT(snd_sbdma), + + /* Output port to use for OPL MIDI playback. Unused. */ + + CONFIG_VARIABLE_INT(snd_mport), + + /* Gamma correction level. A value of zero disables gamma + * correction, while a value in the range 1-4 gives increasing + * levels of gamma correction. + */ + + CONFIG_VARIABLE_INT(usegamma), + + /* @game hexen + * + * Directory in which to store savegames. + */ + + CONFIG_VARIABLE_STRING(savedir), + + /* @game hexen + * + * Controls whether messages are displayed in the heads-up display. + * If this has a non-zero value, messages are displayed. + */ + + CONFIG_VARIABLE_INT(messageson), + + /* @game strife + * + * Name of background flat used by view border. + */ + + CONFIG_VARIABLE_STRING(back_flat), + + /* @game strife + * + * Multiplayer nickname (?). + */ + + CONFIG_VARIABLE_STRING(nickname), + + /* Multiplayer chat macro: message to send when alt+0 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro0), + + /* Multiplayer chat macro: message to send when alt+1 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro1), + + /* Multiplayer chat macro: message to send when alt+2 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro2), + + /* Multiplayer chat macro: message to send when alt+3 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro3), + + /* Multiplayer chat macro: message to send when alt+4 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro4), + + /* Multiplayer chat macro: message to send when alt+5 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro5), + + /* Multiplayer chat macro: message to send when alt+6 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro6), + + /* Multiplayer chat macro: message to send when alt+7 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro7), + + /* Multiplayer chat macro: message to send when alt+8 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro8), + + /* Multiplayer chat macro: message to send when alt+9 is pressed. */ + + CONFIG_VARIABLE_STRING(chatmacro9), + + /* @game strife + * + * Serial port number to use for SERSETUP.EXE (unused). + */ + + CONFIG_VARIABLE_INT(comport), +}; + +static default_collection_t doom_defaults = +{ + doom_defaults_list, + arrlen(doom_defaults_list), + NULL, +}; + +/* @begin_config_file extended */ + +static default_t extra_defaults_list[] = +{ + /* Name of the SDL video driver to use. If this is an empty string, + * the default video driver is used. + */ + + CONFIG_VARIABLE_STRING(video_driver), + + /* Position of the window on the screen when running in windowed + * mode. Accepted values are: "" (empty string) - don't care, + * "center" - place window at center of screen, "x,y" - place + * window at the specified coordinates. + */ + + CONFIG_VARIABLE_STRING(window_position), + + /* If non-zero, the game will run in full screen mode. If zero, + * the game will run in a window. + */ + + CONFIG_VARIABLE_INT(fullscreen), + + /* Index of the display on which the game should run. This has no + * effect if running in windowed mode (fullscreen=0) and + * window_position is not set to "center". + */ + + CONFIG_VARIABLE_INT(video_display), + + /* If non-zero, the screen will be stretched vertically to display + * correctly on a square pixel video mode. + */ + + CONFIG_VARIABLE_INT(aspect_ratio_correct), + + /* If non-zero, the screen will have smooth scaling. */ + + CONFIG_VARIABLE_INT(smooth_pixel_scaling), + + /* If non-zero, forces integer scales for resolution-independent + * rendering. + */ + + CONFIG_VARIABLE_INT(integer_scaling), + + /* If non-zero, any pillar/letter boxes drawn around the game area + * will "flash" when the game palette changes, simulating the VGA + * "porch" + */ + + CONFIG_VARIABLE_INT(vga_porch_flash), + + /* Window width when running in windowed mode. */ + + CONFIG_VARIABLE_INT(window_width), + + /* Window height when running in windowed mode. */ + + CONFIG_VARIABLE_INT(window_height), + + /* Width for screen mode when running fullscreen. + * If this and fullscreen_height are both set to zero, we run + * fullscreen as a desktop window that covers the entire screen, + * rather than ever switching screen modes. It should usually + * be unnecessary to set this value. + */ + + CONFIG_VARIABLE_INT(fullscreen_width), + + /* Height for screen mode when running fullscreen. + * See documentation for fullscreen_width. + */ + + CONFIG_VARIABLE_INT(fullscreen_height), + + /* If non-zero, force the use of a software renderer. For use on + * systems lacking hardware acceleration. + */ + + CONFIG_VARIABLE_INT(force_software_renderer), + + /* Maximum number of pixels to use for intermediate scaling buffer. + * More pixels mean that the screen can be rendered more precisely, + * but there are diminishing returns on quality. The default limits to + * 16,000,000 pixels, which is enough to cover 4K monitor standards. + */ + + CONFIG_VARIABLE_INT(max_scaling_buffer_pixels), + + /* Number of milliseconds to wait on startup after the video mode + * has been set, before the game will start. This allows the + * screen to settle on some monitors that do not display an image + * for a brief interval after changing video modes. + */ + + CONFIG_VARIABLE_INT(startup_delay), + + /* @game heretic hexen strife + * + * If non-zero, display the graphical startup screen. + */ + + CONFIG_VARIABLE_INT(graphical_startup), + + /* @game doom heretic strife + * + * If non-zero, the ENDOOM text screen is displayed when exiting the + * game. If zero, the ENDOOM screen is not displayed. + */ + + CONFIG_VARIABLE_INT(show_endoom), + + /* @game doom strife + * + * If non-zero, a disk activity indicator is displayed when data is read + * from disk. If zero, the disk activity indicator is not displayed. + */ + + CONFIG_VARIABLE_INT(show_diskicon), + + /* If non-zero, save screenshots in PNG format. If zero, screenshots are + * saved in PCX format, as Vanilla Doom does. + */ + + CONFIG_VARIABLE_INT(png_screenshots), + + /* Sound output sample rate, in Hz. Typical values to use are + * 11025, 22050, 44100 and 48000. + */ + + CONFIG_VARIABLE_INT(snd_samplerate), + + /* Maximum number of bytes to allocate for caching converted sound + * effects in memory. If set to zero, there is no limit applied. + */ + + CONFIG_VARIABLE_INT(snd_cachesize), + + /* Maximum size of the output sound buffer size in milliseconds. + * Sound output is generated periodically in slices. Higher values + * might be more efficient but will introduce latency to the + * sound output. The default is 28ms (one slice per tic with the + * 35fps timer). + */ + + CONFIG_VARIABLE_INT(snd_maxslicetime_ms), + + /* If non-zero, sound effects will have their pitch varied up or + * down by a random amount during play. If zero, sound effects + * play back at their default pitch. + */ + + CONFIG_VARIABLE_INT(snd_pitchshift), + + /* External command to invoke to perform MIDI playback. If set to + * the empty string, SDL_mixer's internal MIDI playback is used. + * This only has any effect when snd_musicdevice is set to General + * MIDI output. + */ + + CONFIG_VARIABLE_STRING(snd_musiccmd), + + /* Controls whether libsamplerate support is used for performing + * sample rate conversions of sound effects. Support for this + * must be compiled into the program. + * + * If zero, libsamplerate support is disabled. If non-zero, + * libsamplerate is enabled. Increasing values roughly correspond + * to higher quality conversion; the higher the quality, the + * slower the conversion process. Linear conversion = 1; + * Zero order hold = 2; Fast Sinc filter = 3; Medium quality + * Sinc filter = 4; High quality Sinc filter = 5. + */ + + CONFIG_VARIABLE_INT(use_libsamplerate), + + /* Scaling factor used by libsamplerate. This is used when converting + * sounds internally back into integer form; normally it should not + * be necessary to change it from the default value. The only time + * it might be needed is if a PWAD file is loaded that contains very + * loud sounds, in which case the conversion may cause sound clipping + * and the scale factor should be reduced. The lower the value, the + * quieter the sound effects become, so it should be set as high as is + * possible without clipping occurring. + */ + + CONFIG_VARIABLE_FLOAT(libsamplerate_scale), + + /* Full path to a directory in which WAD files and dehacked patches + * can be placed to be automatically loaded on startup. A subdirectory + * of this directory matching the IWAD name is checked to find the + * files to load. + */ + + CONFIG_VARIABLE_STRING(autoload_path), + + /* Full path to a directory containing configuration files for + * substitute music packs. These packs contain high quality renderings + * of game music to be played instead of using the system's built-in + * MIDI playback. + */ + + CONFIG_VARIABLE_STRING(music_pack_path), + +#ifdef HAVE_FLUIDSYNTH + /* If 1, activate the FluidSynth chorus effects module. If 0, no chorus + * will be added to the output signal. + */ + + CONFIG_VARIABLE_INT(fsynth_chorus_active), + + /* Specifies the modulation depth of the FluidSynth chorus. Default is + * 5.0, range is 0.0 to 256.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_depth), + + /* Specifies the output amplitude of the FluidSynth chorus signal. Default + * is 0.35, range is 0.0 to 10.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_level), + + /* Sets the voice count of the FluidSynth chorus signal. Default is 3, + * range is 0 to 99. + */ + + CONFIG_VARIABLE_INT(fsynth_chorus_nr), + + /* Sets the FluidSynth chorus modulation speed in Hz. Default is 0.3, + * range is 0.1 to 5.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_chorus_speed), + + /* This setting defines how FluidSynth interprets Bank Select messages. + * The default is "gs". Other possible values are "gm", "xg" and "mma". + */ + + CONFIG_VARIABLE_STRING(fsynth_midibankselect), + + /* Sets the number of FluidSynth voices that can be played in parallel. + * Default is 256, range is 1 - 65535. + */ + + CONFIG_VARIABLE_INT(fsynth_polyphony), + + /* If 1, activate the FluidSynth reverb effects module. If 0, no reverb + * will be added to the output signal. + */ + + CONFIG_VARIABLE_INT(fsynth_reverb_active), + + /* Sets the amount of FluidSynth reverb damping. Default is 0.4, range is + * 0.0 to 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_damp), + + /* Sets the FluidSynth reverb amplitude. Default is 0.15, range is 0.0 - + * 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_level), + + /* Sets the room size(i.e. amount of wet) FluidSynth reverb. Default is + * 0.6, range is 0.0 - 1.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_roomsize), + + /* Sets the stereo spread of the FluidSynth reverb signal. Default is + * 0.4, range is 0.0 - 100.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_reverb_width), + + /* Fine tune the FluidSynth output level. Default is 1.0, + * range is 0.0 - 10.0. + */ + + CONFIG_VARIABLE_FLOAT(fsynth_gain), + + /* Full path to a soundfont file to use with FluidSynth MIDI playback. */ + + CONFIG_VARIABLE_STRING(fsynth_sf_path), +#endif /* HAVE_FLUIDSYNTH */ + + /* Full path to a Timidity configuration file to use for MIDI + * playback. The file will be evaluated from the directory where + * it is evaluated, so there is no need to add "dir" commands + * into it. + */ + + CONFIG_VARIABLE_STRING(timidity_cfg_path), + + /* Path to GUS patch files to use when operating in GUS emulation + * mode. + */ + + CONFIG_VARIABLE_STRING(gus_patch_path), + + /* Number of kilobytes of RAM to use in GUS emulation mode. Valid + * values are 256, 512, 768 or 1024. + */ + + CONFIG_VARIABLE_INT(gus_ram_kb), + + /* @game doom strife + * + * If non-zero, the Vanilla savegame limit is enforced; if the + * savegame exceeds 180224 bytes in size, the game will exit with + * an error. If this has a value of zero, there is no limit to + * the size of savegames. + */ + + CONFIG_VARIABLE_INT(vanilla_savegame_limit), + + /* @game doom strife + * + * If non-zero, the Vanilla demo size limit is enforced; the game + * exits with an error when a demo exceeds the demo size limit + * (128KiB by default). If this has a value of zero, there is no + * limit to the size of demos. + */ + + CONFIG_VARIABLE_INT(vanilla_demo_limit), + + /* If non-zero, the game behaves like Vanilla Doom, always assuming + * an American keyboard mapping. If this has a value of zero, the + * native keyboard mapping of the keyboard is used. + */ + + CONFIG_VARIABLE_INT(vanilla_keyboard_mapping), + + /* Name to use in network games for identification. This is only + * used on the "waiting" screen while waiting for the game to start. + */ + + CONFIG_VARIABLE_STRING(player_name), + + /* If this is non-zero, the mouse will be "grabbed" when running + * in windowed mode so that it can be used as an input device. + * When running full screen, this has no effect. + */ + + CONFIG_VARIABLE_INT(grabmouse), + + /* If non-zero, all vertical mouse movement is ignored. This + * emulates the behavior of the "novert" tool available under DOS + * that performs the same function. + */ + + CONFIG_VARIABLE_INT(novert), + + /* Mouse acceleration factor. When the speed of mouse movement + * exceeds the threshold value (mouse_threshold), the speed is + * multiplied by this value. + */ + + CONFIG_VARIABLE_FLOAT(mouse_acceleration), + + /* Mouse acceleration threshold. When the speed of mouse movement + * exceeds this threshold value, the speed is multiplied by an + * acceleration factor (mouse_acceleration). + */ + + CONFIG_VARIABLE_INT(mouse_threshold), + + /* Mouse button to strafe left. */ + + CONFIG_VARIABLE_INT(mouseb_strafeleft), + + /* Mouse button to strafe right. */ + + CONFIG_VARIABLE_INT(mouseb_straferight), + + /* Mouse button to turn left. */ + + CONFIG_VARIABLE_INT(mouseb_turnleft), + + /* Mouse button to turn right. */ + + CONFIG_VARIABLE_INT(mouseb_turnright), + + /* Mouse button to "use" an object, eg. a door or switch. */ + + CONFIG_VARIABLE_INT(mouseb_use), + + /* Mouse button to move backwards. */ + + CONFIG_VARIABLE_INT(mouseb_backward), + + /* Mouse button to cycle to the previous weapon. */ + + CONFIG_VARIABLE_INT(mouseb_prevweapon), + + /* Mouse button to cycle to the next weapon. */ + + CONFIG_VARIABLE_INT(mouseb_nextweapon), + + /* @game heretic + * + * Mouse button to move to the left in the inventory. + */ + + CONFIG_VARIABLE_INT(mouseb_invleft), + + /* @game heretic + * + * Mouse button to move to the right in the inventory. + */ + + CONFIG_VARIABLE_INT(mouseb_invright), + + /* @game heretic hexen + * + * Mouse button to use artifact. + */ + + CONFIG_VARIABLE_INT(mouseb_useartifact), + + /* If non-zero, double-clicking a mouse button acts like pressing + * the "use" key to use an object in-game, eg. a door or switch. + */ + + CONFIG_VARIABLE_INT(dclick_use), + + /* SDL GUID string indicating the joystick to use. An empty string + * indicates that no joystick is configured. + */ + + CONFIG_VARIABLE_STRING(joystick_guid), + + /* Index of SDL joystick to use; this is only used in the case where + * multiple identical joystick devices are connected which have the + * same GUID, to distinguish between devices. + */ + + CONFIG_VARIABLE_INT(joystick_index), + + /* If non-zero, use analog movement when playing with a gamepad. */ + + CONFIG_VARIABLE_INT(use_analog), + + /* Joystick axis to use to for horizontal (X) movement. */ + + CONFIG_VARIABLE_INT(joystick_x_axis), + + /* If non-zero, movement on the horizontal joystick axis is inverted. */ + + CONFIG_VARIABLE_INT(joystick_x_invert), + + /* Joystick turn analog sensitivity, specified as a value between 0 + * and 20. + */ + + CONFIG_VARIABLE_INT(joystick_turn_sensitivity), + + /* Joystick axis to use to for vertical (Y) movement. */ + + CONFIG_VARIABLE_INT(joystick_y_axis), + + /* If non-zero, movement on the vertical joystick axis is inverted. */ + + CONFIG_VARIABLE_INT(joystick_y_invert), + + /* Joystick axis to use to for strafing movement. */ + + CONFIG_VARIABLE_INT(joystick_strafe_axis), + + /* If non-zero, movement on the joystick axis used for strafing + * is inverted. + */ + + CONFIG_VARIABLE_INT(joystick_strafe_invert), + + /* Joystick move and strafe analog sensitivity, specified as a value + * between 0 and 20. + */ + + CONFIG_VARIABLE_INT(joystick_move_sensitivity), + + /* Joystick axis to use to for looking up and down. */ + + CONFIG_VARIABLE_INT(joystick_look_axis), + + /* If non-zero, movement on the joystick axis used for looking + * is inverted. + */ + + CONFIG_VARIABLE_INT(joystick_look_invert), + + /* Joystick look analog sensitivity, specified as a value between 0 + * and 20. + */ + + CONFIG_VARIABLE_INT(joystick_look_sensitivity), + + /* The physical joystick button that corresponds to joystick + * virtual button #0. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button0), + + /* The physical joystick button that corresponds to joystick + * virtual button #1. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button1), + + /* The physical joystick button that corresponds to joystick + * virtual button #2. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button2), + + /* The physical joystick button that corresponds to joystick + * virtual button #3. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button3), + + /* The physical joystick button that corresponds to joystick + * virtual button #4. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button4), + + /* The physical joystick button that corresponds to joystick + * virtual button #5. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button5), + + /* The physical joystick button that corresponds to joystick + * virtual button #6. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button6), + + /* The physical joystick button that corresponds to joystick + * virtual button #7. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button7), + + /* The physical joystick button that corresponds to joystick + * virtual button #8. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button8), + + /* The physical joystick button that corresponds to joystick + * virtual button #9. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button9), + + /* The physical joystick button that corresponds to joystick + * virtual button #10. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button10), + + /* The physical joystick button that corresponds to joystick + * virtual button #11. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button11), + + /* The physical joystick button that corresponds to joystick + * virtual button #12. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button12), + + /* The physical joystick button that corresponds to joystick + * virtual button #13. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button13), + + /* The physical joystick button that corresponds to joystick + * virtual button #14. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button14), + + /* The physical joystick button that corresponds to joystick + * virtual button #15. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button15), + + /* The physical joystick button that corresponds to joystick + * virtual button #16. + */ + + CONFIG_VARIABLE_INT(joystick_physical_button16), + + /* If non-zero, use the SDL_GameController interface instead of the + * SDL_Joystick interface. + */ + + CONFIG_VARIABLE_INT(use_gamepad), + + /* Stores the SDL_GameControllerType of the last configured gamepad. */ + + CONFIG_VARIABLE_INT(gamepad_type), + + /* Joystick x axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_x_dead_zone), + + /* Joystick y axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_y_dead_zone), + + /* Joystick strafe axis dead zone, specified as a percentage of the axis + * max value. + */ + + CONFIG_VARIABLE_INT(joystick_strafe_dead_zone), + + /* Joystick look axis dead zone, specified as a percentage of the axis max + * value. + */ + + CONFIG_VARIABLE_INT(joystick_look_dead_zone), + + /* Joystick virtual button to make the player strafe left. */ + + CONFIG_VARIABLE_INT(joyb_strafeleft), + + /* Joystick virtual button to make the player strafe right. */ + + CONFIG_VARIABLE_INT(joyb_straferight), + + /* Joystick virtual button to activate the menu. */ + + CONFIG_VARIABLE_INT(joyb_menu_activate), + + /* Joystick virtual button to toggle the automap. */ + + CONFIG_VARIABLE_INT(joyb_toggle_automap), + + /* Joystick virtual button that cycles to the previous weapon. */ + + CONFIG_VARIABLE_INT(joyb_prevweapon), + + /* Joystick virtual button that cycles to the next weapon. */ + + CONFIG_VARIABLE_INT(joyb_nextweapon), + + /* @game heretic hexen + * Joystick virtual button to activate artifact. + */ + + CONFIG_VARIABLE_INT(joyb_useartifact), + + /* @game heretic hexen + * Joystick virtual button to move left in the inventory. + */ + + CONFIG_VARIABLE_INT(joyb_invleft), + + /* @game heretic hexen + * Joystick virtual button to move right in the inventory. + */ + + CONFIG_VARIABLE_INT(joyb_invright), + + /* @game heretic hexen + * Joystick virtual button to fly up. + */ + + CONFIG_VARIABLE_INT(joyb_flyup), + + /* @game heretic hexen + * Joystick virtual button to fly down. + */ + + CONFIG_VARIABLE_INT(joyb_flydown), + + /* @game heretic hexen + * Joystick virtual button to center flying. + */ + + CONFIG_VARIABLE_INT(joyb_flycenter), + + /* Key to pause or unpause the game. */ + + CONFIG_VARIABLE_KEY(key_pause), + + /* Key that activates the menu when pressed. */ + + CONFIG_VARIABLE_KEY(key_menu_activate), + + /* Key that moves the cursor up on the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_up), + + /* Key that moves the cursor down on the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_down), + + /* Key that moves the currently selected slider on the menu left. */ + + CONFIG_VARIABLE_KEY(key_menu_left), + + /* Key that moves the currently selected slider on the menu right. */ + + CONFIG_VARIABLE_KEY(key_menu_right), + + /* Key to go back to the previous menu. */ + + CONFIG_VARIABLE_KEY(key_menu_back), + + /* Key to activate the currently selected menu item. */ + + CONFIG_VARIABLE_KEY(key_menu_forward), + + /* Key to answer 'yes' to a question in the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_confirm), + + /* Key to answer 'no' to a question in the menu. */ + + CONFIG_VARIABLE_KEY(key_menu_abort), + + /* Keyboard shortcut to bring up the help screen. */ + + CONFIG_VARIABLE_KEY(key_menu_help), + + /* Keyboard shortcut to bring up the save game menu. */ + + CONFIG_VARIABLE_KEY(key_menu_save), + + /* Keyboard shortcut to bring up the load game menu. */ + + CONFIG_VARIABLE_KEY(key_menu_load), + + /* Keyboard shortcut to bring up the sound volume menu. */ + + CONFIG_VARIABLE_KEY(key_menu_volume), + + /* Keyboard shortcut to toggle the detail level. */ + + CONFIG_VARIABLE_KEY(key_menu_detail), + + /* Keyboard shortcut to quicksave the current game. */ + + CONFIG_VARIABLE_KEY(key_menu_qsave), + + /* Keyboard shortcut to end the game. */ + + CONFIG_VARIABLE_KEY(key_menu_endgame), + + /* Keyboard shortcut to toggle heads-up messages. */ + + CONFIG_VARIABLE_KEY(key_menu_messages), + + /* Keyboard shortcut to load the last quicksave. */ + + CONFIG_VARIABLE_KEY(key_menu_qload), + + /* Keyboard shortcut to quit the game. */ + + CONFIG_VARIABLE_KEY(key_menu_quit), + + /* Keyboard shortcut to toggle the gamma correction level. */ + + CONFIG_VARIABLE_KEY(key_menu_gamma), + + /* Keyboard shortcut to switch view in multiplayer. */ + + CONFIG_VARIABLE_KEY(key_spy), + + /* Keyboard shortcut to increase the screen size. */ + + CONFIG_VARIABLE_KEY(key_menu_incscreen), + + /* Keyboard shortcut to decrease the screen size. */ + + CONFIG_VARIABLE_KEY(key_menu_decscreen), + + /* Keyboard shortcut to save a screenshot. */ + + CONFIG_VARIABLE_KEY(key_menu_screenshot), + + /* Key to toggle the map view. */ + + CONFIG_VARIABLE_KEY(key_map_toggle), + + /* Key to pan north when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_north), + + /* Key to pan south when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_south), + + /* Key to pan east when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_east), + + /* Key to pan west when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_west), + + /* Key to zoom in when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_zoomin), + + /* Key to zoom out when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_zoomout), + + /* Key to zoom out the maximum amount when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_maxzoom), + + /* Key to toggle follow mode when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_follow), + + /* Key to toggle the grid display when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_grid), + + /* Key to set a mark when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_mark), + + /* Key to clear all marks when in the map view. */ + + CONFIG_VARIABLE_KEY(key_map_clearmark), + + /* Key to select weapon 1. */ + + CONFIG_VARIABLE_KEY(key_weapon1), + + /* Key to select weapon 2. */ + + CONFIG_VARIABLE_KEY(key_weapon2), + + /* Key to select weapon 3. */ + + CONFIG_VARIABLE_KEY(key_weapon3), + + /* Key to select weapon 4. */ + + CONFIG_VARIABLE_KEY(key_weapon4), + + /* Key to select weapon 5. */ + + CONFIG_VARIABLE_KEY(key_weapon5), + + /* Key to select weapon 6. */ + + CONFIG_VARIABLE_KEY(key_weapon6), + + /* Key to select weapon 7. */ + + CONFIG_VARIABLE_KEY(key_weapon7), + + /* Key to select weapon 8. */ + + CONFIG_VARIABLE_KEY(key_weapon8), + + /* Key to cycle to the previous weapon. */ + + CONFIG_VARIABLE_KEY(key_prevweapon), + + /* Key to cycle to the next weapon. */ + + CONFIG_VARIABLE_KEY(key_nextweapon), + + /* @game heretic + * + * Key to use "quartz flask" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_quartz), + + /* @game heretic + * + * Key to use "mystic urn" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_urn), + + /* @game heretic + * + * Key to use "timebomb of the ancients" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_bomb), + + /* @game heretic + * + * Key to use "tome of power" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_tome), + + /* @game heretic + * + * Key to use "ring of invincibility" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_ring), + + /* @game heretic + * + * Key to use "chaos device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_chaosdevice), + + /* @game heretic + * + * Key to use "shadowsphere" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_shadowsphere), + + /* @game heretic + * + * Key to use "wings of wrath" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_wings), + + /* @game heretic + * + * Key to use "torch" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_torch), + + /* @game heretic + * + * Key to use "morph ovum" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_morph), + + /* @game hexen + * + * Key to use one of each artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_all), + + /* @game hexen + * + * Key to use "quartz flask" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_health), + + /* @game hexen + * + * Key to use "flechette" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_poisonbag), + + /* @game hexen + * + * Key to use "disc of repulsion" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_blastradius), + + /* @game hexen + * + * Key to use "chaos device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_teleport), + + /* @game hexen + * + * Key to use "banishment device" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_teleportother), + + /* @game hexen + * + * Key to use "porkalator" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_egg), + + /* @game hexen + * + * Key to use "icon of the defender" artifact. + */ + + CONFIG_VARIABLE_KEY(key_arti_invulnerability), + + /* Key to re-display last message. */ + + CONFIG_VARIABLE_KEY(key_message_refresh), + + /* Key to quit the game when recording a demo. */ + + CONFIG_VARIABLE_KEY(key_demo_quit), + + /* Key to send a message during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msg), + + /* Key to send a message to player 1 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer1), + + /* Key to send a message to player 2 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer2), + + /* Key to send a message to player 3 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer3), + + /* Key to send a message to player 4 during multiplayer games. */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer4), + + /* @game hexen strife + * + * Key to send a message to player 5 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer5), + + /* @game hexen strife + * + * Key to send a message to player 6 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer6), + + /* @game hexen strife + * + * Key to send a message to player 7 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer7), + + /* @game hexen strife + * + * Key to send a message to player 8 during multiplayer games. + */ + + CONFIG_VARIABLE_KEY(key_multi_msgplayer8), +}; + +static default_collection_t extra_defaults = +{ + extra_defaults_list, + arrlen(extra_defaults_list), + NULL, +}; + +/* Mapping from DOS keyboard scan code to internal key code (as defined + * in doomkey.h). I think I (fraggle) reused this from somewhere else + * but I can't find where. Anyway, notes: + * * KEY_PAUSE is wrong - it's in the KEY_NUMLOCK spot. This shouldn't + * matter in terms of Vanilla compatibility because neither of + * those were valid for key bindings. + * * There is no proper scan code for PrintScreen (on DOS machines it + * sends an interrupt). So I added a fake scan code of 126 for it. + * The presence of this is important so we can bind PrintScreen as + * a screenshot key. + */ + +static const int scantokey[128] = +{ + 0, + 27, + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '0', + '-', + '=', + KEY_BACKSPACE, + 9, + 'q', + 'w', + 'e', + 'r', + 't', + 'y', + 'u', + 'i', + 'o', + 'p', + '[', + ']', + 13, + KEY_RCTRL, + 'a', + 's', + 'd', + 'f', + 'g', + 'h', + 'j', + 'k', + 'l', + ';', + '\'', + '`', + KEY_RSHIFT, + '\\', + 'z', + 'x', + 'c', + 'v', + 'b', + 'n', + 'm', + ',', + '.', + '/', + KEY_RSHIFT, + KEYP_MULTIPLY, + KEY_RALT, + ' ', + KEY_CAPSLOCK, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + + /* KEY_NUMLOCK? */ + + KEY_PAUSE, + KEY_SCRLCK, + KEY_HOME, + KEY_UPARROW, + KEY_PGUP, + KEY_MINUS, + KEY_LEFTARROW, + KEYP_5, + KEY_RIGHTARROW, + KEYP_PLUS, + KEY_END, + KEY_DOWNARROW, + KEY_PGDN, + KEY_INS, + KEY_DEL, + 0, + 0, + 0, + KEY_F11, + KEY_F12, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + KEY_PRTSCR, + 0, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* DEFAULTS */ + +/* Location where all configuration data is stored - default.cfg, savegames, + * etc. + */ + +const char *configdir; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: search_collection + * + * Description: + * Search a collection for a variable + * + ****************************************************************************/ + +static default_t *search_collection(default_collection_t *collection, + const char *name) +{ + int i; + + for (i = 0; i < collection->numdefaults; ++i) + { + if (!strcmp(name, collection->defaults[i].name)) + { + return &collection->defaults[i]; + } + } + + return NULL; +} + +static void save_default_collection(default_collection_t *collection) +{ + default_t *defaults; + int i; + int v; + FILE *f; + + f = fopen(collection->filename, "w"); + if (!f) return; /* can't write the file, but don't complain */ + + defaults = collection->defaults; + + for (i = 0; i < collection->numdefaults; i++) + { + int chars_written; + + /* Ignore unbound variables */ + + if (!defaults[i].bound) + { + continue; + } + + /* Print the name and line up all values at 30 characters */ + + chars_written = fprintf(f, "%s ", defaults[i].name); + + for (; chars_written < 30; ++chars_written) + fprintf(f, " "); + + /* Print the value */ + + switch (defaults[i].type) + { + case DEFAULT_KEY: + + /* use the untranslated version if we can, to reduce + * the possibility of screwing up the user's config + * file + */ + + v = *defaults[i].location.i; + + if (v == KEY_RSHIFT) + { + /* Special case: for shift, force scan code for + * right shift, as this is what Vanilla uses. + * This overrides the change check below, to fix + * configuration files made by old versions that + * mistakenly used the scan code for left shift. + */ + + v = 54; + } + else if (defaults[i].untranslated && + v == defaults[i].original_translated) + { + /* Has not been changed since the last time we read the config + * file. + */ + + v = defaults[i].untranslated; + } + else + { + /* search for a reverse mapping back to a scancode + * in the scantokey table + */ + + int s; + + for (s = 0; s < 128; ++s) + { + if (scantokey[s] == v) + { + v = s; + break; + } + } + } + + fprintf(f, "%i", v); + break; + + case DEFAULT_INT: + fprintf(f, "%i", *defaults[i].location.i); + break; + + case DEFAULT_INT_HEX: + fprintf(f, "0x%x", *defaults[i].location.i); + break; + + case DEFAULT_FLOAT: + fprintf(f, "%f", *defaults[i].location.f); + break; + + case DEFAULT_STRING: + fprintf(f, "\"%s\"", *defaults[i].location.s); + break; + } + + fprintf(f, "\n"); + } + + fclose(f); +} + +/**************************************************************************** + * Name: parse_int_parameter + * + * Description: + * Parses integer values in the configuration file + * + ****************************************************************************/ + +static int parse_int_parameter(const char *strparm) +{ + int param; + + if (strparm[0] == '0' && strparm[1] == 'x') + sscanf(strparm + 2, "%x", (unsigned int *)¶m); + else + sscanf(strparm, "%i", ¶m); + + return param; +} + +static void set_variable(default_t *def, const char *value) +{ + int intparm; + + /* parameter found */ + + switch (def->type) + { + case DEFAULT_STRING: + *def->location.s = m_string_duplicate(value); + break; + + case DEFAULT_INT: + case DEFAULT_INT_HEX: + *def->location.i = parse_int_parameter(value); + break; + + case DEFAULT_KEY: + + /* translate scancodes read from config + * file (save the old value in untranslated) + */ + + intparm = parse_int_parameter(value); + def->untranslated = intparm; + if (intparm >= 0 && intparm < 128) + { + intparm = scantokey[intparm]; + } + else + { + intparm = 0; + } + + def->original_translated = intparm; + *def->location.i = intparm; + break; + + case DEFAULT_FLOAT: + { + /* Different locales use different decimal separators. + * However, the choice of the current locale isn't always + * under our own control. If the atof() function fails to + * parse the string representing the floating point number + * using the current locale's decimal separator, it will + * return 0, resulting in silent sound effects. To + * mitigate this, we replace the first non-digit, + * non-minus character in the string with the current + * locale's decimal separator before passing it to atof(). + */ + + struct lconv *lc = localeconv(); + char dec; + char *str; + int i = 0; + + dec = lc->decimal_point[0]; + str = m_string_duplicate(value); + + /* Skip sign indicators. */ + + if (str[i] == '-' || str[i] == '+') + { + i++; + } + + for (; str[i] != '\0'; i++) + { + if (!isdigit(str[i])) + { + str[i] = dec; + break; + } + } + + *def->location.f = (float)atof(str); + free(str); + } + break; + } +} + +static void load_default_collection(default_collection_t *collection) +{ + FILE *f; + default_t *def; + char defname[80]; + char strparm[100]; + + /* read the file in, overriding any set defaults */ + + f = fopen(collection->filename, "r"); + + if (f == NULL) + { + /* File not opened, but don't complain. + * It's probably just the first time they ran the game. + */ + + return; + } + + while (!feof(f)) + { + if (fscanf(f, "%79s %99[^\n]\n", defname, strparm) != 2) + { + /* This line doesn't match */ + + continue; + } + + /* Find the setting in the list */ + + def = search_collection(collection, defname); + + if (def == NULL || !def->bound) + { + /* Unknown variable? Unbound variables are also treated + * as unknown. + */ + + continue; + } + + /* Strip off trailing non-printable characters (\r characters + * from DOS text files) + */ + + while (strlen(strparm) > 0 && !isprint(strparm[strlen(strparm) - 1])) + { + strparm[strlen(strparm) - 1] = '\0'; + } + + /* Surrounded by quotes? If so, remove them. */ + + if (strlen(strparm) >= 2 && strparm[0] == '"' && + strparm[strlen(strparm) - 1] == '"') + { + strparm[strlen(strparm) - 1] = '\0'; + memmove(strparm, strparm + 1, sizeof(strparm) - 1); + } + + set_variable(def, strparm); + } + + fclose(f); +} + +/* Get a configuration file variable by its name */ + +static default_t *get_default_for_name(const char *name) +{ + default_t *result; + + /* Try the main list and the extras */ + + result = search_collection(&doom_defaults, name); + + if (result == NULL) + { + result = search_collection(&extra_defaults, name); + } + + /* Not found? Internal error. */ + + if (result == NULL) + { + i_error("Unknown configuration variable: '%s'", name); + } + + return result; +} + +/* Get the path to the default configuration dir to use, if NULL + * is passed to m_set_config_dir. + */ + +static char *get_default_config_dir(void) +{ + /* Configuration settings are stored in an OS-appropriate path + * determined by SDL. On typical Unix systems, this might be + * ~/.local/share/chocolate-doom. On Windows, we behave like + * Vanilla Doom and save in the current directory. + */ + + char *result = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + char *copy = m_string_duplicate(result); + return copy; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Set the default filenames to use for configuration files. */ + +void m_set_config_filenames(const char *main_config, + const char *extra_config) +{ + default_main_config = main_config; + default_extra_config = extra_config; +} + +void m_save_defaults(void) +{ + save_default_collection(&doom_defaults); + save_default_collection(&extra_defaults); +} + +/* Save defaults to alternate filenames */ + +void m_save_defaults_alternate(const char *main, const char *extra) +{ + const char *orig_main; + const char *orig_extra; + + /* Temporarily change the filenames */ + + orig_main = doom_defaults.filename; + orig_extra = extra_defaults.filename; + + doom_defaults.filename = main; + extra_defaults.filename = extra; + + m_save_defaults(); + + /* Restore normal filenames */ + + doom_defaults.filename = orig_main; + extra_defaults.filename = orig_extra; +} + +void m_load_defaults(void) +{ + int i; + + /* This variable is a special snowflake for no good reason. */ + + m_bind_string_variable("autoload_path", &autoload_path); + + /* check for a custom default file */ + + /* @arg + * @vanilla + * + * Load main configuration from the specified file, instead of the + * default. + */ + + i = m_check_parm_with_args("-config", 1); + + if (i) + { + doom_defaults.filename = myargv[i + 1]; + printf("\tdefault file: %s\n", doom_defaults.filename); + } + else + { + doom_defaults.filename = + m_string_join(configdir, default_main_config, NULL); + } + + printf("saving config in %s\n", doom_defaults.filename); + + /* @arg + * + * Load additional configuration from the specified file, instead of + * the default. + */ + + i = m_check_parm_with_args("-extraconfig", 1); + + if (i) + { + extra_defaults.filename = myargv[i + 1]; + printf(" extra configuration file: %s\n", + extra_defaults.filename); + } + else + { + extra_defaults.filename = + m_string_join(configdir, default_extra_config, NULL); + } + + load_default_collection(&doom_defaults); + load_default_collection(&extra_defaults); +} + +/* Bind a variable to a given configuration file variable, by name. */ + +void m_bind_int_variable(const char *name, int *location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_INT || + variable->type == DEFAULT_INT_HEX || variable->type == DEFAULT_KEY); + + variable->location.i = location; + variable->bound = true; +} + +void m_bind_float_variable(const char *name, float *location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_FLOAT); + + variable->location.f = location; + variable->bound = true; +} + +void m_bind_string_variable(const char *name, char **location) +{ + default_t *variable; + + variable = get_default_for_name(name); + assert(variable->type == DEFAULT_STRING); + + variable->location.s = location; + variable->bound = true; +} + +/* Set the value of a particular variable; an API function for other + * parts of the program to assign values to config variables by name. + */ + +boolean m_set_variable(const char *name, const char *value) +{ + default_t *variable; + + variable = get_default_for_name(name); + + if (variable == NULL || !variable->bound) + { + return false; + } + + set_variable(variable, value); + + return true; +} + +const char *m_get_string_variable(const char *name) +{ + default_t *variable; + + variable = get_default_for_name(name); + + if (variable == NULL || !variable->bound || + variable->type != DEFAULT_STRING) + { + return NULL; + } + + return *variable->location.s; +} + +/* SetConfigDir: + * + * Sets the location of the configuration directory, where configuration + * files are stored - default.cfg, chocolate-doom.cfg, savegames, etc. + */ + +void m_set_config_dir(const char *dir) +{ + /* Use the directory that was passed, or find the default. */ + + if (dir != NULL) + { + configdir = dir; + } + else + { + configdir = get_default_config_dir(); + } + + if (strcmp(configdir, exedir) != 0) + { + printf("Using %s for configuration and saves\n", configdir); + } + + /* Make the directory if it doesn't already exist: */ + + m_make_directory(configdir); +} + +/* Set the value of music_pack_path if it is currently empty, and create + * the directory if necessary. + */ + +void m_set_music_pack_dir(void) +{ + const char *current_path; + char *prefdir; + char *music_pack_path; + + current_path = m_get_string_variable("music_pack_path"); + + if (current_path != NULL && strlen(current_path) > 0) + { + return; + } + + prefdir = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + music_pack_path = m_string_join(prefdir, "music-packs", NULL); + + m_make_directory(prefdir); + m_make_directory(music_pack_path); + m_set_variable("music_pack_path", music_pack_path); + + free(music_pack_path); +} + +/* Calculate the path to the directory to use to store save games. + * Creates the directory as necessary. + */ + +char *m_get_save_game_dir(const char *iwadname) +{ + char *savegamedir; + char *topdir; + int p; + + /* @arg + * + * Specify a path from which to load and save games. If the + * directory does not exist then it will automatically be created. + */ + + p = m_check_parm_with_args("-savedir", 1); + if (p) + { + savegamedir = myargv[p + 1]; + if (!m_file_exists(savegamedir)) + { + m_make_directory(savegamedir); + } + + /* add separator at end just in case */ + + savegamedir = m_string_join(savegamedir, DIR_SEPARATOR_S, NULL); + + printf("Save directory changed to %s.\n", savegamedir); + } + + /* If not "doing" a configuration directory (Windows), don't "do" + * a savegame directory, either. + */ + + else if (!strcmp(configdir, exedir)) + { + savegamedir = m_string_duplicate(""); + } + else + { + /* ~/.local/share/chocolate-doom/savegames */ + + topdir = m_string_join(configdir, "savegames", NULL); + m_make_directory(topdir); + + /* eg. ~/.local/share/chocolate-doom/savegames/doom2.wad/ */ + + savegamedir = m_string_join(topdir, DIR_SEPARATOR_S, iwadname, + DIR_SEPARATOR_S, NULL); + + m_make_directory(savegamedir); + + free(topdir); + } + + return savegamedir; +} + +/* Calculate the path to the directory for autoloaded WADs/DEHs. + * Creates the directory as necessary. + */ + +char *m_get_autoload_dir(const char *iwadname) +{ + char *result; + + if (autoload_path == NULL || strlen(autoload_path) == 0) + { + char *prefdir = CONFIG_GAMES_NXDOOM_PREFDIR "/"; + autoload_path = m_string_join(prefdir, "autoload", NULL); + } + + m_make_directory(autoload_path); + + result = m_string_join(autoload_path, DIR_SEPARATOR_S, iwadname, NULL); + m_make_directory(result); + + /* TODO: Add README file */ + + return result; +} diff --git a/games/NXDoom/src/m_config.h b/games/NXDoom/src/m_config.h new file mode 100644 index 000000000..5dd8358b7 --- /dev/null +++ b/games/NXDoom/src/m_config.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_config.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Configuration file interface. + * + ****************************************************************************/ + +#ifndef __M_CONFIG__ +#define __M_CONFIG__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern const char *configdir; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_load_defaults(void); +void m_save_defaults(void); +void m_save_defaults_alternate(const char *main, const char *extra); +void m_set_config_dir(const char *dir); +void m_set_music_pack_dir(void); +void m_bind_int_variable(const char *name, int *variable); +void m_bind_float_variable(const char *name, float *variable); +void m_bind_string_variable(const char *name, char **variable); +boolean m_set_variable(const char *name, const char *value); +const char *m_get_string_variable(const char *name); +void m_set_config_filenames(const char *main_config, + const char *extra_config); +char *m_get_save_game_dir(const char *iwadname); +char *m_get_autoload_dir(const char *iwadname); + +#endif /* __M_CONFIG__ */ diff --git a/games/NXDoom/src/m_controls.c b/games/NXDoom/src/m_controls.c new file mode 100644 index 000000000..2266df2ce --- /dev/null +++ b/games/NXDoom/src/m_controls.c @@ -0,0 +1,467 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_controls.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomkeys.h" +#include "doomtype.h" + +#include "m_config.h" +#include "m_misc.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Keyboard controls */ + +int key_right = KEY_RIGHTARROW; +int key_left = KEY_LEFTARROW; + +int key_up = KEY_UPARROW; +int key_down = KEY_DOWNARROW; +int key_strafeleft = 0x2c; /* Comma, ',' */ +int key_straferight = '.'; +int key_fire = KEY_RCTRL; +int key_use = ' '; +int key_strafe = KEY_RALT; +int key_speed = KEY_RSHIFT; + +/* Heretic keyboard controls */ + +int key_flyup = KEY_PGUP; +int key_flydown = KEY_INS; +int key_flycenter = KEY_HOME; + +int key_lookup = KEY_PGDN; +int key_lookdown = KEY_DEL; +int key_lookcenter = KEY_END; + +int key_invleft = '['; +int key_invright = ']'; +int key_useartifact = KEY_ENTER; + +int key_arti_quartz = 0; +int key_arti_urn = 0; +int key_arti_bomb = 0; +int key_arti_tome = 127; +int key_arti_ring = 0; +int key_arti_chaosdevice = 0; +int key_arti_shadowsphere = 0; +int key_arti_wings = 0; +int key_arti_torch = 0; +int key_arti_morph = 0; + +/* Hexen key controls */ + +int key_jump = '/'; + +int key_arti_all = KEY_BACKSPACE; +int key_arti_health = '\\'; +int key_arti_poisonbag = '0'; +int key_arti_blastradius = '9'; +int key_arti_teleport = '8'; +int key_arti_teleportother = '7'; +int key_arti_egg = '6'; +int key_arti_invulnerability = '5'; + +/* Strife key controls + * + * haleyjd 09/01/10 + * + * Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, + * and key_lookdown, but with different default values. + */ + +int key_usehealth = 'h'; +int key_invquery = 'q'; +int key_mission = 'w'; +int key_invpop = 'z'; +int key_invkey = 'k'; +int key_invhome = KEY_HOME; +int key_invend = KEY_END; +int key_invuse = KEY_ENTER; +int key_invdrop = KEY_BACKSPACE; + +/* Mouse controls */ + +int mousebfire = 0; +int mousebstrafe = 1; +int mousebforward = 2; +int mousebspeed = 3; + +int mousebjump = -1; + +int mousebstrafeleft = -1; +int mousebstraferight = -1; +int mousebturnleft = -1; +int mousebturnright = -1; +int mousebbackward = -1; +int mousebuse = -1; + +int mousebprevweapon = -1; +int mousebnextweapon = -1; +int mousebinvleft = -1; +int mousebinvright = -1; +int mousebuseartifact = -1; + +int key_message_refresh = KEY_ENTER; +int key_pause = KEY_PAUSE; +int key_demo_quit = 'q'; +int key_spy = KEY_F12; + +/* Multiplayer chat keys: */ + +int key_multi_msg = 't'; +int key_multi_msgplayer[8]; + +/* Weapon selection keys: */ + +int key_weapon1 = '1'; +int key_weapon2 = '2'; +int key_weapon3 = '3'; +int key_weapon4 = '4'; +int key_weapon5 = '5'; +int key_weapon6 = '6'; +int key_weapon7 = '7'; +int key_weapon8 = '8'; +int key_prevweapon = 0; +int key_nextweapon = 0; + +/* Map control keys: */ + +int key_map_north = KEY_UPARROW; +int key_map_south = KEY_DOWNARROW; +int key_map_east = KEY_RIGHTARROW; +int key_map_west = KEY_LEFTARROW; +int key_map_zoomin = '='; +int key_map_zoomout = '-'; +int key_map_toggle = KEY_TAB; +int key_map_maxzoom = '0'; +int key_map_follow = 'f'; +int key_map_grid = 'g'; +int key_map_mark = 'm'; +int key_map_clearmark = 'c'; + +/* menu keys: */ + +int key_menu_activate = KEY_ESCAPE; +int key_menu_up = KEY_UPARROW; +int key_menu_down = KEY_DOWNARROW; +int key_menu_left = KEY_LEFTARROW; +int key_menu_right = KEY_RIGHTARROW; +int key_menu_back = KEY_BACKSPACE; +int key_menu_forward = KEY_ENTER; +int key_menu_confirm = 'y'; +int key_menu_abort = 'n'; + +int key_menu_help = KEY_F1; +int key_menu_save = KEY_F2; +int key_menu_load = KEY_F3; +int key_menu_volume = KEY_F4; +int key_menu_detail = KEY_F5; +int key_menu_qsave = KEY_F6; +int key_menu_endgame = KEY_F7; +int key_menu_messages = KEY_F8; +int key_menu_qload = KEY_F9; +int key_menu_quit = KEY_F10; +int key_menu_gamma = KEY_F11; + +int key_menu_incscreen = KEY_EQUALS; +int key_menu_decscreen = KEY_MINUS; +int key_menu_screenshot = 0; + +/* Joystick controls */ + +int joybfire = 0; +int joybstrafe = 1; +int joybuse = 3; +int joybspeed = 2; + +int joybstrafeleft = -1; +int joybstraferight = -1; + +int joybjump = -1; + +int joybprevweapon = -1; +int joybnextweapon = -1; + +int joybmenu = -1; +int joybautomap = -1; + +int joybuseartifact = -1; +int joybinvleft = -1; +int joybinvright = -1; + +int joybflyup = -1; +int joybflydown = -1; +int joybflycenter = -1; + +/* Control whether if a mouse button is double clicked, it acts like + * "use" has been pressed + */ + +int dclick_use = 1; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Bind all of the common controls used by Doom and all other games. */ + +void m_bind_base_controls(void) +{ + m_bind_int_variable("key_right", &key_right); + m_bind_int_variable("key_left", &key_left); + m_bind_int_variable("key_up", &key_up); + m_bind_int_variable("key_down", &key_down); + m_bind_int_variable("key_strafeleft", &key_strafeleft); + m_bind_int_variable("key_straferight", &key_straferight); + m_bind_int_variable("key_fire", &key_fire); + m_bind_int_variable("key_use", &key_use); + m_bind_int_variable("key_strafe", &key_strafe); + m_bind_int_variable("key_speed", &key_speed); + + m_bind_int_variable("mouseb_fire", &mousebfire); + m_bind_int_variable("mouseb_strafe", &mousebstrafe); + m_bind_int_variable("mouseb_forward", &mousebforward); + m_bind_int_variable("mouseb_speed", &mousebspeed); + + m_bind_int_variable("joyb_fire", &joybfire); + m_bind_int_variable("joyb_strafe", &joybstrafe); + m_bind_int_variable("joyb_use", &joybuse); + m_bind_int_variable("joyb_speed", &joybspeed); + + m_bind_int_variable("joyb_menu_activate", &joybmenu); + m_bind_int_variable("joyb_toggle_automap", &joybautomap); + + /* Extra controls that are not in the Vanilla versions: */ + + m_bind_int_variable("joyb_strafeleft", &joybstrafeleft); + m_bind_int_variable("joyb_straferight", &joybstraferight); + m_bind_int_variable("mouseb_strafeleft", &mousebstrafeleft); + m_bind_int_variable("mouseb_straferight", &mousebstraferight); + m_bind_int_variable("mouseb_turnleft", &mousebturnleft); + m_bind_int_variable("mouseb_turnright", &mousebturnright); + m_bind_int_variable("mouseb_use", &mousebuse); + m_bind_int_variable("mouseb_backward", &mousebbackward); + m_bind_int_variable("dclick_use", &dclick_use); + m_bind_int_variable("key_pause", &key_pause); + m_bind_int_variable("key_message_refresh", &key_message_refresh); +} + +void m_bind_heretic_controls(void) +{ + m_bind_int_variable("key_flyup", &key_flyup); + m_bind_int_variable("key_flydown", &key_flydown); + m_bind_int_variable("key_flycenter", &key_flycenter); + + m_bind_int_variable("key_lookup", &key_lookup); + m_bind_int_variable("key_lookdown", &key_lookdown); + m_bind_int_variable("key_lookcenter", &key_lookcenter); + + m_bind_int_variable("key_invleft", &key_invleft); + m_bind_int_variable("key_invright", &key_invright); + m_bind_int_variable("key_useartifact", &key_useartifact); + + m_bind_int_variable("mouseb_invleft", &mousebinvleft); + m_bind_int_variable("mouseb_invright", &mousebinvright); + m_bind_int_variable("mouseb_useartifact", &mousebuseartifact); + + m_bind_int_variable("joyb_invleft", &joybinvleft); + m_bind_int_variable("joyb_invright", &joybinvright); + m_bind_int_variable("joyb_useartifact", &joybuseartifact); + + m_bind_int_variable("joyb_flyup", &joybflyup); + m_bind_int_variable("joyb_flydown", &joybflydown); + m_bind_int_variable("joyb_flycenter", &joybflycenter); + + m_bind_int_variable("key_arti_quartz", &key_arti_quartz); + m_bind_int_variable("key_arti_urn", &key_arti_urn); + m_bind_int_variable("key_arti_bomb", &key_arti_bomb); + m_bind_int_variable("key_arti_tome", &key_arti_tome); + m_bind_int_variable("key_arti_ring", &key_arti_ring); + m_bind_int_variable("key_arti_chaosdevice", &key_arti_chaosdevice); + m_bind_int_variable("key_arti_shadowsphere", &key_arti_shadowsphere); + m_bind_int_variable("key_arti_wings", &key_arti_wings); + m_bind_int_variable("key_arti_torch", &key_arti_torch); + m_bind_int_variable("key_arti_morph", &key_arti_morph); +} + +void m_bind_hexen_controls(void) +{ + m_bind_int_variable("key_jump", &key_jump); + m_bind_int_variable("mouseb_jump", &mousebjump); + m_bind_int_variable("joyb_jump", &joybjump); + + m_bind_int_variable("key_arti_all", &key_arti_all); + m_bind_int_variable("key_arti_health", &key_arti_health); + m_bind_int_variable("key_arti_poisonbag", &key_arti_poisonbag); + m_bind_int_variable("key_arti_blastradius", &key_arti_blastradius); + m_bind_int_variable("key_arti_teleport", &key_arti_teleport); + m_bind_int_variable("key_arti_teleportother", &key_arti_teleportother); + m_bind_int_variable("key_arti_egg", &key_arti_egg); + m_bind_int_variable("key_arti_invulnerability", &key_arti_invulnerability); +} + +void m_bind_strife_controls(void) +{ + /* These are shared with all games, but have different defaults: */ + + key_message_refresh = '/'; + + /* These keys are shared with Heretic/Hexen but have different defaults: */ + + key_jump = 'a'; + key_lookup = KEY_PGUP; + key_lookdown = KEY_PGDN; + key_invleft = KEY_INS; + key_invright = KEY_DEL; + + m_bind_int_variable("key_jump", &key_jump); + m_bind_int_variable("key_look_up", &key_lookup); + m_bind_int_variable("key_look_down", &key_lookdown); + m_bind_int_variable("key_inv_left", &key_invleft); + m_bind_int_variable("key_inv_right", &key_invright); + + /* Custom Strife-only Keys: */ + + m_bind_int_variable("key_use_health", &key_usehealth); + m_bind_int_variable("key_invquery", &key_invquery); + m_bind_int_variable("key_mission", &key_mission); + m_bind_int_variable("key_inv_pop", &key_invpop); + m_bind_int_variable("key_inv_key", &key_invkey); + m_bind_int_variable("key_invHome", &key_invhome); + m_bind_int_variable("key_inv_end", &key_invend); + m_bind_int_variable("key_inv_use", &key_invuse); + m_bind_int_variable("key_inv_drop", &key_invdrop); + + /* Strife also supports jump on mouse and joystick, and in the exact same + * manner as Hexen! + */ + + m_bind_int_variable("mouseb_jump", &mousebjump); + m_bind_int_variable("joyb_jump", &joybjump); + + /* Subset of inventory actions common to Heretic/Hexen and Strife. */ + + m_bind_int_variable("joyb_invleft", &joybinvleft); + m_bind_int_variable("joyb_invright", &joybinvright); + + /* This is technically "invuse" in Strife, but let's reuse the value. */ + + m_bind_int_variable("joyb_useartifact", &joybuseartifact); +} + +void m_bind_weapon_controls(void) +{ + m_bind_int_variable("key_weapon1", &key_weapon1); + m_bind_int_variable("key_weapon2", &key_weapon2); + m_bind_int_variable("key_weapon3", &key_weapon3); + m_bind_int_variable("key_weapon4", &key_weapon4); + m_bind_int_variable("key_weapon5", &key_weapon5); + m_bind_int_variable("key_weapon6", &key_weapon6); + m_bind_int_variable("key_weapon7", &key_weapon7); + m_bind_int_variable("key_weapon8", &key_weapon8); + + m_bind_int_variable("key_prevweapon", &key_prevweapon); + m_bind_int_variable("key_nextweapon", &key_nextweapon); + + m_bind_int_variable("joyb_prevweapon", &joybprevweapon); + m_bind_int_variable("joyb_nextweapon", &joybnextweapon); + + m_bind_int_variable("mouseb_prevweapon", &mousebprevweapon); + m_bind_int_variable("mouseb_nextweapon", &mousebnextweapon); +} + +void m_bind_map_controls(void) +{ + m_bind_int_variable("key_map_north", &key_map_north); + m_bind_int_variable("key_map_south", &key_map_south); + m_bind_int_variable("key_map_east", &key_map_east); + m_bind_int_variable("key_map_west", &key_map_west); + m_bind_int_variable("key_map_zoomin", &key_map_zoomin); + m_bind_int_variable("key_map_zoomout", &key_map_zoomout); + m_bind_int_variable("key_map_toggle", &key_map_toggle); + m_bind_int_variable("key_map_maxzoom", &key_map_maxzoom); + m_bind_int_variable("key_map_follow", &key_map_follow); + m_bind_int_variable("key_map_grid", &key_map_grid); + m_bind_int_variable("key_map_mark", &key_map_mark); + m_bind_int_variable("key_map_clearmark", &key_map_clearmark); +} + +void m_bind_menu_controls(void) +{ + m_bind_int_variable("key_menu_activate", &key_menu_activate); + m_bind_int_variable("key_menu_up", &key_menu_up); + m_bind_int_variable("key_menu_down", &key_menu_down); + m_bind_int_variable("key_menu_left", &key_menu_left); + m_bind_int_variable("key_menu_right", &key_menu_right); + m_bind_int_variable("key_menu_back", &key_menu_back); + m_bind_int_variable("key_menu_forward", &key_menu_forward); + m_bind_int_variable("key_menu_confirm", &key_menu_confirm); + m_bind_int_variable("key_menu_abort", &key_menu_abort); + + m_bind_int_variable("key_menu_help", &key_menu_help); + m_bind_int_variable("key_menu_save", &key_menu_save); + m_bind_int_variable("key_menu_load", &key_menu_load); + m_bind_int_variable("key_menu_volume", &key_menu_volume); + m_bind_int_variable("key_menu_detail", &key_menu_detail); + m_bind_int_variable("key_menu_qsave", &key_menu_qsave); + m_bind_int_variable("key_menu_endgame", &key_menu_endgame); + m_bind_int_variable("key_menu_messages", &key_menu_messages); + m_bind_int_variable("key_menu_qload", &key_menu_qload); + m_bind_int_variable("key_menu_quit", &key_menu_quit); + m_bind_int_variable("key_menu_gamma", &key_menu_gamma); + + m_bind_int_variable("key_menu_incscreen", &key_menu_incscreen); + m_bind_int_variable("key_menu_decscreen", &key_menu_decscreen); + m_bind_int_variable("key_menu_screenshot", &key_menu_screenshot); + m_bind_int_variable("key_demo_quit", &key_demo_quit); + m_bind_int_variable("key_spy", &key_spy); +} + +void m_bind_chat_controls(unsigned int num_players) +{ + char name[32]; /* haleyjd: 20 not large enough - Thank you, come again! */ + unsigned int i; /* haleyjd: signedness conflict */ + + m_bind_int_variable("key_multi_msg", &key_multi_msg); + + for (i = 0; i < num_players; ++i) + { + snprintf(name, sizeof(name), "key_multi_msgplayer%i", i + 1); + m_bind_int_variable(name, &key_multi_msgplayer[i]); + } +} + +/* Apply custom patches to the default values depending on the + * platform we are running on. + */ + +void m_apply_platform_defaults(void) +{ + /* no-op. Add your platform-specific patches here. */ +} diff --git a/games/NXDoom/src/m_controls.h b/games/NXDoom/src/m_controls.h new file mode 100644 index 000000000..fb756ec11 --- /dev/null +++ b/games/NXDoom/src/m_controls.h @@ -0,0 +1,207 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_controls.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef __M_CONTROLS_H__ +#define __M_CONTROLS_H__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int key_right; +extern int key_left; + +extern int key_up; +extern int key_down; +extern int key_strafeleft; +extern int key_straferight; +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; + +extern int key_jump; + +extern int key_flyup; +extern int key_flydown; +extern int key_flycenter; +extern int key_lookup; +extern int key_lookdown; +extern int key_lookcenter; +extern int key_invleft; +extern int key_invright; +extern int key_useartifact; + +/* villsa [STRIFE] strife keys */ + +extern int key_usehealth; +extern int key_invquery; +extern int key_mission; +extern int key_invpop; +extern int key_invkey; +extern int key_invhome; +extern int key_invend; +extern int key_invuse; +extern int key_invdrop; + +extern int key_message_refresh; +extern int key_pause; + +extern int key_multi_msg; +extern int key_multi_msgplayer[8]; + +extern int key_weapon1; +extern int key_weapon2; +extern int key_weapon3; +extern int key_weapon4; +extern int key_weapon5; +extern int key_weapon6; +extern int key_weapon7; +extern int key_weapon8; + +extern int key_arti_quartz; +extern int key_arti_urn; +extern int key_arti_bomb; +extern int key_arti_tome; +extern int key_arti_ring; +extern int key_arti_chaosdevice; +extern int key_arti_shadowsphere; +extern int key_arti_wings; +extern int key_arti_torch; +extern int key_arti_morph; + +extern int key_arti_all; +extern int key_arti_health; +extern int key_arti_poisonbag; +extern int key_arti_blastradius; +extern int key_arti_teleport; +extern int key_arti_teleportother; +extern int key_arti_egg; +extern int key_arti_invulnerability; + +extern int key_demo_quit; +extern int key_spy; +extern int key_prevweapon; +extern int key_nextweapon; + +extern int key_map_north; +extern int key_map_south; +extern int key_map_east; +extern int key_map_west; +extern int key_map_zoomin; +extern int key_map_zoomout; +extern int key_map_toggle; +extern int key_map_maxzoom; +extern int key_map_follow; +extern int key_map_grid; +extern int key_map_mark; +extern int key_map_clearmark; + +/* menu keys: */ + +extern int key_menu_activate; +extern int key_menu_up; +extern int key_menu_down; +extern int key_menu_left; +extern int key_menu_right; +extern int key_menu_back; +extern int key_menu_forward; +extern int key_menu_confirm; +extern int key_menu_abort; + +extern int key_menu_help; +extern int key_menu_save; +extern int key_menu_load; +extern int key_menu_volume; +extern int key_menu_detail; +extern int key_menu_qsave; +extern int key_menu_endgame; +extern int key_menu_messages; +extern int key_menu_qload; +extern int key_menu_quit; +extern int key_menu_gamma; + +extern int key_menu_incscreen; +extern int key_menu_decscreen; +extern int key_menu_screenshot; + +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousebspeed; + +extern int mousebjump; + +extern int mousebstrafeleft; +extern int mousebstraferight; +extern int mousebturnleft; +extern int mousebturnright; +extern int mousebbackward; +extern int mousebuse; + +extern int mousebprevweapon; +extern int mousebnextweapon; +extern int mousebinvleft; +extern int mousebinvright; +extern int mousebuseartifact; + +extern int joybfire; +extern int joybstrafe; +extern int joybuse; +extern int joybspeed; + +extern int joybjump; + +extern int joybstrafeleft; +extern int joybstraferight; + +extern int joybprevweapon; +extern int joybnextweapon; + +extern int joybmenu; +extern int joybautomap; + +extern int joybuseartifact; +extern int joybinvleft; +extern int joybinvright; + +extern int joybflyup; +extern int joybflydown; +extern int joybflycenter; + +extern int dclick_use; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void m_bind_base_controls(void); +void m_bind_heretic_controls(void); +void m_bind_hexen_controls(void); +void m_bind_strife_controls(void); +void m_bind_weapon_controls(void); +void m_bind_map_controls(void); +void m_bind_menu_controls(void); +void m_bind_chat_controls(unsigned int num_players); + +void m_apply_platform_defaults(void); + +#endif /* __M_CONTROLS_H__ */ diff --git a/games/NXDoom/src/m_fixed.c b/games/NXDoom/src/m_fixed.c new file mode 100644 index 000000000..c8931d96c --- /dev/null +++ b/games/NXDoom/src/m_fixed.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_fixed.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Fixed point implementation. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "stdlib.h" + +#include "doomtype.h" +#include "i_system.h" + +#include "m_fixed.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Fixme. __USE_C_FIXED__ or something. */ + +fixed_t fixed_mul(fixed_t a, fixed_t b) +{ + return ((int64_t)a * (int64_t)b) >> FRACBITS; +} + +/* fixed_div, C version. */ + +fixed_t fixed_div(fixed_t a, fixed_t b) +{ + if ((abs(a) >> 14) >= abs(b)) + { + return (a ^ b) < 0 ? INT_MIN : INT_MAX; + } + else + { + int64_t result; + + result = ((int64_t)a << FRACBITS) / b; + + return (fixed_t)result; + } +} diff --git a/games/NXDoom/src/m_fixed.h b/games/NXDoom/src/m_fixed.h new file mode 100644 index 000000000..abb837b31 --- /dev/null +++ b/games/NXDoom/src/m_fixed.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_fixed.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Fixed point arithemtics, implementation. + * + ****************************************************************************/ + +#ifndef __M_FIXED__ +#define __M_FIXED__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Fixed point, 32bit as 16.16. */ + +#define FRACBITS 16 +#define FRACUNIT (1 << FRACBITS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef int fixed_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +fixed_t fixed_mul(fixed_t a, fixed_t b); +fixed_t fixed_div(fixed_t a, fixed_t b); + +#endif /* __M_FIXED__ */ diff --git a/games/NXDoom/src/m_misc.c b/games/NXDoom/src/m_misc.c new file mode 100644 index 000000000..515ff46f0 --- /dev/null +++ b/games/NXDoom/src/m_misc.c @@ -0,0 +1,621 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_misc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Miscellaneous. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "doomtype.h" + +#include "i_system.h" +#include "m_misc.h" +#include "z_zone.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void m_make_directory(const char *path) +{ + mkdir(path, 0755); +} + +boolean m_file_exists(const char *filename) +{ + FILE *fstream; + + fstream = fopen(filename, "r"); + + if (fstream != NULL) + { + fclose(fstream); + return true; + } + else + { + /* If we can't open because the file is a directory, the + * "file" exists at least! + */ + + return errno == EISDIR; + } +} + +/* Check if a file exists by probing for common case variation of its + * filename. Returns a newly allocated string that the caller is responsible + * for freeing. + */ + +char *m_file_case_exists(const char *path) +{ + char *path_dup; + char *filename; + char *ext; + + path_dup = m_string_duplicate(path); + + /* 0: actual path */ + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + filename = strrchr(path_dup, DIR_SEPARATOR); + if (filename != NULL) + { + filename++; + } + else + { + filename = path_dup; + } + + /* 1: lowercase filename, e.g. doom2.wad */ + + m_force_lowercase(filename); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + /* 2: uppercase filename, e.g. DOOM2.WAD */ + + m_force_uppercase(filename); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + + /* 3. uppercase basename with lowercase extension, e.g. DOOM2.wad */ + + ext = strrchr(path_dup, '.'); + if (ext != NULL && ext > filename) + { + m_force_lowercase(ext + 1); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + } + + /* 4. lowercase filename with uppercase first letter, e.g. Doom2.wad */ + + if (strlen(filename) > 1) + { + m_force_lowercase(filename + 1); + + if (m_file_exists(path_dup)) + { + return path_dup; + } + } + + /* 5. no luck */ + + free(path_dup); + return NULL; +} + +long m_file_length(FILE *handle) +{ + long savedpos; + long length; + + /* save the current position in the file */ + + savedpos = ftell(handle); + + /* jump to the end and find the length */ + + fseek(handle, 0, SEEK_END); + length = ftell(handle); + + /* go back to the old location */ + + fseek(handle, savedpos, SEEK_SET); + + return length; +} + +boolean m_write_file(const char *name, const void *source, int length) +{ + FILE *handle; + int count; + + handle = fopen(name, "wb"); + + if (handle == NULL) return false; + + count = fwrite(source, 1, length, handle); + fclose(handle); + + if (count < length) return false; + + return true; +} + +int m_read_file(const char *name, byte **buffer) +{ + FILE *handle; + int count; + int length; + byte *buf; + + handle = fopen(name, "rb"); + if (handle == NULL) i_error("Couldn't read file %s", name); + + /* find the size of the file by seeking to the end and + * reading the current position + */ + + length = m_file_length(handle); + + buf = z_malloc(length + 1, PU_STATIC, NULL); + count = fread(buf, 1, length, handle); + fclose(handle); + + if (count < length) i_error("Couldn't read file %s", name); + + buf[length] = '\0'; + *buffer = buf; + return length; +} + +/* Returns the path to a temporary file of the given name, stored + * inside the system temporary directory. + * + * The returned value must be freed with z_free after use. + */ + +char *m_temp_file(const char *s) +{ + const char *tempdir; + + /* Check the $TMPDIR environment variable to find the location. */ + + tempdir = getenv("TMPDIR"); + + if (tempdir == NULL) + { + tempdir = "/tmp"; + } + + return m_string_join(tempdir, DIR_SEPARATOR_S, s, NULL); +} + +boolean m_str_to_int(const char *str, int *result) +{ + return sscanf(str, " 0x%x", (unsigned int *)result) == 1 || + sscanf(str, " 0X%x", (unsigned int *)result) == 1 || + sscanf(str, " 0%o", (unsigned int *)result) == 1 || + sscanf(str, " %d", result) == 1; +} + +/* Returns the directory portion of the given path, without the trailing + * slash separator character. If no directory is described in the path, + * the string "." is returned. In either case, the result is newly allocated + * and must be freed by the caller after use. + */ + +char *m_dir_name(const char *path) +{ + char *result; + const char *pf; + const char *pb; + + pf = strrchr(path, '/'); + pb = NULL; + + if (pf == NULL && pb == NULL) + { + return m_string_duplicate("."); + } + else + { + const char *p = (pb > pf) ? pb : pf; + result = m_string_duplicate(path); + result[p - path] = '\0'; + return result; + } +} + +/* Returns the base filename described by the given path (without the + * directory name). The result points inside path and nothing new is + * allocated. + */ + +const char *m_base_name(const char *path) +{ + const char *pf; + const char *pb; + + pf = strrchr(path, '/'); + pb = NULL; + + if (pf == NULL && pb == NULL) + { + return path; + } + else + { + const char *p = (pb > pf) ? pb : pf; + return p + 1; + } +} + +void m_extract_file_base(const char *path, char *dest) +{ + const char *src; + const char *filename; + int length; + + src = path + strlen(path) - 1; + + /* back up until a \ or the start */ + + while (src != path && *(src - 1) != DIR_SEPARATOR) + { + src--; + } + + filename = src; + + /* Copy up to eight characters + * Note: Vanilla Doom exits with an error if a filename is specified + * with a base of more than eight characters. To remove the 8.3 + * filename limit, instead we simply truncate the name. + */ + + length = 0; + memset(dest, 0, 8); + + while (*src != '\0' && *src != '.') + { + if (length >= 8) + { + printf("Warning: Truncated '%s' lump name to '%.8s'.\n", filename, + dest); + break; + } + + dest[length++] = toupper((int)*src++); + } +} + +/**************************************************************************** + * Name: m_force_uppercase + * + * Description: + * (PROC) Change string to uppercase. + * + ****************************************************************************/ + +void m_force_uppercase(char *text) +{ + char *p; + + for (p = text; *p != '\0'; ++p) + { + *p = toupper(*p); + } +} + +/**************************************************************************** + * Name: m_force_lowercase + * + * Description: + * (PROC) Change string to lowercase. + * + ****************************************************************************/ + +void m_force_lowercase(char *text) +{ + char *p; + + for (p = text; *p != '\0'; ++p) + { + *p = tolower(*p); + } +} + +/**************************************************************************** + * Name: m_string_duplicate + * + * Description: + * Safe version of strdup() that checks the string was successfully + * allocated. + * + ****************************************************************************/ + +char *m_string_duplicate(const char *orig) +{ + char *result; + + result = strdup(orig); + + if (result == NULL) + { + i_error("Failed to duplicate string (length %zu)\n", strlen(orig)); + } + + return result; +} + +char *m_string_replace(const char *haystack, const char *needle, + const char *replacement) +{ + char *result; + char *dst; + const char *p; + size_t needle_len = strlen(needle); + size_t result_len; + size_t dst_len; + + /* Iterate through occurrences of 'needle' and calculate the size of + * the new string. + */ + + result_len = strlen(haystack) + 1; + p = haystack; + + for (; ; ) + { + p = strstr(p, needle); + if (p == NULL) + { + break; + } + + p += needle_len; + result_len += strlen(replacement) - needle_len; + } + + /* Construct new string. */ + + result = malloc(result_len); + if (result == NULL) + { + i_error("m_string_replace: Failed to allocate new string"); + return NULL; + } + + dst = result; + dst_len = result_len; + p = haystack; + + while (*p != '\0') + { + if (!strncmp(p, needle, needle_len)) + { + m_str_copy(dst, replacement, dst_len); + p += needle_len; + dst += strlen(replacement); + dst_len -= strlen(replacement); + } + else + { + *dst = *p; + ++dst; + --dst_len; + ++p; + } + } + + *dst = '\0'; + + return result; +} + +/* Safe string copy function that works like OpenBSD's strlcpy(). + * Returns true if the string was not truncated. + */ + +boolean m_str_copy(char *dest, const char *src, size_t dest_size) +{ + size_t len; + + if (dest_size >= 1) + { + dest[dest_size - 1] = '\0'; + strncpy(dest, src, dest_size - 1); + } + else + { + return false; + } + + len = strlen(dest); + return src[len] == '\0'; +} + +/* Safe string concat function that works like OpenBSD's strlcat(). + * Returns true if string not truncated. + */ + +boolean m_string_concat(char *dest, const char *src, size_t dest_size) +{ + size_t offset; + + offset = strlen(dest); + if (offset > dest_size) + { + offset = dest_size; + } + + return m_str_copy(dest + offset, src, dest_size - offset); +} + +/* Returns true if 's' begins with the specified prefix. */ + +boolean m_string_starts_with(const char *s, const char *prefix) +{ + return strlen(s) >= strlen(prefix) && + strncmp(s, prefix, strlen(prefix)) == 0; +} + +/* Returns true if 's' ends with the specified suffix. */ + +boolean m_string_ends_with(const char *s, const char *suffix) +{ + return strlen(s) >= strlen(suffix) && + strcmp(s + strlen(s) - strlen(suffix), suffix) == 0; +} + +/* Return a newly-malloced string with all the strings given as arguments + * concatenated together. + */ + +char *m_string_join(const char *s, ...) +{ + char *result; + const char *v; + va_list args; + size_t result_len; + + result_len = strlen(s) + 1; + + va_start(args, s); + + for (; ; ) + { + v = va_arg(args, const char *); + if (v == NULL) + { + break; + } + + result_len += strlen(v); + } + + va_end(args); + + result = malloc(result_len); + + if (result == NULL) + { + i_error("m_string_join: Failed to allocate new string."); + return NULL; + } + + m_str_copy(result, s, result_len); + + va_start(args, s); + + for (; ; ) + { + v = va_arg(args, const char *); + if (v == NULL) + { + break; + } + + m_string_concat(result, v, result_len); + } + + va_end(args); + + return result; +} + +/**************************************************************************** + * Name: m_normalize_slashes + * + * Description: + * Remove trailing slashes, translate backslashes to slashes. The string to + * normalize is passed and returned in str. + * + * killough 11/98: rewritten + * + * [STRIFE] - haleyjd 20110210: Borrowed from Eternity and adapted to + * respect the DIR_SEPARATOR define used by Choco Doom. This routine + * originated in BOOM. + * + ****************************************************************************/ + +void m_normalize_slashes(char *str) +{ + char *p; + + /* Convert all slashes/backslashes to DIR_SEPARATOR */ + + for (p = str; *p; p++) + { + if ((*p == '/' || *p == '\\') && *p != DIR_SEPARATOR) + { + *p = DIR_SEPARATOR; + } + } + + /* Remove trailing slashes */ + + while (p > str && *--p == DIR_SEPARATOR) + { + *p = 0; + } + + /* Collapse multiple slashes */ + + for (p = str; (*str++ = *p); ) + { + if (*p++ == DIR_SEPARATOR) + { + while (*p == DIR_SEPARATOR) + { + p++; + } + } + } +} diff --git a/games/NXDoom/src/m_misc.h b/games/NXDoom/src/m_misc.h new file mode 100644 index 000000000..0b310371e --- /dev/null +++ b/games/NXDoom/src/m_misc.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/src/m_misc.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Miscellaneous. + * + ****************************************************************************/ + +#ifndef __M_MISC__ +#define __M_MISC__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* debugging code to check there are no loops in a linked list + * disabled unless explicitly requested + */ + +#ifdef DEBUG_LINKED_LISTS + +#define LINKED_LIST_CHECK_NO_CYCLE(list_type, list, next_member) \ + do \ + { \ + if (list != NULL) \ + { \ + list_type *slow, *fast; \ + slow = list; \ + fast = list->next_member; \ + while (fast) \ + { \ + if (!fast->next_member) \ + { \ + break; \ + } \ + fast = fast->next_member->next_member; \ + slow = slow->next_member; \ + if (slow == fast) \ + { \ + fprintf(stderr, "loop in linked list " #list " in %s:%d", \ + __FILE__, __LINE__); \ + __builtin_trap(); \ + } \ + } \ + } \ + } \ + while (0) + +#else /* DEBUG_LINKED_LISTS */ + +#define LINKED_LIST_CHECK_NO_CYCLE(list_type, list, next_member) \ + do \ + { \ + } \ + while (0) + +#endif /* DEBUG_LINKED_LISTS */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean m_write_file(const char *name, const void *source, int length); +int m_read_file(const char *name, byte **buffer); +void m_make_directory(const char *dir); +char *m_temp_file(const char *s); +boolean m_file_exists(const char *file); +char *m_file_case_exists(const char *file); +long m_file_length(FILE *handle); +boolean m_str_to_int(const char *str, int *result); +char *m_dir_name(const char *path); +const char *m_base_name(const char *path); +void m_extract_file_base(const char *path, char *dest); +void m_force_uppercase(char *text); +void m_force_lowercase(char *text); +char *m_string_duplicate(const char *orig); +boolean m_str_copy(char *dest, const char *src, size_t dest_size); +boolean m_string_concat(char *dest, const char *src, size_t dest_size); +char *m_string_replace(const char *haystack, const char *needle, + const char *replacement); +char *m_string_join(const char *s, ...); +boolean m_string_starts_with(const char *s, const char *prefix); +boolean m_string_ends_with(const char *s, const char *suffix); +void m_normalize_slashes(char *str); + +#endif /* __M_MISC__ */ diff --git a/games/NXDoom/src/manifest.xml b/games/NXDoom/src/manifest.xml new file mode 100644 index 000000000..05a19e7ad --- /dev/null +++ b/games/NXDoom/src/manifest.xml @@ -0,0 +1,12 @@ + + + + + + + true + + + + diff --git a/games/NXDoom/src/memio.c b/games/NXDoom/src/memio.c new file mode 100644 index 000000000..aeb8ac8a8 --- /dev/null +++ b/games/NXDoom/src/memio.c @@ -0,0 +1,215 @@ +/**************************************************************************** + * apps/games/NXDoom/src/memio.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Emulates the IO functions in C stdio.h reading and writing to + * memory. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "memio.h" + +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + MODE_READ, + MODE_WRITE, +} memfile_mode_t; + +struct _MEMFILE +{ + unsigned char *buf; + size_t buflen; + size_t alloced; + unsigned int position; + memfile_mode_t mode; +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Open a memory area for reading */ + +MEMFILE *mem_fopen_read(void *buf, size_t buflen) +{ + MEMFILE *file; + + file = z_malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->buf = (unsigned char *)buf; + file->buflen = buflen; + file->position = 0; + file->mode = MODE_READ; + + return file; +} + +/* Read bytes */ + +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream) +{ + size_t items; + + if (stream->mode != MODE_READ) + { + printf("not a read stream\n"); + return -1; + } + + /* Trying to read more bytes than we have left? */ + + items = nmemb; + + if (items * size > stream->buflen - stream->position) + { + items = (stream->buflen - stream->position) / size; + } + + /* Copy bytes to buffer */ + + memcpy(buf, stream->buf + stream->position, items * size); + + /* Update position */ + + stream->position += items * size; + + return items; +} + +/* Open a memory area for writing */ + +MEMFILE *mem_fopen_write(void) +{ + MEMFILE *file; + + file = z_malloc(sizeof(MEMFILE), PU_STATIC, 0); + + file->alloced = 1024; + file->buf = z_malloc(file->alloced, PU_STATIC, 0); + file->buflen = 0; + file->position = 0; + file->mode = MODE_WRITE; + + return file; +} + +/* Write bytes to stream */ + +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, + MEMFILE *stream) +{ + size_t bytes; + + if (stream->mode != MODE_WRITE) + { + return -1; + } + + /* More bytes than can fit in the buffer? + * If so, reallocate bigger. + */ + + bytes = size * nmemb; + + while (bytes > stream->alloced - stream->position) + { + unsigned char *newbuf; + + newbuf = z_malloc(stream->alloced * 2, PU_STATIC, 0); + memcpy(newbuf, stream->buf, stream->alloced); + z_free(stream->buf); + stream->buf = newbuf; + stream->alloced *= 2; + } + + /* Copy into buffer */ + + memcpy(stream->buf + stream->position, ptr, bytes); + stream->position += bytes; + + if (stream->position > stream->buflen) stream->buflen = stream->position; + + return nmemb; +} + +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen) +{ + *buf = stream->buf; + *buflen = stream->buflen; +} + +void mem_fclose(MEMFILE *stream) +{ + if (stream->mode == MODE_WRITE) + { + z_free(stream->buf); + } + + z_free(stream); +} + +long mem_ftell(MEMFILE *stream) +{ + return stream->position; +} + +int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence) +{ + unsigned int newpos; + + switch (whence) + { + case MEM_SEEK_SET: + newpos = (int)position; + break; + + case MEM_SEEK_CUR: + newpos = (int)(stream->position + position); + break; + + case MEM_SEEK_END: + newpos = (int)(stream->buflen + position); + break; + default: + return -1; + } + + if (newpos < stream->buflen) + { + stream->position = newpos; + return 0; + } + else + { + printf("Error seeking to %u\n", newpos); + return -1; + } +} diff --git a/games/NXDoom/src/memio.h b/games/NXDoom/src/memio.h new file mode 100644 index 000000000..275fbc753 --- /dev/null +++ b/games/NXDoom/src/memio.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/memio.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef MEMIO_H +#define MEMIO_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _MEMFILE MEMFILE; + +typedef enum +{ + MEM_SEEK_SET, + MEM_SEEK_CUR, + MEM_SEEK_END, +} mem_rel_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +MEMFILE *mem_fopen_read(void *buf, size_t buflen); +size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream); +MEMFILE *mem_fopen_write(void); +size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, + MEMFILE *stream); +void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen); +void mem_fclose(MEMFILE *stream); +long mem_ftell(MEMFILE *stream); +int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence); + +#endif /* MEMIO_H */ diff --git a/games/NXDoom/src/midifile.c b/games/NXDoom/src/midifile.c new file mode 100644 index 000000000..8374ac50d --- /dev/null +++ b/games/NXDoom/src/midifile.c @@ -0,0 +1,878 @@ +/**************************************************************************** + * apps/games/NXDoom/src/midifile.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Reading of MIDI files. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" +#include "i_swap.h" +#include "i_system.h" +#include "m_misc.h" +#include "midifile.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HEADER_CHUNK_ID "MThd" +#define TRACK_CHUNK_ID "MTrk" +#define MAX_BUFFER_SIZE 0x10000 + +/* haleyjd 09/09/10: packing required */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct chunk_header_t +{ + byte chunk_id[4]; + unsigned int chunk_size; +} end_packed_struct; + +typedef struct chunk_header_t chunk_header_t; + +begin_packed_struct struct midi_header_t +{ + chunk_header_t chunk_header; + unsigned short format_type; + unsigned short num_tracks; + unsigned short time_division; +} begin_packed_struct; + +typedef struct midi_header_t midi_header_t; + +/* haleyjd 09/09/10: packing off. */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +typedef struct +{ + /* Length in bytes: */ + + unsigned int data_len; + + /* Events in this track: */ + + midi_event_t *events; + int num_events; +} midi_track_t; + +struct midi_track_iter_s +{ + midi_track_t *track; + unsigned int position; + unsigned int loop_point; +}; + +struct midi_file_s +{ + midi_header_t header; + + /* All tracks in this file: */ + + midi_track_t *tracks; + unsigned int num_tracks; + + /* Data buffer used to store data read for SysEx or meta events: */ + + byte *buffer; + unsigned int buffer_size; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check the header of a chunk: */ + +static boolean check_chunk_header(chunk_header_t *chunk, + const char *expected_id) +{ + boolean result; + + result = (memcmp((char *)chunk->chunk_id, expected_id, 4) == 0); + + if (!result) + { + fprintf(stderr, + "check_chunk_header: Expected '%s' chunk header, " + "got '%c%c%c%c'\n", + expected_id, chunk->chunk_id[0], chunk->chunk_id[1], + chunk->chunk_id[2], chunk->chunk_id[3]); + } + + return result; +} + +/* Read a single byte. Returns false on error. */ + +static boolean read_byte(byte *result, FILE *stream) +{ + int c; + + c = fgetc(stream); + + if (c == EOF) + { + fprintf(stderr, "read_byte: Unexpected end of file\n"); + return false; + } + else + { + *result = (byte)c; + + return true; + } +} + +/* Read a variable-length value. */ + +static boolean read_variable_length(unsigned int *result, FILE *stream) +{ + int i; + byte b = 0; + + *result = 0; + + for (i = 0; i < 4; ++i) + { + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_variable_length: Error while reading " + "variable-length value\n"); + return false; + } + + /* Insert the bottom seven bits from this byte. */ + + *result <<= 7; + *result |= b & 0x7f; + + /* If the top bit is not set, this is the end. */ + + if ((b & 0x80) == 0) + { + return true; + } + } + + fprintf(stderr, "read_variable_length: Variable-length value too " + "long: maximum of four bytes\n"); + return false; +} + +/* Read a byte sequence into the data buffer. */ + +static void *read_byte_sequence(unsigned int num_bytes, FILE *stream) +{ + unsigned int i; + byte *result; + + /* Allocate a buffer. Allocate one extra byte, as malloc(0) is + * non-portable. + */ + + result = malloc(num_bytes + 1); + + if (result == NULL) + { + fprintf(stderr, "read_byte_sequence: Failed to allocate buffer\n"); + return NULL; + } + + /* Read the data: */ + + for (i = 0; i < num_bytes; ++i) + { + if (!read_byte(&result[i], stream)) + { + fprintf(stderr, + "read_byte_sequence: Error while reading byte %u\n", i); + free(result); + return NULL; + } + } + + return result; +} + +/* Read a MIDI channel event. + * two_param indicates that the event type takes two parameters + * (three byte) otherwise it is single parameter (two byte) + */ + +static boolean read_channel_event(midi_event_t *event, byte event_type, + boolean two_param, FILE *stream) +{ + byte b = 0; + + /* Set basics: */ + + event->event_type = event_type & 0xf0; + event->data.channel.channel = event_type & 0x0f; + + /* Read parameters: */ + + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_channel_event: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param1 = b; + + /* Second parameter: */ + + if (two_param) + { + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_channel_event: Error while reading channel " + "event parameters\n"); + return false; + } + + event->data.channel.param2 = b; + } + + return true; +} + +/* Read sysex event: */ + +static boolean read_sys_ex_event(midi_event_t *event, int event_type, + FILE *stream) +{ + event->event_type = event_type; + + if (!read_variable_length(&event->data.sysex.length, stream)) + { + fprintf(stderr, "read_sys_ex_event: Failed to read length of " + "SysEx block\n"); + return false; + } + + /* Read the byte sequence: */ + + event->data.sysex.data = + read_byte_sequence(event->data.sysex.length, stream); + + if (event->data.sysex.data == NULL) + { + fprintf(stderr, + "read_sys_ex_event: Failed while reading SysEx event\n"); + return false; + } + + return true; +} + +/* Read meta event: */ + +static boolean read_meta_event(midi_event_t *event, FILE *stream) +{ + byte b = 0; + + event->event_type = MIDI_EVENT_META; + + /* Read meta event type: */ + + if (!read_byte(&b, stream)) + { + fprintf(stderr, "read_meta_event: Failed to read meta event type\n"); + return false; + } + + event->data.meta.type = b; + + /* Read length of meta event data: */ + + if (!read_variable_length(&event->data.meta.length, stream)) + { + fprintf(stderr, "read_sys_ex_event: Failed to read length of " + "SysEx block\n"); + return false; + } + + /* Read the byte sequence: */ + + event->data.meta.data = + read_byte_sequence(event->data.meta.length, stream); + + if (event->data.meta.data == NULL) + { + fprintf(stderr, + "read_sys_ex_event: Failed while reading SysEx event\n"); + return false; + } + + return true; +} + +static boolean read_event(midi_event_t *event, unsigned int *last_event_type, + FILE *stream) +{ + byte event_type = 0; + + if (!read_variable_length(&event->delta_time, stream)) + { + fprintf(stderr, "read_event: Failed to read event timestamp\n"); + return false; + } + + if (!read_byte(&event_type, stream)) + { + fprintf(stderr, "read_event: Failed to read event type\n"); + return false; + } + + /* All event types have their top bit set. Therefore, if + * the top bit is not set, it is because we are using the "same + * as previous event type" shortcut to save a byte. Skip back + * a byte so that we read this byte again. + */ + + if ((event_type & 0x80) == 0) + { + event_type = *last_event_type; + + if (fseek(stream, -1, SEEK_CUR) < 0) + { + fprintf(stderr, "read_event: Unable to seek in stream\n"); + return false; + } + } + else + { + *last_event_type = event_type; + } + + /* Check event type: */ + + switch (event_type & 0xf0) + { + /* Two parameter channel events: */ + + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PITCH_BEND: + return read_channel_event(event, event_type, true, stream); + + /* Single parameter channel events: */ + + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + return read_channel_event(event, event_type, false, stream); + + default: + break; + } + + /* Specific value? */ + + switch (event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + return read_sys_ex_event(event, event_type, stream); + + case MIDI_EVENT_META: + return read_meta_event(event, stream); + + default: + break; + } + + fprintf(stderr, "read_event: Unknown MIDI event type: 0x%x\n", event_type); + return false; +} + +/* Free an event: */ + +static void free_event(midi_event_t *event) +{ + /* Some event types have dynamically allocated buffers assigned + * to them that must be freed. + */ + + switch (event->event_type) + { + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + free(event->data.sysex.data); + break; + + case MIDI_EVENT_META: + free(event->data.meta.data); + break; + + default: + break; /* Nothing to do. */ + } +} + +/* Read and check the track chunk header */ + +static boolean read_track_header(midi_track_t *track, FILE *stream) +{ + size_t records_read; + chunk_header_t chunk_header; + + records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!check_chunk_header(&chunk_header, TRACK_CHUNK_ID)) + { + return false; + } + + track->data_len = be32toh(chunk_header.chunk_size); + + return true; +} + +static boolean read_track(midi_track_t *track, FILE *stream) +{ + midi_event_t *new_events; + midi_event_t *event; + unsigned int last_event_type; + + track->num_events = 0; + track->events = NULL; + + /* Read the header: */ + + if (!read_track_header(track, stream)) + { + return false; + } + + /* Then the events: */ + + last_event_type = 0; + + for (; ; ) + { + /* Resize the track slightly larger to hold another event: */ + + new_events = i_realloc(track->events, + sizeof(midi_event_t) * (track->num_events + 1)); + track->events = new_events; + + /* Read the next event: */ + + event = &track->events[track->num_events]; + if (!read_event(event, &last_event_type, stream)) + { + return false; + } + + ++track->num_events; + + /* End of track? */ + + if (event->event_type == MIDI_EVENT_META && + event->data.meta.type == MIDI_META_END_OF_TRACK) + { + break; + } + } + + return true; +} + +/* Free a track: */ + +static void free_track(midi_track_t *track) +{ + unsigned int i; + + for (i = 0; i < track->num_events; ++i) + { + free_event(&track->events[i]); + } + + free(track->events); +} + +static boolean read_all_tracks(midi_file_t *file, FILE *stream) +{ + unsigned int i; + + /* Allocate list of tracks and read each track: */ + + file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks); + + if (file->tracks == NULL) + { + return false; + } + + memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks); + + /* Read each track: */ + + for (i = 0; i < file->num_tracks; ++i) + { + if (!read_track(&file->tracks[i], stream)) + { + return false; + } + } + + return true; +} + +/* Read and check the header chunk. */ + +static boolean read_file_header(midi_file_t *file, FILE *stream) +{ + size_t records_read; + unsigned int format_type; + + records_read = fread(&file->header, sizeof(midi_header_t), 1, stream); + + if (records_read < 1) + { + return false; + } + + if (!check_chunk_header(&file->header.chunk_header, HEADER_CHUNK_ID) || + be32toh(file->header.chunk_header.chunk_size) != 6) + { + fprintf(stderr, + "read_file_header: Invalid MIDI chunk header! " + "chunk_size=%i\n", + be32toh(file->header.chunk_header.chunk_size)); + return false; + } + + format_type = be32toh(file->header.format_type); + file->num_tracks = be32toh(file->header.num_tracks); + + if ((format_type != 0 && format_type != 1) || file->num_tracks < 1) + { + fprintf(stderr, "read_file_header: Only type 0/1 " + "MIDI files supported!\n"); + return false; + } + + return true; +} + +void midi_free_file(midi_file_t *file) +{ + int i; + + if (file->tracks != NULL) + { + for (i = 0; i < file->num_tracks; ++i) + { + free_track(&file->tracks[i]); + } + + free(file->tracks); + } + + free(file); +} + +midi_file_t *midi_loadfile(char *filename) +{ + midi_file_t *file; + FILE *stream; + + file = malloc(sizeof(midi_file_t)); + + if (file == NULL) + { + return NULL; + } + + file->tracks = NULL; + file->num_tracks = 0; + file->buffer = NULL; + file->buffer_size = 0; + + /* Open file */ + + stream = fopen(filename, "rb"); + + if (stream == NULL) + { + fprintf(stderr, "midi_loadfile: Failed to open '%s'\n", filename); + midi_free_file(file); + return NULL; + } + + /* Read MIDI file header */ + + if (!read_file_header(file, stream)) + { + fclose(stream); + midi_free_file(file); + return NULL; + } + + /* Read all tracks: */ + + if (!read_all_tracks(file, stream)) + { + fclose(stream); + midi_free_file(file); + return NULL; + } + + fclose(stream); + + return file; +} + +#ifdef TEST +static char *midi_event_type_to_string(midi_event_type_t event_type) +{ + switch (event_type) + { + case MIDI_EVENT_NOTE_OFF: + return "MIDI_EVENT_NOTE_OFF"; + case MIDI_EVENT_NOTE_ON: + return "MIDI_EVENT_NOTE_ON"; + case MIDI_EVENT_AFTERTOUCH: + return "MIDI_EVENT_AFTERTOUCH"; + case MIDI_EVENT_CONTROLLER: + return "MIDI_EVENT_CONTROLLER"; + case MIDI_EVENT_PROGRAM_CHANGE: + return "MIDI_EVENT_PROGRAM_CHANGE"; + case MIDI_EVENT_CHAN_AFTERTOUCH: + return "MIDI_EVENT_CHAN_AFTERTOUCH"; + case MIDI_EVENT_PITCH_BEND: + return "MIDI_EVENT_PITCH_BEND"; + case MIDI_EVENT_SYSEX: + return "MIDI_EVENT_SYSEX"; + case MIDI_EVENT_SYSEX_SPLIT: + return "MIDI_EVENT_SYSEX_SPLIT"; + case MIDI_EVENT_META: + return "MIDI_EVENT_META"; + + default: + return "(unknown)"; + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Get the number of tracks in a MIDI file. */ + +unsigned int midi_num_tracks(midi_file_t *file) +{ + return file->num_tracks; +} + +/* Start iterating over the events in a track. */ + +midi_track_iter_t *midi_iterate_track(midi_file_t *file, unsigned int track) +{ + midi_track_iter_t *iter; + + assert(track < file->num_tracks); + + iter = malloc(sizeof(*iter)); + iter->track = &file->tracks[track]; + iter->position = 0; + iter->loop_point = 0; + + return iter; +} + +void midi_free_iterator(midi_track_iter_t *iter) +{ + free(iter); +} + +/* Get the time until the next MIDI event in a track. */ + +unsigned int midi_get_delta_time(midi_track_iter_t *iter) +{ + if (iter->position < iter->track->num_events) + { + midi_event_t *next_event; + + next_event = &iter->track->events[iter->position]; + + return next_event->delta_time; + } + else + { + return 0; + } +} + +/* Get a pointer to the next MIDI event. */ + +int midi_get_next_event(midi_track_iter_t *iter, midi_event_t **event) +{ + if (iter->position < iter->track->num_events) + { + *event = &iter->track->events[iter->position]; + ++iter->position; + + return 1; + } + else + { + return 0; + } +} + +unsigned int midi_get_file_time_division(midi_file_t *file) +{ + short result = be16toh(file->header.time_division); + + /* Negative time division indicates SMPTE time and must be handled + * differently. + */ + + if (result < 0) + { + return (signed int)(-(result / 256)) * + (signed int)(result & 0xff); + } + else + { + return result; + } +} + +void midi_restart_iterator(midi_track_iter_t *iter) +{ + iter->position = 0; + iter->loop_point = 0; +} + +void midi_set_loop_point(midi_track_iter_t *iter) +{ + iter->loop_point = iter->position; +} + +void midi_restart_at_loop_point(midi_track_iter_t *iter) +{ + iter->position = iter->loop_point; +} + +#ifdef TEST +void print_track(midi_track_t *track) +{ + midi_event_t *event; + unsigned int i; + + for (i = 0; i < track->num_events; ++i) + { + event = &track->events[i]; + + if (event->delta_time > 0) + { + printf("Delay: %u ticks\n", event->delta_time); + } + + printf("Event type: %s (%i)\n", + midi_event_type_to_string(event->event_type), + event->event_type); + + switch (event->event_type) + { + case MIDI_EVENT_NOTE_OFF: + case MIDI_EVENT_NOTE_ON: + case MIDI_EVENT_AFTERTOUCH: + case MIDI_EVENT_CONTROLLER: + case MIDI_EVENT_PROGRAM_CHANGE: + case MIDI_EVENT_CHAN_AFTERTOUCH: + case MIDI_EVENT_PITCH_BEND: + printf("\tChannel: %u\n", event->data.channel.channel); + printf("\tParameter 1: %u\n", event->data.channel.param1); + printf("\tParameter 2: %u\n", event->data.channel.param2); + break; + + case MIDI_EVENT_SYSEX: + case MIDI_EVENT_SYSEX_SPLIT: + printf("\tLength: %u\n", event->data.sysex.length); + break; + + case MIDI_EVENT_META: + printf("\tMeta type: %u\n", event->data.meta.type); + printf("\tLength: %u\n", event->data.meta.length); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + midi_file_t *file; + unsigned int i; + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + exit(1); + } + + file = midi_loadfile(argv[1]); + + if (file == NULL) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + exit(1); + } + + for (i = 0; i < file->num_tracks; ++i) + { + printf("\n== Track %u ==\n\n", i); + + print_track(&file->tracks[i]); + } + + return 0; +} +#endif diff --git a/games/NXDoom/src/midifile.h b/games/NXDoom/src/midifile.h new file mode 100644 index 000000000..e6dc9166e --- /dev/null +++ b/games/NXDoom/src/midifile.h @@ -0,0 +1,251 @@ +/**************************************************************************** + * apps/games/NXDoom/src/midifile.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * MIDI file parsing. + * + ****************************************************************************/ + +#ifndef MIDIFILE_H +#define MIDIFILE_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MIDI_CHANNELS_PER_TRACK 16 + +#define MIDI_RPN_MSB 0x00 +#define MIDI_RPN_PITCH_BEND_SENS_LSB 0x00 +#define MIDI_RPN_FINE_TUNING_LSB 0x01 +#define MIDI_RPN_COARSE_TUNING_LSB 0x02 +#define MIDI_RPN_NULL 0x7f + +#define EMIDI_LOOP_FLAG 0x7f + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct midi_file_s midi_file_t; +typedef struct midi_track_iter_s midi_track_iter_t; + +typedef enum +{ + MIDI_EVENT_NOTE_OFF = 0x80, + MIDI_EVENT_NOTE_ON = 0x90, + MIDI_EVENT_AFTERTOUCH = 0xa0, + MIDI_EVENT_CONTROLLER = 0xb0, + MIDI_EVENT_PROGRAM_CHANGE = 0xc0, + MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0, + MIDI_EVENT_PITCH_BEND = 0xe0, + + MIDI_EVENT_SYSEX = 0xf0, + MIDI_EVENT_SYSEX_SPLIT = 0xf7, + MIDI_EVENT_META = 0xff, +} midi_event_type_t; + +typedef enum +{ + MIDI_CONTROLLER_BANK_SELECT_MSB = 0x00, + MIDI_CONTROLLER_MODULATION = 0x01, + MIDI_CONTROLLER_BREATH_CONTROL = 0x02, + MIDI_CONTROLLER_FOOT_CONTROL = 0x04, + MIDI_CONTROLLER_PORTAMENTO = 0x05, + MIDI_CONTROLLER_DATA_ENTRY_MSB = 0x06, + MIDI_CONTROLLER_VOLUME_MSB = 0x07, + MIDI_CONTROLLER_PAN = 0x0a, + MIDI_CONTROLLER_EXPRESSION = 0x0b, + + MIDI_CONTROLLER_BANK_SELECT_LSB = 0x20, + MIDI_CONTROLLER_DATA_ENTRY_LSB = 0x26, + MIDI_CONTROLLER_VOLUME_LSB = 0X27, + + MIDI_CONTROLLER_HOLD1_PEDAL = 0x40, + MIDI_CONTROLLER_SOFT_PEDAL = 0x43, + + MIDI_CONTROLLER_REVERB = 0x5b, + MIDI_CONTROLLER_CHORUS = 0x5d, + + MIDI_CONTROLLER_NRPN_LSB = 0x62, + MIDI_CONTROLLER_NRPN_MSB = 0x63, + MIDI_CONTROLLER_RPN_LSB = 0x64, + MIDI_CONTROLLER_RPN_MSB = 0x65, + + MIDI_CONTROLLER_ALL_SOUND_OFF = 0x78, + MIDI_CONTROLLER_RESET_ALL_CTRLS = 0x79, + MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7b, + + MIDI_CONTROLLER_POLY_MODE_OFF = 0x7e, + MIDI_CONTROLLER_POLY_MODE_ON = 0x7f, +} midi_controller_t; + +typedef enum +{ + MIDI_META_SEQUENCE_NUMBER = 0x00, + + MIDI_META_TEXT = 0x01, + MIDI_META_COPYRIGHT = 0x02, + MIDI_META_TRACK_NAME = 0x03, + MIDI_META_INSTR_NAME = 0x04, + MIDI_META_LYRICS = 0x05, + MIDI_META_MARKER = 0x06, + MIDI_META_CUE_POINT = 0x07, + + MIDI_META_CHANNEL_PREFIX = 0x20, + MIDI_META_END_OF_TRACK = 0x2f, + + MIDI_META_SET_TEMPO = 0x51, + MIDI_META_SMPTE_OFFSET = 0x54, + MIDI_META_TIME_SIGNATURE = 0x58, + MIDI_META_KEY_SIGNATURE = 0x59, + MIDI_META_SEQUENCER_SPECIFIC = 0x7f, +} midi_meta_event_type_t; + +typedef enum +{ + EMIDI_DEVICE_GENERAL_MIDI = 0x00, + EMIDI_DEVICE_SOUND_CANVAS = 0x01, + EMIDI_DEVICE_AWE32 = 0x02, + EMIDI_DEVICE_WAVE_BLASTER = 0x03, + EMIDI_DEVICE_SOUND_BLASTER = 0x04, + EMIDI_DEVICE_PRO_AUDIO = 0x05, + EMIDI_DEVICE_SOUND_MAN_16 = 0x06, + EMIDI_DEVICE_ADLIB = 0x07, + EMIDI_DEVICE_SOUNDSCAPE = 0x08, + EMIDI_DEVICE_ULTRASOUND = 0x09, + EMIDI_DEVICE_ALL = 0x7f, +} emidi_device_t; + +typedef enum +{ + EMIDI_CONTROLLER_TRACK_DESIGNATION = 0x6e, + EMIDI_CONTROLLER_TRACK_EXCLUSION = 0x6f, + EMIDI_CONTROLLER_PROGRAM_CHANGE = 0x70, + EMIDI_CONTROLLER_VOLUME = 0x71, + EMIDI_CONTROLLER_LOOP_BEGIN = 0x74, + EMIDI_CONTROLLER_LOOP_END = 0x75, + EMIDI_CONTROLLER_GLOBAL_LOOP_BEGIN = 0x76, + EMIDI_CONTROLLER_GLOBAL_LOOP_END = 0x77, +} emidi_controller_t; + +typedef struct +{ + /* Meta event type: */ + + unsigned int type; + + /* Length: */ + + unsigned int length; + + /* Meta event data: */ + + byte *data; +} midi_meta_event_data_t; + +typedef struct +{ + /* Length: */ + + unsigned int length; + + /* Event data: */ + + byte *data; +} midi_sysex_event_data_t; + +typedef struct +{ + /* The channel number to which this applies: */ + + unsigned int channel; + + /* Extra parameters: */ + + unsigned int param1; + unsigned int param2; +} midi_channel_event_data_t; + +typedef struct +{ + /* Time between the previous event and this event. */ + + unsigned int delta_time; + + /* Type of event: */ + + midi_event_type_t event_type; + + union + { + midi_channel_event_data_t channel; + midi_meta_event_data_t meta; + midi_sysex_event_data_t sysex; + } data; +} midi_event_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Load a MIDI file. */ + +midi_file_t *midi_loadfile(char *filename); + +/* Free a MIDI file. */ + +void midi_free_file(midi_file_t *file); + +/* Get the time division value from the MIDI header. */ + +unsigned int midi_get_file_time_division(midi_file_t *file); + +/* Get the number of tracks in a MIDI file. */ + +unsigned int midi_num_tracks(midi_file_t *file); + +/* Start iterating over the events in a track. */ + +midi_track_iter_t *midi_iterate_track(midi_file_t *file, + unsigned int track_num); + +/* Free an iterator. */ + +void midi_free_iterator(midi_track_iter_t *iter); + +/* Get the time until the next MIDI event in a track. */ + +unsigned int midi_get_delta_time(midi_track_iter_t *iter); + +/* Get a pointer to the next MIDI event. */ + +int midi_get_next_event(midi_track_iter_t *iter, midi_event_t **event); + +/* Reset an iterator to the beginning of a track. */ + +void midi_restart_iterator(midi_track_iter_t *iter); + +/* Set loop point to current position. */ + +void midi_set_loop_point(midi_track_iter_t *iter); + +/* Set position to saved loop point. */ + +void midi_restart_at_loop_point(midi_track_iter_t *iter); + +#endif /* MIDIFILE_H */ diff --git a/games/NXDoom/src/mus2mid.c b/games/NXDoom/src/mus2mid.c new file mode 100644 index 000000000..502e10252 --- /dev/null +++ b/games/NXDoom/src/mus2mid.c @@ -0,0 +1,794 @@ +/**************************************************************************** + * apps/games/NXDoom/src/mus2mid.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2006 Ben Ryves 2006 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * mus2mid.c - Ben Ryves 2006 - http: *benryves.com - benryves@benryves.com + * Use to convert a MUS file into a single track, type 0 MIDI file. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_swap.h" + +#include "memio.h" +#include "mus2mid.h" + +#ifdef STANDALONE +#include "m_misc.h" +#include "z_zone.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NUM_CHANNELS 16 + +#define MIDI_PERCUSSION_CHAN 9 +#define MUS_PERCUSSION_CHAN 15 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* MUS event codes */ + +typedef enum +{ + mus_releasekey = 0x00, + mus_presskey = 0x10, + mus_pitchwheel = 0x20, + mus_systemevent = 0x30, + mus_changecontroller = 0x40, + mus_scoreend = 0x60 +} musevent; + +/* MIDI event codes */ + +typedef enum +{ + midi_releasekey = 0x80, + midi_presskey = 0x90, + midi_aftertouchkey = 0xa0, + midi_changecontroller = 0xb0, + midi_changepatch = 0xc0, + midi_aftertouchchannel = 0xd0, + midi_pitchwheel = 0xe0 +} midievent; + +/* Structure to hold MUS file header */ + +begin_packed_struct struct musheader +{ + byte id[4]; + unsigned short scorelength; + unsigned short scorestart; + unsigned short primarychannels; + unsigned short secondarychannels; + unsigned short instrumentcount; +} end_packed_struct; + +typedef struct musheader musheader; + +/* Standard MIDI type 0 header + track header */ + +static const byte midiheader[] = +{ + 'M', 'T', 'h', 'd', /* Main header */ + 0x00, 0x00, 0x00, 0x06, /* Header size */ + 0x00, 0x00, /* MIDI type (0) */ + 0x00, 0x01, /* Number of tracks */ + 0x00, 0x46, /* Resolution */ + 'M', 'T', 'r', 'k', /* Start of track */ + 0x00, 0x00, 0x00, 0x00 /* Placeholder for track length */ +}; + +/* Cached channel velocities */ + +static byte channelvelocities[] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, +}; + +/* Timestamps between sequences of MUS events */ + +static unsigned int queuedtime = 0; + +/* Counter for the length of the track */ + +static unsigned int tracksize; + +static const byte controller_map[] = +{ + 0x00, 0x20, 0x01, 0x07, 0x0a, 0x0b, 0x5b, 0x5d, + 0x40, 0x43, 0x78, 0x7b, 0x7e, 0x7f, 0x79, +}; + +static int channel_map[NUM_CHANNELS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Write timestamp to a MIDI file. */ + +static boolean write_time(unsigned int time, MEMFILE *midioutput) +{ + unsigned int buffer = time & 0x7f; + byte writeval; + + while ((time >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((time & 0x7f) | 0x80); + } + + for (; ; ) + { + writeval = (byte)(buffer & 0xff); + + if (mem_fwrite(&writeval, 1, 1, midioutput) != 1) + { + return true; + } + + ++tracksize; + + if ((buffer & 0x80) != 0) + { + buffer >>= 8; + } + else + { + queuedtime = 0; + return false; + } + } +} + +/* Write the end of track marker */ + +static boolean write_end_track(MEMFILE *midioutput) +{ + byte endtrack[] = + { + 0xff, 0x2f, 0x00 + }; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(endtrack, 1, 3, midioutput) != 3) + { + return true; + } + + tracksize += 3; + return false; +} + +/* Write a key press event */ + +static boolean write_press_key(byte channel, byte key, byte velocity, + MEMFILE *midioutput) +{ + byte working = midi_presskey | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = key & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = velocity & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a key release event */ + +static boolean write_release_key(byte channel, byte key, MEMFILE *midioutput) +{ + byte working = midi_releasekey | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = key & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = 0; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a pitch wheel/bend event */ + +static boolean write_pitch_wheel(byte channel, short wheel, + MEMFILE *midioutput) +{ + byte working = midi_pitchwheel | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = wheel & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = (wheel >> 7) & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + return false; +} + +/* Write a patch change event */ + +static boolean write_change_patch(byte channel, byte patch, + MEMFILE *midioutput) +{ + byte working = midi_changepatch | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = patch & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 2; + + return false; +} + +/* Write a valued controller change event */ + +static boolean write_change_controller_valued(byte channel, byte control, + byte value, MEMFILE *midioutput) +{ + byte working = midi_changecontroller | channel; + + if (write_time(queuedtime, midioutput)) + { + return true; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + working = control & 0x7f; + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + /* Quirk in vanilla DOOM? MUS controller values should be + * 7-bit, not 8-bit. + */ + + working = value; /* & 0x7F; */ + + /* Fix on said quirk to stop MIDI players from complaining that + * the value is out of range: + */ + + if (working & 0x80) + { + working = 0x7f; + } + + if (mem_fwrite(&working, 1, 1, midioutput) != 1) + { + return true; + } + + tracksize += 3; + + return false; +} + +/* Write a valueless controller change event */ + +static boolean write_change_controller_valueless(byte channel, byte control, + MEMFILE *midioutput) +{ + return write_change_controller_valued(channel, control, 0, midioutput); +} + +/* Allocate a free MIDI channel. */ + +static int allocate_midi_channel(void) +{ + int result; + int max; + int i; + + /* Find the current highest-allocated channel. */ + + max = -1; + + for (i = 0; i < NUM_CHANNELS; ++i) + { + if (channel_map[i] > max) + { + max = channel_map[i]; + } + } + + /* max is now equal to the highest-allocated MIDI channel. We can + * now allocate the next available channel. This also works if + * no channels are currently allocated (max=-1) + */ + + result = max + 1; + + /* Don't allocate the MIDI percussion channel! */ + + if (result == MIDI_PERCUSSION_CHAN) + { + ++result; + } + + return result; +} + +/* Given a MUS channel number, get the MIDI channel number to use + * in the outputted file. + */ + +static int get_midi_channel(int mus_channel, MEMFILE *midioutput) +{ + /* Find the MIDI channel to use for this MUS channel. + * MUS channel 15 is the percusssion channel. + */ + + if (mus_channel == MUS_PERCUSSION_CHAN) + { + return MIDI_PERCUSSION_CHAN; + } + else + { + /* If a MIDI channel hasn't been allocated for this MUS channel + * yet, allocate the next free MIDI channel. + */ + + if (channel_map[mus_channel] == -1) + { + channel_map[mus_channel] = allocate_midi_channel(); + + /* First time using the channel, send an "all notes off" + * event. This fixes "The D_DDTBLU disease" described here: + * https: *www.doomworld.com/vb/source-ports/66802-the + */ + + write_change_controller_valueless(channel_map[mus_channel], 0x7b, + midioutput); + } + + return channel_map[mus_channel]; + } +} + +static boolean read_mus_header(MEMFILE *file, musheader *header) +{ + boolean result; + + result = + mem_fread(&header->id, sizeof(byte), 4, file) == 4 && + mem_fread(&header->scorelength, sizeof(short), 1, file) == 1 && + mem_fread(&header->scorestart, sizeof(short), 1, file) == 1 && + mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1 && + mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1 && + mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1; + + if (result) + { + header->scorelength = SHORT(header->scorelength); + header->scorestart = SHORT(header->scorestart); + header->primarychannels = SHORT(header->primarychannels); + header->secondarychannels = SHORT(header->secondarychannels); + header->instrumentcount = SHORT(header->instrumentcount); + } + + return result; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Read a MUS file from a stream (musinput) and output a MIDI file to + * a stream (midioutput). + * + * Returns 0 on success or 1 on failure. + */ + +boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput) +{ + /* Header for the MUS file */ + + musheader musfileheader; + + /* Descriptor for the current MUS event */ + + byte eventdescriptor; + int channel; /* Channel number */ + musevent event; + + /* Bunch of vars read from MUS lump */ + + byte key; + byte controllernumber; + byte controllervalue; + + /* Buffer used for MIDI track size record */ + + byte tracksizebuffer[4]; + + /* Flag for when the score end marker is hit. */ + + int hitscoreend = 0; + + /* Temp working byte */ + + byte working; + + /* Used in building up time delays */ + + unsigned int timedelay; + + /* Initialise channel map to mark all channels as unused. */ + + for (channel = 0; channel < NUM_CHANNELS; ++channel) + { + channel_map[channel] = -1; + } + + /* Grab the header */ + + if (!read_mus_header(musinput, &musfileheader)) + { + return true; + } + + /* Check MUS header */ + +#ifdef CHECK_MUS_HEADER + if (musfileheader.id[0] != 'M' || musfileheader.id[1] != 'U' || + musfileheader.id[2] != 'S' || musfileheader.id[3] != 0x1a) + { + return true; + } +#endif + + /* Seek to where the data is held */ + + if (mem_fseek(musinput, (long)musfileheader.scorestart, MEM_SEEK_SET) != 0) + { + return true; + } + + /* So, we can assume the MUS file is faintly legit. Let's start + * writing MIDI data... + */ + + mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput); + tracksize = 0; + + /* Now, process the MUS file: */ + + while (!hitscoreend) + { + /* Handle a block of events: */ + + while (!hitscoreend) + { + /* Fetch channel number and event code: */ + + if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1) + { + return true; + } + + channel = get_midi_channel(eventdescriptor & 0x0f, midioutput); + event = eventdescriptor & 0x70; + + switch (event) + { + case mus_releasekey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (write_release_key(channel, key, midioutput)) + { + return true; + } + + break; + + case mus_presskey: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + return true; + } + + if (key & 0x80) + { + if (mem_fread(&channelvelocities[channel], 1, 1, + musinput) != 1) + { + return true; + } + + channelvelocities[channel] &= 0x7f; + } + + if (write_press_key(channel, key, channelvelocities[channel], + midioutput)) + { + return true; + } + + break; + + case mus_pitchwheel: + if (mem_fread(&key, 1, 1, musinput) != 1) + { + break; + } + + if (write_pitch_wheel(channel, (short)(key * 64), midioutput)) + { + return true; + } + + break; + + case mus_systemevent: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + + if (controllernumber < 10 || controllernumber > 14) + { + return true; + } + + if (write_change_controller_valueless( + channel, controller_map[controllernumber], midioutput)) + { + return true; + } + + break; + + case mus_changecontroller: + if (mem_fread(&controllernumber, 1, 1, musinput) != 1) + { + return true; + } + + if (mem_fread(&controllervalue, 1, 1, musinput) != 1) + { + return true; + } + + if (controllernumber == 0) + { + if (write_change_patch(channel, controllervalue, + midioutput)) + { + return true; + } + } + else + { + if (controllernumber < 1 || controllernumber > 9) + { + return true; + } + + if (write_change_controller_valued( + channel, controller_map[controllernumber], + controllervalue, midioutput)) + { + return true; + } + } + + break; + + case mus_scoreend: + hitscoreend = 1; + break; + + default: + return true; + break; + } + + if (eventdescriptor & 0x80) + { + break; + } + } + + /* Now we need to read the time code: */ + + if (!hitscoreend) + { + timedelay = 0; + for (; ; ) + { + if (mem_fread(&working, 1, 1, musinput) != 1) + { + return true; + } + + timedelay = timedelay * 128 + (working & 0x7f); + if ((working & 0x80) == 0) + { + break; + } + } + + queuedtime += timedelay; + } + } + + /* End of track */ + + if (write_end_track(midioutput)) + { + return true; + } + + /* Write the track size into the stream */ + + if (mem_fseek(midioutput, 18, MEM_SEEK_SET)) + { + return true; + } + + tracksizebuffer[0] = (tracksize >> 24) & 0xff; + tracksizebuffer[1] = (tracksize >> 16) & 0xff; + tracksizebuffer[2] = (tracksize >> 8) & 0xff; + tracksizebuffer[3] = tracksize & 0xff; + + if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4) + { + return true; + } + + return false; +} + +#ifdef STANDALONE +int main(int argc, char *argv[]) +{ + MEMFILE *src, *dst; + byte *infile; + long infile_len; + void *outfile; + size_t outfile_len; + + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + exit(-1); + } + + z_init(); + + infile_len = m_read_file(argv[1], &infile); + + src = mem_fopen_read(infile, infile_len); + dst = mem_fopen_write(); + + if (mus2mid(src, dst)) + { + fprintf(stderr, "mus2mid() failed\n"); + exit(-1); + } + + /* Write result to output file: */ + + mem_get_buf(dst, &outfile, &outfile_len); + + m_write_file(argv[2], outfile, outfile_len); + + return 0; +} +#endif /* STANDALONE */ diff --git a/games/NXDoom/src/mus2mid.h b/games/NXDoom/src/mus2mid.h new file mode 100644 index 000000000..e8cafc4a8 --- /dev/null +++ b/games/NXDoom/src/mus2mid.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/src/mus2mid.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * Copyright(C) 2006 Ben Ryves 2006 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * mus2mid.h - Ben Ryves 2006 - http: *benryves.com - benryves@benryves.com + * Use to convert a MUS file into a single track, type 0 MIDI file. + * + ****************************************************************************/ + +#ifndef MUS2MID_H +#define MUS2MID_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" +#include "memio.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput); + +#endif /* MUS2MID_H */ diff --git a/games/NXDoom/src/net_client.c b/games/NXDoom/src/net_client.c new file mode 100644 index 000000000..fb589f692 --- /dev/null +++ b/games/NXDoom/src/net_client.c @@ -0,0 +1,1311 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_client.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Network client code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "d_loop.h" +#include "deh_main.h" +#include "deh_str.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_config.h" +#include "m_fixed.h" +#include "m_misc.h" +#include "net_client.h" +#include "net_common.h" +#include "net_defs.h" +#include "net_gui.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_server.h" +#include "net_structrw.h" +#include "w_checksum.h" +#include "w_wad.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define net_cl_expand_ticnum(b) net_expand_tic_num(recvwindow_start, (b)) + +#define KP 0.1 +#define KI 0.01 +#define KD 0.02 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + /* waiting for the game to launch */ + + CLIENT_STATE_WAITING_LAUNCH, + + /* waiting for the game to start */ + + CLIENT_STATE_WAITING_START, + + /* in game */ + + CLIENT_STATE_IN_GAME, +} net_clientstate_t; + +/* Type of structure used in the receive window */ + +typedef struct +{ + /* Whether this tic has been received yet */ + + boolean active; + + /* Last time we sent a resend request for this tic */ + + unsigned int resend_time; + + /* Tic data from server */ + + net_full_ticcmd_t cmd; +} net_server_recv_t; + +/* Type of structure used in the send window */ + +typedef struct +{ + /* Whether this slot is active yet */ + + boolean active; + + /* The tic number */ + + unsigned int seq; + + /* Time the command was generated */ + + unsigned int time; + + /* Ticcmd diff */ + + net_ticdiff_t cmd; +} net_server_send_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static net_connection_t client_connection; +static net_clientstate_t client_state; +static net_addr_t *server_addr; +static net_context_t *client_context; + +/* game settings, as received from the server when the game started */ + +static net_gamesettings_t settings; + +/* The last ticcmd constructed */ + +static ticcmd_t last_ticcmd; + +/* Buffer of ticcmd diffs being sent to the server */ + +static net_server_send_t send_queue[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* Receive window */ + +static ticcmd_t recvwindow_cmd_base[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static int recvwindow_start; +static net_server_recv_t recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + +/* Whether we need to send an acknowledgement and + * when gamedata was last received. + */ + +static boolean need_to_acknowledge; +static unsigned int gamedata_recv_time; + +/* The latency (time between when we sent our command and we got all + * the other players' commands from the server) for the last tic we + * received. We include this latency in tics we send to the server so + * that they can adjust to us. + */ + +static int last_latency; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Why did the server reject us? */ + +char *net_client_reject_reason = NULL; + +/* true if the client code is in use */ + +boolean net_client_connected; + +/* true if we have received waiting data from the server, + * and the wait data that was received. + */ + +boolean net_client_received_wait_data; +net_waitdata_t net_client_wait_data; + +/* Waiting at the initial wait screen for the game to be launched? */ + +boolean net_waiting_for_launch = false; + +/* Name that we send to the server */ + +char *net_player_name = NULL; + +/* Connected but not participating in the game (observer) */ + +boolean drone = false; + +/* Hash checksums of our wad directory and dehacked data. */ + +sha1_digest_t net_local_wad_sha1sum; +sha1_digest_t net_local_deh_sha1sum; + +/* Are we playing with the freedoom IWAD? */ + +unsigned int net_local_is_freedoom; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Called when we become disconnected from the server */ + +static void net_cl_disconnected(void) +{ + d_receive_tic(NULL, NULL); +} + +/* Called when a packet is received from the server containing game + * data. This updates the clock synchronization variable (offsetms) + * using a PID filter that keeps client clocks in sync. + */ + +static void update_clock_sync(unsigned int seq, unsigned int remote_latency) +{ + static int last_error; + static int cumul_error; + int latency; + int error; + + if (seq == send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq) + { + latency = i_get_time_ms() - + send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].time; + } + else if (seq > send_queue[seq % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq) + { + /* We have received the ticcmd from the server before we have + * even sent ours + */ + + latency = 0; + } + else + { + return; + } + + /* PID filter. These are manually trained parameters. */ + + /* How does our latency compare to the worst other player? */ + + error = latency - remote_latency; + cumul_error += error; + + offsetms = KP * (FRACUNIT * error) - KI * (FRACUNIT * cumul_error) + + (KD * FRACUNIT) * (last_error - error); + + last_error = error; + last_latency = latency; + + net_log_info( + "client: latency %d, remote %d -> offset=%dms, cumul_error=%d", + latency, remote_latency, offsetms / FRACUNIT, cumul_error); +} + +/* Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as + * patches against recvwindow_cmd_base. Place the results into + * the d_net.c structures (netcmds/nettics) and save the new ticcmd + * back into recvwindow_cmd_base. + */ + +static void net_cl_expand_full_ticcmd(net_full_ticcmd_t *cmd, + unsigned int seq, ticcmd_t *ticcmds) +{ + int i; + + /* Expand tic diffs for all players */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (i == settings.consoleplayer && !drone) + { + continue; + } + + if (cmd->playeringame[i]) + { + net_ticdiff_t *diff; + + diff = &cmd->cmds[i]; + + /* Use the ticcmd diff to patch the previous ticcmd to + * the new ticcmd + */ + + net_ticcmd_patch(&recvwindow_cmd_base[i], diff, &ticcmds[i]); + + /* Store a copy for next time */ + + recvwindow_cmd_base[i] = ticcmds[i]; + } + } +} + +/* Advance the receive window */ + +static void net_cl_advance_window(void) +{ + ticcmd_t ticcmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + + while (recvwindow[0].active) + { + /* Expand tic diff data into d_net.c structures */ + + net_cl_expand_full_ticcmd(&recvwindow[0].cmd, recvwindow_start, + ticcmds); + d_receive_tic(ticcmds, recvwindow[0].cmd.playeringame); + + /* Advance the window */ + + memmove(recvwindow, recvwindow + 1, + sizeof(net_server_recv_t) * + (CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1)); + memset(&recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1], 0, + sizeof(net_server_recv_t)); + + ++recvwindow_start; + + net_log_info("client: advanced receive window to %d", + recvwindow_start); + } +} + +/* Shut down the client code, etc. Invoked after a disconnect. */ + +static void net_cl_shutdown(void) +{ + if (net_client_connected) + { + net_client_connected = false; + + net_release_address(server_addr); + + /* Shut down network module, etc. To do. */ + } +} + +static void net_cl_send_game_data_ack(void) +{ + net_packet_t *packet; + + packet = net_new_packet(10); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_ACK); + net_write_int8(packet, recvwindow_start & 0xff); + + net_conn_send_packet(&client_connection, packet); + + net_free_packet(packet); + + need_to_acknowledge = false; +} + +static void net_cl_send_tics(int start, int end) +{ + net_packet_t *packet; + int i; + + if (!net_client_connected) + { + /* Disconnected from server */ + + return; + } + + if (start < 0) start = 0; + + /* Build a new packet to send to the server */ + + packet = net_new_packet(512); + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA); + + /* Write the start tic and number of tics. Send only the low byte + * of start - it can be inferred by the server. + */ + + net_write_int8(packet, recvwindow_start & 0xff); + net_write_int8(packet, start & 0xff); + net_write_int8(packet, end - start + 1); + + /* Add the tics. */ + + for (i = start; i <= end; ++i) + { + net_server_send_t *sendobj; + + sendobj = &send_queue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + net_write_int16(packet, last_latency); + + net_write_ticcmd_diff(packet, &sendobj->cmd, settings.lowres_turn); + } + + /* Send the packet */ + + net_conn_send_packet(&client_connection, packet); + + /* All done! */ + + net_free_packet(packet); + + /* Acknowledgement has been sent as part of the packet */ + + need_to_acknowledge = false; +} + +/* Parse a SYN packet received back from the server indicating a successful + * connection attempt. + */ + +static void net_cl_parse_syn(net_packet_t *packet) +{ + net_protocol_t protocol; + char *server_version; + + net_log_info("client: processing SYN response"); + + server_version = net_read_safe_string(packet); + if (server_version == NULL) + { + net_log_err("client: failed to read server version"); + return; + } + + protocol = net_read_protocol(packet); + if (protocol == NET_PROTOCOL_UNKNOWN) + { + net_log_err("client: can't find a common protocol"); + return; + } + + /* We are now successfully connected. */ + + net_log_info("client: connected to server"); + client_connection.state = NET_CONN_STATE_CONNECTED; + client_connection.protocol = protocol; + + /* Even though we have negotiated a compatible protocol, the game may still + * desync. Chocolate Doom's philosophy makes this unlikely, but if we're + * playing with a forked version, or even against a different version that + * fixes a compatibility issue, we may still have problems. + */ + + if (strcmp(server_version, PACKAGE_STRING) != 0) + { + fprintf(stderr, + "NET_CL_ParseSYN: This is '%s', but the server is " + "'%s'. It is possible that this mismatch may cause the game " + "to desync.\n", + PACKAGE_STRING, server_version); + } +} + +static void set_reject_reason(const char *s) +{ + free(net_client_reject_reason); + if (s != NULL) + { + net_client_reject_reason = strdup(s); + } + else + { + net_client_reject_reason = NULL; + } +} + +static void net_cl_parse_reject(net_packet_t *packet) +{ + char *msg; + + msg = net_read_safe_string(packet); + if (msg == NULL) + { + return; + } + + if (client_connection.state == NET_CONN_STATE_CONNECTING) + { + client_connection.state = NET_CONN_STATE_DISCONNECTED; + client_connection.disconnect_reason = NET_DISCONNECT_REMOTE; + set_reject_reason(msg); + } +} + +/* data received while we are waiting for the game to start */ + +static void net_cl_parse_waiting_data(net_packet_t *packet) +{ + net_waitdata_t wait_data; + + if (!net_read_wait_data(packet, &wait_data)) + { + /* Invalid packet? */ + + return; + } + + if (wait_data.num_players > wait_data.max_players || + wait_data.ready_players > wait_data.num_players || + wait_data.max_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS) + { + /* insane data */ + + return; + } + + if ((wait_data.consoleplayer >= 0 && drone) || + (wait_data.consoleplayer < 0 && !drone) || + (wait_data.consoleplayer >= wait_data.num_players)) + { + /* Invalid player number */ + + return; + } + + memcpy(&net_client_wait_data, &wait_data, sizeof(net_waitdata_t)); + net_client_received_wait_data = true; +} + +static void net_cl_parse_launch(net_packet_t *packet) +{ + unsigned int num_players; + + net_log_info("client: processing launch packet"); + + if (client_state != CLIENT_STATE_WAITING_LAUNCH) + { + net_log_err("client: not in waiting launch state, client_state=%d", + client_state); + return; + } + + /* The launch packet contains the number of players that will be + * in the game when it starts, so that we can do the startup + * progress indicator (the wait data is unreliable). + */ + + if (!net_read_int8(packet, &num_players)) + { + net_log_err("client: failed to read number of players"); + return; + } + + net_client_wait_data.num_players = num_players; + client_state = CLIENT_STATE_WAITING_START; + net_log_err("client: now waiting for game start"); +} + +static void net_cl_parse_game_start(net_packet_t *packet) +{ + net_log_info("client: processing game start packet"); + + if (!net_read_settings(packet, &settings)) + { + net_log_err("client: failed to read settings"); + return; + } + + if (client_state != CLIENT_STATE_WAITING_START) + { + net_log_err("client: not in waiting start state, client_state=%d", + client_state); + return; + } + + if (settings.num_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS || + settings.consoleplayer >= (signed int)settings.num_players) + { + /* insane values */ + + net_log_err("client: bad settings, num_players=%d, consoleplayer=%d", + settings.num_players, settings.consoleplayer); + return; + } + + if ((drone && settings.consoleplayer >= 0) || + (!drone && settings.consoleplayer < 0)) + { + /* Invalid player number: must be positive for real players, + * negative for drones + */ + + net_log_err("client: mismatch: drone=%d, consoleplayer=%d", drone, + settings.consoleplayer); + return; + } + + net_log_info("client: beginning game state"); + client_state = CLIENT_STATE_IN_GAME; + + /* Clear the receive window */ + + memset(recvwindow, 0, sizeof(recvwindow)); + recvwindow_start = 0; + memset(&recvwindow_cmd_base, 0, sizeof(recvwindow_cmd_base)); + + /* Clear the send queue */ + + memset(&send_queue, 0x00, sizeof(send_queue)); +} + +static void net_cl_send_resend_request(int start, int end) +{ + net_packet_t *packet; + unsigned int nowtime; + int i; + + packet = net_new_packet(64); + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); + net_write_int32(packet, start); + net_write_int8(packet, end - start + 1); + net_conn_send_packet(&client_connection, packet); + net_free_packet(packet); + + nowtime = i_get_time_ms(); + + /* Save the time we sent the resend request */ + + for (i = start; i <= end; ++i) + { + int index; + + index = i - recvwindow_start; + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) continue; + + recvwindow[index].resend_time = nowtime; + } +} + +/* Check for expired resend requests */ + +static void net_cl_check_resends(void) +{ + int i; + int resend_start; + int resend_end; + unsigned int nowtime; + boolean maybe_deadlocked; + + nowtime = i_get_time_ms(); + maybe_deadlocked = nowtime - gamedata_recv_time > 1000; + + resend_start = -1; + resend_end = -1; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + net_server_recv_t *recvobj; + boolean need_resend; + + recvobj = &recvwindow[i]; + + /* if need_resend is true, this tic needs another retransmit + * request (300ms timeout) + */ + + need_resend = !recvobj->active && recvobj->resend_time != 0 && + nowtime > recvobj->resend_time + 300; + + /* if no game data has been received in a long time, we may be in + * a deadlock scenario where tics from the server have been lost, so + * we've stopped generating any more, so the server isn't sending us + * any, so we don't get any to trigger a resend request. So force the + * first few tics in the receive window to be requested. + */ + + if (i == 0 && !recvobj->active && recvobj->resend_time == 0 && + maybe_deadlocked) + { + need_resend = true; + } + + if (need_resend) + { + /* Start a new run of resend tics? */ + + if (resend_start < 0) + { + resend_start = i; + } + + resend_end = i; + } + else if (resend_start >= 0) + { + /* End of a run of resend tics */ + + net_log_info("client: resend request timed out for %d-%d (%d)", + recvwindow_start + resend_start, + recvwindow_start + resend_end, + recvwindow[resend_start].resend_time); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end); + resend_start = -1; + } + } + + if (resend_start >= 0) + { + net_log_info("client: resend request timed out for %d-%d (%d)", + recvwindow_start + resend_start, + recvwindow_start + resend_end, + recvwindow[resend_start].resend_time); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end); + } + + /* We have received some data from the server and not acknowledged + * it yet. Normally this gets acknowledged when we send our game + * data, but if the client is a drone we need to do this. + */ + + if (need_to_acknowledge && nowtime - gamedata_recv_time > 200) + { + net_log_info("client: no game data received since %d: triggering ack", + gamedata_recv_time); + net_cl_send_game_data_ack(); + } +} + +/* Parsing of NET_PACKET_TYPE_GAMEDATA packets + * (packets containing the actual ticcmd data) + */ + +static void net_cl_parse_game_data(net_packet_t *packet) +{ + net_server_recv_t *recvobj; + unsigned int seq; + unsigned int num_tics; + unsigned int nowtime; + int resend_start; + int resend_end; + size_t i; + int index; + + net_log_info("client: processing game data packet"); + + /* Read header */ + + if (!net_read_int8(packet, &seq) || !net_read_int8(packet, &num_tics)) + { + net_log_info("client: error: failed to read header"); + return; + } + + nowtime = i_get_time_ms(); + + /* Whatever happens, we now need to send an acknowledgement of our + * current receive point. + */ + + if (!need_to_acknowledge) + { + need_to_acknowledge = true; + gamedata_recv_time = nowtime; + } + + /* Expand byte value into the full tic number */ + + seq = net_cl_expand_ticnum(seq); + net_log_info("client: got game data, seq=%d, num_tics=%d", seq, num_tics); + + for (i = 0; i < num_tics; ++i) + { + net_full_ticcmd_t cmd; + + index = seq - recvwindow_start + i; + + if (!net_read_full_ticcmd(packet, &cmd, settings.lowres_turn)) + { + net_log_err("client: failed to read ticcmd %zu", i); + return; + } + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Out of range of the recv window */ + + continue; + } + + /* Store in the receive window */ + + recvobj = &recvwindow[index]; + + recvobj->active = true; + recvobj->cmd = cmd; + net_log_info("client: stored tic %zu in receive window", seq + i); + + /* If a packet is lost or arrives out of order, we might get + * the tic in the next packet instead (because of extratic). + * If that's the case then the latency for receiving that tic + * now will be bogus. So we only use the last tic in the packet + * to trigger a clock sync update. + */ + + if (i == num_tics - 1) + { + update_clock_sync(seq + i, cmd.latency); + } + } + + /* Has this been received out of sequence, ie. have we not received + * all tics before the first tic in this packet? If so, send a + * resend request. + */ + + resend_end = seq - recvwindow_start; + + if (resend_end <= 0) return; + + if (resend_end >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + resend_end = CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1; + } + + index = resend_end - 1; + resend_start = resend_end; + + while (index >= 0) + { + recvobj = &recvwindow[index]; + + if (recvobj->active) + { + /* ended our run of unreceived tics */ + + break; + } + + if (recvobj->resend_time != 0) + { + /* Already sent a resend request for this tic */ + + break; + } + + resend_start = index; + --index; + } + + /* Possibly send a resend request */ + + if (resend_start < resend_end) + { + net_log_info("client: request resend for %d-%d before %d", + recvwindow_start + resend_start, + recvwindow_start + resend_end - 1, seq); + net_cl_send_resend_request(recvwindow_start + resend_start, + recvwindow_start + resend_end - 1); + } +} + +/* Parse a resend request from the server due to a dropped packet */ + +static void net_cl_parse_resend_request(net_packet_t *packet) +{ + static unsigned int start; + static unsigned int end; + static unsigned int num_tics; + + net_log_info("client: processing resend request"); + + if (drone) + { + /* Drones don't send gamedata. */ + + net_log_err("client: resend request but we're a drone?"); + return; + } + + if (!net_read_int32(packet, &start) || !net_read_int8(packet, &num_tics)) + { + net_log_err("client: couldn't read start and num_tics"); + return; + } + + end = start + num_tics - 1; + + net_log_info("client: resend request: start=%d, num_tics=%d", start, + num_tics); + + /* Check we have the tics being requested. If not, reduce the + * window of tics to only what we have. + */ + + while ( + start <= end && + (!send_queue[start % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].active || + send_queue[start % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq != start)) + { + ++start; + } + + while (start <= end && + (!send_queue[end % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].active || + send_queue[end % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS].seq != end)) + { + --end; + } + + /* Resend those tics */ + + if (start <= end) + { + net_log_info("client: resending %d-%d", start, end); + net_cl_send_tics(start, end); + } + else + { + net_log_info("client: don't have the tics to resend"); + } +} + +/* Console message that the server wants the client to print */ + +static void net_cl_parse_console_message(net_packet_t *packet) +{ + char *msg; + + msg = net_read_safe_string(packet); + + if (msg == NULL) + { + return; + } + + printf("Message from server:\n%s\n", msg); +} + +/* parse a received packet */ + +static void net_cl_parse_packet(net_packet_t *packet) +{ + unsigned int packet_type; + + if (!net_read_int16(packet, &packet_type)) + { + return; + } + + net_log_info("client: packet from server, type %d", + packet_type & ~NET_RELIABLE_PACKET); + net_log_packet(packet); + + if (net_conn_packet(&client_connection, packet, &packet_type)) + { + /* Packet eaten by the common connection code */ + } + else + { + switch (packet_type) + { + case NET_PACKET_TYPE_SYN: + net_cl_parse_syn(packet); + break; + + case NET_PACKET_TYPE_REJECTED: + net_cl_parse_reject(packet); + break; + + case NET_PACKET_TYPE_WAITING_DATA: + net_cl_parse_waiting_data(packet); + break; + + case NET_PACKET_TYPE_LAUNCH: + net_cl_parse_launch(packet); + break; + + case NET_PACKET_TYPE_GAMESTART: + net_cl_parse_game_start(packet); + break; + + case NET_PACKET_TYPE_GAMEDATA: + net_cl_parse_game_data(packet); + break; + + case NET_PACKET_TYPE_GAMEDATA_RESEND: + net_cl_parse_resend_request(packet); + break; + + case NET_PACKET_TYPE_CONSOLE_MESSAGE: + net_cl_parse_console_message(packet); + break; + + default: + break; + } + } +} + +static void net_cl_send_syn(net_connect_data_t *data) +{ + net_packet_t *packet; + + net_log_info("client: sending SYN"); + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_SYN); + net_write_int32(packet, NET_MAGIC_NUMBER); + net_write_string(packet, PACKAGE_STRING); + net_write_protocol_list(packet); + net_write_connect_data(packet, data); + net_write_string(packet, net_player_name); + net_conn_send_packet(&client_connection, packet); + net_free_packet(packet); +} + +static void net_cl_init(void) +{ + /* Try to set from the USER and USERNAME environment variables + * Otherwise, fallback to "Player" + */ + + if (net_player_name == NULL) + { + net_player_name = "Player"; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_cl_launch_game(void) +{ + net_conn_new_reliable(&client_connection, NET_PACKET_TYPE_LAUNCH); +} + +void net_cl_start_game(net_gamesettings_t *p_settings) +{ + net_packet_t *packet; + + /* Start from a ticcmd of all zeros */ + + memset(&last_ticcmd, 0, sizeof(ticcmd_t)); + + /* Send packet */ + + packet = + net_conn_new_reliable(&client_connection, NET_PACKET_TYPE_GAMESTART); + + net_write_settings(packet, p_settings); +} + +/* Add a new ticcmd to the send queue */ + +void net_cl_send_ticcmd(ticcmd_t *ticcmd, int maketic) +{ + net_ticdiff_t diff; + net_server_send_t *sendobj; + int starttic; + int endtic; + + /* Calculate the difference to the last ticcmd */ + + net_ticcmd_diff(&last_ticcmd, ticcmd, &diff); + + /* Store in the send queue */ + + sendobj = &send_queue[maketic % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + sendobj->active = true; + sendobj->seq = maketic; + sendobj->time = i_get_time_ms(); + sendobj->cmd = diff; + + last_ticcmd = *ticcmd; + + /* Send to server. */ + + starttic = maketic - settings.extratics; + endtic = maketic; + + if (starttic < 0) starttic = 0; + + net_log_info("client: generated tic %d, sending %d-%d", maketic, starttic, + endtic); + net_cl_send_tics(starttic, endtic); +} + +/* "Run" the client code: check for new packets, send packets as + * needed + */ + +void net_cl_run(void) +{ + net_addr_t *addr; + net_packet_t *packet; + + if (!net_client_connected) + { + return; + } + + while (net_recv_packet(client_context, &addr, &packet)) + { + /* only accept packets from the server */ + + if (addr == server_addr) + { + net_cl_parse_packet(packet); + } + + net_free_packet(packet); + net_release_address(addr); + } + + /* Run the common connection code to send any packets as needed */ + + net_conn_run(&client_connection); + + if (client_connection.state == NET_CONN_STATE_DISCONNECTED || + client_connection.state == NET_CONN_STATE_DISCONNECTED_SLEEP) + { + net_cl_disconnected(); + + net_cl_shutdown(); + } + + net_waiting_for_launch = + client_connection.state == NET_CONN_STATE_CONNECTED && + client_state == CLIENT_STATE_WAITING_LAUNCH; + + if (client_state == CLIENT_STATE_IN_GAME) + { + /* Possibly advance the receive window */ + + net_cl_advance_window(); + + /* Check if our resend requests have timed out */ + + net_cl_check_resends(); + } +} + +/* Connect to a server */ + +boolean net_cl_connect(net_addr_t *addr, net_connect_data_t *data) +{ + int start_time; + int last_send_time; + boolean sent_hole_punch; + + server_addr = addr; + net_reference_address(addr); + + memcpy(net_local_wad_sha1sum, data->wad_sha1sum, sizeof(sha1_digest_t)); + memcpy(net_local_deh_sha1sum, data->deh_sha1sum, sizeof(sha1_digest_t)); + net_local_is_freedoom = data->is_freedoom; + + /* create a new network I/O context and add just the necessary module */ + + client_context = net_new_context(); + + /* initialize module for client mode */ + + if (!addr->module->init_client()) + { + set_reject_reason("Failed to initialize client module"); + return false; + } + + net_add_module(client_context, addr->module); + + net_client_connected = true; + net_client_received_wait_data = false; + sent_hole_punch = false; + + net_conn_init_client(&client_connection, addr, NET_PROTOCOL_UNKNOWN); + + /* try to connect */ + + start_time = i_get_time_ms(); + last_send_time = -1; + set_reject_reason("Unknown reason"); + + while (client_connection.state == NET_CONN_STATE_CONNECTING) + { + int nowtime = i_get_time_ms(); + + /* Send a SYN packet every second. */ + + if (nowtime - last_send_time > 1000 || last_send_time < 0) + { + net_cl_send_syn(data); + last_send_time = nowtime; + } + + /* time out after 5 seconds */ + + if (nowtime - start_time > 5000) + { + set_reject_reason("No response from server"); + break; + } + + if (!sent_hole_punch && nowtime - start_time > 2000) + { + net_log_warn("client: no response to SYN, requesting hole punch"); + net_request_hole_punch(client_context, addr); + sent_hole_punch = true; + } + + /* run client code */ + + net_cl_run(); + + /* run the server, just in case we are doing a loopback connect */ + + net_sv_run(); + + /* Don't hog the CPU */ + + usleep(1000); + } + + if (client_connection.state == NET_CONN_STATE_CONNECTED) + { + /* connected ok! */ + + net_log_info("client: connected successfully"); + set_reject_reason(NULL); + client_state = CLIENT_STATE_WAITING_LAUNCH; + drone = data->drone; + + return true; + } + else + { + /* failed to connect */ + + net_log_err("client: failed to connect"); + net_cl_shutdown(); + + return false; + } +} + +/* read game settings received from server */ + +boolean net_cl_get_settings(net_gamesettings_t *_settings) +{ + if (client_state != CLIENT_STATE_IN_GAME) + { + return false; + } + + memcpy(_settings, &settings, sizeof(net_gamesettings_t)); + + return true; +} + +/* disconnect from the server */ + +void net_cl_disconnect(void) +{ + int start_time; + + if (!net_client_connected) + { + return; + } + + net_log_info("client: beginning disconnect"); + net_conn_disconnect(&client_connection); + + start_time = i_get_time_ms(); + + while (client_connection.state != NET_CONN_STATE_DISCONNECTED && + client_connection.state != NET_CONN_STATE_DISCONNECTED_SLEEP) + { + if (i_get_time_ms() - start_time > 5000) + { + /* time out after 5 seconds */ + + net_log_err("client: no acknowledgement of disconnect received"); + client_state = CLIENT_STATE_WAITING_START; + + fprintf(stderr, "net_cl_disconnect: Timeout while disconnecting " + "from server\n"); + break; + } + + net_cl_run(); + net_sv_run(); + + usleep(1000); + } + + /* Finished sending disconnect packets, etc. */ + + net_log_info("client: disconnect complete"); + net_cl_shutdown(); +} + +void net_init(void) +{ + net_cl_init(); +} + +void net_bind_variables(void) +{ + m_bind_string_variable("player_name", &net_player_name); +} diff --git a/games/NXDoom/src/net_client.h b/games/NXDoom/src/net_client.h new file mode 100644 index 000000000..abe3d1b82 --- /dev/null +++ b/games/NXDoom/src/net_client.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_client.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Network client code + * + ****************************************************************************/ + +#ifndef NET_CLIENT_H +#define NET_CLIENT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_ticcmd.h" +#include "doomtype.h" +#include "net_defs.h" +#include "sha1.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern boolean net_client_connected; +extern boolean net_client_received_wait_data; +extern net_waitdata_t net_client_wait_data; +extern char *net_client_reject_reason; +extern boolean net_waiting_for_launch; +extern char *net_player_name; + +extern sha1_digest_t net_server_wad_sha1sum; +extern sha1_digest_t net_server_deh_sha1sum; +extern unsigned int net_server_is_freedoom; +extern sha1_digest_t net_local_wad_sha1sum; +extern sha1_digest_t net_local_deh_sha1sum; +extern unsigned int net_local_is_freedoom; + +extern boolean drone; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean net_cl_connect(net_addr_t *addr, net_connect_data_t *data); +void net_cl_disconnect(void); +void net_cl_run(void); +void net_cl_launch_game(void); +void net_cl_start_game(net_gamesettings_t *settings); +void net_cl_send_ticcmd(ticcmd_t *ticcmd, int maketic); +boolean net_cl_get_settings(net_gamesettings_t *_settings); +void net_init(void); + +void net_bind_variables(void); + +#endif /* NET_CLIENT_H */ diff --git a/games/NXDoom/src/net_common.c b/games/NXDoom/src/net_common.c new file mode 100644 index 000000000..62dac01ca --- /dev/null +++ b/games/NXDoom/src/net_common.c @@ -0,0 +1,532 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_common.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Common code shared between the client and server + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "d_mode.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_common.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* connections time out after 30 seconds */ + +#define CONNECTION_TIMEOUT_LEN 30 + +/* maximum time between sending packets */ + +#define KEEPALIVE_PERIOD 1 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* reliable packet that is guaranteed to reach its destination */ + +struct net_reliable_packet_s +{ + net_packet_t *packet; + int last_send_time; + int seq; + net_reliable_packet_t *next; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void net_conn_init(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + conn->last_send_time = -1; + conn->num_retries = 0; + conn->addr = addr; + conn->protocol = protocol; + conn->reliable_packets = NULL; + conn->reliable_send_seq = 0; + conn->reliable_recv_seq = 0; + conn->keepalive_recv_time = i_get_time_ms(); +} + +static void net_conn_parse_disconnect(net_connection_t *conn, + net_packet_t *packet) +{ + net_packet_t *reply; + + /* Other end wants to disconnect + * Send a DISCONNECT_ACK reply. + */ + + reply = net_new_packet(10); + net_write_int16(reply, NET_PACKET_TYPE_DISCONNECT_ACK); + net_conn_send_packet(conn, reply); + net_free_packet(reply); + + conn->last_send_time = i_get_time_ms(); + + conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP; + conn->disconnect_reason = NET_DISCONNECT_REMOTE; +} + +/* Parse a DISCONNECT_ACK packet */ + +static void net_conn_parse_disconnect_ack(net_connection_t *conn, + net_packet_t *packet) +{ + if (conn->state == NET_CONN_STATE_DISCONNECTING) + { + /* We have received an acknowledgement to our disconnect + * request. We have been disconnected successfully. + */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + conn->last_send_time = -1; + } +} + +static void net_conn_parse_reliable_ack(net_connection_t *conn, + net_packet_t *packet) +{ + unsigned int seq; + + if (!net_read_int8(packet, &seq)) + { + return; + } + + if (conn->reliable_packets == NULL) + { + return; + } + + /* Is this an acknowledgement for the first packet in the list? */ + + if (seq == (unsigned int)((conn->reliable_packets->seq + 1) & 0xff)) + { + net_reliable_packet_t *rp; + + /* Discard it, then. + * Unlink from the list. + */ + + rp = conn->reliable_packets; + conn->reliable_packets = rp->next; + + net_free_packet(rp->packet); + free(rp); + } +} + +/* Process the header of a reliable packet + * + * Returns true if the packet should be discarded (incorrect sequence) + */ + +static boolean net_connect_reliable_packet(net_connection_t *conn, + net_packet_t *packet) +{ + unsigned int seq; + net_packet_t *reply; + boolean result; + + /* Read the sequence number */ + + if (!net_read_int8(packet, &seq)) + { + return true; + } + + if (seq != (unsigned int)(conn->reliable_recv_seq & 0xff)) + { + /* This is not the next expected packet in the sequence! + * + * Discard the packet. If we were smart, we would use a proper + * sliding window protocol to do this, but I'm lazy. + */ + + result = true; + } + else + { + /* Now we can receive the next packet in the sequence. */ + + conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff; + + result = false; + } + + /* Send an acknowledgement */ + + /* Note: this is braindead. It would be much more sensible to + * include this in the next packet, rather than the overhead of + * sending a complete packet just for one byte of information. + */ + + reply = net_new_packet(10); + + net_write_int16(reply, NET_PACKET_TYPE_RELIABLE_ACK); + net_write_int8(reply, conn->reliable_recv_seq & 0xff); + + net_conn_send_packet(conn, reply); + + net_free_packet(reply); + + return result; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize as a client connection */ + +void net_conn_init_client(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + net_conn_init(conn, addr, protocol); + conn->state = NET_CONN_STATE_CONNECTING; +} + +/* Initialize as a server connection */ + +void net_conn_init_server(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol) +{ + net_conn_init(conn, addr, protocol); + conn->state = NET_CONN_STATE_CONNECTED; +} + +/* Send a packet to a connection + * All packets should be sent through this interface, as it maintains the + * keepalive_send_time counter. + */ + +void net_conn_send_packet(net_connection_t *conn, net_packet_t *packet) +{ + conn->keepalive_send_time = i_get_time_ms(); + net_send_packet(conn->addr, packet); +} + +/* Process a packet received by the server + * + * Returns true if eaten by common code + */ + +boolean net_conn_packet(net_connection_t *conn, net_packet_t *packet, + unsigned int *packet_type) +{ + conn->keepalive_recv_time = i_get_time_ms(); + + /* Is this a reliable packet? */ + + if (*packet_type & NET_RELIABLE_PACKET) + { + if (net_connect_reliable_packet(conn, packet)) + { + /* Invalid packet: eat it. */ + + return true; + } + + /* Remove the reliable bit */ + + *packet_type &= ~NET_RELIABLE_PACKET; + } + + switch (*packet_type) + { + case NET_PACKET_TYPE_DISCONNECT: + net_conn_parse_disconnect(conn, packet); + break; + case NET_PACKET_TYPE_DISCONNECT_ACK: + net_conn_parse_disconnect_ack(conn, packet); + break; + case NET_PACKET_TYPE_KEEPALIVE: + break; /* No special action needed. */ + case NET_PACKET_TYPE_RELIABLE_ACK: + net_conn_parse_reliable_ack(conn, packet); + break; + default: + return false; /* Not a common packet */ + } + + /* We found a packet that we found interesting, and ate it. */ + + return true; +} + +void net_conn_disconnect(net_connection_t *conn) +{ + if (conn->state != NET_CONN_STATE_DISCONNECTED && + conn->state != NET_CONN_STATE_DISCONNECTING && + conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP) + { + conn->state = NET_CONN_STATE_DISCONNECTING; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + conn->last_send_time = -1; + conn->num_retries = 0; + } +} + +void net_conn_run(net_connection_t *conn) +{ + net_packet_t *packet; + unsigned int nowtime; + + nowtime = i_get_time_ms(); + + if (conn->state == NET_CONN_STATE_CONNECTED) + { + /* Check the keepalive counters */ + + if (nowtime - conn->keepalive_recv_time > + CONNECTION_TIMEOUT_LEN * 1000) + { + /* Haven't received any packets from the other end in a long + * time. Assume disconnected. + */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_TIMEOUT; + } + + if (nowtime - conn->keepalive_send_time > KEEPALIVE_PERIOD * 1000) + { + /* We have not sent anything in a long time. + * Send a keepalive. + */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_KEEPALIVE); + net_conn_send_packet(conn, packet); + net_free_packet(packet); + } + + /* Check the reliable packet list. Has the first packet in the + * list timed out? + * + * NB. This is braindead, we have a fixed time of one second. + */ + + if (conn->reliable_packets != NULL && + (conn->reliable_packets->last_send_time < 0 || + nowtime - conn->reliable_packets->last_send_time > 1000)) + { + /* Packet timed out, time to resend */ + + net_conn_send_packet(conn, conn->reliable_packets->packet); + conn->reliable_packets->last_send_time = nowtime; + } + } + else if (conn->state == NET_CONN_STATE_DISCONNECTING) + { + /* Waiting for a reply to our DISCONNECT request. */ + + if (conn->last_send_time < 0 || nowtime - conn->last_send_time > 1000) + { + /* it has been a second since the last disconnect packet + * was sent, and still no reply. + */ + + if (conn->num_retries < MAX_RETRIES) + { + /* send another disconnect */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_DISCONNECT); + net_conn_send_packet(conn, packet); + net_free_packet(packet); + conn->last_send_time = nowtime; + + ++conn->num_retries; + } + else + { + /* No more retries allowed. Force disconnect. */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_LOCAL; + } + } + } + else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP) + { + /* We are disconnected, waiting in case we need to send + * a DISCONNECT_ACK to the server again. + */ + + if (nowtime - conn->last_send_time > 5000) + { + /* Idle for 5 seconds, switch state */ + + conn->state = NET_CONN_STATE_DISCONNECTED; + conn->disconnect_reason = NET_DISCONNECT_REMOTE; + } + } +} + +net_packet_t *net_conn_new_reliable(net_connection_t *conn, int packet_type) +{ + net_packet_t *packet; + net_reliable_packet_t *rp; + net_reliable_packet_t **listend; + + /* Generate a packet with the right header */ + + packet = net_new_packet(100); + + net_write_int16(packet, packet_type | NET_RELIABLE_PACKET); + + /* write the low byte of the send sequence number */ + + net_write_int8(packet, conn->reliable_send_seq & 0xff); + + /* Add to the list of reliable packets */ + + rp = malloc(sizeof(net_reliable_packet_t)); + rp->packet = packet; + rp->next = NULL; + rp->seq = conn->reliable_send_seq; + rp->last_send_time = -1; + + for (listend = &conn->reliable_packets; *listend != NULL; + listend = &((*listend)->next)) + ; + + *listend = rp; + + /* Count along the sequence */ + + conn->reliable_send_seq = (conn->reliable_send_seq + 1) & 0xff; + + /* Finished */ + + return packet; +} + +/* Used to expand the least significant byte of a tic number into + * the full tic number, from the current tic number + */ + +unsigned int net_expand_tic_num(unsigned int relative, unsigned int b) +{ + unsigned int l; + unsigned int h; + unsigned int result; + + h = relative & ~0xff; + l = relative & 0xff; + + result = h | b; + + if (l < 0x40 && b > 0xb0) result -= 0x100; + if (l > 0xb0 && b < 0x40) result += 0x100; + + return result; +} + +/* Check that game settings are valid */ + +boolean net_valid_game_settings(game_mode_t mode, gamemission_t mission, + net_gamesettings_t *settings) +{ + if (settings->ticdup <= 0) + { + return false; + } + + if (settings->extratics < 0) + { + return false; + } + + if (settings->deathmatch < 0 || settings->deathmatch > 2) + { + return false; + } + + if (settings->skill < sk_noitems || settings->skill > sk_nightmare) + { + return false; + } + + if (!d_valid_game_version(mission, settings->gameversion)) + { + return false; + } + + if (!d_valid_episode_map(mission, mode, settings->episode, settings->map)) + { + return false; + } + + return true; +} + +#ifdef CONFIG_GAMES_NXDOOM_NET_LOGS +void net_log_packet(net_packet_t *packet) +{ + int i; + int bytes; + + bytes = packet->len - packet->pos; + if (bytes == 0) + { + return; + } + + fprintf(stderr, "\t%02x", packet->data[packet->pos]); + + for (i = 1; i < bytes; ++i) + { + if ((i % 16) == 0) + { + fprintf(stderr, "\n\t"); + } + else + { + fprintf(stderr, " "); + } + + fprintf(stderr, "%02x", packet->data[packet->pos + i]); + } + + fprintf(stderr, "\n"); +} +#endif /* CONFIG_GAMES_NXDOOM_NET_LOGS */ diff --git a/games/NXDoom/src/net_common.h b/games/NXDoom/src/net_common.h new file mode 100644 index 000000000..09770a1b2 --- /dev/null +++ b/games/NXDoom/src/net_common.h @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_common.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Common code shared between the client and server + * + ****************************************************************************/ + +#ifndef NET_COMMON_H +#define NET_COMMON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_mode.h" +#include "net_defs.h" +#include "net_packet.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_RETRIES 5 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + /* Client has sent a SYN, is waiting for a SYN in response. */ + + NET_CONN_STATE_CONNECTING, + + /* Successfully connected. */ + + NET_CONN_STATE_CONNECTED, + + /* Sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply */ + + NET_CONN_STATE_DISCONNECTING, + + /* Client successfully disconnected */ + + NET_CONN_STATE_DISCONNECTED, + + /* We are disconnected, but in a sleep state, waiting for several + * seconds. This is in case the DISCONNECT_ACK we sent failed + * to arrive, and we need to send another one. We keep this as + * a valid connection for a few seconds until we are sure that + * the other end has successfully disconnected as well. + */ + + NET_CONN_STATE_DISCONNECTED_SLEEP, +} net_connstate_t; + +/* Reason a connection was terminated */ + +typedef enum +{ + /* As the result of a local disconnect request */ + + NET_DISCONNECT_LOCAL, + + /* As the result of a remote disconnect request */ + + NET_DISCONNECT_REMOTE, + + /* Timeout (no data received in a long time) */ + + NET_DISCONNECT_TIMEOUT, +} net_disconnect_reason_t; + +typedef struct net_reliable_packet_s net_reliable_packet_t; + +typedef struct +{ + net_connstate_t state; + net_disconnect_reason_t disconnect_reason; + net_addr_t *addr; + net_protocol_t protocol; + int last_send_time; + int num_retries; + int keepalive_send_time; + int keepalive_recv_time; + net_reliable_packet_t *reliable_packets; + int reliable_send_seq; + int reliable_recv_seq; +} net_connection_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_conn_send_packet(net_connection_t *conn, net_packet_t *packet); +void net_conn_init_client(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol); +void net_conn_init_server(net_connection_t *conn, net_addr_t *addr, + net_protocol_t protocol); +boolean net_conn_packet(net_connection_t *conn, net_packet_t *packet, + unsigned int *packet_type); +void net_conn_disconnect(net_connection_t *conn); +void net_conn_run(net_connection_t *conn); +net_packet_t *net_conn_new_reliable(net_connection_t *conn, int packet_type); + +/* Other miscellaneous common functions */ + +unsigned int net_expand_tic_num(unsigned int relative, unsigned int b); +boolean net_valid_game_settings(game_mode_t mode, gamemission_t mission, + net_gamesettings_t *settings); + +/* Conditional logging */ + +#ifdef CONFIG_GAMES_NXDOOM_NET_LOGS +#define net_log_info(fmt, ...) syslog(LOG_USER | LOG_INFO, fmt, ##__VA_ARGS__) +#define net_log_warn(fmt, ...) \ + syslog(LOG_USER | LOG_WARNING, fmt, ##__VA_ARGS__) +#define net_log_err(fmt, ...) syslog(LOG_USER | LOG_ERR, fmt, ##__VA_ARGS__) + +void net_log_packet(net_packet_t *packet); +#else +#define net_log_info(fmt, ...) +#define net_log_warn(fmt, ...) +#define net_log_err(fmt, ...) + +#define net_log_packet(pkt) +#endif /* CONFIG_GAMES_NXDOOM_NET_LOGS */ + +#endif /* NET_COMMON_H */ diff --git a/games/NXDoom/src/net_dedicated.c b/games/NXDoom/src/net_dedicated.c new file mode 100644 index 000000000..8c59fb7bb --- /dev/null +++ b/games/NXDoom/src/net_dedicated.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_dedicated.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Dedicated server code. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" + +#include "i_system.h" +#include "i_timer.h" + +#include "m_argv.h" + +#include "net_common.h" +#include "net_sdl.h" +#include "net_server.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* People can become confused about how dedicated servers work. Game + * options are specified to the controlling player who is the first to + * join a game. Bomb out with an error message if game options are + * specified to a dedicated server. + */ + +static const char *not_dedicated_options[] = +{ + "-deh", "-iwad", "-cdrom", "-gameversion", "-nomonsters", + "-respawn", "-fast", "-altdeath", "-deathmatch", "-turbo", + "-merge", "-af", "-as", "-aa", "-file", + "-wart", "-skill", "-episode", "-timer", "-avg", + "-warp", "-loadgame", "-longtics", "-extratics", "-dup", + "-shorttics", NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void check_for_client_options(void) +{ + int i; + + for (i = 0; not_dedicated_options[i] != NULL; ++i) + { + if (m_check_parm(not_dedicated_options[i]) > 0) + { + i_error("The command line parameter '%s' was specified to a " + "dedicated server.\nGame parameters should be specified " + "to the first player to join a server, \nnot to the " + "server itself. ", + not_dedicated_options[i]); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_dedicated_server(void) +{ + check_for_client_options(); + + net_sv_init(); + net_sv_add_module(&net_sdl_module); + net_sv_register_with_master(); + + while (true) + { + net_sv_run(); + + /* TODO: Block on socket instead of polling. */ + + usleep(1000); + } +} diff --git a/games/NXDoom/src/net_dedicated.h b/games/NXDoom/src/net_dedicated.h new file mode 100644 index 000000000..2f87bc5d5 --- /dev/null +++ b/games/NXDoom/src/net_dedicated.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_dedicated.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Dedicated server code. + * + ****************************************************************************/ + +#ifndef NET_DEDICATED_H +#define NET_DEDICATED_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_dedicated_server(void); + +#endif /* NET_DEDICATED_H */ diff --git a/games/NXDoom/src/net_defs.h b/games/NXDoom/src/net_defs.h new file mode 100644 index 000000000..be7358630 --- /dev/null +++ b/games/NXDoom/src/net_defs.h @@ -0,0 +1,323 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_defs.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Definitions for use in networking code. + * + ****************************************************************************/ + +#ifndef NET_DEFS_H +#define NET_DEFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "d_ticcmd.h" +#include "doomtype.h" +#include "sha1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Absolute maximum number of "nodes" in the game. This is different to + * NET_MAXPLAYERS, as there may be observers that are not participating + * (eg. left/right monitors) + */ + +#define MAXNETNODES 16 + +#ifndef CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME + +/* We don't need player names. TODO: I should be smarter about this + * and avoid compiling ANY networking related stuff (i.e. player name + * members in the structs below) at all when net stuff is not enabled. + */ + +#define CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME */ + +/* The maximum number of players, multiplayer/networking. + * This is the maximum supported by the networking code; individual games + * have their own values for MAXPLAYERS that can be smaller. + */ + +#ifndef CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS + +/* No players if not set up for networking */ + +#define CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS */ + +/* Networking and tick handling related. */ + +#ifndef CONFIG_GAMES_NXDOOM_NET_BACKUPTICS + +/* Surely we don't need any for a local game? */ + +#define CONFIG_GAMES_NXDOOM_NET_BACKUPTICS (1) +#endif /* CONFIG_GAMES_NXDOOM_NET_BACKUPTICS */ + +/* Magic number sent when connecting to check this is a valid client */ + +#define NET_MAGIC_NUMBER 1454104972U + +/* Old magic number used by Chocolate Doom versions before v3.0: */ + +#define NET_OLD_MAGIC_NUMBER 3436803284U + +/* header field value indicating that the packet is a reliable packet */ + +#define NET_RELIABLE_PACKET (1 << 15) + +#define NET_TICDIFF_FORWARD (1 << 0) +#define NET_TICDIFF_SIDE (1 << 1) +#define NET_TICDIFF_TURN (1 << 2) +#define NET_TICDIFF_BUTTONS (1 << 3) +#define NET_TICDIFF_CONSISTANCY (1 << 4) +#define NET_TICDIFF_CHATCHAR (1 << 5) +#define NET_TICDIFF_RAVEN (1 << 6) +#define NET_TICDIFF_STRIFE (1 << 7) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _net_module_s net_module_t; +typedef struct _net_packet_s net_packet_t; +typedef struct _net_addr_s net_addr_t; +typedef struct _net_context_s net_context_t; + +struct _net_packet_s +{ + byte *data; + size_t len; + size_t alloced; + unsigned int pos; +}; + +struct _net_module_s +{ + /* Initialize this module for use as a client */ + + boolean (*init_client)(void); + + /* Initialize this module for use as a server */ + + boolean (*init_server)(void); + + /* Send a packet */ + + void (*send_packet)(net_addr_t *addr, net_packet_t *packet); + + /* Check for new packets to receive + * + * Returns true if packet received + */ + + boolean (*recv_packet)(net_addr_t **addr, net_packet_t **packet); + + /* Converts an address to a string */ + + void (*addr_to_string)(net_addr_t *addr, char *buffer, int buffer_len); + + /* Free back an address when no longer in use */ + + void (*free_address)(net_addr_t *addr); + + /* Try to resolve a name to an address */ + + net_addr_t *(*resolve_address)(const char *addr); +}; + +/* net_addr_t */ + +struct _net_addr_s +{ + net_module_t *module; + int refcount; + void *handle; +}; + +/* Supported protocols. If you're developing a fork of Chocolate + * Doom, you can add your own entry to this list while maintaining + * compatibility with Chocolate Doom servers. Higher-numbered enum values + * will be preferred when negotiating a protocol for the client and server + * to use, so the order matters. + * NOTE: The values in this enum do not have any special value outside of + * the program they're compiled in. What matters is the string + * representation. + */ + +typedef enum +{ + /* Protocol introduced with Chocolate Doom v3.0. Each compatibility- + * breaking change to the network protocol will produce a new protocol + * number in this enum. + */ + + NET_PROTOCOL_CHOCOLATE_DOOM_0, + + /* Add your own protocol here; be sure to add a name for it to the list + * in net_common.c too. + */ + + NET_NUM_PROTOCOLS, + NET_PROTOCOL_UNKNOWN, +} net_protocol_t; + +/* packet types */ + +typedef enum +{ + NET_PACKET_TYPE_SYN, + NET_PACKET_TYPE_ACK, /* deprecated */ + NET_PACKET_TYPE_REJECTED, + NET_PACKET_TYPE_KEEPALIVE, + NET_PACKET_TYPE_WAITING_DATA, + NET_PACKET_TYPE_GAMESTART, + NET_PACKET_TYPE_GAMEDATA, + NET_PACKET_TYPE_GAMEDATA_ACK, + NET_PACKET_TYPE_DISCONNECT, + NET_PACKET_TYPE_DISCONNECT_ACK, + NET_PACKET_TYPE_RELIABLE_ACK, + NET_PACKET_TYPE_GAMEDATA_RESEND, + NET_PACKET_TYPE_CONSOLE_MESSAGE, + NET_PACKET_TYPE_QUERY, + NET_PACKET_TYPE_QUERY_RESPONSE, + NET_PACKET_TYPE_LAUNCH, + NET_PACKET_TYPE_NAT_HOLE_PUNCH, +} net_packet_type_t; + +typedef enum +{ + NET_MASTER_PACKET_TYPE_ADD, + NET_MASTER_PACKET_TYPE_ADD_RESPONSE, + NET_MASTER_PACKET_TYPE_QUERY, + NET_MASTER_PACKET_TYPE_QUERY_RESPONSE, + NET_MASTER_PACKET_TYPE_GET_METADATA, + NET_MASTER_PACKET_TYPE_GET_METADATA_RESPONSE, + NET_MASTER_PACKET_TYPE_SIGN_START, + NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, + NET_MASTER_PACKET_TYPE_SIGN_END, + NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, + NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH, + NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL, +} net_master_packet_type_t; + +/* Settings specified when the client connects to the server. */ + +typedef struct +{ + int gamemode; + int gamemission; + int lowres_turn; + int drone; + int max_players; + int is_freedoom; + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + int player_class; +} net_connect_data_t; + +/* Game settings sent by client to server when initiating game start, + * and received from the server by clients when the game starts. + */ + +typedef struct +{ + int ticdup; + int extratics; + int deathmatch; + int episode; + int nomonsters; + int fast_monsters; + int respawn_monsters; + int map; + int skill; + int gameversion; + int lowres_turn; + int new_sync; + int timelimit; + int loadgame; + int random; /* [Strife only] */ + + /* These fields are only used by the server when sending a game + * start message: + */ + + int num_players; + int consoleplayer; + + /* Hexen player classes: */ + + int player_classes[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} net_gamesettings_t; + +typedef struct +{ + unsigned int diff; + ticcmd_t cmd; +} net_ticdiff_t; + +/* Complete set of ticcmds from all players */ + +typedef struct +{ + signed int latency; + unsigned int seq; + boolean playeringame[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + net_ticdiff_t cmds[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +} net_full_ticcmd_t; + +/* Data sent in response to server queries */ + +typedef struct +{ + const char *version; + int server_state; + int num_players; + int max_players; + int gamemode; + int gamemission; + const char *description; + net_protocol_t protocol; +} net_querydata_t; + +/* Data sent by the server while waiting for the game to start. */ + +typedef struct +{ + int num_players; + int num_drones; + int ready_players; + int max_players; + int is_controller; + int consoleplayer; + char player_names[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME]; + char player_addrs[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME]; + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + int is_freedoom; +} net_waitdata_t; + +#endif /* NET_DEFS_H */ diff --git a/games/NXDoom/src/net_gui.c b/games/NXDoom/src/net_gui.c new file mode 100644 index 000000000..f731693ca --- /dev/null +++ b/games/NXDoom/src/net_gui.c @@ -0,0 +1,469 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_gui.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Graphical stuff related to the networking code: + * + * * The client waiting screen when we are waiting for the server to + * start the game. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" +#include "doomkeys.h" + +#include "i_system.h" +#include "i_timer.h" +#include "i_video.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_client.h" +#include "net_gui.h" +#include "net_query.h" +#include "net_server.h" + +#include "textscreen.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static txt_window_t *window; +static int old_max_players; +static txt_label_t *player_labels[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static txt_label_t *ip_labels[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static txt_label_t *drone_label; +static txt_label_t *master_msg_label; +static boolean had_warning; + +/* Number of players we expect to be in the game. When the number is + * reached, we auto-start the game (if we're the controller). If + * zero, do not autostart. + */ + +static int expected_nodes; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void escape_pressed(TXT_UNCAST_ARG(widget), void *unused) +{ + txt_shutdown(); + i_quit(); +} + +static void start_game(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused)) +{ + net_cl_launch_game(); +} + +static void open_wait_dialog(void) +{ + txt_window_action_t *cancel; + + txt_set_desktop_title(PACKAGE_STRING); + + window = txt_new_window("Waiting for game start..."); + + txt_add_widget(window, txt_new_label("\nPlease wait...\n\n")); + + cancel = txt_new_window_action(KEY_ESCAPE, "Cancel"); + txt_signal_connect(cancel, "pressed", escape_pressed, NULL); + + txt_set_window_action(window, TXT_HORIZ_LEFT, cancel); + txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_BOTTOM, + TXT_SCREEN_W / 2, TXT_SCREEN_H - 9); + + old_max_players = 0; +} + +static void build_window(void) +{ + char buf[50]; + txt_table_t *table; + int i; + + txt_clear_table(window); + table = txt_new_table(3); + txt_add_widget(window, table); + + /* Add spacers */ + + txt_add_widget(table, NULL); + txt_add_widget(table, txt_new_strut(25, 1)); + txt_add_widget(table, txt_new_strut(17, 1)); + + /* Player labels */ + + for (i = 0; i < net_client_wait_data.max_players; ++i) + { + snprintf(buf, sizeof(buf), " %i. ", i + 1); + txt_add_widget(table, txt_new_label(buf)); + player_labels[i] = txt_new_label(""); + ip_labels[i] = txt_new_label(""); + txt_add_widget(table, player_labels[i]); + txt_add_widget(table, ip_labels[i]); + } + + drone_label = txt_new_label(""); + + txt_add_widget(window, drone_label); +} + +static void update_gui(void) +{ + txt_window_action_t *startgame; + char buf[50]; + unsigned int i; + + /* If the value of max_players changes, we must rebuild the + * contents of the window. This includes when the first + * waiting data packet is received. + */ + + if (net_client_received_wait_data) + { + if (net_client_wait_data.max_players != old_max_players) + { + build_window(); + } + } + else + { + return; + } + + for (i = 0; i < net_client_wait_data.max_players; ++i) + { + txt_color_t color = TXT_COLOR_BRIGHT_WHITE; + + if ((signed)i == net_client_wait_data.consoleplayer) + { + color = TXT_COLOR_YELLOW; + } + + txt_set_fg_colour(player_labels[i], color); + txt_set_fg_colour(ip_labels[i], color); + + if (i < net_client_wait_data.num_players) + { + txt_set_label(player_labels[i], + net_client_wait_data.player_names[i]); + txt_set_label(ip_labels[i], net_client_wait_data.player_addrs[i]); + } + else + { + txt_set_label(player_labels[i], ""); + txt_set_label(ip_labels[i], ""); + } + } + + if (net_client_wait_data.num_drones > 0) + { + snprintf(buf, sizeof(buf), " (+%i observer clients)", + net_client_wait_data.num_drones); + txt_set_label(drone_label, buf); + } + else + { + txt_set_label(drone_label, ""); + } + + if (net_client_wait_data.is_controller) + { + startgame = txt_new_window_action(' ', "Start game"); + txt_signal_connect(startgame, "pressed", start_game, NULL); + } + else + { + startgame = NULL; + } + + txt_set_window_action(window, TXT_HORIZ_RIGHT, startgame); +} + +static void build_master_status_window(void) +{ + txt_window_t *master_window; + + master_window = txt_new_window(NULL); + master_msg_label = txt_new_label(""); + txt_add_widget(master_window, master_msg_label); + + /* This window is here purely for information, so it should be + * in the background. + */ + + txt_lower_window(master_window); + txt_set_window_position(master_window, TXT_HORIZ_CENTER, TXT_VERT_CENTER, + TXT_SCREEN_W / 2, TXT_SCREEN_H - 4); + txt_set_window_action(master_window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(master_window, TXT_HORIZ_CENTER, NULL); + txt_set_window_action(master_window, TXT_HORIZ_RIGHT, NULL); +} + +static void check_master_status(void) +{ + boolean added; + + if (!net_query_check_added_to_master(&added)) + { + return; + } + + if (master_msg_label == NULL) + { + build_master_status_window(); + } + + if (added) + { + txt_set_label( + master_msg_label, + "Your server is now registered with the global master server.\n" + "Other players can find your server online."); + } + else + { + txt_set_label( + master_msg_label, + "Failed to register with the master server. Your server is not\n" + "publicly accessible. You may need to reconfigure your Internet\n" + "router to add a port forward for UDP port 2342. Look up\n" + "information on port forwarding online."); + } +} + +static void print_sha1_digest(const char *s, const byte *digest) +{ + unsigned int i; + + printf("%s: ", s); + + for (i = 0; i < sizeof(sha1_digest_t); ++i) + { + printf("%02x", digest[i]); + } + + printf("\n"); +} + +static void close_window(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(p_window)) +{ + TXT_CAST_ARG(txt_window_t, p_window); + + txt_close_window(p_window); +} + +static void check_sha1_sums(void) +{ + boolean correct_wad; + boolean correct_deh; + boolean same_freedoom; + txt_window_t *l_window; + txt_window_action_t *cont_button; + + if (!net_client_received_wait_data || had_warning) + { + return; + } + + correct_wad = + memcmp(net_local_wad_sha1sum, net_client_wait_data.wad_sha1sum, + sizeof(sha1_digest_t)) == 0; + correct_deh = + memcmp(net_local_deh_sha1sum, net_client_wait_data.deh_sha1sum, + sizeof(sha1_digest_t)) == 0; + same_freedoom = net_client_wait_data.is_freedoom == net_local_is_freedoom; + + if (correct_wad && correct_deh && same_freedoom) + { + return; + } + + if (!correct_wad) + { + printf("Warning: WAD SHA1 does not match server:\n"); + print_sha1_digest("Local", net_local_wad_sha1sum); + print_sha1_digest("Server", net_client_wait_data.wad_sha1sum); + } + + if (!same_freedoom) + { + printf("Warning: Mixing Freedoom with non-Freedoom\n"); + printf("Local: %u Server: %i\n", net_local_is_freedoom, + net_client_wait_data.is_freedoom); + } + + if (!correct_deh) + { + printf("Warning: Dehacked SHA1 does not match server:\n"); + print_sha1_digest("Local", net_local_deh_sha1sum); + print_sha1_digest("Server", net_client_wait_data.deh_sha1sum); + } + + l_window = txt_new_window("WARNING!"); + + cont_button = txt_new_window_action(KEY_ENTER, "Continue"); + txt_signal_connect(cont_button, "pressed", close_window, l_window); + + txt_set_window_action(l_window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(l_window, TXT_HORIZ_CENTER, cont_button); + txt_set_window_action(l_window, TXT_HORIZ_RIGHT, NULL); + + if (!same_freedoom) + { + /* If Freedoom and Doom IWADs are mixed, the WAD directory + * will be wrong, but this is not necessarily a problem. + * Display a different message to the WAD directory message. + */ + + if (net_local_is_freedoom) + { + txt_add_widget( + l_window, + txt_new_label( + "You are using the Freedoom IWAD to play with players\n" + "using an official Doom IWAD. Make sure that you are\n" + "playing the same levels as other players.\n")); + } + else + { + txt_add_widget( + l_window, + txt_new_label( + "You are using an official IWAD to play with players\n" + "using the Freedoom IWAD. Make sure that you are\n" + "playing the same levels as other players.\n")); + } + } + else if (!correct_wad) + { + txt_add_widget( + l_window, + txt_new_label( + "Your WAD directory does not match other players in the game.\n" + "Check that you have loaded the exact same WAD files as other\n" + "players.\n") + ); + } + + if (!correct_deh) + { + txt_add_widget( + l_window, + txt_new_label( + "Your dehacked signature does not match other players in the\n" + "game. Check that you have loaded the same dehacked patches\n" + "as other players.\n")); + } + + txt_add_widget( + l_window, + txt_new_label("If you continue, this may cause your game to desync.")); + + had_warning = true; +} + +static void parse_command_line_args(void) +{ + int i; + + /* @arg + * @category net + * + * Autostart the netgame when n nodes (clients) have joined the server. + */ + + i = m_check_parm_with_args("-nodes", 1); + if (i > 0) + { + expected_nodes = atoi(myargv[i + 1]); + } +} + +static void check_auto_latch(void) +{ + int nodes; + + if (net_client_received_wait_data && net_client_wait_data.is_controller && + expected_nodes > 0) + { + nodes = + net_client_wait_data.num_players + net_client_wait_data.num_drones; + + if (nodes >= expected_nodes) + { + start_game(NULL, NULL); + expected_nodes = 0; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_wait_for_launch(void) +{ + if (!txt_init()) + { + fprintf(stderr, "Failed to initialize GUI\n"); + exit(-1); + } + + /* Romero's "funky blue" color */ + + txt_set_colour(TXT_COLOR_BLUE, 0x04, 0x14, 0x40); + + parse_command_line_args(); + open_wait_dialog(); + had_warning = false; + + while (net_waiting_for_launch) + { + update_gui(); + check_auto_latch(); + check_sha1_sums(); + check_master_status(); + + txt_dispatch_events(); + txt_draw_desktop(); + + net_cl_run(); + net_sv_run(); + + if (!net_client_connected) + { + i_error("Lost connection to server"); + } + + txt_sleep(100); + } + + txt_shutdown(); +} diff --git a/games/NXDoom/src/net_gui.h b/games/NXDoom/src/net_gui.h new file mode 100644 index 000000000..377b6646c --- /dev/null +++ b/games/NXDoom/src/net_gui.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_gui.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Graphical stuff related to the networking code: + * + * * The client waiting screen when we are waiting for the server to + * start the game. + * + ****************************************************************************/ + +#ifndef NET_GUI_H +#define NET_GUI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void net_wait_for_launch(void); + +#endif /* NET_GUI_H */ diff --git a/games/NXDoom/src/net_io.c b/games/NXDoom/src/net_io.c new file mode 100644 index 000000000..0c9c4a4fb --- /dev/null +++ b/games/NXDoom/src/net_io.c @@ -0,0 +1,170 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Network packet I/O. Base layer for sending/receiving packets, + * through the network module system + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "i_system.h" +#include "net_defs.h" +#include "net_io.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_MODULES 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct _net_context_s +{ + net_module_t *modules[MAX_MODULES]; + int num_modules; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_addr_t net_broadcast_addr; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +net_context_t *net_new_context(void) +{ + net_context_t *context; + + context = z_malloc(sizeof(net_context_t), PU_STATIC, 0); + context->num_modules = 0; + + return context; +} + +void net_add_module(net_context_t *context, net_module_t *module) +{ + if (context->num_modules >= MAX_MODULES) + { + i_error("net_add_module: No more modules for context"); + } + + context->modules[context->num_modules] = module; + ++context->num_modules; +} + +net_addr_t *net_resolve_address(net_context_t *context, const char *addr) +{ + int i; + net_addr_t *result; + + for (i = 0; i < context->num_modules; ++i) + { + result = context->modules[i]->resolve_address(addr); + + if (result != NULL) + { + net_reference_address(result); + return result; + } + } + + return NULL; +} + +void net_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + addr->module->send_packet(addr, packet); +} + +void net_send_broadcast(net_context_t *context, net_packet_t *packet) +{ + int i; + + for (i = 0; i < context->num_modules; ++i) + { + context->modules[i]->send_packet(&net_broadcast_addr, packet); + } +} + +boolean net_recv_packet(net_context_t *context, net_addr_t **addr, + net_packet_t **packet) +{ + int i; + + /* check all modules for new packets */ + + for (i = 0; i < context->num_modules; ++i) + { + if (context->modules[i]->recv_packet(addr, packet)) + { + net_reference_address(*addr); + return true; + } + } + + return false; +} + +/* Note: this prints into a static buffer, calling again overwrites + * the first result + */ + +char *net_addr_to_string(net_addr_t *addr) +{ + static char buf[128]; + + addr->module->addr_to_string(addr, buf, sizeof(buf) - 1); + + return buf; +} + +void net_reference_address(net_addr_t *addr) +{ + if (addr == NULL) + { + return; + } + + ++addr->refcount; +} + +void net_release_address(net_addr_t *addr) +{ + if (addr == NULL) + { + return; + } + + --addr->refcount; + if (addr->refcount <= 0) + { + addr->module->free_address(addr); + } +} diff --git a/games/NXDoom/src/net_io.h b/games/NXDoom/src/net_io.h new file mode 100644 index 000000000..d83d3ace3 --- /dev/null +++ b/games/NXDoom/src/net_io.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Network packet manipulation (net_packet_t) + * + ****************************************************************************/ + +#ifndef NET_IO_H +#define NET_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_addr_t net_broadcast_addr; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Create a new network context. */ + +net_context_t *net_new_context(void); + +/* Add a network module to a context. */ + +void net_add_module(net_context_t *context, net_module_t *module); + +/* Send a packet to the given address. */ + +void net_send_packet(net_addr_t *addr, net_packet_t *packet); + +/* Send a broadcast using all modules in the given context. */ + +void net_send_broadcast(net_context_t *context, net_packet_t *packet); + +/* Check all modules in the given context and receive a packet, returning + * true if a packet was received. The result is stored in *packet and the + * source is stored in *addr, with an implicit reference added. The packet + * must be freed by the caller and the reference released. + */ + +boolean net_recv_packet(net_context_t *context, net_addr_t **addr, + net_packet_t **packet); + +/* Return a string representation of the given address. The result points + * to a static buffer and will become invalid with the next call. + */ + +char *net_addr_to_string(net_addr_t *addr); + +/* Add a reference to the given address. */ + +void net_reference_address(net_addr_t *addr); + +/* Release a reference to the given address. When there are no more + * references, the address will be freed. + */ + +void net_release_address(net_addr_t *addr); + +/* Resolve a string representation of an address. If successful, a net_addr_t + * pointer is received with an implicit reference that must be freed by the + * caller when it is no longer needed. + */ + +net_addr_t *net_resolve_address(net_context_t *context, const char *address); + +#endif /* NET_IO_H */ diff --git a/games/NXDoom/src/net_loop.c b/games/NXDoom/src/net_loop.c new file mode 100644 index 000000000..d31d29cfc --- /dev/null +++ b/games/NXDoom/src/net_loop.c @@ -0,0 +1,272 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_loop.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Loopback network module for server compiled into the client + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "net_defs.h" +#include "net_loop.h" +#include "net_packet.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_QUEUE_SIZE 16 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + net_packet_t *packets[MAX_QUEUE_SIZE]; + int head; + int tail; +} packet_queue_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static packet_queue_t client_queue; +static packet_queue_t server_queue; +static net_addr_t client_addr; +static net_addr_t server_addr; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean net_cl_init_client(void); +static boolean net_cl_init_server(void); +static void net_cl_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_cl_recv_packet(net_addr_t **addr, net_packet_t **packet); +static void net_cl_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_cl_free_address(net_addr_t *addr); +static net_addr_t *net_cl_resolve_address(const char *address); + +static boolean net_sv_init_client(void); +static boolean net_sv_init_server(void); +static void net_sv_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_sv_recv_packet(net_addr_t **addr, net_packet_t **packet); +static void net_sv_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_sv_free_address(net_addr_t *addr); +static net_addr_t *net_sv_resolve_address(const char *address); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_module_t net_loop_client_module = +{ + net_cl_init_client, net_cl_init_server, net_cl_send_packet, + net_cl_recv_packet, net_cl_addr_to_string, net_cl_free_address, + net_cl_resolve_address, +}; + +net_module_t net_loop_server_module = +{ + net_sv_init_client, net_sv_init_server, net_sv_send_packet, + net_sv_recv_packet, net_sv_addr_to_string, net_sv_free_address, + net_sv_resolve_address, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void queue_init(packet_queue_t *queue) +{ + queue->head = queue->tail = 0; +} + +static void queue_push(packet_queue_t *queue, net_packet_t *packet) +{ + int new_tail; + + new_tail = (queue->tail + 1) % MAX_QUEUE_SIZE; + + if (new_tail == queue->head) + { + /* queue is full */ + + return; + } + + queue->packets[queue->tail] = packet; + queue->tail = new_tail; +} + +static net_packet_t *queue_pop(packet_queue_t *queue) +{ + net_packet_t *packet; + + if (queue->tail == queue->head) + { + /* queue empty */ + + return NULL; + } + + packet = queue->packets[queue->head]; + queue->head = (queue->head + 1) % MAX_QUEUE_SIZE; + + return packet; +} + +/**************************************************************************** + * Client-end code + ****************************************************************************/ + +static boolean net_cl_init_client(void) +{ + queue_init(&client_queue); + + return true; +} + +static boolean net_cl_init_server(void) +{ + i_error("NET_CL_init_server: attempted to initialize client pipe end as a " + "server!"); + return false; +} + +static void net_cl_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + queue_push(&server_queue, net_packet_dup(packet)); +} + +static boolean net_cl_recv_packet(net_addr_t **addr, net_packet_t **packet) +{ + net_packet_t *popped; + + popped = queue_pop(&client_queue); + + if (popped != NULL) + { + *packet = popped; + *addr = &client_addr; + client_addr.module = &net_loop_client_module; + + return true; + } + + return false; +} + +static void net_cl_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ + snprintf(buffer, buffer_len, "local server"); +} + +static void net_cl_free_address(net_addr_t *addr) +{ +} + +static net_addr_t *net_cl_resolve_address(const char *address) +{ + if (address == NULL) + { + client_addr.module = &net_loop_client_module; + + return &client_addr; + } + else + { + return NULL; + } +} + +/**************************************************************************** + * Server-end code + ****************************************************************************/ + +static boolean net_sv_init_client(void) +{ + i_error("net_sv_initClient: attempted to initialize server pipe end as a " + "client!"); + return false; +} + +static boolean net_sv_init_server(void) +{ + queue_init(&server_queue); + return true; +} + +static void net_sv_send_packet(net_addr_t *addr, net_packet_t *packet) +{ + queue_push(&client_queue, net_packet_dup(packet)); +} + +static boolean net_sv_recv_packet(net_addr_t **addr, net_packet_t **packet) +{ + net_packet_t *popped; + + popped = queue_pop(&server_queue); + + if (popped != NULL) + { + *packet = popped; + *addr = &server_addr; + server_addr.module = &net_loop_server_module; + + return true; + } + + return false; +} + +static void net_sv_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ + snprintf(buffer, buffer_len, "local client"); +} + +static void net_sv_free_address(net_addr_t *addr) +{ +} + +static net_addr_t *net_sv_resolve_address(const char *address) +{ + if (address == NULL) + { + server_addr.module = &net_loop_server_module; + return &server_addr; + } + else + { + return NULL; + } +} diff --git a/games/NXDoom/src/net_loop.h b/games/NXDoom/src/net_loop.h new file mode 100644 index 000000000..222660ffc --- /dev/null +++ b/games/NXDoom/src/net_loop.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_loop.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Loopback network module for server compiled into the client + * + ****************************************************************************/ + +#ifndef NET_LOOP_H +#define NET_LOOP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_module_t net_loop_client_module; +extern net_module_t net_loop_server_module; + +#endif /* NET_LOOP_H */ diff --git a/games/NXDoom/src/net_packet.c b/games/NXDoom/src/net_packet.c new file mode 100644 index 000000000..d7a57e1e2 --- /dev/null +++ b/games/NXDoom/src/net_packet.c @@ -0,0 +1,357 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_packet.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Network packet manipulation (net_packet_t) + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_packet.h" +#include "m_misc.h" +#include "z_zone.h" +#include +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_total_packet_memory = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Dynamically increases the size of a packet */ + +static void net_increase_packet(net_packet_t *packet) +{ + byte *newdata; + + g_total_packet_memory -= packet->alloced; + + packet->alloced *= 2; + + newdata = z_malloc(packet->alloced, PU_STATIC, 0); + + memcpy(newdata, packet->data, packet->len); + + z_free(packet->data); + packet->data = newdata; + + g_total_packet_memory += packet->alloced; +} + +#if 0 /* Unused */ +static boolean net_read_sint32(net_packet_t *packet, signed int *data) +{ + if (net_read_int32(packet, (unsigned int *)data)) + { + if (*data & (1U << 31)) + { + *data &= ~(1U << 31); + *data -= (1U << 31); + } + + return true; + } + else + { + return false; + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +net_packet_t *net_new_packet(int initial_size) +{ + net_packet_t *packet; + + packet = (net_packet_t *)z_malloc(sizeof(net_packet_t), PU_STATIC, 0); + + if (initial_size == 0) initial_size = 256; + + packet->alloced = initial_size; + packet->data = z_malloc(initial_size, PU_STATIC, 0); + packet->len = 0; + packet->pos = 0; + + g_total_packet_memory += sizeof(net_packet_t) + initial_size; + + /* printf("total packet memory: %i bytes\n", total_packet_memory); + * printf("%p: allocated\n", packet); + */ + + return packet; +} + +/* duplicates an existing packet */ + +net_packet_t *net_packet_dup(net_packet_t *packet) +{ + net_packet_t *newpacket; + + newpacket = net_new_packet(packet->len); + memcpy(newpacket->data, packet->data, packet->len); + newpacket->len = packet->len; + + return newpacket; +} + +void net_free_packet(net_packet_t *packet) +{ + /* printf("%p: destroyed\n", packet); */ + + g_total_packet_memory -= sizeof(net_packet_t) + packet->alloced; + z_free(packet->data); + z_free(packet); +} + +/* Read a byte from the packet, returning true if read + * successfully + */ + +boolean net_read_int8(net_packet_t *packet, unsigned int *data) +{ + if (packet->pos + 1 > packet->len) return false; + + *data = packet->data[packet->pos]; + + packet->pos += 1; + + return true; +} + +/* Read a 16-bit integer from the packet, returning true if read + * successfully + */ + +boolean net_read_int16(net_packet_t *packet, unsigned int *data) +{ + byte *p; + + if (packet->pos + 2 > packet->len) return false; + + p = packet->data + packet->pos; + + *data = (p[0] << 8) | p[1]; + packet->pos += 2; + + return true; +} + +/* Read a 32-bit integer from the packet, returning true if read + * successfully + */ + +boolean net_read_int32(net_packet_t *packet, unsigned int *data) +{ + byte *p; + + if (packet->pos + 4 > packet->len) return false; + + p = packet->data + packet->pos; + + *data = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + packet->pos += 4; + + return true; +} + +/* Signed read functions */ + +boolean net_read_sint8(net_packet_t *packet, signed int *data) +{ + if (net_read_int8(packet, (unsigned int *)data)) + { + if (*data & (1 << 7)) + { + *data &= ~(1 << 7); + *data -= (1 << 7); + } + + return true; + } + else + { + return false; + } +} + +boolean net_read_sint16(net_packet_t *packet, signed int *data) +{ + if (net_read_int16(packet, (unsigned int *)data)) + { + if (*data & (1 << 15)) + { + *data &= ~(1 << 15); + *data -= (1 << 15); + } + + return true; + } + else + { + return false; + } +} + +/* Read a string from the packet. Returns NULL if a terminating + * NUL character was not found before the end of the packet. + */ + +char *net_read_string(net_packet_t *packet) +{ + char *start; + + start = (char *)packet->data + packet->pos; + + /* Search forward for a NUL character */ + + while (packet->pos < packet->len && packet->data[packet->pos] != '\0') + { + ++packet->pos; + } + + if (packet->pos >= packet->len) + { + /* Reached the end of the packet */ + + return NULL; + } + + /* packet->data[packet->pos] == '\0': We have reached a terminating + * NULL. Skip past this NULL and continue reading immediately + * after it. + */ + + ++packet->pos; + + return start; +} + +/* Read a string from the packet, but (potentially) modify it to strip + * out any unprintable characters which could be malicious control codes. + * Note that this may modify the original packet contents. + */ + +char *net_read_safe_string(net_packet_t *packet) +{ + char *r; + char *w; + char *result; + + result = net_read_string(packet); + if (result == NULL) + { + return NULL; + } + + /* w is always <= r, so we never produce a longer string than the original. + */ + + w = result; + for (r = result; *r != '\0'; ++r) + { + /* TODO: This is a very naive way of producing a safe string; only + * ASCII characters are allowed. Probably this should really support + * UTF-8 characters as well. + */ + + if (isprint(*r) || *r == '\n') + { + *w = *r; + ++w; + } + } + + *w = '\0'; + + return result; +} + +/* Write a single byte to the packet */ + +void net_write_int8(net_packet_t *packet, unsigned int i) +{ + if (packet->len + 1 > packet->alloced) net_increase_packet(packet); + + packet->data[packet->len] = i; + packet->len += 1; +} + +/* Write a 16-bit integer to the packet */ + +void net_write_int16(net_packet_t *packet, unsigned int i) +{ + byte *p; + + if (packet->len + 2 > packet->alloced) net_increase_packet(packet); + + p = packet->data + packet->len; + + p[0] = (i >> 8) & 0xff; + p[1] = i & 0xff; + + packet->len += 2; +} + +/* Write a single byte to the packet */ + +void net_write_int32(net_packet_t *packet, unsigned int i) +{ + byte *p; + + if (packet->len + 4 > packet->alloced) net_increase_packet(packet); + + p = packet->data + packet->len; + + p[0] = (i >> 24) & 0xff; + p[1] = (i >> 16) & 0xff; + p[2] = (i >> 8) & 0xff; + p[3] = i & 0xff; + + packet->len += 4; +} + +void net_write_string(net_packet_t *packet, const char *string) +{ + byte *p; + size_t string_size; + + string_size = strlen(string) + 1; + + /* Increase the packet size until large enough to hold the string */ + + while (packet->len + string_size > packet->alloced) + { + net_increase_packet(packet); + } + + p = packet->data + packet->len; + + m_str_copy((char *)p, string, string_size); + + packet->len += string_size; +} diff --git a/games/NXDoom/src/net_packet.h b/games/NXDoom/src/net_packet.h new file mode 100644 index 000000000..f2d412b95 --- /dev/null +++ b/games/NXDoom/src/net_packet.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_packet.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Definitions for use in networking code. + * + ****************************************************************************/ + +#ifndef NET_PACKET_H +#define NET_PACKET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +net_packet_t *net_new_packet(int initial_size); +net_packet_t *net_packet_dup(net_packet_t *packet); +void net_free_packet(net_packet_t *packet); + +boolean net_read_int8(net_packet_t *packet, unsigned int *data); +boolean net_read_int16(net_packet_t *packet, unsigned int *data); +boolean net_read_int32(net_packet_t *packet, unsigned int *data); + +boolean net_read_sint8(net_packet_t *packet, signed int *data); +boolean net_read_sint16(net_packet_t *packet, signed int *data); + +char *net_read_string(net_packet_t *packet); +char *net_read_safe_string(net_packet_t *packet); + +void net_write_int8(net_packet_t *packet, unsigned int i); +void net_write_int16(net_packet_t *packet, unsigned int i); +void net_write_int32(net_packet_t *packet, unsigned int i); + +void net_write_string(net_packet_t *packet, const char *string); + +#endif /* NET_PACKET_H */ diff --git a/games/NXDoom/src/net_query.c b/games/NXDoom/src/net_query.c new file mode 100644 index 000000000..ea80d8ccd --- /dev/null +++ b/games/NXDoom/src/net_query.c @@ -0,0 +1,1063 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_query.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Querying servers to find their current status. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_system.h" +#include "i_timer.h" +#include "m_misc.h" + +#include "net_common.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* DNS address of the Internet master server. */ + +#define MASTER_SERVER_ADDRESS "master.chocolate-doom.org:2342" + +/* Time to wait for a response before declaring a timeout. */ + +#define QUERY_TIMEOUT_SECS 2 + +/* Time to wait for secure demo signatures before declaring a timeout. */ + +#define SIGNATURE_TIMEOUT_SECS 5 + +/* Number of query attempts to make before giving up on a server. */ + +#define QUERY_MAX_ATTEMPTS 3 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + QUERY_TARGET_SERVER, /* Normal server target. */ + QUERY_TARGET_MASTER, /* The master server. */ + QUERY_TARGET_BROADCAST /* Send a broadcast query */ +} query_target_type_t; + +typedef enum +{ + QUERY_TARGET_QUEUED, /* Query not yet sent */ + QUERY_TARGET_QUERIED, /* Query sent, waiting response */ + QUERY_TARGET_RESPONDED, /* Response received */ + QUERY_TARGET_NO_RESPONSE +} query_target_state_t; + +typedef struct +{ + query_target_type_t type; + query_target_state_t state; + net_addr_t *addr; + net_querydata_t data; + unsigned int ping_time; + unsigned int query_time; + unsigned int query_attempts; + boolean printed; +} query_target_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static boolean g_registered_with_master = false; +static boolean g_got_master_response = false; + +static net_context_t *g_query_context; +static query_target_t *g_targets; +static int g_num_targets; + +static boolean g_query_loop_running = false; +static boolean g_printed_header = false; +static int g_last_query_time = 0; + +#if 0 +static char *g_securedemo_start_message = NULL; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void formatted_printf(int wide, const char *s, ...) PRINTF_ATTR(2, 3); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Send a query to the master server. */ + +static void net_query_send_master_query(net_addr_t *addr) +{ + net_packet_t *packet; + + packet = net_new_packet(4); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_QUERY); + net_send_packet(addr, packet); + net_free_packet(packet); + + /* We also send a NAT_HOLE_PUNCH_ALL packet so that servers behind + * NAT gateways will open themselves up to us. + */ + + packet = net_new_packet(4); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL); + net_send_packet(addr, packet); + net_free_packet(packet); +} + +/* Given the specified address, find the target associated. If no + * target is found, and 'create' is true, a new target is created. + */ + +static query_target_t *get_target_for_addr(net_addr_t *addr, boolean create) +{ + query_target_t *target; + int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].addr == addr) + { + return &g_targets[i]; + } + } + + if (!create) + { + return NULL; + } + + g_targets = + i_realloc(g_targets, sizeof(query_target_t) * (g_num_targets + 1)); + + target = &g_targets[g_num_targets]; + target->type = QUERY_TARGET_SERVER; + target->state = QUERY_TARGET_QUEUED; + target->printed = false; + target->query_attempts = 0; + target->addr = addr; + net_reference_address(addr); + ++g_num_targets; + + return target; +} + +static void free_targets(void) +{ + int i; + + for (i = 0; i < g_num_targets; ++i) + { + net_release_address(g_targets[i].addr); + } + + free(g_targets); + g_targets = NULL; + g_num_targets = 0; +} + +/* Transmit a query packet */ + +static void net_query_send_query(net_addr_t *addr) +{ + net_packet_t *request; + + request = net_new_packet(10); + net_write_int16(request, NET_PACKET_TYPE_QUERY); + + if (addr == NULL) + { + net_send_broadcast(g_query_context, request); + } + else + { + net_send_packet(addr, request); + } + + net_free_packet(request); +} + +static void net_query_parse_response(net_addr_t *addr, net_packet_t *packet, + net_query_callback_t callback, + void *user_data) +{ + unsigned int packet_type; + net_querydata_t querydata; + query_target_t *target; + + /* Read the header */ + + if (!net_read_int16(packet, &packet_type) || + packet_type != NET_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + /* Read query data */ + + if (!net_read_query_data(packet, &querydata)) + { + return; + } + + /* Find the target that responded. */ + + target = get_target_for_addr(addr, false); + + /* If the target is not found, it may be because we are doing + * a LAN broadcast search, in which case we need to create a + * target for the new responder. + */ + + if (target == NULL) + { + query_target_t *broadcast_target; + + broadcast_target = get_target_for_addr(NULL, false); + + /* Not in broadcast mode, unexpected response that came out + * of nowhere. Ignore. + */ + + if (broadcast_target == NULL || + broadcast_target->state != QUERY_TARGET_QUERIED) + { + return; + } + + /* Create new target. */ + + target = get_target_for_addr(addr, true); + broadcast_target = get_target_for_addr(NULL, false); + target->state = QUERY_TARGET_QUERIED; + target->query_time = broadcast_target->query_time; + } + + if (target->state != QUERY_TARGET_RESPONDED) + { + target->state = QUERY_TARGET_RESPONDED; + memcpy(&target->data, &querydata, sizeof(net_querydata_t)); + + /* Calculate RTT. */ + + target->ping_time = i_get_time_ms() - target->query_time; + + /* Invoke callback to signal that we have a new address. */ + + callback(addr, &target->data, target->ping_time, user_data); + } +} + +/* Parse a response packet from the master server. */ + +static void net_query_parse_master_response(net_addr_t *master_addr, + net_packet_t *packet) +{ + unsigned int packet_type; + query_target_t *target; + char *addr_str; + net_addr_t *addr; + + /* Read the header. We are only interested in query responses. */ + + if (!net_read_int16(packet, &packet_type) || + packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE) + { + return; + } + + /* Read a list of strings containing the addresses of servers + * that the master knows about. + */ + + for (; ; ) + { + addr_str = net_read_string(packet); + + if (addr_str == NULL) + { + break; + } + + /* Resolve address and add to targets list if it is not already + * there. + */ + + addr = net_resolve_address(g_query_context, addr_str); + if (addr != NULL) + { + get_target_for_addr(addr, true); + net_release_address(addr); + } + } + + /* Mark the master as having responded. */ + + target = get_target_for_addr(master_addr, true); + target->state = QUERY_TARGET_RESPONDED; +} + +static void net_query_parse_packet(net_addr_t *addr, net_packet_t *packet, + net_query_callback_t callback, + void *user_data) +{ + query_target_t *target; + + /* This might be the master server responding. */ + + target = get_target_for_addr(addr, false); + + if (target != NULL && target->type == QUERY_TARGET_MASTER) + { + net_query_parse_master_response(addr, packet); + } + else + { + net_query_parse_response(addr, packet, callback, user_data); + } +} + +static void net_query_get_response(net_query_callback_t callback, + void *user_data) +{ + net_addr_t *addr; + net_packet_t *packet; + + if (net_recv_packet(g_query_context, &addr, &packet)) + { + net_query_parse_packet(addr, packet, callback, user_data); + net_release_address(addr); + net_free_packet(packet); + } +} + +/* Find a target we have not yet queried and send a query. */ + +static void send_one_query(void) +{ + unsigned int now; + unsigned int i; + + now = i_get_time_ms(); + + /* Rate limit - only send one query every 50ms. */ + + if (now - g_last_query_time < 50) + { + return; + } + + for (i = 0; i < g_num_targets; ++i) + { + /* Not queried yet? + * Or last query timed out without a response? + */ + + if (g_targets[i].state == QUERY_TARGET_QUEUED || + (g_targets[i].state == QUERY_TARGET_QUERIED && + now - g_targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)) + { + break; + } + } + + if (i >= g_num_targets) + { + return; + } + + /* Found a target to query. Send a query; how to do this depends on + * the target type. + */ + + switch (g_targets[i].type) + { + case QUERY_TARGET_SERVER: + net_query_send_query(g_targets[i].addr); + break; + + case QUERY_TARGET_BROADCAST: + net_query_send_query(NULL); + break; + + case QUERY_TARGET_MASTER: + net_query_send_master_query(g_targets[i].addr); + break; + } + + /* printf("Queried %s\n", net_addr_to_string(targets[i].addr)); */ + + g_targets[i].state = QUERY_TARGET_QUERIED; + g_targets[i].query_time = now; + ++g_targets[i].query_attempts; + + g_last_query_time = now; +} + +/* Time out servers that have been queried and not responded. */ + +static void check_target_timeouts(void) +{ + unsigned int i; + unsigned int now; + + now = i_get_time_ms(); + + for (i = 0; i < g_num_targets; ++i) + { + /* printf("target %i: state %i, queries %i, query time %i\n", + * i, targets[i].state, targets[i].query_attempts, + * now - targets[i].query_time); + */ + + /* We declare a target to be "no response" when we've sent + * multiple query packets to it (QUERY_MAX_ATTEMPTS) and + * received no response to any of them. + */ + + if (g_targets[i].state == QUERY_TARGET_QUERIED && + g_targets[i].query_attempts >= QUERY_MAX_ATTEMPTS && + now - g_targets[i].query_time > QUERY_TIMEOUT_SECS * 1000) + { + g_targets[i].state = QUERY_TARGET_NO_RESPONSE; + + if (g_targets[i].type == QUERY_TARGET_MASTER) + { + fprintf(stderr, "net_master_query: no response " + "from master server.\n"); + } + } + } +} + +/* If all targets have responded or timed out, returns true. */ + +static boolean all_targets_done(void) +{ + unsigned int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].state != QUERY_TARGET_RESPONDED && + g_targets[i].state != QUERY_TARGET_NO_RESPONSE) + { + return false; + } + } + + return true; +} + +/* Stop the query loop */ + +static void net_query_exit_loop(void) +{ + g_query_loop_running = false; +} + +/* Loop waiting for responses. + * The specified callback is invoked when a new server responds. + */ + +static void net_query_query_loop(net_query_callback_t callback, + void *user_data) +{ + g_query_loop_running = true; + + while (g_query_loop_running && net_query_poll(callback, user_data)) + { + /* Don't thrash the CPU */ + + usleep(1000); + } +} + +static void net_query_init(void) +{ + if (g_query_context == NULL) + { + g_query_context = net_new_context(); + net_add_module(g_query_context, &net_sdl_module); + net_sdl_module.init_client(); + } + + free(g_targets); + g_targets = NULL; + g_num_targets = 0; + + g_printed_header = false; +} + +/* Callback that exits the query loop when the first server is found. */ + +static void net_query_exit_callback(net_addr_t *addr, net_querydata_t *data, + unsigned int ping_time, void *user_data) +{ + net_query_exit_loop(); +} + +/* Search the targets list and find a target that has responded. + * If none have responded, returns NULL. + */ + +static query_target_t *find_first_responder(void) +{ + unsigned int i; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].type == QUERY_TARGET_SERVER && + g_targets[i].state == QUERY_TARGET_RESPONDED) + { + return &g_targets[i]; + } + } + + return NULL; +} + +/* Return a count of the number of responses. */ + +static int get_num_responses(void) +{ + unsigned int i; + int result; + + result = 0; + + for (i = 0; i < g_num_targets; ++i) + { + if (g_targets[i].type == QUERY_TARGET_SERVER && + g_targets[i].state == QUERY_TARGET_RESPONDED) + { + ++result; + } + } + + return result; +} + +static void formatted_printf(int wide, const char *s, ...) +{ + va_list args; + int i; + + va_start(args, s); + i = vprintf(s, args); + va_end(args); + + while (i < wide) + { + putchar(' '); + ++i; + } +} + +static const char *game_description(game_mode_t mode, gamemission_t mission) +{ + switch (mission) + { + case doom: + if (mode == shareware) + return "swdoom"; + else if (mode == registered) + return "regdoom"; + else if (mode == retail) + return "ultdoom"; + else + return "doom"; + case doom2: + return "doom2"; + case pack_tnt: + return "tnt"; + case pack_plut: + return "plutonia"; + case pack_chex: + return "chex"; + case pack_hacx: + return "hacx"; + case heretic: + return "heretic"; + case hexen: + return "hexen"; + case strife: + return "strife"; + default: + return "?"; + } +} + +static void print_header(void) +{ + int i; + + putchar('\n'); + formatted_printf(5, "Ping"); + formatted_printf(18, "Address"); + formatted_printf(8, "Players"); + puts("Description"); + + for (i = 0; i < 70; ++i) + putchar('='); + putchar('\n'); +} + +/* Callback function that just prints information in a table. */ + +static void net_query_print_callback(net_addr_t *addr, net_querydata_t *data, + unsigned int ping_time, void *user_data) +{ + /* If this is the first server, print the header. */ + + if (!g_printed_header) + { + print_header(); + g_printed_header = true; + } + + formatted_printf(5, "%4i", ping_time); + formatted_printf(22, "%s", net_addr_to_string(addr)); + formatted_printf(4, "%i/%i ", data->num_players, data->max_players); + + if (data->gamemode != indetermined) + { + printf("(%s) ", game_description(data->gamemode, data->gamemission)); + } + + if (data->server_state) + { + printf("(game running) "); + } + + printf("%s\n", data->description); +} + +#if 0 /* UNUSED */ + +/* Block until a packet of the given type is received from the given + * address. + */ + +static net_packet_t *block_for_packet(net_addr_t *addr, + unsigned int packet_type, + unsigned int timeout_ms) +{ + net_packet_t *packet; + net_addr_t *packet_src; + unsigned int read_packet_type; + unsigned int start_time; + + start_time = i_get_time_ms(); + + while (i_get_time_ms() < start_time + timeout_ms) + { + if (!net_recv_packet(g_query_context, &packet_src, &packet)) + { + usleep(20000); + continue; + } + + /* Caller doesn't need additional reference. */ + + net_release_address(packet_src); + + if (packet_src == addr && net_read_int16(packet, &read_packet_type) && + packet_type == read_packet_type) + { + return packet; + } + + net_free_packet(packet); + } + + /* Timeout - no response. */ + + return NULL; +} + +/* Query master server for secure demo start seed value. */ + +static boolean net_start_secure_demo(prng_seed_t seed) +{ + net_packet_t *request, *response; + net_addr_t *master_addr; + char *signature; + boolean result; + + net_query_init(); + master_addr = net_query_resolve_master(g_query_context); + + /* Send request packet to master server. */ + + request = net_new_packet(10); + net_write_int16(request, NET_MASTER_PACKET_TYPE_SIGN_START); + net_send_packet(master_addr, request); + net_free_packet(request); + + /* Block for response and read contents. + * The signed start message will be saved for later. + */ + + response = block_for_packet(master_addr, + NET_MASTER_PACKET_TYPE_SIGN_START_RESPONSE, + SIGNATURE_TIMEOUT_SECS * 1000); + + result = false; + + if (response != NULL) + { + if (net_read_prng_seed(response, seed)) + { + signature = net_read_string(response); + + if (signature != NULL) + { + g_securedemo_start_message = m_string_duplicate(signature); + result = true; + } + } + + net_free_packet(response); + } + + return result; +} + +/* Query master server for secure demo end signature. */ + +static char *net_end_secure_demo(sha1_digest_t demo_hash) +{ + net_packet_t *request, *response; + net_addr_t *master_addr; + char *signature; + + master_addr = net_query_resolve_master(g_query_context); + + /* Construct end request and send to master server. */ + + request = net_new_packet(10); + net_write_int16(request, NET_MASTER_PACKET_TYPE_SIGN_END); + net_write_sha1_sum(request, demo_hash); + net_write_string(request, g_securedemo_start_message); + net_send_packet(master_addr, request); + net_free_packet(request); + + /* Block for response. The response packet simply contains a string + * with the ASCII signature. + */ + + response = + block_for_packet(master_addr, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, + SIGNATURE_TIMEOUT_SECS * 1000); + + if (response == NULL) + { + return NULL; + } + + signature = net_read_string(response); + + net_free_packet(response); + + return signature; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Resolve the master server address. */ + +net_addr_t *net_query_resolve_master(net_context_t *context) +{ + net_addr_t *addr; + + addr = net_resolve_address(context, MASTER_SERVER_ADDRESS); + + if (addr == NULL) + { + fprintf(stderr, + "Warning: Failed to resolve address " + "for master server: %s\n", + MASTER_SERVER_ADDRESS); + } + + return addr; +} + +/* Send a registration packet to the master server to register + * ourselves with the global list. + */ + +void net_query_add_to_master(net_addr_t *master_addr) +{ + net_packet_t *packet; + + packet = net_new_packet(10); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_ADD); + net_send_packet(master_addr, packet); + net_free_packet(packet); +} + +/* Process a packet received from the master server. */ + +void net_query_add_response(net_packet_t *packet) +{ + unsigned int result; + + if (!net_read_int16(packet, &result)) + { + return; + } + + if (result != 0) + { + /* Only show the message once. */ + + if (!g_registered_with_master) + { + printf("Registered with master server at %s\n", + MASTER_SERVER_ADDRESS); + g_registered_with_master = true; + } + } + else + { + /* Always show rejections. */ + + printf("Failed to register with master server at %s\n", + MASTER_SERVER_ADDRESS); + } + + g_got_master_response = true; +} + +boolean net_query_check_added_to_master(boolean *result) +{ + /* Got response from master yet? */ + + if (!g_got_master_response) + { + return false; + } + + *result = g_registered_with_master; + return true; +} + +/* Send a hole punch request to the master server for the server at the + * given address. + */ + +void net_request_hole_punch(net_context_t *context, net_addr_t *addr) +{ + net_addr_t *master_addr; + net_packet_t *packet; + + master_addr = net_query_resolve_master(context); + if (master_addr == NULL) + { + return; + } + + packet = net_new_packet(32); + net_write_int16(packet, NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH); + net_write_string(packet, net_addr_to_string(addr)); + net_send_packet(master_addr, packet); + + net_free_packet(packet); + net_release_address(master_addr); +} + +/* Polling function, invoked periodically to send queries and + * interpret new responses received from remote servers. + * Returns zero when the query sequence has completed and all targets + * have returned responses or timed out. + */ + +int net_query_poll(net_query_callback_t callback, void *user_data) +{ + check_target_timeouts(); + + /* Send a query. This will only send a single query at once. */ + + send_one_query(); + + /* Check for a response */ + + net_query_get_response(callback, user_data); + + return !all_targets_done(); +} + +int net_start_lan_query(void) +{ + query_target_t *target; + + net_query_init(); + + /* Add a broadcast target to the list. */ + + target = get_target_for_addr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + return 1; +} + +int net_start_master_query(void) +{ + net_addr_t *master; + query_target_t *target; + + net_query_init(); + + /* Resolve master address and add to targets list. */ + + master = net_query_resolve_master(g_query_context); + + if (master == NULL) + { + return 0; + } + + target = get_target_for_addr(master, true); + target->type = QUERY_TARGET_MASTER; + net_release_address(master); + + return 1; +} + +void net_lan_query(void) +{ + if (net_start_lan_query()) + { + printf("\nSearching for servers on local LAN ...\n"); + + net_query_query_loop(net_query_print_callback, NULL); + + printf("\n%i server(s) found.\n", get_num_responses()); + free_targets(); + } +} + +void net_master_query(void) +{ + if (net_start_master_query()) + { + printf("\nSearching for servers on Internet ...\n"); + + net_query_query_loop(net_query_print_callback, NULL); + + printf("\n%i server(s) found.\n", get_num_responses()); + free_targets(); + } +} + +void net_query_address(const char *addr_str) +{ + net_addr_t *addr; + query_target_t *target; + + net_query_init(); + + addr = net_resolve_address(g_query_context, addr_str); + + if (addr == NULL) + { + i_error("net_query_address: Host '%s' not found!", addr_str); + } + + /* Add the address to the list of targets. */ + + target = get_target_for_addr(addr, true); + + printf("\nQuerying '%s'...\n", addr_str); + + /* Run query loop. */ + + net_query_query_loop(net_query_exit_callback, NULL); + + /* Check if the target responded. */ + + if (target->state == QUERY_TARGET_RESPONDED) + { + net_query_print_callback(addr, &target->data, target->ping_time, NULL); + net_release_address(addr); + free_targets(); + } + else + { + i_error("No response from '%s'", addr_str); + } +} + +net_addr_t *net_find_lan_server(void) +{ + query_target_t *target; + query_target_t *responder; + net_addr_t *result; + + net_query_init(); + + /* Add a broadcast target to the list. */ + + target = get_target_for_addr(NULL, true); + target->type = QUERY_TARGET_BROADCAST; + + /* Run the query loop, and stop at the first target found. */ + + net_query_query_loop(net_query_exit_callback, NULL); + + responder = find_first_responder(); + + if (responder != NULL) + { + result = responder->addr; + net_reference_address(result); + } + else + { + result = NULL; + } + + free_targets(); + return result; +} diff --git a/games/NXDoom/src/net_query.h b/games/NXDoom/src/net_query.h new file mode 100644 index 000000000..70dacfecd --- /dev/null +++ b/games/NXDoom/src/net_query.h @@ -0,0 +1,61 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_query.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Querying servers to find their current status. + * + ****************************************************************************/ + +#ifndef NET_QUERY_H +#define NET_QUERY_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*net_query_callback_t)(net_addr_t *addr, + net_querydata_t *querydata, + unsigned int ping_time, + void *user_data); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern int net_start_lan_query(void); +extern int net_start_master_query(void); + +extern void net_lan_query(void); +extern void net_master_query(void); +extern void net_query_address(const char *addr); +extern net_addr_t *net_find_lan_server(void); + +extern int net_query_poll(net_query_callback_t callback, void *user_data); + +extern net_addr_t *net_query_resolve_master(net_context_t *context); +extern void net_query_add_to_master(net_addr_t *master_addr); +extern boolean net_query_check_added_to_master(boolean *result); +extern void net_query_add_response(net_packet_t *packet); +extern void net_request_hole_punch(net_context_t *context, net_addr_t *addr); + +#endif /* NET_QUERY_H */ diff --git a/games/NXDoom/src/net_sdl.c b/games/NXDoom/src/net_sdl.c new file mode 100644 index 000000000..b57f4d206 --- /dev/null +++ b/games/NXDoom/src/net_sdl.c @@ -0,0 +1,102 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_sdl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Networking module which uses SDL_net + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" +#include "m_misc.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_packet.h" +#include "net_sdl.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static boolean net_null_init_client(void); +static boolean net_null_init_server(void); +static void net_null_send_packet(net_addr_t *addr, net_packet_t *packet); +static boolean net_null_recv_packet(net_addr_t **addr, + net_packet_t **packet); +static void net_null_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len); +static void net_null_free_address(net_addr_t *addr); +static net_addr_t *net_null_resolve_address(const char *address); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +net_module_t net_sdl_module = +{ + net_null_init_client, net_null_init_server, net_null_send_packet, + net_null_recv_packet, net_null_addr_to_string, net_null_free_address, + net_null_resolve_address, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static net_addr_t *net_null_resolve_address(const char *address) +{ + return NULL; +} + +static boolean net_null_init_client(void) +{ + return false; +} + +static boolean net_null_init_server(void) +{ + return false; +} + +static void net_null_send_packet(net_addr_t *addr, net_packet_t *packet) +{ +} + +static boolean net_null_recv_packet(net_addr_t **addr, + net_packet_t **packet) +{ + return false; +} + +static void net_null_addr_to_string(net_addr_t *addr, char *buffer, + int buffer_len) +{ +} + +static void net_null_free_address(net_addr_t *addr) +{ +} diff --git a/games/NXDoom/src/net_sdl.h b/games/NXDoom/src/net_sdl.h new file mode 100644 index 000000000..6f972fd1d --- /dev/null +++ b/games/NXDoom/src/net_sdl.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_sdl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Networking module which uses SDL_net + * + * + ****************************************************************************/ + +#ifndef NET_SDL_H +#define NET_SDL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "net_defs.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern net_module_t net_sdl_module; + +#endif /* NET_SDL_H */ diff --git a/games/NXDoom/src/net_server.c b/games/NXDoom/src/net_server.c new file mode 100644 index 000000000..61b5246f1 --- /dev/null +++ b/games/NXDoom/src/net_server.c @@ -0,0 +1,2210 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_server.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Network server code + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "config.h" + +#include "d_mode.h" +#include "doomtype.h" +#include "i_system.h" +#include "i_timer.h" +#include "m_argv.h" +#include "m_misc.h" + +#include "net_client.h" +#include "net_common.h" +#include "net_defs.h" +#include "net_io.h" +#include "net_loop.h" +#include "net_packet.h" +#include "net_query.h" +#include "net_sdl.h" +#include "net_server.h" +#include "net_structrw.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* How often to refresh our registration with the master server. */ + +#define MASTER_REFRESH_PERIOD 30 /* twice per minute */ + +/* How often to re-resolve the address of the master server? */ + +#define MASTER_RESOLVE_PERIOD 8 * 60 * 60 /* 8 hours */ + +#define net_sv_expand_ticnum(b) net_expand_tic_num(recvwindow_start, (b)) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + /* waiting for the game to be "launched" (key player to press the start + * button) + */ + + SERVER_WAITING_LAUNCH, + + /* game has been launched, we are waiting for all players to be ready + * so the game can start. + */ + + SERVER_WAITING_START, + + /* in a game */ + + SERVER_IN_GAME, +} net_server_state_t; + +typedef struct +{ + boolean active; + int player_number; + net_addr_t *addr; + net_connection_t connection; + int last_send_time; + char *name; + + /* If true, the client has sent the NET_PACKET_TYPE_GAMESTART + * message indicating that it is ready for the game to start. + */ + + boolean ready; + + /* Time that this client connected to the server. + * This is used to determine the controller (oldest client). + */ + + unsigned int connect_time; + + /* Last time new gamedata was received from this client */ + + int last_gamedata_time; + + /* recording a demo without -longtics */ + + boolean recording_lowres; + + /* send queue: items to send to the client + * this is a circular buffer + */ + + int sendseq; + net_full_ticcmd_t sendqueue[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + /* Latest acknowledged by the client */ + + unsigned int acknowledged; + + /* Value of max_players specified by the client on connect. */ + + int max_players; + + /* Observer: receives data but does not participate in the game. */ + + boolean drone; + + /* SHA1 hash sums of the client's WAD directory and dehacked data */ + + sha1_digest_t wad_sha1sum; + sha1_digest_t deh_sha1sum; + + /* Is this client is playing with the Freedoom IWAD? */ + + unsigned int is_freedoom; + + /* Player class (for Hexen) */ + + int player_class; +} net_client_t; + +/* structure used for the recv window */ + +typedef struct +{ + /* Whether this tic has been received yet */ + + boolean active; + + /* Latency value received from the client */ + + signed int latency; + + /* Last time we sent a resend request for this tic */ + + unsigned int resend_time; + + /* Tic data itself */ + + net_ticdiff_t diff; +} net_client_recv_t; + +typedef enum +{ + RANGE_LOCALHOST, /* Same process or 127.x */ + RANGE_PRIVATE, /* RFC 1918 */ + RANGE_PUBLIC, /* The public Internet */ +} ip_range_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static net_server_state_t server_state; +static boolean server_initialized = false; +static net_client_t clients[MAXNETNODES]; +static net_client_t *sv_players[CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; +static net_context_t *server_context; +static unsigned int sv_gamemode; +static unsigned int sv_gamemission; +static net_gamesettings_t sv_settings; + +/* For registration with master server: */ + +static net_addr_t *master_server = NULL; +static unsigned int master_refresh_time; +static unsigned int master_resolve_time; + +/* receive window */ + +static unsigned int recvwindow_start; +static net_client_recv_t recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS] + [CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS]; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void net_sv_send_console_message(net_client_t *client, const char *s, + ...) PRINTF_ATTR(2, 3); +static void net_sv_broadcast_message(const char *s, ...) PRINTF_ATTR(1, 2); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void net_sv_disconnect_client(net_client_t *client) +{ + if (client->active) + { + net_conn_disconnect(&client->connection); + } +} + +static boolean client_connected(net_client_t *client) +{ + /* Check that the client is properly connected: ie. not in the + * process of connecting or disconnecting + */ + + return client->active && + client->connection.state == NET_CONN_STATE_CONNECTED; +} + +/* Send a message to be displayed on a client's console */ + +static void net_sv_send_console_message(net_client_t *client, const char *s, + ...) +{ + char buf[1024]; + va_list args; + net_packet_t *packet; + + va_start(args, s); + vsnprintf(buf, sizeof(buf), s, args); + va_end(args); + + packet = net_conn_new_reliable(&client->connection, + NET_PACKET_TYPE_CONSOLE_MESSAGE); + + net_write_string(packet, buf); +} + +/* Send a message to all clients */ + +static void net_sv_broadcast_message(const char *s, ...) +{ + char buf[1024]; + va_list args; + int i; + + va_start(args, s); + vsnprintf(buf, sizeof(buf), s, args); + va_end(args); + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + net_sv_send_console_message(&clients[i], "%s", buf); + } + } + + printf("%s\n", buf); +} + +/* Assign player numbers to connected clients */ + +static void net_sv_assign_players(void) +{ + int i; + int pl; + + pl = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + if (!clients[i].drone) + { + sv_players[pl] = &clients[i]; + sv_players[pl]->player_number = pl; + ++pl; + } + else + { + clients[i].player_number = -1; + } + } + } + + for (; pl < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++pl) + { + sv_players[pl] = NULL; + } +} + +/* Returns the number of players currently connected. */ + +static int net_sv_num_players(void) +{ + int i; + int result; + + result = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && client_connected(sv_players[i])) + { + result += 1; + } + } + + return result; +} + +/* Returns the number of players ready to start the game. */ + +static int net_sv_num_ready_players(void) +{ + int result = 0; + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && !clients[i].drone && + clients[i].ready) + { + ++result; + } + } + + return result; +} + +/* Returns the maximum number of players that can play. */ + +static int net_sv_max_players(void) +{ + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + return clients[i].max_players; + } + } + + return CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; +} + +/* Returns the number of drones currently connected. */ + +static int net_sv_num_drones(void) +{ + int i; + int result; + + result = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && clients[i].drone) + { + result += 1; + } + } + + return result; +} + +/* returns the number of clients connected */ + +static int net_sv_num_clients(void) +{ + int count; + int i; + + count = 0; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + ++count; + } + } + + return count; +} + +/* returns a pointer to the client which controls the server */ + +static net_client_t *net_sv_controller(void) +{ + net_client_t *best; + int i; + + /* Find the oldest client (first to connect). */ + + best = NULL; + + for (i = 0; i < MAXNETNODES; ++i) + { + /* Can't be controller? */ + + if (!client_connected(&clients[i]) || clients[i].drone) + { + continue; + } + + if (best == NULL || clients[i].connect_time < best->connect_time) + { + best = &clients[i]; + } + } + + return best; +} + +static ip_range_t client_address_range(const char *addr) +{ + if (!strcmp(addr, "local client") || m_string_starts_with(addr, "127.")) + { + return RANGE_LOCALHOST; + } + + if (m_string_starts_with(addr, "10.") || + m_string_starts_with(addr, "192.168.")) + { + return RANGE_PRIVATE; + } + + return RANGE_PUBLIC; +} + +static void net_sv_send_waiting_data(net_client_t *client) +{ + net_waitdata_t wait_data; + net_packet_t *packet; + net_client_t *controller; + ip_range_t client_range, player_range; + const char *addr; + int i; + + net_sv_assign_players(); + + controller = net_sv_controller(); + + wait_data.num_players = net_sv_num_players(); + wait_data.num_drones = net_sv_num_drones(); + wait_data.ready_players = net_sv_num_ready_players(); + wait_data.max_players = net_sv_max_players(); + wait_data.is_controller = (client == controller); + wait_data.consoleplayer = client->player_number; + + /* Send the WAD and dehacked checksums of the controlling client. + * If no controller found (?), send the details that the client + * is expecting anyway. + */ + + if (controller == NULL) + { + controller = client; + } + + memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum, + sizeof(sha1_digest_t)); + memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum, + sizeof(sha1_digest_t)); + wait_data.is_freedoom = controller->is_freedoom; + + /* We only send IP addresses to locally-connected clients (including + * the 127.* loopback range): + */ + + addr = net_addr_to_string(client->connection.addr); + client_range = client_address_range(addr); + + /* set name and address of each player: */ + + for (i = 0; i < wait_data.num_players; ++i) + { + m_str_copy(wait_data.player_names[i], sv_players[i]->name, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + + /* For privacy, only local clients or those on a LAN get to see + * addresses. Public clients only get to see their own address, + * though we do reveal localhost addresses since they're harmless, + * and we do reveal when a client is connected via LAN. + */ + + addr = net_addr_to_string(sv_players[i]->addr); + player_range = client_address_range(addr); + if (client_range == RANGE_LOCALHOST || client_range == RANGE_PRIVATE || + i == wait_data.consoleplayer || player_range == RANGE_LOCALHOST) + { + m_str_copy(wait_data.player_addrs[i], addr, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + } + else if (player_range == RANGE_PRIVATE) + { + snprintf(wait_data.player_addrs[i], + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME, + "[LAN player]"); + } + else + { + snprintf(wait_data.player_addrs[i], + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME, + "[address hidden]"); + } + } + + /* Construct packet: */ + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_WAITING_DATA); + net_write_wait_data(packet, &wait_data); + + /* Send packet to client and free */ + + net_conn_send_packet(&client->connection, packet); + net_free_packet(packet); +} + +/* Find the latest tic which has been acknowledged as received by + * all clients. + */ + +static unsigned int net_sv_latest_acknowledged(void) +{ + unsigned int lowtic = UINT_MAX; + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i])) + { + if (clients[i].acknowledged < lowtic) + { + lowtic = clients[i].acknowledged; + } + } + } + + return lowtic; +} + +/* Possibly advance the recv window if all connected clients have + * used the data in the window + */ + +static void net_sv_advance_window(void) +{ + unsigned int lowtic; + int i; + + if (net_sv_num_players() <= 0) + { + return; + } + + lowtic = net_sv_latest_acknowledged(); + + /* Advance the recv window until it catches up with lowtic */ + + while (recvwindow_start < lowtic) + { + boolean should_advance; + + /* Check we have tics from all players for first tic in + * the recv window + */ + + should_advance = true; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] == NULL || !client_connected(sv_players[i])) + { + continue; + } + + if (!recvwindow[0][i].active) + { + should_advance = false; + break; + } + } + + if (!should_advance) + { + /* The first tic is not complete: ie. we have not + * received tics from all connected players. This can + * happen if only one player is in the game. + */ + + break; + } + + /* Advance the window */ + + memmove(recvwindow, recvwindow + 1, + sizeof(*recvwindow) * + (CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1)); + memset(&recvwindow[CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1], + 0, sizeof(*recvwindow)); + ++recvwindow_start; + net_log_info("server: advanced receive window to %d", + recvwindow_start); + } +} + +/* Given an address, find the corresponding client */ + +static net_client_t *net_sv_find_client(net_addr_t *addr) +{ + int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active && clients[i].addr == addr) + { + /* found the client */ + + return &clients[i]; + } + } + + return NULL; +} + +/* send a rejection packet to a client */ + +static void net_sv_send_reject(net_addr_t *addr, const char *msg) +{ + net_packet_t *packet; + + net_log_info("server: sending reject to %s", net_addr_to_string(addr)); + + packet = net_new_packet(10); + net_write_int16(packet, NET_PACKET_TYPE_REJECTED); + net_write_string(packet, msg); + net_send_packet(addr, packet); + net_free_packet(packet); +} + +static void net_sv_init_new_client(net_client_t *client, net_addr_t *addr, + net_protocol_t protocol) +{ + client->active = true; + client->connect_time = i_get_time_ms(); + net_conn_init_server(&client->connection, addr, protocol); + client->addr = addr; + net_reference_address(addr); + client->last_send_time = -1; + + /* init the ticcmd send queue */ + + client->sendseq = 0; + client->acknowledged = 0; + client->drone = false; + client->ready = false; + + client->last_gamedata_time = 0; + + memset(client->sendqueue, 0xff, sizeof(client->sendqueue)); + + net_log_info("server: initialized new client from %s", + net_addr_to_string(addr)); +} + +/* parse a SYN from a client(initiating a connection) */ + +static void net_sv_parse_syn(net_packet_t *packet, net_client_t *client, + net_addr_t *addr) +{ + unsigned int magic; + net_connect_data_t data; + net_packet_t *reply; + net_protocol_t protocol; + char *player_name; + char *client_version; + int num_players; + int i; + + net_log_info("server: processing SYN packet"); + + /* Read the magic number and check it is the expected one. */ + + if (!net_read_int32(packet, &magic)) + { + net_log_err("server: no magic number for SYN"); + return; + } + + switch (magic) + { + case NET_MAGIC_NUMBER: + break; + + case NET_OLD_MAGIC_NUMBER: + net_log_err("server: client using old magic number: %d", magic); + net_sv_send_reject( + addr, + "You are using an old client version that is not supported by " + "this server. This server is running " PACKAGE_STRING "."); + return; + + default: + net_log_err("server: wrong magic number: %d", magic); + return; + } + + /* Read the client version string. We actually now only use this when + * sending a reject message, as we only reject if we can't negotiate a + * common protocol (below). + */ + + client_version = net_read_string(packet); + if (client_version == NULL) + { + net_log_err("server: no version from client"); + return; + } + + /* Read the client's list of accepted protocols. Net play between forks + * of Chocolate Doom is accepted provided that they can negotiate a + * common accepted protocol. + */ + + protocol = net_read_protocol_list(packet); + if (protocol == NET_PROTOCOL_UNKNOWN) + { + char reject_msg[256]; + + snprintf(reject_msg, sizeof(reject_msg), + "Version mismatch: server version is: " PACKAGE_STRING "; " + "client is: %s. No common compatible protocol could be " + "negotiated.", + client_version); + net_sv_send_reject(addr, reject_msg); + net_log_err("server: no common protocol"); + return; + } + + /* Read connect data, and check that the game mode/mission are valid + * and the max_players value is in a sensible range. + */ + + if (!net_read_connect_data(packet, &data)) + { + net_log_err("server: failed to read connect data"); + return; + } + + if (!d_valid_game_mode(data.gamemission, data.gamemode) || + data.max_players > CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS) + { + net_log_err("server: invalid connect data, max_players=%d, " + "gamemission=%d, gamemode=%d", + data.max_players, data.gamemission, data.gamemode); + return; + } + + /* Read the player's name */ + + player_name = net_read_string(packet); + if (player_name == NULL) + { + net_log_err("server: failed to read player name"); + return; + } + + /* At this point we have received a valid SYN. */ + + /* Not accepting new connections? */ + + if (server_state != SERVER_WAITING_LAUNCH) + { + net_log_err("server: not in waiting launch state, server_state=%d", + server_state); + net_sv_send_reject(addr, + "Server is not currently accepting connections"); + return; + } + + /* Before accepting a new client, check that there is a slot free. */ + + net_sv_assign_players(); + num_players = net_sv_num_players(); + + if ((!data.drone && num_players >= net_sv_max_players()) || + net_sv_num_clients() >= MAXNETNODES) + { + net_log_info("server: no more players, num_players=%d, max=%d", + num_players, net_sv_max_players()); + net_sv_send_reject(addr, "Server is full!"); + return; + } + + /* TODO: Add server option to allow rejecting clients which set + * lowres_turn. This is potentially desirable as the presence of such + * clients affects turning resolution. + */ + + /* Adopt the game mode and mission of the first connecting client: */ + + if (num_players == 0 && !data.drone) + { + sv_gamemode = data.gamemode; + sv_gamemission = data.gamemission; + net_log_info("server: new game, mode=%d, mission=%d", sv_gamemode, + sv_gamemission); + } + + /* Check the connecting client is playing the same game as all + * the other clients + */ + + if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission) + { + char msg[128]; + net_log_warn("server: wrong mode/mission, %d != %d || %d != %d", + data.gamemode, sv_gamemode, data.gamemission, + sv_gamemission); + snprintf(msg, sizeof(msg), + "Game mismatch: server is %s (%s), client is %s (%s)", + d_game_mission_string(sv_gamemission), + d_game_mode_string(sv_gamemode), + d_game_mission_string(data.gamemission), + d_game_mode_string(data.gamemode)); + + net_sv_send_reject(addr, msg); + return; + } + + /* Allocate a client slot if there isn't one already */ + + if (client == NULL) + { + /* find a slot, or return if none found */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!clients[i].active) + { + client = &clients[i]; + break; + } + } + + if (client == NULL) + { + return; + } + } + else + { + /* If this is a recently-disconnected client, deactivate + * to allow immediate reconnection + */ + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED) + { + client->active = false; + } + } + + /* Client already connected? */ + + if (client->active) + { + net_log_warn("server: client is already initialized (duplicate SYN?)"); + return; + } + + /* Activate, initialize connection */ + + net_sv_init_new_client(client, addr, protocol); + + /* Save the SHA1 checksums and other details. */ + + memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t)); + memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t)); + client->is_freedoom = data.is_freedoom; + client->max_players = data.max_players; + client->name = m_string_duplicate(player_name); + client->recording_lowres = data.lowres_turn; + client->drone = data.drone; + client->player_class = data.player_class; + + /* Send a reply back to the client, indicating a successful connection + * and specifying the protocol that will be used for communications. + */ + + reply = net_conn_new_reliable(&client->connection, NET_PACKET_TYPE_SYN); + net_write_string(reply, PACKAGE_STRING); + net_write_protocol(reply, protocol); +} + +/* Parse a launch packet. This is sent by the key player when the "start" + * button is pressed, and causes the startup process to continue. + */ + +static void net_sv_parse_launch(net_packet_t *packet, net_client_t *client) +{ + net_packet_t *launchpacket; + int num_players; + unsigned int i; + + net_log_info("server: processing launch packet"); + + /* Only the controller can launch the game. */ + + if (client != net_sv_controller()) + { + net_log_err("server: this client isn't the controller, %p != %p", + client, net_sv_controller()); + return; + } + + /* Can only launch when we are in the waiting state. */ + + if (server_state != SERVER_WAITING_LAUNCH) + { + net_log_err("server: not in waiting launch state, state=%d", + server_state); + return; + } + + /* Forward launch on to all clients. */ + + net_log_info("server: sending launch to all clients"); + net_sv_assign_players(); + num_players = net_sv_num_players(); + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!client_connected(&clients[i])) continue; + + launchpacket = net_conn_new_reliable(&clients[i].connection, + NET_PACKET_TYPE_LAUNCH); + net_write_int8(launchpacket, num_players); + } + + /* Now in launch state. */ + + server_state = SERVER_WAITING_START; +} + +/* Transition to the in-game state and send all players the start game + * message. Invoked once all players have indicated they are ready to + * start the game. + */ + +static void start_game(void) +{ + net_packet_t *startpacket; + unsigned int i; + int nowtime; + + /* Assign player numbers */ + + net_sv_assign_players(); + + /* Check if anyone is recording a demo and set lowres_turn if so. */ + + sv_settings.lowres_turn = false; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && sv_players[i]->recording_lowres) + { + sv_settings.lowres_turn = true; + } + } + + sv_settings.num_players = net_sv_num_players(); + + /* Copy player classes: */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL) + { + sv_settings.player_classes[i] = sv_players[i]->player_class; + } + else + { + sv_settings.player_classes[i] = 0; + } + } + + nowtime = i_get_time_ms(); + + /* Send start packets to each connected node */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (!client_connected(&clients[i])) continue; + + clients[i].last_gamedata_time = nowtime; + + startpacket = net_conn_new_reliable(&clients[i].connection, + NET_PACKET_TYPE_GAMESTART); + + sv_settings.consoleplayer = clients[i].player_number; + + net_write_settings(startpacket, &sv_settings); + } + + /* Change server state */ + + net_log_info("server: beginning game state"); + server_state = SERVER_IN_GAME; + + memset(recvwindow, 0, sizeof(recvwindow)); + recvwindow_start = 0; +} + +/* Returns true when all nodes have indicated readiness to start the game. */ + +static boolean all_nodes_ready(void) +{ + unsigned int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && !clients[i].ready) + { + return false; + } + } + + return true; +} + +/* Check if the game should start, and if so, start it. */ + +static void check_start_game(void) +{ + if (!all_nodes_ready()) + { + net_log_warn("server: not all clients ready to start yet"); + return; + } + + net_log_info("server: all clients ready, starting game"); + start_game(); +} + +/* Send waiting data with current status to all nodes that are ready to + * start the game. + */ + +static void send_all_waiting_data(void) +{ + unsigned int i; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (client_connected(&clients[i]) && clients[i].ready) + { + net_sv_send_waiting_data(&clients[i]); + } + } +} + +/* Parse a game start packet */ + +static void net_sv_parse_game_start(net_packet_t *packet, + net_client_t *client) +{ + net_gamesettings_t settings; + + net_log_info("server: processing game start packet"); + + /* Can only start a game if we are in the waiting start state. */ + + if (server_state != SERVER_WAITING_START) + { + net_log_info( + "server: error: not in waiting start state, server_state=%d", + server_state); + return; + } + + if (client == net_sv_controller()) + { + if (!net_read_settings(packet, &settings)) + { + /* Malformed packet */ + + net_log_err("server: no settings from controller"); + return; + } + + /* Check the game settings are valid */ + + if (!net_valid_game_settings(sv_gamemode, sv_gamemission, &settings)) + { + net_log_err("server: invalid game settings"); + return; + } + + sv_settings = settings; + } + + client->ready = true; + + check_start_game(); + + /* Update all ready clients with the current state (number of players + * ready, etc.). This is used by games that show startup progress + * (eg. Hexen's spinal loading) + */ + + send_all_waiting_data(); +} + +/* Send a resend request to a client */ + +static void net_sv_send_resend_request(net_client_t *client, int start, + int end) +{ + net_packet_t *packet; + net_client_recv_t *recvobj; + int i; + unsigned int nowtime; + int index; + + net_log_info("server: send resend to %s for tics %d-%d", + net_addr_to_string(client->addr), start, end); + + packet = net_new_packet(20); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND); + net_write_int32(packet, start); + net_write_int8(packet, end - start + 1); + + net_conn_send_packet(&client->connection, packet); + net_free_packet(packet); + + /* Store the time we send the resend request */ + + nowtime = i_get_time_ms(); + + for (i = start; i <= end; ++i) + { + index = i - recvwindow_start; + + if (index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Outside the range */ + + continue; + } + + recvobj = &recvwindow[index][client->player_number]; + + recvobj->resend_time = nowtime; + } +} + +/* Check for expired resend requests */ + +static void net_sv_check_resends(net_client_t *client) +{ + int i; + int player; + int resend_start; + int resend_end; + unsigned int nowtime; + + nowtime = i_get_time_ms(); + + player = client->player_number; + resend_start = -1; + resend_end = -1; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + net_client_recv_t *recvobj; + boolean need_resend; + + recvobj = &recvwindow[i][player]; + + /* if need_resend is true, this tic needs another retransmit + * request (300ms timeout) + */ + + need_resend = !recvobj->active && recvobj->resend_time != 0 && + nowtime > recvobj->resend_time + 300; + + if (need_resend) + { + /* Start a new run of resend tics? */ + + if (resend_start < 0) + { + resend_start = i; + } + + resend_end = i; + } + else if (resend_start >= 0) + { + /* End of a run of resend tics */ + + net_log_warn( + "server: resend request to %s timed out for %d-%d (%p)", + net_addr_to_string(client->addr), + recvwindow_start + resend_start, + recvwindow_start + resend_end, + &recvwindow[resend_start][player].resend_time); + + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end); + + resend_start = -1; + } + } + + if (resend_start >= 0) + { + net_log_warn("server: resend request to %s timed out for %d-%d (%p)", + net_addr_to_string(client->addr), + recvwindow_start + resend_start, + recvwindow_start + resend_end, + &recvwindow[resend_start][player].resend_time); + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end); + } +} + +/* Process game data from a client */ + +static void net_sv_parse_game_data(net_packet_t *packet, + net_client_t *client) +{ + net_client_recv_t *recvobj; + unsigned int seq; + unsigned int ackseq; + unsigned int num_tics; + unsigned int nowtime; + size_t i; + int player; + int resend_start; + int resend_end; + int index; + + if (server_state != SERVER_IN_GAME) + { + net_log_err("server: not in game state: server_state=%d", + server_state); + return; + } + + if (client->drone) + { + /* Drones do not contribute any game data. */ + + net_log_err("server: game data from a drone?"); + return; + } + + player = client->player_number; + + /* Read header */ + + if (!net_read_int8(packet, &ackseq) || !net_read_int8(packet, &seq) || + !net_read_int8(packet, &num_tics)) + { + net_log_err("server: failed to read header"); + return; + } + + net_log_info("server: got game data, seq=%d, num_tics=%d, ackseq=%d", seq, + num_tics, ackseq); + + /* Get the current time */ + + nowtime = i_get_time_ms(); + + /* Expand 8-bit values to the full sequence number */ + + ackseq = net_sv_expand_ticnum(ackseq); + seq = net_sv_expand_ticnum(seq); + + /* Sanity checks */ + + for (i = 0; i < num_tics; ++i) + { + net_ticdiff_t diff; + signed int latency; + + if (!net_read_sint16(packet, &latency) || + !net_read_ticcmd_diff(packet, &diff, sv_settings.lowres_turn)) + { + return; + } + + index = seq + i - recvwindow_start; + + if (index < 0 || index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + /* Not in range of the recv window */ + + continue; + } + + recvobj = &recvwindow[index][player]; + recvobj->active = true; + recvobj->diff = diff; + recvobj->latency = latency; + + client->last_gamedata_time = nowtime; + net_log_info("server: stored tic %zu for player %d", seq + i, player); + } + + /* Higher acknowledgement point? */ + + if (ackseq > client->acknowledged) + { + net_log_info("server: acknowledged up to %d", ackseq); + client->acknowledged = ackseq; + } + + /* Has this been received out of sequence, ie. have we not received + * all tics before the first tic in this packet? If so, send a + * resend request. + */ + + resend_end = seq - recvwindow_start; + + if (resend_end <= 0) return; + + if (resend_end >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + resend_end = CONFIG_GAMES_NXDOOM_NET_BACKUPTICS - 1; + } + + index = resend_end - 1; + resend_start = resend_end; + + while (index >= 0) + { + recvobj = &recvwindow[index][player]; + + if (recvobj->active) + { + /* ended our run of unreceived tics */ + + break; + } + + if (recvobj->resend_time != 0) + { + /* Already sent a resend request for this tic */ + + break; + } + + resend_start = index; + --index; + } + + /* Possibly send a resend request */ + + if (resend_start < resend_end) + { + net_log_info("server: request resend for %d-%d before %d", + recvwindow_start + resend_start, + recvwindow_start + resend_end - 1, seq); + net_sv_send_resend_request(client, recvwindow_start + resend_start, + recvwindow_start + resend_end - 1); + } +} + +static void net_sv_parse_game_data_ack(net_packet_t *packet, + net_client_t *client) +{ + unsigned int ackseq; + + net_log_info("server: processing game data ack packet"); + + if (server_state != SERVER_IN_GAME) + { + net_log_err("server: not in game state, server_state=%d", + server_state); + return; + } + + /* Read header */ + + if (!net_read_int8(packet, &ackseq)) + { + net_log_err("server: missing acknowledgement field"); + return; + } + + /* Expand 8-bit values to the full sequence number */ + + ackseq = net_sv_expand_ticnum(ackseq); + + /* Higher acknowledgement point than we already have? */ + + if (ackseq > client->acknowledged) + { + net_log_info("server: acknowledged up to %d", ackseq); + client->acknowledged = ackseq; + } +} + +static void net_sv_send_tics(net_client_t *client, unsigned int start, + unsigned int end) +{ + net_packet_t *packet; + unsigned int i; + + packet = net_new_packet(500); + + net_write_int16(packet, NET_PACKET_TYPE_GAMEDATA); + + /* Send the start tic and number of tics */ + + net_write_int8(packet, start & 0xff); + net_write_int8(packet, end - start + 1); + + /* Write the tics */ + + for (i = start; i <= end; ++i) + { + net_full_ticcmd_t *cmd; + + cmd = &client->sendqueue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (i != cmd->seq) + { + i_error("Wanted to send %i, but %i is in its place", i, cmd->seq); + } + + /* Add command */ + + net_write_full_ticcmd(packet, cmd, sv_settings.lowres_turn); + } + + /* Send packet */ + + net_conn_send_packet(&client->connection, packet); + + net_free_packet(packet); +} + +/* Parse a retransmission request from a client */ + +static void net_sv_parse_resend_request(net_packet_t *packet, + net_client_t *client) +{ + unsigned int start; + unsigned int last; + unsigned int num_tics; + unsigned int i; + + net_log_info("server: processing resend request"); + + /* Read the starting tic and number of tics */ + + if (!net_read_int32(packet, &start) || !net_read_int8(packet, &num_tics)) + { + net_log_err("server: missing fields for resend"); + return; + } + + /* Check we have all the requested tics */ + + last = start + num_tics - 1; + + for (i = start; i <= last; ++i) + { + net_full_ticcmd_t *cmd; + + cmd = &client->sendqueue[i % CONFIG_GAMES_NXDOOM_NET_BACKUPTICS]; + + if (i != cmd->seq) + { + /* We do not have the requested tic (any more) + * This is pretty fatal. We could disconnect the client, + * but then again this could be a spoofed packet. Just + * ignore it. + */ + + net_log_err("server: don't have tic %d any more, " + "can't resend", + i); + return; + } + } + + /* Resend those tics */ + + net_log_info("server: resending tics %d-%d", start, last); + net_sv_send_tics(client, start, last); +} + +static void net_sv_parse_hole_punch(net_packet_t *packet) +{ + const char *addr_string; + net_packet_t *sendpacket; + net_addr_t *addr; + + addr_string = net_read_string(packet); + if (addr_string == NULL) + { + net_log_err("server: hole punch request but no address provided"); + return; + } + + addr = net_resolve_address(server_context, addr_string); + if (addr == NULL) + { + net_log_err("server: failed to resolve address: %s", addr_string); + return; + } + + sendpacket = net_new_packet(16); + net_write_int16(sendpacket, NET_PACKET_TYPE_NAT_HOLE_PUNCH); + net_send_packet(addr, sendpacket); + net_free_packet(sendpacket); + net_release_address(addr); + net_log_info("server: sent hole punch to %s", addr_string); +} + +static void net_sv_master_packet(net_packet_t *packet) +{ + unsigned int packet_type; + + /* Read the packet type */ + + if (!net_read_int16(packet, &packet_type)) + { + net_log_err("server: no packet type in master server message"); + return; + } + + net_log_info("server: packet from master server; type %d", packet_type); + net_log_packet(packet); + + switch (packet_type) + { + case NET_MASTER_PACKET_TYPE_ADD_RESPONSE: + net_query_add_response(packet); + break; + + case NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH: + net_sv_parse_hole_punch(packet); + break; + } +} + +/* Send a response back to the client */ + +static void net_sv_send_query_response(net_addr_t *addr) +{ + net_packet_t *reply; + net_querydata_t querydata; + int p; + + /* Version */ + + querydata.version = PACKAGE_STRING; + + /* Server state */ + + querydata.server_state = server_state; + + /* Number of players/maximum players */ + + querydata.num_players = net_sv_num_players(); + querydata.max_players = net_sv_max_players(); + + /* Game mode/mission */ + + querydata.gamemode = sv_gamemode; + querydata.gamemission = sv_gamemission; + + /* @category net + * @arg + * + * When starting a network server, specify a name for the server. + */ + + p = m_check_parm_with_args("-servername", 1); + + if (p > 0) + { + querydata.description = myargv[p + 1]; + } + else + { + querydata.description = "Unnamed server"; + } + + /* Send it and we're done. */ + + net_log_info("server: sending query response to %s", + net_addr_to_string(addr)); + reply = net_new_packet(64); + net_write_int16(reply, NET_PACKET_TYPE_QUERY_RESPONSE); + net_write_query_data(reply, &querydata); + net_send_packet(addr, reply); + net_free_packet(reply); +} + +/* Process a packet received by the server */ + +static void net_sv_packet(net_packet_t *packet, net_addr_t *addr) +{ + net_client_t *client; + unsigned int packet_type; + + /* Response from master server? */ + + if (addr != NULL && addr == master_server) + { + net_sv_master_packet(packet); + return; + } + + /* Find which client this packet came from */ + + client = net_sv_find_client(addr); + + /* Read the packet type */ + + if (!net_read_int16(packet, &packet_type)) + { + /* no packet type */ + + return; + } + + net_log_info("server: packet from %s; type %d", net_addr_to_string(addr), + packet_type & ~NET_RELIABLE_PACKET); + net_log_packet(packet); + + if (packet_type == NET_PACKET_TYPE_SYN) + { + net_sv_parse_syn(packet, client, addr); + } + else if (packet_type == NET_PACKET_TYPE_QUERY) + { + net_sv_send_query_response(addr); + } + else if (client == NULL) + { + /* Must come from a valid client; ignore otherwise */ + } + else if (net_conn_packet(&client->connection, packet, &packet_type)) + { + /* Packet was eaten by the common connection code */ + } + else + { + switch (packet_type) + { + case NET_PACKET_TYPE_GAMESTART: + net_sv_parse_game_start(packet, client); + break; + case NET_PACKET_TYPE_LAUNCH: + net_sv_parse_launch(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA: + net_sv_parse_game_data(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA_ACK: + net_sv_parse_game_data_ack(packet, client); + break; + case NET_PACKET_TYPE_GAMEDATA_RESEND: + net_sv_parse_resend_request(packet, client); + break; + default: + break; /* unknown packet type */ + } + } +} + +static void net_sv_pump_send_queue(net_client_t *client) +{ + net_full_ticcmd_t cmd; + int recv_index; + int num_players; + int i; + int starttic; + int endtic; + + /* If a client has not sent any acknowledgments for a while, + * wait until they catch up. + */ + + if (client->sendseq - net_sv_latest_acknowledged() > 40) + { + return; + } + + /* Work out the index into the receive window */ + + recv_index = client->sendseq - recvwindow_start; + + if (recv_index < 0 || recv_index >= CONFIG_GAMES_NXDOOM_NET_BACKUPTICS) + { + return; + } + + /* Check if we can generate a new entry for the send queue + * using the data in recvwindow. + */ + + num_players = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] == client) + { + /* Client does not rely on itself for data */ + + continue; + } + + if (sv_players[i] == NULL || !client_connected(sv_players[i])) + { + continue; + } + + if (!recvwindow[recv_index][i].active) + { + /* We do not have this player's ticcmd, so we cannot generate a + * complete command yet. + */ + + return; + } + + ++num_players; + } + + /* If this is a game with only a single player in it, we might + * be sending a ticcmd set containing 0 ticcmds. This is fine; + * however, there's nothing to stop the game running on ahead + * and never stopping. Don't let the server get too far ahead + * of the client. + */ + + if (num_players == 0 && client->sendseq > recvwindow_start + 10) + { + return; + } + + /* We have all data we need to generate a command for this tic. */ + + cmd.seq = client->sendseq; + + /* Add ticcmds from all players */ + + cmd.latency = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + net_client_recv_t *recvobj; + + if (sv_players[i] == client) + { + /* Not the player we are sending to */ + + cmd.playeringame[i] = false; + continue; + } + + if (sv_players[i] == NULL || !recvwindow[recv_index][i].active) + { + cmd.playeringame[i] = false; + continue; + } + + cmd.playeringame[i] = true; + + recvobj = &recvwindow[recv_index][i]; + + cmd.cmds[i] = recvobj->diff; + + if (recvobj->latency > cmd.latency) cmd.latency = recvobj->latency; + } + + /* Add into the queue */ + + client->sendqueue[client->sendseq % + CONFIG_GAMES_NXDOOM_NET_BACKUPTICS] = cmd; + + /* Transmit the new tic to the client */ + + starttic = client->sendseq - sv_settings.extratics; + endtic = client->sendseq; + + if (starttic < 0) starttic = 0; + + net_log_info("server: send tics %d-%d to %s", starttic, endtic, + net_addr_to_string(client->addr)); + net_sv_send_tics(client, starttic, endtic); + + ++client->sendseq; +} + +/* Called when all players have disconnected. Return to listening for + * players to start a new game, and disconnect any drones still connected. + */ + +static void net_sv_game_ended(void) +{ + int i; + + server_state = SERVER_WAITING_LAUNCH; + sv_gamemode = indetermined; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_disconnect_client(&clients[i]); + } + } +} + +/* Prevent against deadlock: resend requests are usually only + * triggered if we miss a packet and receive the next one. + * If we miss a whole load of packets, we can end up in a + * deadlock situation where the client will not send any more. + * If we don't receive any game data in a while, trigger a resend + * request for the next tic we're expecting. + */ + +static void net_sv_check_deadlock(net_client_t *client) +{ + int nowtime; + int i; + + /* Don't expect game data from clients. */ + + if (client->drone) + { + return; + } + + nowtime = i_get_time_ms(); + + /* If we haven't received anything for a long time, it may be a deadlock. */ + + if (nowtime - client->last_gamedata_time > 1000) + { + net_log_warn("server: no gamedata from %s since %d - deadlock?", + net_addr_to_string(client->addr), + client->last_gamedata_time); + + /* Search the receive window for the first tic we are expecting + * from this player. + */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS; ++i) + { + if (!recvwindow[i][client->player_number].active) + { + net_log_warn( + "server: deadlock: sending resend request for %d-%d", + recvwindow_start + i, recvwindow_start + i + 5); + + /* Found a tic we haven't received. Send a resend request. */ + + net_sv_send_resend_request(client, recvwindow_start + i, + recvwindow_start + i + 5); + + client->last_gamedata_time = nowtime; + break; + } + } + + /* If we sent a resend request to break the deadlock, also trigger a + * resend of any tics we have sitting in the send queue, in case the + * client is blocked waiting on tics from us that have been lost. + * This fixes deadlock with some older clients which do not send + * resends to break deadlock. + */ + + if (i < CONFIG_GAMES_NXDOOM_NET_BACKUPTICS && + client->sendseq > client->acknowledged) + { + net_log_warn("server: also resending tics %d-%d to break deadlock", + client->acknowledged, client->sendseq - 1); + net_sv_send_tics(client, client->acknowledged, + client->sendseq - 1); + } + } +} + +/* Perform any needed action on a client */ + +static void net_sv_run_client(net_client_t *client) +{ + /* Run common code */ + + net_conn_run(&client->connection); + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED && + client->connection.disconnect_reason == NET_DISCONNECT_TIMEOUT) + { + net_log_warn("server: client at %s timed out", + net_addr_to_string(client->addr)); + net_sv_broadcast_message("Client '%s' timed out and disconnected", + client->name); + } + + /* Is this client disconnected? */ + + if (client->connection.state == NET_CONN_STATE_DISCONNECTED) + { + client->active = false; + + /* If we were about to start a game, any player disconnecting + * should cause an abort. + */ + + if (server_state == SERVER_WAITING_START && !client->drone) + { + net_sv_broadcast_message("Game startup aborted because " + "player '%s' disconnected.", + client->name); + net_sv_game_ended(); + } + + free(client->name); + net_release_address(client->addr); + + /* Are there any clients left connected? If not, return the + * server to the waiting-for-players state. + * + * Disconnect any drones still connected. + */ + + if (net_sv_num_players() <= 0) + { + net_log_info("server: no player clients left, game ended"); + net_sv_game_ended(); + } + } + + if (!client_connected(client)) + { + /* client has not yet finished connecting */ + + return; + } + + if (server_state == SERVER_WAITING_LAUNCH) + { + /* Waiting for the game to start */ + + /* Send information once every second */ + + if (client->last_send_time < 0 || + i_get_time_ms() - client->last_send_time > 1000) + { + net_sv_send_waiting_data(client); + client->last_send_time = i_get_time_ms(); + } + } + + if (server_state == SERVER_IN_GAME) + { + net_sv_pump_send_queue(client); + net_sv_check_deadlock(client); + } +} + +static void update_master_server(void) +{ + unsigned int now; + + now = i_get_time_ms(); + + /* The address of the master server can change. Periodically + * re-resolve the master server to update. + */ + + if (now - master_resolve_time > MASTER_RESOLVE_PERIOD * 1000) + { + net_addr_t *new_addr; + + new_addr = net_query_resolve_master(server_context); + net_release_address(master_server); + master_server = new_addr; + + master_resolve_time = now; + } + + /* Possibly refresh our registration with the master server. */ + + if (now - master_refresh_time > MASTER_REFRESH_PERIOD * 1000) + { + net_query_add_to_master(master_server); + master_refresh_time = now; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Add a network module to the server context */ + +void net_sv_add_module(net_module_t *module) +{ + module->init_server(); + net_add_module(server_context, module); +} + +/* Initialize server and wait for connections */ + +void net_sv_init(void) +{ + int i; + + /* initialize send/receive context */ + + server_context = net_new_context(); + + /* no clients yet */ + + for (i = 0; i < MAXNETNODES; ++i) + { + clients[i].active = false; + } + + net_sv_assign_players(); + + server_state = SERVER_WAITING_LAUNCH; + sv_gamemode = indetermined; + server_initialized = true; +} + +void net_sv_register_with_master(void) +{ + /* @category net + * + * When running a server, don't register with the global master server. + * Implies -server. + */ + + if (!m_check_parm("-privateserver")) + { + master_server = net_query_resolve_master(server_context); + } + else + { + master_server = NULL; + } + + /* Send request. */ + + if (master_server != NULL) + { + net_query_add_to_master(master_server); + master_refresh_time = i_get_time_ms(); + master_resolve_time = master_refresh_time; + } +} + +/* Run server code to check for new packets/send packets as the server + * requires + */ + +void net_sv_run(void) +{ + net_addr_t *addr; + net_packet_t *packet; + int i; + + if (!server_initialized) + { + return; + } + + while (net_recv_packet(server_context, &addr, &packet)) + { + net_sv_packet(packet, addr); + net_free_packet(packet); + net_release_address(addr); + } + + if (master_server != NULL) + { + update_master_server(); + } + + /* "Run" any clients that may have things to do, independent of responses + * to received packets + */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_run_client(&clients[i]); + } + } + + switch (server_state) + { + case SERVER_WAITING_LAUNCH: + break; + + case SERVER_WAITING_START: + check_start_game(); + break; + + case SERVER_IN_GAME: + net_sv_advance_window(); + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (sv_players[i] != NULL && client_connected(sv_players[i])) + { + net_sv_check_resends(sv_players[i]); + } + } + break; + } +} + +void net_sv_shutdown(void) +{ + int i; + boolean running; + int start_time; + + if (!server_initialized) + { + return; + } + + fprintf(stderr, "SV: Shutting down server...\n"); + + /* Disconnect all clients */ + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + net_sv_disconnect_client(&clients[i]); + } + } + + /* Wait for all clients to finish disconnecting */ + + start_time = i_get_time_ms(); + running = true; + + while (running) + { + /* Check if any clients are still not finished */ + + running = false; + + for (i = 0; i < MAXNETNODES; ++i) + { + if (clients[i].active) + { + running = true; + } + } + + /* Timed out? */ + + if (i_get_time_ms() - start_time > 5000) + { + running = false; + fprintf(stderr, + "SV: Timed out waiting for clients to disconnect.\n"); + } + + /* Run the client code in case this is a loopback client. */ + + net_cl_run(); + net_sv_run(); + + /* Don't hog the CPU */ + + usleep(1000); + } +} diff --git a/games/NXDoom/src/net_server.h b/games/NXDoom/src/net_server.h new file mode 100644 index 000000000..9d1db98b9 --- /dev/null +++ b/games/NXDoom/src/net_server.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_server.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Network server code + * + ****************************************************************************/ + +#ifndef NET_SERVER_H +#define NET_SERVER_H + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* initialize server and wait for connections */ + +void net_sv_init(void); + +/* run server: check for new packets received etc. */ + +void net_sv_run(void); + +/* Shut down the server + * Blocks until all clients disconnect, or until a 5 second timeout + */ + +void net_sv_shutdown(void); + +/* Add a network module to the context used by the server */ + +void net_sv_add_module(net_module_t *module); + +/* Register server with master server. */ + +void net_sv_register_with_master(void); + +#endif /* NET_SERVER_H */ diff --git a/games/NXDoom/src/net_structrw.c b/games/NXDoom/src/net_structrw.c new file mode 100644 index 000000000..371aa4f7a --- /dev/null +++ b/games/NXDoom/src/net_structrw.c @@ -0,0 +1,714 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_structrw.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Reading and writing various structures into packets + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "net_packet.h" +#include "net_structrw.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct protocol_name +{ + net_protocol_t protocol; + const char *name; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* String names for the enum values in net_protocol_t, which are what is + * sent over the wire. Every enum value must have an entry in this list. + */ + +static struct protocol_name g_protocol_names[] = +{ + {NET_PROTOCOL_CHOCOLATE_DOOM_0, "CHOCOLATE_DOOM_0"}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static boolean net_read_blob(net_packet_t *packet, uint8_t *buf, size_t len) +{ + unsigned int b; + int i; + + for (i = 0; i < len; ++i) + { + if (!net_read_int8(packet, &b)) + { + return false; + } + + buf[i] = b; + } + + return true; +} + +static void net_write_blob(net_packet_t *packet, uint8_t *buf, size_t len) +{ + int i; + + for (i = 0; i < len; ++i) + { + net_write_int8(packet, buf[i]); + } +} + +static net_protocol_t parse_protocol_name(const char *name) +{ + int i; + + for (i = 0; i < arrlen(g_protocol_names); ++i) + { + if (!strcmp(g_protocol_names[i].name, name)) + { + return g_protocol_names[i].protocol; + } + } + + return NET_PROTOCOL_UNKNOWN; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void net_write_connect_data(net_packet_t *packet, net_connect_data_t *data) +{ + net_write_int8(packet, data->gamemode); + net_write_int8(packet, data->gamemission); + net_write_int8(packet, data->lowres_turn); + net_write_int8(packet, data->drone); + net_write_int8(packet, data->max_players); + net_write_int8(packet, data->is_freedoom); + net_write_sha1_sum(packet, data->wad_sha1sum); + net_write_sha1_sum(packet, data->deh_sha1sum); + net_write_int8(packet, data->player_class); +} + +boolean net_read_connect_data(net_packet_t *packet, net_connect_data_t *data) +{ + return net_read_int8(packet, (unsigned int *)&data->gamemode) && + net_read_int8(packet, (unsigned int *)&data->gamemission) && + net_read_int8(packet, (unsigned int *)&data->lowres_turn) && + net_read_int8(packet, (unsigned int *)&data->drone) && + net_read_int8(packet, (unsigned int *)&data->max_players) && + net_read_int8(packet, (unsigned int *)&data->is_freedoom) && + net_read_sha1_sum(packet, data->wad_sha1sum) && + net_read_sha1_sum(packet, data->deh_sha1sum) && + net_read_int8(packet, (unsigned int *)&data->player_class); +} + +void net_write_settings(net_packet_t *packet, net_gamesettings_t *settings) +{ + int i; + + net_write_int8(packet, settings->ticdup); + net_write_int8(packet, settings->extratics); + net_write_int8(packet, settings->deathmatch); + net_write_int8(packet, settings->nomonsters); + net_write_int8(packet, settings->fast_monsters); + net_write_int8(packet, settings->respawn_monsters); + net_write_int8(packet, settings->episode); + net_write_int8(packet, settings->map); + net_write_int8(packet, settings->skill); + net_write_int8(packet, settings->gameversion); + net_write_int8(packet, settings->lowres_turn); + net_write_int8(packet, settings->new_sync); + net_write_int32(packet, settings->timelimit); + net_write_int8(packet, settings->loadgame); + net_write_int8(packet, settings->random); + net_write_int8(packet, settings->num_players); + net_write_int8(packet, settings->consoleplayer); + + for (i = 0; i < settings->num_players; ++i) + { + net_write_int8(packet, settings->player_classes[i]); + } +} + +boolean net_read_settings(net_packet_t *packet, net_gamesettings_t *settings) +{ + boolean success; + int i; + + success = + net_read_int8(packet, (unsigned int *)&settings->ticdup) && + net_read_int8(packet, (unsigned int *)&settings->extratics) && + net_read_int8(packet, (unsigned int *)&settings->deathmatch) && + net_read_int8(packet, (unsigned int *)&settings->nomonsters) && + net_read_int8(packet, (unsigned int *)&settings->fast_monsters) && + net_read_int8(packet, (unsigned int *)&settings->respawn_monsters) && + net_read_int8(packet, (unsigned int *)&settings->episode) && + net_read_int8(packet, (unsigned int *)&settings->map) && + net_read_sint8(packet, &settings->skill) && + net_read_int8(packet, (unsigned int *)&settings->gameversion) && + net_read_int8(packet, (unsigned int *)&settings->lowres_turn) && + net_read_int8(packet, (unsigned int *)&settings->new_sync) && + net_read_int32(packet, (unsigned int *)&settings->timelimit) && + net_read_sint8(packet, (signed int *)&settings->loadgame) && + net_read_int8(packet, (unsigned int *)&settings->random) && + net_read_int8(packet, (unsigned int *)&settings->num_players) && + net_read_sint8(packet, (signed int *)&settings->consoleplayer); + + if (!success) + { + return false; + } + + for (i = 0; + i < settings->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + if (!net_read_int8(packet, + (unsigned int *)&settings->player_classes[i])) + { + return false; + } + } + + return true; +} + +boolean net_read_query_data(net_packet_t *packet, net_querydata_t *query) +{ + boolean success; + + query->version = net_read_safe_string(packet); + + success = query->version != NULL && + net_read_int8(packet, (unsigned int *)&query->server_state) && + net_read_int8(packet, (unsigned int *)&query->num_players) && + net_read_int8(packet, (unsigned int *)&query->max_players) && + net_read_int8(packet, (unsigned int *)&query->gamemode) && + net_read_int8(packet, (unsigned int *)&query->gamemission); + + if (!success) + { + return false; + } + + query->description = net_read_safe_string(packet); + + /* We read the list of protocols supported by the server. However, + * old versions of Chocolate Doom do not support this field; it is + * okay if it cannot be successfully read. + */ + + query->protocol = net_read_protocol_list(packet); + + return query->description != NULL; +} + +void net_write_query_data(net_packet_t *packet, net_querydata_t *query) +{ + net_write_string(packet, query->version); + net_write_int8(packet, query->server_state); + net_write_int8(packet, query->num_players); + net_write_int8(packet, query->max_players); + net_write_int8(packet, query->gamemode); + net_write_int8(packet, query->gamemission); + net_write_string(packet, query->description); + + /* Write a list of all supported protocols. Note that the query->protocol + * field is ignored here; it is only used when receiving. + */ + + net_write_protocol_list(packet); +} + +void net_write_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn) +{ + /* Header */ + + net_write_int8(packet, diff->diff); + + /* Write the fields which are enabled: */ + + if (diff->diff & NET_TICDIFF_FORWARD) + net_write_int8(packet, diff->cmd.forwardmove); + if (diff->diff & NET_TICDIFF_SIDE) + net_write_int8(packet, diff->cmd.sidemove); + if (diff->diff & NET_TICDIFF_TURN) + { + if (lowres_turn) + { + net_write_int8(packet, diff->cmd.angleturn / 256); + } + else + { + net_write_int16(packet, diff->cmd.angleturn); + } + } + + if (diff->diff & NET_TICDIFF_BUTTONS) + net_write_int8(packet, diff->cmd.buttons); + if (diff->diff & NET_TICDIFF_CONSISTANCY) + net_write_int8(packet, diff->cmd.consistency); + if (diff->diff & NET_TICDIFF_CHATCHAR) + net_write_int8(packet, diff->cmd.chatchar); + if (diff->diff & NET_TICDIFF_RAVEN) + { + net_write_int8(packet, diff->cmd.lookfly); + net_write_int8(packet, diff->cmd.arti); + } + + if (diff->diff & NET_TICDIFF_STRIFE) + { + net_write_int8(packet, diff->cmd.buttons2); + net_write_int16(packet, diff->cmd.inventory); + } +} + +boolean net_read_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn) +{ + unsigned int val; + signed int sval; + + /* Read header */ + + if (!net_read_int8(packet, &diff->diff)) return false; + + /* Read fields */ + + if (diff->diff & NET_TICDIFF_FORWARD) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.forwardmove = sval; + } + + if (diff->diff & NET_TICDIFF_SIDE) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.sidemove = sval; + } + + if (diff->diff & NET_TICDIFF_TURN) + { + if (lowres_turn) + { + if (!net_read_sint8(packet, &sval)) return false; + diff->cmd.angleturn = sval * 256; + } + else + { + if (!net_read_sint16(packet, &sval)) return false; + diff->cmd.angleturn = sval; + } + } + + if (diff->diff & NET_TICDIFF_BUTTONS) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.buttons = val; + } + + if (diff->diff & NET_TICDIFF_CONSISTANCY) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.consistency = val; + } + + if (diff->diff & NET_TICDIFF_CHATCHAR) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.chatchar = val; + } + + if (diff->diff & NET_TICDIFF_RAVEN) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.lookfly = val; + + if (!net_read_int8(packet, &val)) return false; + diff->cmd.arti = val; + } + + if (diff->diff & NET_TICDIFF_STRIFE) + { + if (!net_read_int8(packet, &val)) return false; + diff->cmd.buttons2 = val; + + if (!net_read_int16(packet, &val)) return false; + diff->cmd.inventory = val; + } + + return true; +} + +void net_ticcmd_diff(ticcmd_t *tic1, ticcmd_t *tic2, net_ticdiff_t *diff) +{ + diff->diff = 0; + diff->cmd = *tic2; + + if (tic1->forwardmove != tic2->forwardmove) + diff->diff |= NET_TICDIFF_FORWARD; + if (tic1->sidemove != tic2->sidemove) diff->diff |= NET_TICDIFF_SIDE; + if (tic1->angleturn != tic2->angleturn) diff->diff |= NET_TICDIFF_TURN; + if (tic1->buttons != tic2->buttons) diff->diff |= NET_TICDIFF_BUTTONS; + if (tic1->consistency != tic2->consistency) + diff->diff |= NET_TICDIFF_CONSISTANCY; + if (tic2->chatchar != 0) diff->diff |= NET_TICDIFF_CHATCHAR; + + /* Heretic/Hexen-specific */ + + if (tic1->lookfly != tic2->lookfly || tic2->arti != 0) + diff->diff |= NET_TICDIFF_RAVEN; + + /* Strife-specific */ + + if (tic1->buttons2 != tic2->buttons2 || tic2->inventory != 0) + diff->diff |= NET_TICDIFF_STRIFE; +} + +void net_ticcmd_patch(ticcmd_t *src, net_ticdiff_t *diff, ticcmd_t *dest) +{ + memmove(dest, src, sizeof(ticcmd_t)); + + /* Apply the diff */ + + if (diff->diff & NET_TICDIFF_FORWARD) + dest->forwardmove = diff->cmd.forwardmove; + if (diff->diff & NET_TICDIFF_SIDE) dest->sidemove = diff->cmd.sidemove; + if (diff->diff & NET_TICDIFF_TURN) dest->angleturn = diff->cmd.angleturn; + if (diff->diff & NET_TICDIFF_BUTTONS) dest->buttons = diff->cmd.buttons; + if (diff->diff & NET_TICDIFF_CONSISTANCY) + dest->consistency = diff->cmd.consistency; + + if (diff->diff & NET_TICDIFF_CHATCHAR) + dest->chatchar = diff->cmd.chatchar; + else + dest->chatchar = 0; + + /* Heretic/Hexen specific: */ + + if (diff->diff & NET_TICDIFF_RAVEN) + { + dest->lookfly = diff->cmd.lookfly; + dest->arti = diff->cmd.arti; + } + else + { + dest->arti = 0; + } + + /* Strife-specific: */ + + if (diff->diff & NET_TICDIFF_STRIFE) + { + dest->buttons2 = diff->cmd.buttons2; + dest->inventory = diff->cmd.inventory; + } + else + { + dest->inventory = 0; + } +} + +/* net_full_ticcmd_t */ + +boolean net_read_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn) +{ + unsigned int bitfield; + int i; + + /* Latency */ + + if (!net_read_sint16(packet, &cmd->latency)) + { + return false; + } + + /* Regenerate playeringame from the "header" bitfield */ + + if (!net_read_int8(packet, &bitfield)) + { + return false; + } + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + cmd->playeringame[i] = (bitfield & (1 << i)) != 0; + } + + /* Read cmds */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + if (!net_read_ticcmd_diff(packet, &cmd->cmds[i], lowres_turn)) + { + return false; + } + } + } + + return true; +} + +void net_write_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn) +{ + unsigned int bitfield; + int i; + + /* Write the latency */ + + net_write_int16(packet, cmd->latency); + + /* Write "header" byte indicating which players are active + * in this ticcmd + */ + + bitfield = 0; + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + bitfield |= 1 << i; + } + } + + net_write_int8(packet, bitfield); + + /* Write player ticcmds */ + + for (i = 0; i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; ++i) + { + if (cmd->playeringame[i]) + { + net_write_ticcmd_diff(packet, &cmd->cmds[i], lowres_turn); + } + } +} + +void net_write_wait_data(net_packet_t *packet, net_waitdata_t *data) +{ + int i; + + net_write_int8(packet, data->num_players); + net_write_int8(packet, data->num_drones); + net_write_int8(packet, data->ready_players); + net_write_int8(packet, data->max_players); + net_write_int8(packet, data->is_controller); + net_write_int8(packet, data->consoleplayer); + + for (i = 0; + i < data->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + net_write_string(packet, data->player_names[i]); + net_write_string(packet, data->player_addrs[i]); + } + + net_write_sha1_sum(packet, data->wad_sha1sum); + net_write_sha1_sum(packet, data->deh_sha1sum); + net_write_int8(packet, data->is_freedoom); +} + +boolean net_read_wait_data(net_packet_t *packet, net_waitdata_t *data) +{ + int i; + char *s; + + if (!net_read_int8(packet, (unsigned int *)&data->num_players) || + !net_read_int8(packet, (unsigned int *)&data->num_drones) || + !net_read_int8(packet, (unsigned int *)&data->ready_players) || + !net_read_int8(packet, (unsigned int *)&data->max_players) || + !net_read_int8(packet, (unsigned int *)&data->is_controller) || + !net_read_sint8(packet, &data->consoleplayer)) + { + return false; + } + + for (i = 0; + i < data->num_players && i < CONFIG_GAMES_NXDOOM_NET_MAXPLAYERS; + ++i) + { + s = net_read_string(packet); + + if (s == NULL || strlen(s) >= CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME) + { + return false; + } + + m_str_copy(data->player_names[i], s, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + + s = net_read_string(packet); + + if (s == NULL || strlen(s) >= CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME) + { + return false; + } + + m_str_copy(data->player_addrs[i], s, + CONFIG_GAMES_NXDOOM_NET_MAXPLAYERNAME); + } + + return net_read_sha1_sum(packet, data->wad_sha1sum) && + net_read_sha1_sum(packet, data->deh_sha1sum) && + net_read_int8(packet, (unsigned int *)&data->is_freedoom); +} + +boolean net_read_sha1_sum(net_packet_t *packet, sha1_digest_t digest) +{ + return net_read_blob(packet, digest, sizeof(sha1_digest_t)); +} + +void net_write_sha1_sum(net_packet_t *packet, sha1_digest_t digest) +{ + net_write_blob(packet, digest, sizeof(sha1_digest_t)); +} + +boolean net_read_prng_seed(net_packet_t *packet, prng_seed_t seed) +{ + return net_read_blob(packet, seed, sizeof(prng_seed_t)); +} + +void net_write_prng_seed(net_packet_t *packet, prng_seed_t seed) +{ + net_write_blob(packet, seed, sizeof(prng_seed_t)); +} + +/* net_read_protocol reads a single string-format protocol name from the + * given packet, returning NET_PROTOCOL_UNKNOWN if the string describes an + * unknown protocol. + */ + +net_protocol_t net_read_protocol(net_packet_t *packet) +{ + const char *name; + + name = net_read_string(packet); + if (name == NULL) + { + return NET_PROTOCOL_UNKNOWN; + } + + return parse_protocol_name(name); +} + +/* net_write_protocol writes a single string-format protocol name to a + * packet. + */ + +void net_write_protocol(net_packet_t *packet, net_protocol_t protocol) +{ + int i; + + for (i = 0; i < arrlen(g_protocol_names); ++i) + { + if (g_protocol_names[i].protocol == protocol) + { + net_write_string(packet, g_protocol_names[i].name); + return; + } + } + + /* If you add an entry to the net_protocol_t enum, a corresponding entry + * must be added to the protocol_names list. + */ + + i_error("net_write_protocol: protocol %d missing from protocol_names " + "list; please add it.", + protocol); +} + +/* net_read_protocol_list reads a list of string-format protocol names from + * the given packet, returning a single protocol number. The protocol that is + * returned is the last protocol in the list that is a supported protocol. If + * no recognized protocols are read, NET_PROTOCOL_UNKNOWN is returned. + */ + +net_protocol_t net_read_protocol_list(net_packet_t *packet) +{ + net_protocol_t result; + unsigned int num_protocols; + int i; + + if (!net_read_int8(packet, &num_protocols)) + { + return NET_PROTOCOL_UNKNOWN; + } + + result = NET_PROTOCOL_UNKNOWN; + + for (i = 0; i < num_protocols; ++i) + { + net_protocol_t p; + const char *name; + + name = net_read_string(packet); + if (name == NULL) + { + return NET_PROTOCOL_UNKNOWN; + } + + p = parse_protocol_name(name); + if (p != NET_PROTOCOL_UNKNOWN) + { + result = p; + } + } + + return result; +} + +/* net_write_protocol_list writes a list of string-format protocol names into + * the given packet, all the supported protocols in the net_protocol_t enum. + * This is slightly different to other functions in this file, in that there + * is nothing the caller can "choose" to write; the built-in list of all + * protocols is always sent. + */ + +void net_write_protocol_list(net_packet_t *packet) +{ + int i; + + net_write_int8(packet, NET_NUM_PROTOCOLS); + + for (i = 0; i < NET_NUM_PROTOCOLS; ++i) + { + net_write_protocol(packet, i); + } +} diff --git a/games/NXDoom/src/net_structrw.h b/games/NXDoom/src/net_structrw.h new file mode 100644 index 000000000..2a924173c --- /dev/null +++ b/games/NXDoom/src/net_structrw.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * apps/games/NXDoom/src/net_structrw.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef NET_STRUCTRW_H +#define NET_STRUCTRW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "aes_prng.h" +#include "net_defs.h" +#include "net_packet.h" +#include "sha1.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void net_write_connect_data(net_packet_t *packet, net_connect_data_t *data); +boolean net_read_connect_data(net_packet_t *packet, + net_connect_data_t *data); + +extern void net_write_settings(net_packet_t *packet, + net_gamesettings_t *settings); +extern boolean net_read_settings(net_packet_t *packet, + net_gamesettings_t *settings); + +extern void net_write_query_data(net_packet_t *packet, + net_querydata_t *querydata); +extern boolean net_read_query_data(net_packet_t *packet, + net_querydata_t *querydata); + +extern void net_write_ticcmd_diff(net_packet_t *packet, net_ticdiff_t *diff, + boolean lowres_turn); +extern boolean net_read_ticcmd_diff(net_packet_t *packet, + net_ticdiff_t *diff, boolean lowres_turn); +extern void net_ticcmd_diff(ticcmd_t *tic1, ticcmd_t *tic2, + net_ticdiff_t *diff); +extern void net_ticcmd_patch(ticcmd_t *src, net_ticdiff_t *diff, + ticcmd_t *dest); + +boolean net_read_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn); +void net_write_full_ticcmd(net_packet_t *packet, net_full_ticcmd_t *cmd, + boolean lowres_turn); + +boolean net_read_sha1_sum(net_packet_t *packet, sha1_digest_t digest); +void net_write_sha1_sum(net_packet_t *packet, sha1_digest_t digest); + +void net_write_wait_data(net_packet_t *packet, net_waitdata_t *data); +boolean net_read_wait_data(net_packet_t *packet, net_waitdata_t *data); + +boolean net_read_prng_seed(net_packet_t *packet, prng_seed_t seed); +void net_write_prng_seed(net_packet_t *packet, prng_seed_t seed); + +/* Protocol list exchange. */ + +net_protocol_t net_read_protocol(net_packet_t *packet); +void net_write_protocol(net_packet_t *packet, net_protocol_t protocol); +net_protocol_t net_read_protocol_list(net_packet_t *packet); +void net_write_protocol_list(net_packet_t *packet); + +#endif /* NET_STRUCTRW_H */ diff --git a/games/NXDoom/src/p_rejectpad.c b/games/NXDoom/src/p_rejectpad.c new file mode 100644 index 000000000..3099d59f9 --- /dev/null +++ b/games/NXDoom/src/p_rejectpad.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * apps/games/NXDoom/src/p_rejectpad.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Padding of Reject Lump + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "m_argv.h" +#include "p_rejectpad.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Pad the REJECT lump with extra data when the lump is too small, + * to simulate a REJECT buffer overflow in Vanilla Doom. + */ + +void pad_reject_array(byte *array, unsigned int len, int totallines) +{ + unsigned int i; + unsigned int byte_num; + byte *dest; + unsigned int padvalue; + + /* Values to pad the REJECT array with: */ + + unsigned int rejectpad[4] = + { + 0, /* Size */ + 0, /* Part of z_zone block header */ + 50, /* PU_LEVEL */ + 0x1d4a11 /* DOOM_CONST_ZONEID */ + }; + + rejectpad[0] = ((totallines * 4 + 3) & ~3) + 24; + + /* Copy values from rejectpad into the destination array. */ + + dest = array; + + for (i = 0; i < len && i < sizeof(rejectpad); ++i) + { + byte_num = i % 4; + *dest = (rejectpad[i / 4] >> (byte_num * 8)) & 0xff; + ++dest; + } + + /* We only have a limited pad size. Print a warning if the + * REJECT lump is too small. + */ + + if (len > sizeof(rejectpad)) + { + fprintf(stderr, + "pad_reject_array: REJECT lump too short to pad! (%u > %i)\n", + len, (int)sizeof(rejectpad)); + + /* Pad remaining space with 0 (or 0xff, if specified on command line). + */ + + if (m_check_parm("-reject_pad_with_ff")) + { + padvalue = 0xff; + } + else + { + padvalue = 0x00; + } + + memset(array + sizeof(rejectpad), padvalue, len - sizeof(rejectpad)); + } +} diff --git a/games/NXDoom/src/p_rejectpad.h b/games/NXDoom/src/p_rejectpad.h new file mode 100644 index 000000000..adb0372dd --- /dev/null +++ b/games/NXDoom/src/p_rejectpad.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/src/p_rejectpad.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Padding of Reject Lump + * + ****************************************************************************/ + +#ifndef __P_REJECTPAD__ +#define __P_REJECTPAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Padding of Reject Lump */ + +void pad_reject_array(byte *array, unsigned int len, int totallines); + +#endif /* __P_REJECTPAD__ */ diff --git a/games/NXDoom/src/sha1.h b/games/NXDoom/src/sha1.h new file mode 100644 index 000000000..eb0f7c360 --- /dev/null +++ b/games/NXDoom/src/sha1.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * apps/games/NXDoom/src/sha1.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * SHA-1 digest. + * + ****************************************************************************/ + +#ifndef __SHA1_H__ +#define __SHA1_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define sha1_updatestring(ctx, str) \ + sha1update((ctx), (byte *)(str), strlen((str)) + 1) + +#define sha1_updateint32(ctx, val) \ + do \ + { \ + byte __sha1__tmp__buf[4]; \ + __sha1__tmp__buf[0] = ((val) >> 24) & 0xff; \ + __sha1__tmp__buf[1] = ((val) >> 16) & 0xff; \ + __sha1__tmp__buf[2] = ((val) >> 8) & 0xff; \ + __sha1__tmp__buf[3] = (val) & 0xff; \ + sha1update((ctx), __sha1__tmp__buf, 4); \ + } \ + while (0) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef byte sha1_digest_t[SHA1_DIGEST_LENGTH]; + +#endif /* __SHA1_H__ */ diff --git a/games/NXDoom/src/tables.c b/games/NXDoom/src/tables.c new file mode 100644 index 000000000..61f0198c3 --- /dev/null +++ b/games/NXDoom/src/tables.c @@ -0,0 +1,2369 @@ +/**************************************************************************** + * apps/games/NXDoom/src/tables.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Lookup tables. + * Do not try to look them up :-). + * In the order of appearance: + * + * int finetangent[4096] - Tangens LUT. + * Should work with BAM fairly well (12 of 16bit, + * effectively, by shifting). + * + * int finesine[10240] - Sine lookup. + * Guess what, serves as cosine, too. + * Remarkable thing is, how to use BAMs with this? + * + * int tantoangle[2049] - ArcTan LUT, + * maps tan(angle) to angle fast. Gotta search. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "tables.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const fixed_t finetangent[4096] = +{ + -170910304, -56965752, -34178904, -24413316, -18988036, -15535599, + -13145455, -11392683, -10052327, -8994149, -8137527, -7429880, + -6835455, -6329090, -5892567, -5512368, -5178251, -4882318, + -4618375, -4381502, -4167737, -3973855, -3797206, -3635590, + -3487165, -3350381, -3223918, -3106651, -2997613, -2895966, + -2800983, -2712030, -2628549, -2550052, -2476104, -2406322, + -2340362, -2277919, -2218719, -2162516, -2109087, -2058233, + -2009771, -1963536, -1919378, -1877161, -1836758, -1798063, + -1760956, -1725348, -1691149, -1658278, -1626658, -1596220, + -1566898, -1538632, -1511367, -1485049, -1459630, -1435065, + -1411312, -1388330, -1366084, -1344537, -1323658, -1303416, + -1283783, -1264730, -1246234, -1228269, -1210813, -1193846, + -1177345, -1161294, -1145673, -1130465, -1115654, -1101225, + -1087164, -1073455, -1060087, -1047046, -1034322, -1021901, + -1009774, -997931, -986361, -975054, -964003, -953199, + -942633, -932298, -922186, -912289, -902602, -893117, + -883829, -874730, -865817, -857081, -848520, -840127, + -831898, -823827, -815910, -808143, -800521, -793041, + -785699, -778490, -771411, -764460, -757631, -750922, + -744331, -737853, -731486, -725227, -719074, -713023, + -707072, -701219, -695462, -689797, -684223, -678737, + -673338, -668024, -662792, -657640, -652568, -647572, + -642651, -637803, -633028, -628323, -623686, -619117, + -614613, -610174, -605798, -601483, -597229, -593033, + -588896, -584815, -580789, -576818, -572901, -569035, + -565221, -561456, -557741, -554074, -550455, -546881, + -543354, -539870, -536431, -533034, -529680, -526366, + -523094, -519861, -516667, -513512, -510394, -507313, + -504269, -501261, -498287, -495348, -492443, -489571, + -486732, -483925, -481150, -478406, -475692, -473009, + -470355, -467730, -465133, -462565, -460024, -457511, + -455024, -452564, -450129, -447720, -445337, -442978, + -440643, -438332, -436045, -433781, -431540, -429321, + -427125, -424951, -422798, -420666, -418555, -416465, + -414395, -412344, -410314, -408303, -406311, -404338, + -402384, -400448, -398530, -396630, -394747, -392882, + -391034, -389202, -387387, -385589, -383807, -382040, + -380290, -378555, -376835, -375130, -373440, -371765, + -370105, -368459, -366826, -365208, -363604, -362013, + -360436, -358872, -357321, -355783, -354257, -352744, + -351244, -349756, -348280, -346816, -345364, -343924, + -342495, -341078, -339671, -338276, -336892, -335519, + -334157, -332805, -331464, -330133, -328812, -327502, + -326201, -324910, -323629, -322358, -321097, -319844, + -318601, -317368, -316143, -314928, -313721, -312524, + -311335, -310154, -308983, -307819, -306664, -305517, + -304379, -303248, -302126, -301011, -299904, -298805, + -297714, -296630, -295554, -294485, -293423, -292369, + -291322, -290282, -289249, -288223, -287204, -286192, + -285186, -284188, -283195, -282210, -281231, -280258, + -279292, -278332, -277378, -276430, -275489, -274553, + -273624, -272700, -271782, -270871, -269965, -269064, + -268169, -267280, -266397, -265519, -264646, -263779, + -262917, -262060, -261209, -260363, -259522, -258686, + -257855, -257029, -256208, -255392, -254581, -253774, + -252973, -252176, -251384, -250596, -249813, -249035, + -248261, -247492, -246727, -245966, -245210, -244458, + -243711, -242967, -242228, -241493, -240763, -240036, + -239314, -238595, -237881, -237170, -236463, -235761, + -235062, -234367, -233676, -232988, -232304, -231624, + -230948, -230275, -229606, -228941, -228279, -227621, + -226966, -226314, -225666, -225022, -224381, -223743, + -223108, -222477, -221849, -221225, -220603, -219985, + -219370, -218758, -218149, -217544, -216941, -216341, + -215745, -215151, -214561, -213973, -213389, -212807, + -212228, -211652, -211079, -210509, -209941, -209376, + -208815, -208255, -207699, -207145, -206594, -206045, + -205500, -204956, -204416, -203878, -203342, -202809, + -202279, -201751, -201226, -200703, -200182, -199664, + -199149, -198636, -198125, -197616, -197110, -196606, + -196105, -195606, -195109, -194614, -194122, -193631, + -193143, -192658, -192174, -191693, -191213, -190736, + -190261, -189789, -189318, -188849, -188382, -187918, + -187455, -186995, -186536, -186080, -185625, -185173, + -184722, -184274, -183827, -183382, -182939, -182498, + -182059, -181622, -181186, -180753, -180321, -179891, + -179463, -179037, -178612, -178190, -177769, -177349, + -176932, -176516, -176102, -175690, -175279, -174870, + -174463, -174057, -173653, -173251, -172850, -172451, + -172053, -171657, -171263, -170870, -170479, -170089, + -169701, -169315, -168930, -168546, -168164, -167784, + -167405, -167027, -166651, -166277, -165904, -165532, + -165162, -164793, -164426, -164060, -163695, -163332, + -162970, -162610, -162251, -161893, -161537, -161182, + -160828, -160476, -160125, -159775, -159427, -159079, + -158734, -158389, -158046, -157704, -157363, -157024, + -156686, -156349, -156013, -155678, -155345, -155013, + -154682, -154352, -154024, -153697, -153370, -153045, + -152722, -152399, -152077, -151757, -151438, -151120, + -150803, -150487, -150172, -149859, -149546, -149235, + -148924, -148615, -148307, -148000, -147693, -147388, + -147084, -146782, -146480, -146179, -145879, -145580, + -145282, -144986, -144690, -144395, -144101, -143808, + -143517, -143226, -142936, -142647, -142359, -142072, + -141786, -141501, -141217, -140934, -140651, -140370, + -140090, -139810, -139532, -139254, -138977, -138701, + -138426, -138152, -137879, -137607, -137335, -137065, + -136795, -136526, -136258, -135991, -135725, -135459, + -135195, -134931, -134668, -134406, -134145, -133884, + -133625, -133366, -133108, -132851, -132594, -132339, + -132084, -131830, -131576, -131324, -131072, -130821, + -130571, -130322, -130073, -129825, -129578, -129332, + -129086, -128841, -128597, -128353, -128111, -127869, + -127627, -127387, -127147, -126908, -126669, -126432, + -126195, -125959, -125723, -125488, -125254, -125020, + -124787, -124555, -124324, -124093, -123863, -123633, + -123404, -123176, -122949, -122722, -122496, -122270, + -122045, -121821, -121597, -121374, -121152, -120930, + -120709, -120489, -120269, -120050, -119831, -119613, + -119396, -119179, -118963, -118747, -118532, -118318, + -118104, -117891, -117678, -117466, -117254, -117044, + -116833, -116623, -116414, -116206, -115998, -115790, + -115583, -115377, -115171, -114966, -114761, -114557, + -114354, -114151, -113948, -113746, -113545, -113344, + -113143, -112944, -112744, -112546, -112347, -112150, + -111952, -111756, -111560, -111364, -111169, -110974, + -110780, -110586, -110393, -110200, -110008, -109817, + -109626, -109435, -109245, -109055, -108866, -108677, + -108489, -108301, -108114, -107927, -107741, -107555, + -107369, -107184, -107000, -106816, -106632, -106449, + -106266, -106084, -105902, -105721, -105540, -105360, + -105180, -105000, -104821, -104643, -104465, -104287, + -104109, -103933, -103756, -103580, -103404, -103229, + -103054, -102880, -102706, -102533, -102360, -102187, + -102015, -101843, -101671, -101500, -101330, -101159, + -100990, -100820, -100651, -100482, -100314, -100146, + -99979, -99812, -99645, -99479, -99313, -99148, + -98982, -98818, -98653, -98489, -98326, -98163, + -98000, -97837, -97675, -97513, -97352, -97191, + -97030, -96870, -96710, -96551, -96391, -96233, + -96074, -95916, -95758, -95601, -95444, -95287, + -95131, -94975, -94819, -94664, -94509, -94354, + -94200, -94046, -93892, -93739, -93586, -93434, + -93281, -93129, -92978, -92826, -92675, -92525, + -92375, -92225, -92075, -91926, -91777, -91628, + -91480, -91332, -91184, -91036, -90889, -90742, + -90596, -90450, -90304, -90158, -90013, -89868, + -89724, -89579, -89435, -89292, -89148, -89005, + -88862, -88720, -88577, -88435, -88294, -88152, + -88011, -87871, -87730, -87590, -87450, -87310, + -87171, -87032, -86893, -86755, -86616, -86479, + -86341, -86204, -86066, -85930, -85793, -85657, + -85521, -85385, -85250, -85114, -84980, -84845, + -84710, -84576, -84443, -84309, -84176, -84043, + -83910, -83777, -83645, -83513, -83381, -83250, + -83118, -82987, -82857, -82726, -82596, -82466, + -82336, -82207, -82078, -81949, -81820, -81691, + -81563, -81435, -81307, -81180, -81053, -80925, + -80799, -80672, -80546, -80420, -80294, -80168, + -80043, -79918, -79793, -79668, -79544, -79420, + -79296, -79172, -79048, -78925, -78802, -78679, + -78557, -78434, -78312, -78190, -78068, -77947, + -77826, -77705, -77584, -77463, -77343, -77223, + -77103, -76983, -76864, -76744, -76625, -76506, + -76388, -76269, -76151, -76033, -75915, -75797, + -75680, -75563, -75446, -75329, -75213, -75096, + -74980, -74864, -74748, -74633, -74517, -74402, + -74287, -74172, -74058, -73944, -73829, -73715, + -73602, -73488, -73375, -73262, -73149, -73036, + -72923, -72811, -72699, -72587, -72475, -72363, + -72252, -72140, -72029, -71918, -71808, -71697, + -71587, -71477, -71367, -71257, -71147, -71038, + -70929, -70820, -70711, -70602, -70494, -70385, + -70277, -70169, -70061, -69954, -69846, -69739, + -69632, -69525, -69418, -69312, -69205, -69099, + -68993, -68887, -68781, -68676, -68570, -68465, + -68360, -68255, -68151, -68046, -67942, -67837, + -67733, -67629, -67526, -67422, -67319, -67216, + -67113, -67010, -66907, -66804, -66702, -66600, + -66498, -66396, -66294, -66192, -66091, -65989, + -65888, -65787, -65686, -65586, -65485, -65385, + -65285, -65185, -65085, -64985, -64885, -64786, + -64687, -64587, -64488, -64389, -64291, -64192, + -64094, -63996, -63897, -63799, -63702, -63604, + -63506, -63409, -63312, -63215, -63118, -63021, + -62924, -62828, -62731, -62635, -62539, -62443, + -62347, -62251, -62156, -62060, -61965, -61870, + -61775, -61680, -61585, -61491, -61396, -61302, + -61208, -61114, -61020, -60926, -60833, -60739, + -60646, -60552, -60459, -60366, -60273, -60181, + -60088, -59996, -59903, -59811, -59719, -59627, + -59535, -59444, -59352, -59261, -59169, -59078, + -58987, -58896, -58805, -58715, -58624, -58534, + -58443, -58353, -58263, -58173, -58083, -57994, + -57904, -57815, -57725, -57636, -57547, -57458, + -57369, -57281, -57192, -57104, -57015, -56927, + -56839, -56751, -56663, -56575, -56487, -56400, + -56312, -56225, -56138, -56051, -55964, -55877, + -55790, -55704, -55617, -55531, -55444, -55358, + -55272, -55186, -55100, -55015, -54929, -54843, + -54758, -54673, -54587, -54502, -54417, -54333, + -54248, -54163, -54079, -53994, -53910, -53826, + -53741, -53657, -53574, -53490, -53406, -53322, + -53239, -53156, -53072, -52989, -52906, -52823, + -52740, -52657, -52575, -52492, -52410, -52327, + -52245, -52163, -52081, -51999, -51917, -51835, + -51754, -51672, -51591, -51509, -51428, -51347, + -51266, -51185, -51104, -51023, -50942, -50862, + -50781, -50701, -50621, -50540, -50460, -50380, + -50300, -50221, -50141, -50061, -49982, -49902, + -49823, -49744, -49664, -49585, -49506, -49427, + -49349, -49270, -49191, -49113, -49034, -48956, + -48878, -48799, -48721, -48643, -48565, -48488, + -48410, -48332, -48255, -48177, -48100, -48022, + -47945, -47868, -47791, -47714, -47637, -47560, + -47484, -47407, -47331, -47254, -47178, -47102, + -47025, -46949, -46873, -46797, -46721, -46646, + -46570, -46494, -46419, -46343, -46268, -46193, + -46118, -46042, -45967, -45892, -45818, -45743, + -45668, -45593, -45519, -45444, -45370, -45296, + -45221, -45147, -45073, -44999, -44925, -44851, + -44778, -44704, -44630, -44557, -44483, -44410, + -44337, -44263, -44190, -44117, -44044, -43971, + -43898, -43826, -43753, -43680, -43608, -43535, + -43463, -43390, -43318, -43246, -43174, -43102, + -43030, -42958, -42886, -42814, -42743, -42671, + -42600, -42528, -42457, -42385, -42314, -42243, + -42172, -42101, -42030, -41959, -41888, -41817, + -41747, -41676, -41605, -41535, -41465, -41394, + -41324, -41254, -41184, -41113, -41043, -40973, + -40904, -40834, -40764, -40694, -40625, -40555, + -40486, -40416, -40347, -40278, -40208, -40139, + -40070, -40001, -39932, -39863, -39794, -39726, + -39657, -39588, -39520, -39451, -39383, -39314, + -39246, -39178, -39110, -39042, -38973, -38905, + -38837, -38770, -38702, -38634, -38566, -38499, + -38431, -38364, -38296, -38229, -38161, -38094, + -38027, -37960, -37893, -37826, -37759, -37692, + -37625, -37558, -37491, -37425, -37358, -37291, + -37225, -37158, -37092, -37026, -36959, -36893, + -36827, -36761, -36695, -36629, -36563, -36497, + -36431, -36365, -36300, -36234, -36168, -36103, + -36037, -35972, -35907, -35841, -35776, -35711, + -35646, -35580, -35515, -35450, -35385, -35321, + -35256, -35191, -35126, -35062, -34997, -34932, + -34868, -34803, -34739, -34675, -34610, -34546, + -34482, -34418, -34354, -34289, -34225, -34162, + -34098, -34034, -33970, -33906, -33843, -33779, + -33715, -33652, -33588, -33525, -33461, -33398, + -33335, -33272, -33208, -33145, -33082, -33019, + -32956, -32893, -32830, -32767, -32705, -32642, + -32579, -32516, -32454, -32391, -32329, -32266, + -32204, -32141, -32079, -32017, -31955, -31892, + -31830, -31768, -31706, -31644, -31582, -31520, + -31458, -31396, -31335, -31273, -31211, -31150, + -31088, -31026, -30965, -30904, -30842, -30781, + -30719, -30658, -30597, -30536, -30474, -30413, + -30352, -30291, -30230, -30169, -30108, -30048, + -29987, -29926, -29865, -29805, -29744, -29683, + -29623, -29562, -29502, -29441, -29381, -29321, + -29260, -29200, -29140, -29080, -29020, -28959, + -28899, -28839, -28779, -28719, -28660, -28600, + -28540, -28480, -28420, -28361, -28301, -28241, + -28182, -28122, -28063, -28003, -27944, -27884, + -27825, -27766, -27707, -27647, -27588, -27529, + -27470, -27411, -27352, -27293, -27234, -27175, + -27116, -27057, -26998, -26940, -26881, -26822, + -26763, -26705, -26646, -26588, -26529, -26471, + -26412, -26354, -26295, -26237, -26179, -26120, + -26062, -26004, -25946, -25888, -25830, -25772, + -25714, -25656, -25598, -25540, -25482, -25424, + -25366, -25308, -25251, -25193, -25135, -25078, + -25020, -24962, -24905, -24847, -24790, -24732, + -24675, -24618, -24560, -24503, -24446, -24389, + -24331, -24274, -24217, -24160, -24103, -24046, + -23989, -23932, -23875, -23818, -23761, -23704, + -23647, -23591, -23534, -23477, -23420, -23364, + -23307, -23250, -23194, -23137, -23081, -23024, + -22968, -22911, -22855, -22799, -22742, -22686, + -22630, -22573, -22517, -22461, -22405, -22349, + -22293, -22237, -22181, -22125, -22069, -22013, + -21957, -21901, -21845, -21789, -21733, -21678, + -21622, -21566, -21510, -21455, -21399, -21343, + -21288, -21232, -21177, -21121, -21066, -21010, + -20955, -20900, -20844, -20789, -20734, -20678, + -20623, -20568, -20513, -20457, -20402, -20347, + -20292, -20237, -20182, -20127, -20072, -20017, + -19962, -19907, -19852, -19797, -19742, -19688, + -19633, -19578, -19523, -19469, -19414, -19359, + -19305, -19250, -19195, -19141, -19086, -19032, + -18977, -18923, -18868, -18814, -18760, -18705, + -18651, -18597, -18542, -18488, -18434, -18380, + -18325, -18271, -18217, -18163, -18109, -18055, + -18001, -17946, -17892, -17838, -17784, -17731, + -17677, -17623, -17569, -17515, -17461, -17407, + -17353, -17300, -17246, -17192, -17138, -17085, + -17031, -16977, -16924, -16870, -16817, -16763, + -16710, -16656, -16603, -16549, -16496, -16442, + -16389, -16335, -16282, -16229, -16175, -16122, + -16069, -16015, -15962, -15909, -15856, -15802, + -15749, -15696, -15643, -15590, -15537, -15484, + -15431, -15378, -15325, -15272, -15219, -15166, + -15113, -15060, -15007, -14954, -14901, -14848, + -14795, -14743, -14690, -14637, -14584, -14531, + -14479, -14426, -14373, -14321, -14268, -14215, + -14163, -14110, -14057, -14005, -13952, -13900, + -13847, -13795, -13742, -13690, -13637, -13585, + -13533, -13480, -13428, -13375, -13323, -13271, + -13218, -13166, -13114, -13062, -13009, -12957, + -12905, -12853, -12800, -12748, -12696, -12644, + -12592, -12540, -12488, -12436, -12383, -12331, + -12279, -12227, -12175, -12123, -12071, -12019, + -11967, -11916, -11864, -11812, -11760, -11708, + -11656, -11604, -11552, -11501, -11449, -11397, + -11345, -11293, -11242, -11190, -11138, -11086, + -11035, -10983, -10931, -10880, -10828, -10777, + -10725, -10673, -10622, -10570, -10519, -10467, + -10415, -10364, -10312, -10261, -10209, -10158, + -10106, -10055, -10004, -9952, -9901, -9849, + -9798, -9747, -9695, -9644, -9592, -9541, + -9490, -9438, -9387, -9336, -9285, -9233, + -9182, -9131, -9080, -9028, -8977, -8926, + -8875, -8824, -8772, -8721, -8670, -8619, + -8568, -8517, -8466, -8414, -8363, -8312, + -8261, -8210, -8159, -8108, -8057, -8006, + -7955, -7904, -7853, -7802, -7751, -7700, + -7649, -7598, -7547, -7496, -7445, -7395, + -7344, -7293, -7242, -7191, -7140, -7089, + -7038, -6988, -6937, -6886, -6835, -6784, + -6733, -6683, -6632, -6581, -6530, -6480, + -6429, -6378, -6327, -6277, -6226, -6175, + -6124, -6074, -6023, -5972, -5922, -5871, + -5820, -5770, -5719, -5668, -5618, -5567, + -5517, -5466, -5415, -5365, -5314, -5264, + -5213, -5162, -5112, -5061, -5011, -4960, + -4910, -4859, -4808, -4758, -4707, -4657, + -4606, -4556, -4505, -4455, -4404, -4354, + -4303, -4253, -4202, -4152, -4101, -4051, + -4001, -3950, -3900, -3849, -3799, -3748, + -3698, -3648, -3597, -3547, -3496, -3446, + -3395, -3345, -3295, -3244, -3194, -3144, + -3093, -3043, -2992, -2942, -2892, -2841, + -2791, -2741, -2690, -2640, -2590, -2539, + -2489, -2439, -2388, -2338, -2288, -2237, + -2187, -2137, -2086, -2036, -1986, -1935, + -1885, -1835, -1784, -1734, -1684, -1633, + -1583, -1533, -1483, -1432, -1382, -1332, + -1281, -1231, -1181, -1131, -1080, -1030, + -980, -929, -879, -829, -779, -728, + -678, -628, -578, -527, -477, -427, + -376, -326, -276, -226, -175, -125, + -75, -25, 25, 75, 125, 175, + 226, 276, 326, 376, 427, 477, + 527, 578, 628, 678, 728, 779, + 829, 879, 929, 980, 1030, 1080, + 1131, 1181, 1231, 1281, 1332, 1382, + 1432, 1483, 1533, 1583, 1633, 1684, + 1734, 1784, 1835, 1885, 1935, 1986, + 2036, 2086, 2137, 2187, 2237, 2288, + 2338, 2388, 2439, 2489, 2539, 2590, + 2640, 2690, 2741, 2791, 2841, 2892, + 2942, 2992, 3043, 3093, 3144, 3194, + 3244, 3295, 3345, 3395, 3446, 3496, + 3547, 3597, 3648, 3698, 3748, 3799, + 3849, 3900, 3950, 4001, 4051, 4101, + 4152, 4202, 4253, 4303, 4354, 4404, + 4455, 4505, 4556, 4606, 4657, 4707, + 4758, 4808, 4859, 4910, 4960, 5011, + 5061, 5112, 5162, 5213, 5264, 5314, + 5365, 5415, 5466, 5517, 5567, 5618, + 5668, 5719, 5770, 5820, 5871, 5922, + 5972, 6023, 6074, 6124, 6175, 6226, + 6277, 6327, 6378, 6429, 6480, 6530, + 6581, 6632, 6683, 6733, 6784, 6835, + 6886, 6937, 6988, 7038, 7089, 7140, + 7191, 7242, 7293, 7344, 7395, 7445, + 7496, 7547, 7598, 7649, 7700, 7751, + 7802, 7853, 7904, 7955, 8006, 8057, + 8108, 8159, 8210, 8261, 8312, 8363, + 8414, 8466, 8517, 8568, 8619, 8670, + 8721, 8772, 8824, 8875, 8926, 8977, + 9028, 9080, 9131, 9182, 9233, 9285, + 9336, 9387, 9438, 9490, 9541, 9592, + 9644, 9695, 9747, 9798, 9849, 9901, + 9952, 10004, 10055, 10106, 10158, 10209, + 10261, 10312, 10364, 10415, 10467, 10519, + 10570, 10622, 10673, 10725, 10777, 10828, + 10880, 10931, 10983, 11035, 11086, 11138, + 11190, 11242, 11293, 11345, 11397, 11449, + 11501, 11552, 11604, 11656, 11708, 11760, + 11812, 11864, 11916, 11967, 12019, 12071, + 12123, 12175, 12227, 12279, 12331, 12383, + 12436, 12488, 12540, 12592, 12644, 12696, + 12748, 12800, 12853, 12905, 12957, 13009, + 13062, 13114, 13166, 13218, 13271, 13323, + 13375, 13428, 13480, 13533, 13585, 13637, + 13690, 13742, 13795, 13847, 13900, 13952, + 14005, 14057, 14110, 14163, 14215, 14268, + 14321, 14373, 14426, 14479, 14531, 14584, + 14637, 14690, 14743, 14795, 14848, 14901, + 14954, 15007, 15060, 15113, 15166, 15219, + 15272, 15325, 15378, 15431, 15484, 15537, + 15590, 15643, 15696, 15749, 15802, 15856, + 15909, 15962, 16015, 16069, 16122, 16175, + 16229, 16282, 16335, 16389, 16442, 16496, + 16549, 16603, 16656, 16710, 16763, 16817, + 16870, 16924, 16977, 17031, 17085, 17138, + 17192, 17246, 17300, 17353, 17407, 17461, + 17515, 17569, 17623, 17677, 17731, 17784, + 17838, 17892, 17946, 18001, 18055, 18109, + 18163, 18217, 18271, 18325, 18380, 18434, + 18488, 18542, 18597, 18651, 18705, 18760, + 18814, 18868, 18923, 18977, 19032, 19086, + 19141, 19195, 19250, 19305, 19359, 19414, + 19469, 19523, 19578, 19633, 19688, 19742, + 19797, 19852, 19907, 19962, 20017, 20072, + 20127, 20182, 20237, 20292, 20347, 20402, + 20457, 20513, 20568, 20623, 20678, 20734, + 20789, 20844, 20900, 20955, 21010, 21066, + 21121, 21177, 21232, 21288, 21343, 21399, + 21455, 21510, 21566, 21622, 21678, 21733, + 21789, 21845, 21901, 21957, 22013, 22069, + 22125, 22181, 22237, 22293, 22349, 22405, + 22461, 22517, 22573, 22630, 22686, 22742, + 22799, 22855, 22911, 22968, 23024, 23081, + 23137, 23194, 23250, 23307, 23364, 23420, + 23477, 23534, 23591, 23647, 23704, 23761, + 23818, 23875, 23932, 23989, 24046, 24103, + 24160, 24217, 24274, 24331, 24389, 24446, + 24503, 24560, 24618, 24675, 24732, 24790, + 24847, 24905, 24962, 25020, 25078, 25135, + 25193, 25251, 25308, 25366, 25424, 25482, + 25540, 25598, 25656, 25714, 25772, 25830, + 25888, 25946, 26004, 26062, 26120, 26179, + 26237, 26295, 26354, 26412, 26471, 26529, + 26588, 26646, 26705, 26763, 26822, 26881, + 26940, 26998, 27057, 27116, 27175, 27234, + 27293, 27352, 27411, 27470, 27529, 27588, + 27647, 27707, 27766, 27825, 27884, 27944, + 28003, 28063, 28122, 28182, 28241, 28301, + 28361, 28420, 28480, 28540, 28600, 28660, + 28719, 28779, 28839, 28899, 28959, 29020, + 29080, 29140, 29200, 29260, 29321, 29381, + 29441, 29502, 29562, 29623, 29683, 29744, + 29805, 29865, 29926, 29987, 30048, 30108, + 30169, 30230, 30291, 30352, 30413, 30474, + 30536, 30597, 30658, 30719, 30781, 30842, + 30904, 30965, 31026, 31088, 31150, 31211, + 31273, 31335, 31396, 31458, 31520, 31582, + 31644, 31706, 31768, 31830, 31892, 31955, + 32017, 32079, 32141, 32204, 32266, 32329, + 32391, 32454, 32516, 32579, 32642, 32705, + 32767, 32830, 32893, 32956, 33019, 33082, + 33145, 33208, 33272, 33335, 33398, 33461, + 33525, 33588, 33652, 33715, 33779, 33843, + 33906, 33970, 34034, 34098, 34162, 34225, + 34289, 34354, 34418, 34482, 34546, 34610, + 34675, 34739, 34803, 34868, 34932, 34997, + 35062, 35126, 35191, 35256, 35321, 35385, + 35450, 35515, 35580, 35646, 35711, 35776, + 35841, 35907, 35972, 36037, 36103, 36168, + 36234, 36300, 36365, 36431, 36497, 36563, + 36629, 36695, 36761, 36827, 36893, 36959, + 37026, 37092, 37158, 37225, 37291, 37358, + 37425, 37491, 37558, 37625, 37692, 37759, + 37826, 37893, 37960, 38027, 38094, 38161, + 38229, 38296, 38364, 38431, 38499, 38566, + 38634, 38702, 38770, 38837, 38905, 38973, + 39042, 39110, 39178, 39246, 39314, 39383, + 39451, 39520, 39588, 39657, 39726, 39794, + 39863, 39932, 40001, 40070, 40139, 40208, + 40278, 40347, 40416, 40486, 40555, 40625, + 40694, 40764, 40834, 40904, 40973, 41043, + 41113, 41184, 41254, 41324, 41394, 41465, + 41535, 41605, 41676, 41747, 41817, 41888, + 41959, 42030, 42101, 42172, 42243, 42314, + 42385, 42457, 42528, 42600, 42671, 42743, + 42814, 42886, 42958, 43030, 43102, 43174, + 43246, 43318, 43390, 43463, 43535, 43608, + 43680, 43753, 43826, 43898, 43971, 44044, + 44117, 44190, 44263, 44337, 44410, 44483, + 44557, 44630, 44704, 44778, 44851, 44925, + 44999, 45073, 45147, 45221, 45296, 45370, + 45444, 45519, 45593, 45668, 45743, 45818, + 45892, 45967, 46042, 46118, 46193, 46268, + 46343, 46419, 46494, 46570, 46646, 46721, + 46797, 46873, 46949, 47025, 47102, 47178, + 47254, 47331, 47407, 47484, 47560, 47637, + 47714, 47791, 47868, 47945, 48022, 48100, + 48177, 48255, 48332, 48410, 48488, 48565, + 48643, 48721, 48799, 48878, 48956, 49034, + 49113, 49191, 49270, 49349, 49427, 49506, + 49585, 49664, 49744, 49823, 49902, 49982, + 50061, 50141, 50221, 50300, 50380, 50460, + 50540, 50621, 50701, 50781, 50862, 50942, + 51023, 51104, 51185, 51266, 51347, 51428, + 51509, 51591, 51672, 51754, 51835, 51917, + 51999, 52081, 52163, 52245, 52327, 52410, + 52492, 52575, 52657, 52740, 52823, 52906, + 52989, 53072, 53156, 53239, 53322, 53406, + 53490, 53574, 53657, 53741, 53826, 53910, + 53994, 54079, 54163, 54248, 54333, 54417, + 54502, 54587, 54673, 54758, 54843, 54929, + 55015, 55100, 55186, 55272, 55358, 55444, + 55531, 55617, 55704, 55790, 55877, 55964, + 56051, 56138, 56225, 56312, 56400, 56487, + 56575, 56663, 56751, 56839, 56927, 57015, + 57104, 57192, 57281, 57369, 57458, 57547, + 57636, 57725, 57815, 57904, 57994, 58083, + 58173, 58263, 58353, 58443, 58534, 58624, + 58715, 58805, 58896, 58987, 59078, 59169, + 59261, 59352, 59444, 59535, 59627, 59719, + 59811, 59903, 59996, 60088, 60181, 60273, + 60366, 60459, 60552, 60646, 60739, 60833, + 60926, 61020, 61114, 61208, 61302, 61396, + 61491, 61585, 61680, 61775, 61870, 61965, + 62060, 62156, 62251, 62347, 62443, 62539, + 62635, 62731, 62828, 62924, 63021, 63118, + 63215, 63312, 63409, 63506, 63604, 63702, + 63799, 63897, 63996, 64094, 64192, 64291, + 64389, 64488, 64587, 64687, 64786, 64885, + 64985, 65085, 65185, 65285, 65385, 65485, + 65586, 65686, 65787, 65888, 65989, 66091, + 66192, 66294, 66396, 66498, 66600, 66702, + 66804, 66907, 67010, 67113, 67216, 67319, + 67422, 67526, 67629, 67733, 67837, 67942, + 68046, 68151, 68255, 68360, 68465, 68570, + 68676, 68781, 68887, 68993, 69099, 69205, + 69312, 69418, 69525, 69632, 69739, 69846, + 69954, 70061, 70169, 70277, 70385, 70494, + 70602, 70711, 70820, 70929, 71038, 71147, + 71257, 71367, 71477, 71587, 71697, 71808, + 71918, 72029, 72140, 72252, 72363, 72475, + 72587, 72699, 72811, 72923, 73036, 73149, + 73262, 73375, 73488, 73602, 73715, 73829, + 73944, 74058, 74172, 74287, 74402, 74517, + 74633, 74748, 74864, 74980, 75096, 75213, + 75329, 75446, 75563, 75680, 75797, 75915, + 76033, 76151, 76269, 76388, 76506, 76625, + 76744, 76864, 76983, 77103, 77223, 77343, + 77463, 77584, 77705, 77826, 77947, 78068, + 78190, 78312, 78434, 78557, 78679, 78802, + 78925, 79048, 79172, 79296, 79420, 79544, + 79668, 79793, 79918, 80043, 80168, 80294, + 80420, 80546, 80672, 80799, 80925, 81053, + 81180, 81307, 81435, 81563, 81691, 81820, + 81949, 82078, 82207, 82336, 82466, 82596, + 82726, 82857, 82987, 83118, 83250, 83381, + 83513, 83645, 83777, 83910, 84043, 84176, + 84309, 84443, 84576, 84710, 84845, 84980, + 85114, 85250, 85385, 85521, 85657, 85793, + 85930, 86066, 86204, 86341, 86479, 86616, + 86755, 86893, 87032, 87171, 87310, 87450, + 87590, 87730, 87871, 88011, 88152, 88294, + 88435, 88577, 88720, 88862, 89005, 89148, + 89292, 89435, 89579, 89724, 89868, 90013, + 90158, 90304, 90450, 90596, 90742, 90889, + 91036, 91184, 91332, 91480, 91628, 91777, + 91926, 92075, 92225, 92375, 92525, 92675, + 92826, 92978, 93129, 93281, 93434, 93586, + 93739, 93892, 94046, 94200, 94354, 94509, + 94664, 94819, 94975, 95131, 95287, 95444, + 95601, 95758, 95916, 96074, 96233, 96391, + 96551, 96710, 96870, 97030, 97191, 97352, + 97513, 97675, 97837, 98000, 98163, 98326, + 98489, 98653, 98818, 98982, 99148, 99313, + 99479, 99645, 99812, 99979, 100146, 100314, + 100482, 100651, 100820, 100990, 101159, 101330, + 101500, 101671, 101843, 102015, 102187, 102360, + 102533, 102706, 102880, 103054, 103229, 103404, + 103580, 103756, 103933, 104109, 104287, 104465, + 104643, 104821, 105000, 105180, 105360, 105540, + 105721, 105902, 106084, 106266, 106449, 106632, + 106816, 107000, 107184, 107369, 107555, 107741, + 107927, 108114, 108301, 108489, 108677, 108866, + 109055, 109245, 109435, 109626, 109817, 110008, + 110200, 110393, 110586, 110780, 110974, 111169, + 111364, 111560, 111756, 111952, 112150, 112347, + 112546, 112744, 112944, 113143, 113344, 113545, + 113746, 113948, 114151, 114354, 114557, 114761, + 114966, 115171, 115377, 115583, 115790, 115998, + 116206, 116414, 116623, 116833, 117044, 117254, + 117466, 117678, 117891, 118104, 118318, 118532, + 118747, 118963, 119179, 119396, 119613, 119831, + 120050, 120269, 120489, 120709, 120930, 121152, + 121374, 121597, 121821, 122045, 122270, 122496, + 122722, 122949, 123176, 123404, 123633, 123863, + 124093, 124324, 124555, 124787, 125020, 125254, + 125488, 125723, 125959, 126195, 126432, 126669, + 126908, 127147, 127387, 127627, 127869, 128111, + 128353, 128597, 128841, 129086, 129332, 129578, + 129825, 130073, 130322, 130571, 130821, 131072, + 131324, 131576, 131830, 132084, 132339, 132594, + 132851, 133108, 133366, 133625, 133884, 134145, + 134406, 134668, 134931, 135195, 135459, 135725, + 135991, 136258, 136526, 136795, 137065, 137335, + 137607, 137879, 138152, 138426, 138701, 138977, + 139254, 139532, 139810, 140090, 140370, 140651, + 140934, 141217, 141501, 141786, 142072, 142359, + 142647, 142936, 143226, 143517, 143808, 144101, + 144395, 144690, 144986, 145282, 145580, 145879, + 146179, 146480, 146782, 147084, 147388, 147693, + 148000, 148307, 148615, 148924, 149235, 149546, + 149859, 150172, 150487, 150803, 151120, 151438, + 151757, 152077, 152399, 152722, 153045, 153370, + 153697, 154024, 154352, 154682, 155013, 155345, + 155678, 156013, 156349, 156686, 157024, 157363, + 157704, 158046, 158389, 158734, 159079, 159427, + 159775, 160125, 160476, 160828, 161182, 161537, + 161893, 162251, 162610, 162970, 163332, 163695, + 164060, 164426, 164793, 165162, 165532, 165904, + 166277, 166651, 167027, 167405, 167784, 168164, + 168546, 168930, 169315, 169701, 170089, 170479, + 170870, 171263, 171657, 172053, 172451, 172850, + 173251, 173653, 174057, 174463, 174870, 175279, + 175690, 176102, 176516, 176932, 177349, 177769, + 178190, 178612, 179037, 179463, 179891, 180321, + 180753, 181186, 181622, 182059, 182498, 182939, + 183382, 183827, 184274, 184722, 185173, 185625, + 186080, 186536, 186995, 187455, 187918, 188382, + 188849, 189318, 189789, 190261, 190736, 191213, + 191693, 192174, 192658, 193143, 193631, 194122, + 194614, 195109, 195606, 196105, 196606, 197110, + 197616, 198125, 198636, 199149, 199664, 200182, + 200703, 201226, 201751, 202279, 202809, 203342, + 203878, 204416, 204956, 205500, 206045, 206594, + 207145, 207699, 208255, 208815, 209376, 209941, + 210509, 211079, 211652, 212228, 212807, 213389, + 213973, 214561, 215151, 215745, 216341, 216941, + 217544, 218149, 218758, 219370, 219985, 220603, + 221225, 221849, 222477, 223108, 223743, 224381, + 225022, 225666, 226314, 226966, 227621, 228279, + 228941, 229606, 230275, 230948, 231624, 232304, + 232988, 233676, 234367, 235062, 235761, 236463, + 237170, 237881, 238595, 239314, 240036, 240763, + 241493, 242228, 242967, 243711, 244458, 245210, + 245966, 246727, 247492, 248261, 249035, 249813, + 250596, 251384, 252176, 252973, 253774, 254581, + 255392, 256208, 257029, 257855, 258686, 259522, + 260363, 261209, 262060, 262917, 263779, 264646, + 265519, 266397, 267280, 268169, 269064, 269965, + 270871, 271782, 272700, 273624, 274553, 275489, + 276430, 277378, 278332, 279292, 280258, 281231, + 282210, 283195, 284188, 285186, 286192, 287204, + 288223, 289249, 290282, 291322, 292369, 293423, + 294485, 295554, 296630, 297714, 298805, 299904, + 301011, 302126, 303248, 304379, 305517, 306664, + 307819, 308983, 310154, 311335, 312524, 313721, + 314928, 316143, 317368, 318601, 319844, 321097, + 322358, 323629, 324910, 326201, 327502, 328812, + 330133, 331464, 332805, 334157, 335519, 336892, + 338276, 339671, 341078, 342495, 343924, 345364, + 346816, 348280, 349756, 351244, 352744, 354257, + 355783, 357321, 358872, 360436, 362013, 363604, + 365208, 366826, 368459, 370105, 371765, 373440, + 375130, 376835, 378555, 380290, 382040, 383807, + 385589, 387387, 389202, 391034, 392882, 394747, + 396630, 398530, 400448, 402384, 404338, 406311, + 408303, 410314, 412344, 414395, 416465, 418555, + 420666, 422798, 424951, 427125, 429321, 431540, + 433781, 436045, 438332, 440643, 442978, 445337, + 447720, 450129, 452564, 455024, 457511, 460024, + 462565, 465133, 467730, 470355, 473009, 475692, + 478406, 481150, 483925, 486732, 489571, 492443, + 495348, 498287, 501261, 504269, 507313, 510394, + 513512, 516667, 519861, 523094, 526366, 529680, + 533034, 536431, 539870, 543354, 546881, 550455, + 554074, 557741, 561456, 565221, 569035, 572901, + 576818, 580789, 584815, 588896, 593033, 597229, + 601483, 605798, 610174, 614613, 619117, 623686, + 628323, 633028, 637803, 642651, 647572, 652568, + 657640, 662792, 668024, 673338, 678737, 684223, + 689797, 695462, 701219, 707072, 713023, 719074, + 725227, 731486, 737853, 744331, 750922, 757631, + 764460, 771411, 778490, 785699, 793041, 800521, + 808143, 815910, 823827, 831898, 840127, 848520, + 857081, 865817, 874730, 883829, 893117, 902602, + 912289, 922186, 932298, 942633, 953199, 964003, + 975054, 986361, 997931, 1009774, 1021901, 1034322, + 1047046, 1060087, 1073455, 1087164, 1101225, 1115654, + 1130465, 1145673, 1161294, 1177345, 1193846, 1210813, + 1228269, 1246234, 1264730, 1283783, 1303416, 1323658, + 1344537, 1366084, 1388330, 1411312, 1435065, 1459630, + 1485049, 1511367, 1538632, 1566898, 1596220, 1626658, + 1658278, 1691149, 1725348, 1760956, 1798063, 1836758, + 1877161, 1919378, 1963536, 2009771, 2058233, 2109087, + 2162516, 2218719, 2277919, 2340362, 2406322, 2476104, + 2550052, 2628549, 2712030, 2800983, 2895966, 2997613, + 3106651, 3223918, 3350381, 3487165, 3635590, 3797206, + 3973855, 4167737, 4381502, 4618375, 4882318, 5178251, + 5512368, 5892567, 6329090, 6835455, 7429880, 8137527, + 8994149, 10052327, 11392683, 13145455, 15535599, 18988036, + 24413316, 34178904, 56965752, 170910304, +}; + +const fixed_t finesine[10240] = +{ + 25, 75, 125, 175, 226, 276, 326, 376, 427, + 477, 527, 578, 628, 678, 728, 779, 829, 879, + 929, 980, 1030, 1080, 1130, 1181, 1231, 1281, 1331, + 1382, 1432, 1482, 1532, 1583, 1633, 1683, 1733, 1784, + 1834, 1884, 1934, 1985, 2035, 2085, 2135, 2186, 2236, + 2286, 2336, 2387, 2437, 2487, 2537, 2587, 2638, 2688, + 2738, 2788, 2839, 2889, 2939, 2989, 3039, 3090, 3140, + 3190, 3240, 3291, 3341, 3391, 3441, 3491, 3541, 3592, + 3642, 3692, 3742, 3792, 3843, 3893, 3943, 3993, 4043, + 4093, 4144, 4194, 4244, 4294, 4344, 4394, 4445, 4495, + 4545, 4595, 4645, 4695, 4745, 4796, 4846, 4896, 4946, + 4996, 5046, 5096, 5146, 5197, 5247, 5297, 5347, 5397, + 5447, 5497, 5547, 5597, 5647, 5697, 5748, 5798, 5848, + 5898, 5948, 5998, 6048, 6098, 6148, 6198, 6248, 6298, + 6348, 6398, 6448, 6498, 6548, 6598, 6648, 6698, 6748, + 6798, 6848, 6898, 6948, 6998, 7048, 7098, 7148, 7198, + 7248, 7298, 7348, 7398, 7448, 7498, 7548, 7598, 7648, + 7697, 7747, 7797, 7847, 7897, 7947, 7997, 8047, 8097, + 8147, 8196, 8246, 8296, 8346, 8396, 8446, 8496, 8545, + 8595, 8645, 8695, 8745, 8794, 8844, 8894, 8944, 8994, + 9043, 9093, 9143, 9193, 9243, 9292, 9342, 9392, 9442, + 9491, 9541, 9591, 9640, 9690, 9740, 9790, 9839, 9889, + 9939, 9988, 10038, 10088, 10137, 10187, 10237, 10286, 10336, + 10386, 10435, 10485, 10534, 10584, 10634, 10683, 10733, 10782, + 10832, 10882, 10931, 10981, 11030, 11080, 11129, 11179, 11228, + 11278, 11327, 11377, 11426, 11476, 11525, 11575, 11624, 11674, + 11723, 11773, 11822, 11872, 11921, 11970, 12020, 12069, 12119, + 12168, 12218, 12267, 12316, 12366, 12415, 12464, 12514, 12563, + 12612, 12662, 12711, 12760, 12810, 12859, 12908, 12957, 13007, + 13056, 13105, 13154, 13204, 13253, 13302, 13351, 13401, 13450, + 13499, 13548, 13597, 13647, 13696, 13745, 13794, 13843, 13892, + 13941, 13990, 14040, 14089, 14138, 14187, 14236, 14285, 14334, + 14383, 14432, 14481, 14530, 14579, 14628, 14677, 14726, 14775, + 14824, 14873, 14922, 14971, 15020, 15069, 15118, 15167, 15215, + 15264, 15313, 15362, 15411, 15460, 15509, 15557, 15606, 15655, + 15704, 15753, 15802, 15850, 15899, 15948, 15997, 16045, 16094, + 16143, 16191, 16240, 16289, 16338, 16386, 16435, 16484, 16532, + 16581, 16629, 16678, 16727, 16775, 16824, 16872, 16921, 16970, + 17018, 17067, 17115, 17164, 17212, 17261, 17309, 17358, 17406, + 17455, 17503, 17551, 17600, 17648, 17697, 17745, 17793, 17842, + 17890, 17939, 17987, 18035, 18084, 18132, 18180, 18228, 18277, + 18325, 18373, 18421, 18470, 18518, 18566, 18614, 18663, 18711, + 18759, 18807, 18855, 18903, 18951, 19000, 19048, 19096, 19144, + 19192, 19240, 19288, 19336, 19384, 19432, 19480, 19528, 19576, + 19624, 19672, 19720, 19768, 19816, 19864, 19912, 19959, 20007, + 20055, 20103, 20151, 20199, 20246, 20294, 20342, 20390, 20438, + 20485, 20533, 20581, 20629, 20676, 20724, 20772, 20819, 20867, + 20915, 20962, 21010, 21057, 21105, 21153, 21200, 21248, 21295, + 21343, 21390, 21438, 21485, 21533, 21580, 21628, 21675, 21723, + 21770, 21817, 21865, 21912, 21960, 22007, 22054, 22102, 22149, + 22196, 22243, 22291, 22338, 22385, 22433, 22480, 22527, 22574, + 22621, 22668, 22716, 22763, 22810, 22857, 22904, 22951, 22998, + 23045, 23092, 23139, 23186, 23233, 23280, 23327, 23374, 23421, + 23468, 23515, 23562, 23609, 23656, 23703, 23750, 23796, 23843, + 23890, 23937, 23984, 24030, 24077, 24124, 24171, 24217, 24264, + 24311, 24357, 24404, 24451, 24497, 24544, 24591, 24637, 24684, + 24730, 24777, 24823, 24870, 24916, 24963, 25009, 25056, 25102, + 25149, 25195, 25241, 25288, 25334, 25381, 25427, 25473, 25520, + 25566, 25612, 25658, 25705, 25751, 25797, 25843, 25889, 25936, + 25982, 26028, 26074, 26120, 26166, 26212, 26258, 26304, 26350, + 26396, 26442, 26488, 26534, 26580, 26626, 26672, 26718, 26764, + 26810, 26856, 26902, 26947, 26993, 27039, 27085, 27131, 27176, + 27222, 27268, 27313, 27359, 27405, 27450, 27496, 27542, 27587, + 27633, 27678, 27724, 27770, 27815, 27861, 27906, 27952, 27997, + 28042, 28088, 28133, 28179, 28224, 28269, 28315, 28360, 28405, + 28451, 28496, 28541, 28586, 28632, 28677, 28722, 28767, 28812, + 28858, 28903, 28948, 28993, 29038, 29083, 29128, 29173, 29218, + 29263, 29308, 29353, 29398, 29443, 29488, 29533, 29577, 29622, + 29667, 29712, 29757, 29801, 29846, 29891, 29936, 29980, 30025, + 30070, 30114, 30159, 30204, 30248, 30293, 30337, 30382, 30426, + 30471, 30515, 30560, 30604, 30649, 30693, 30738, 30782, 30826, + 30871, 30915, 30959, 31004, 31048, 31092, 31136, 31181, 31225, + 31269, 31313, 31357, 31402, 31446, 31490, 31534, 31578, 31622, + 31666, 31710, 31754, 31798, 31842, 31886, 31930, 31974, 32017, + 32061, 32105, 32149, 32193, 32236, 32280, 32324, 32368, 32411, + 32455, 32499, 32542, 32586, 32630, 32673, 32717, 32760, 32804, + 32847, 32891, 32934, 32978, 33021, 33065, 33108, 33151, 33195, + 33238, 33281, 33325, 33368, 33411, 33454, 33498, 33541, 33584, + 33627, 33670, 33713, 33756, 33799, 33843, 33886, 33929, 33972, + 34015, 34057, 34100, 34143, 34186, 34229, 34272, 34315, 34358, + 34400, 34443, 34486, 34529, 34571, 34614, 34657, 34699, 34742, + 34785, 34827, 34870, 34912, 34955, 34997, 35040, 35082, 35125, + 35167, 35210, 35252, 35294, 35337, 35379, 35421, 35464, 35506, + 35548, 35590, 35633, 35675, 35717, 35759, 35801, 35843, 35885, + 35927, 35969, 36011, 36053, 36095, 36137, 36179, 36221, 36263, + 36305, 36347, 36388, 36430, 36472, 36514, 36555, 36597, 36639, + 36681, 36722, 36764, 36805, 36847, 36889, 36930, 36972, 37013, + 37055, 37096, 37137, 37179, 37220, 37262, 37303, 37344, 37386, + 37427, 37468, 37509, 37551, 37592, 37633, 37674, 37715, 37756, + 37797, 37838, 37879, 37920, 37961, 38002, 38043, 38084, 38125, + 38166, 38207, 38248, 38288, 38329, 38370, 38411, 38451, 38492, + 38533, 38573, 38614, 38655, 38695, 38736, 38776, 38817, 38857, + 38898, 38938, 38979, 39019, 39059, 39100, 39140, 39180, 39221, + 39261, 39301, 39341, 39382, 39422, 39462, 39502, 39542, 39582, + 39622, 39662, 39702, 39742, 39782, 39822, 39862, 39902, 39942, + 39982, 40021, 40061, 40101, 40141, 40180, 40220, 40260, 40300, + 40339, 40379, 40418, 40458, 40497, 40537, 40576, 40616, 40655, + 40695, 40734, 40773, 40813, 40852, 40891, 40931, 40970, 41009, + 41048, 41087, 41127, 41166, 41205, 41244, 41283, 41322, 41361, + 41400, 41439, 41478, 41517, 41556, 41595, 41633, 41672, 41711, + 41750, 41788, 41827, 41866, 41904, 41943, 41982, 42020, 42059, + 42097, 42136, 42174, 42213, 42251, 42290, 42328, 42366, 42405, + 42443, 42481, 42520, 42558, 42596, 42634, 42672, 42711, 42749, + 42787, 42825, 42863, 42901, 42939, 42977, 43015, 43053, 43091, + 43128, 43166, 43204, 43242, 43280, 43317, 43355, 43393, 43430, + 43468, 43506, 43543, 43581, 43618, 43656, 43693, 43731, 43768, + 43806, 43843, 43880, 43918, 43955, 43992, 44029, 44067, 44104, + 44141, 44178, 44215, 44252, 44289, 44326, 44363, 44400, 44437, + 44474, 44511, 44548, 44585, 44622, 44659, 44695, 44732, 44769, + 44806, 44842, 44879, 44915, 44952, 44989, 45025, 45062, 45098, + 45135, 45171, 45207, 45244, 45280, 45316, 45353, 45389, 45425, + 45462, 45498, 45534, 45570, 45606, 45642, 45678, 45714, 45750, + 45786, 45822, 45858, 45894, 45930, 45966, 46002, 46037, 46073, + 46109, 46145, 46180, 46216, 46252, 46287, 46323, 46358, 46394, + 46429, 46465, 46500, 46536, 46571, 46606, 46642, 46677, 46712, + 46747, 46783, 46818, 46853, 46888, 46923, 46958, 46993, 47028, + 47063, 47098, 47133, 47168, 47203, 47238, 47273, 47308, 47342, + 47377, 47412, 47446, 47481, 47516, 47550, 47585, 47619, 47654, + 47688, 47723, 47757, 47792, 47826, 47860, 47895, 47929, 47963, + 47998, 48032, 48066, 48100, 48134, 48168, 48202, 48237, 48271, + 48305, 48338, 48372, 48406, 48440, 48474, 48508, 48542, 48575, + 48609, 48643, 48676, 48710, 48744, 48777, 48811, 48844, 48878, + 48911, 48945, 48978, 49012, 49045, 49078, 49112, 49145, 49178, + 49211, 49244, 49278, 49311, 49344, 49377, 49410, 49443, 49476, + 49509, 49542, 49575, 49608, 49640, 49673, 49706, 49739, 49771, + 49804, 49837, 49869, 49902, 49935, 49967, 50000, 50032, 50065, + 50097, 50129, 50162, 50194, 50226, 50259, 50291, 50323, 50355, + 50387, 50420, 50452, 50484, 50516, 50548, 50580, 50612, 50644, + 50675, 50707, 50739, 50771, 50803, 50834, 50866, 50898, 50929, + 50961, 50993, 51024, 51056, 51087, 51119, 51150, 51182, 51213, + 51244, 51276, 51307, 51338, 51369, 51401, 51432, 51463, 51494, + 51525, 51556, 51587, 51618, 51649, 51680, 51711, 51742, 51773, + 51803, 51834, 51865, 51896, 51926, 51957, 51988, 52018, 52049, + 52079, 52110, 52140, 52171, 52201, 52231, 52262, 52292, 52322, + 52353, 52383, 52413, 52443, 52473, 52503, 52534, 52564, 52594, + 52624, 52653, 52683, 52713, 52743, 52773, 52803, 52832, 52862, + 52892, 52922, 52951, 52981, 53010, 53040, 53069, 53099, 53128, + 53158, 53187, 53216, 53246, 53275, 53304, 53334, 53363, 53392, + 53421, 53450, 53479, 53508, 53537, 53566, 53595, 53624, 53653, + 53682, 53711, 53739, 53768, 53797, 53826, 53854, 53883, 53911, + 53940, 53969, 53997, 54026, 54054, 54082, 54111, 54139, 54167, + 54196, 54224, 54252, 54280, 54308, 54337, 54365, 54393, 54421, + 54449, 54477, 54505, 54533, 54560, 54588, 54616, 54644, 54672, + 54699, 54727, 54755, 54782, 54810, 54837, 54865, 54892, 54920, + 54947, 54974, 55002, 55029, 55056, 55084, 55111, 55138, 55165, + 55192, 55219, 55246, 55274, 55300, 55327, 55354, 55381, 55408, + 55435, 55462, 55489, 55515, 55542, 55569, 55595, 55622, 55648, + 55675, 55701, 55728, 55754, 55781, 55807, 55833, 55860, 55886, + 55912, 55938, 55965, 55991, 56017, 56043, 56069, 56095, 56121, + 56147, 56173, 56199, 56225, 56250, 56276, 56302, 56328, 56353, + 56379, 56404, 56430, 56456, 56481, 56507, 56532, 56557, 56583, + 56608, 56633, 56659, 56684, 56709, 56734, 56760, 56785, 56810, + 56835, 56860, 56885, 56910, 56935, 56959, 56984, 57009, 57034, + 57059, 57083, 57108, 57133, 57157, 57182, 57206, 57231, 57255, + 57280, 57304, 57329, 57353, 57377, 57402, 57426, 57450, 57474, + 57498, 57522, 57546, 57570, 57594, 57618, 57642, 57666, 57690, + 57714, 57738, 57762, 57785, 57809, 57833, 57856, 57880, 57903, + 57927, 57950, 57974, 57997, 58021, 58044, 58067, 58091, 58114, + 58137, 58160, 58183, 58207, 58230, 58253, 58276, 58299, 58322, + 58345, 58367, 58390, 58413, 58436, 58459, 58481, 58504, 58527, + 58549, 58572, 58594, 58617, 58639, 58662, 58684, 58706, 58729, + 58751, 58773, 58795, 58818, 58840, 58862, 58884, 58906, 58928, + 58950, 58972, 58994, 59016, 59038, 59059, 59081, 59103, 59125, + 59146, 59168, 59190, 59211, 59233, 59254, 59276, 59297, 59318, + 59340, 59361, 59382, 59404, 59425, 59446, 59467, 59488, 59509, + 59530, 59551, 59572, 59593, 59614, 59635, 59656, 59677, 59697, + 59718, 59739, 59759, 59780, 59801, 59821, 59842, 59862, 59883, + 59903, 59923, 59944, 59964, 59984, 60004, 60025, 60045, 60065, + 60085, 60105, 60125, 60145, 60165, 60185, 60205, 60225, 60244, + 60264, 60284, 60304, 60323, 60343, 60363, 60382, 60402, 60421, + 60441, 60460, 60479, 60499, 60518, 60537, 60556, 60576, 60595, + 60614, 60633, 60652, 60671, 60690, 60709, 60728, 60747, 60766, + 60785, 60803, 60822, 60841, 60859, 60878, 60897, 60915, 60934, + 60952, 60971, 60989, 61007, 61026, 61044, 61062, 61081, 61099, + 61117, 61135, 61153, 61171, 61189, 61207, 61225, 61243, 61261, + 61279, 61297, 61314, 61332, 61350, 61367, 61385, 61403, 61420, + 61438, 61455, 61473, 61490, 61507, 61525, 61542, 61559, 61577, + 61594, 61611, 61628, 61645, 61662, 61679, 61696, 61713, 61730, + 61747, 61764, 61780, 61797, 61814, 61831, 61847, 61864, 61880, + 61897, 61913, 61930, 61946, 61963, 61979, 61995, 62012, 62028, + 62044, 62060, 62076, 62092, 62108, 62125, 62141, 62156, 62172, + 62188, 62204, 62220, 62236, 62251, 62267, 62283, 62298, 62314, + 62329, 62345, 62360, 62376, 62391, 62407, 62422, 62437, 62453, + 62468, 62483, 62498, 62513, 62528, 62543, 62558, 62573, 62588, + 62603, 62618, 62633, 62648, 62662, 62677, 62692, 62706, 62721, + 62735, 62750, 62764, 62779, 62793, 62808, 62822, 62836, 62850, + 62865, 62879, 62893, 62907, 62921, 62935, 62949, 62963, 62977, + 62991, 63005, 63019, 63032, 63046, 63060, 63074, 63087, 63101, + 63114, 63128, 63141, 63155, 63168, 63182, 63195, 63208, 63221, + 63235, 63248, 63261, 63274, 63287, 63300, 63313, 63326, 63339, + 63352, 63365, 63378, 63390, 63403, 63416, 63429, 63441, 63454, + 63466, 63479, 63491, 63504, 63516, 63528, 63541, 63553, 63565, + 63578, 63590, 63602, 63614, 63626, 63638, 63650, 63662, 63674, + 63686, 63698, 63709, 63721, 63733, 63745, 63756, 63768, 63779, + 63791, 63803, 63814, 63825, 63837, 63848, 63859, 63871, 63882, + 63893, 63904, 63915, 63927, 63938, 63949, 63960, 63971, 63981, + 63992, 64003, 64014, 64025, 64035, 64046, 64057, 64067, 64078, + 64088, 64099, 64109, 64120, 64130, 64140, 64151, 64161, 64171, + 64181, 64192, 64202, 64212, 64222, 64232, 64242, 64252, 64261, + 64271, 64281, 64291, 64301, 64310, 64320, 64330, 64339, 64349, + 64358, 64368, 64377, 64387, 64396, 64405, 64414, 64424, 64433, + 64442, 64451, 64460, 64469, 64478, 64487, 64496, 64505, 64514, + 64523, 64532, 64540, 64549, 64558, 64566, 64575, 64584, 64592, + 64601, 64609, 64617, 64626, 64634, 64642, 64651, 64659, 64667, + 64675, 64683, 64691, 64699, 64707, 64715, 64723, 64731, 64739, + 64747, 64754, 64762, 64770, 64777, 64785, 64793, 64800, 64808, + 64815, 64822, 64830, 64837, 64844, 64852, 64859, 64866, 64873, + 64880, 64887, 64895, 64902, 64908, 64915, 64922, 64929, 64936, + 64943, 64949, 64956, 64963, 64969, 64976, 64982, 64989, 64995, + 65002, 65008, 65015, 65021, 65027, 65033, 65040, 65046, 65052, + 65058, 65064, 65070, 65076, 65082, 65088, 65094, 65099, 65105, + 65111, 65117, 65122, 65128, 65133, 65139, 65144, 65150, 65155, + 65161, 65166, 65171, 65177, 65182, 65187, 65192, 65197, 65202, + 65207, 65212, 65217, 65222, 65227, 65232, 65237, 65242, 65246, + 65251, 65256, 65260, 65265, 65270, 65274, 65279, 65283, 65287, + 65292, 65296, 65300, 65305, 65309, 65313, 65317, 65321, 65325, + 65329, 65333, 65337, 65341, 65345, 65349, 65352, 65356, 65360, + 65363, 65367, 65371, 65374, 65378, 65381, 65385, 65388, 65391, + 65395, 65398, 65401, 65404, 65408, 65411, 65414, 65417, 65420, + 65423, 65426, 65429, 65431, 65434, 65437, 65440, 65442, 65445, + 65448, 65450, 65453, 65455, 65458, 65460, 65463, 65465, 65467, + 65470, 65472, 65474, 65476, 65478, 65480, 65482, 65484, 65486, + 65488, 65490, 65492, 65494, 65496, 65497, 65499, 65501, 65502, + 65504, 65505, 65507, 65508, 65510, 65511, 65513, 65514, 65515, + 65516, 65518, 65519, 65520, 65521, 65522, 65523, 65524, 65525, + 65526, 65527, 65527, 65528, 65529, 65530, 65530, 65531, 65531, + 65532, 65532, 65533, 65533, 65534, 65534, 65534, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65534, 65534, 65534, 65533, 65533, 65532, + 65532, 65531, 65531, 65530, 65530, 65529, 65528, 65527, 65527, + 65526, 65525, 65524, 65523, 65522, 65521, 65520, 65519, 65518, + 65516, 65515, 65514, 65513, 65511, 65510, 65508, 65507, 65505, + 65504, 65502, 65501, 65499, 65497, 65496, 65494, 65492, 65490, + 65488, 65486, 65484, 65482, 65480, 65478, 65476, 65474, 65472, + 65470, 65467, 65465, 65463, 65460, 65458, 65455, 65453, 65450, + 65448, 65445, 65442, 65440, 65437, 65434, 65431, 65429, 65426, + 65423, 65420, 65417, 65414, 65411, 65408, 65404, 65401, 65398, + 65395, 65391, 65388, 65385, 65381, 65378, 65374, 65371, 65367, + 65363, 65360, 65356, 65352, 65349, 65345, 65341, 65337, 65333, + 65329, 65325, 65321, 65317, 65313, 65309, 65305, 65300, 65296, + 65292, 65287, 65283, 65279, 65274, 65270, 65265, 65260, 65256, + 65251, 65246, 65242, 65237, 65232, 65227, 65222, 65217, 65212, + 65207, 65202, 65197, 65192, 65187, 65182, 65177, 65171, 65166, + 65161, 65155, 65150, 65144, 65139, 65133, 65128, 65122, 65117, + 65111, 65105, 65099, 65094, 65088, 65082, 65076, 65070, 65064, + 65058, 65052, 65046, 65040, 65033, 65027, 65021, 65015, 65008, + 65002, 64995, 64989, 64982, 64976, 64969, 64963, 64956, 64949, + 64943, 64936, 64929, 64922, 64915, 64908, 64902, 64895, 64887, + 64880, 64873, 64866, 64859, 64852, 64844, 64837, 64830, 64822, + 64815, 64808, 64800, 64793, 64785, 64777, 64770, 64762, 64754, + 64747, 64739, 64731, 64723, 64715, 64707, 64699, 64691, 64683, + 64675, 64667, 64659, 64651, 64642, 64634, 64626, 64617, 64609, + 64600, 64592, 64584, 64575, 64566, 64558, 64549, 64540, 64532, + 64523, 64514, 64505, 64496, 64487, 64478, 64469, 64460, 64451, + 64442, 64433, 64424, 64414, 64405, 64396, 64387, 64377, 64368, + 64358, 64349, 64339, 64330, 64320, 64310, 64301, 64291, 64281, + 64271, 64261, 64252, 64242, 64232, 64222, 64212, 64202, 64192, + 64181, 64171, 64161, 64151, 64140, 64130, 64120, 64109, 64099, + 64088, 64078, 64067, 64057, 64046, 64035, 64025, 64014, 64003, + 63992, 63981, 63971, 63960, 63949, 63938, 63927, 63915, 63904, + 63893, 63882, 63871, 63859, 63848, 63837, 63825, 63814, 63803, + 63791, 63779, 63768, 63756, 63745, 63733, 63721, 63709, 63698, + 63686, 63674, 63662, 63650, 63638, 63626, 63614, 63602, 63590, + 63578, 63565, 63553, 63541, 63528, 63516, 63504, 63491, 63479, + 63466, 63454, 63441, 63429, 63416, 63403, 63390, 63378, 63365, + 63352, 63339, 63326, 63313, 63300, 63287, 63274, 63261, 63248, + 63235, 63221, 63208, 63195, 63182, 63168, 63155, 63141, 63128, + 63114, 63101, 63087, 63074, 63060, 63046, 63032, 63019, 63005, + 62991, 62977, 62963, 62949, 62935, 62921, 62907, 62893, 62879, + 62865, 62850, 62836, 62822, 62808, 62793, 62779, 62764, 62750, + 62735, 62721, 62706, 62692, 62677, 62662, 62648, 62633, 62618, + 62603, 62588, 62573, 62558, 62543, 62528, 62513, 62498, 62483, + 62468, 62453, 62437, 62422, 62407, 62391, 62376, 62360, 62345, + 62329, 62314, 62298, 62283, 62267, 62251, 62236, 62220, 62204, + 62188, 62172, 62156, 62141, 62125, 62108, 62092, 62076, 62060, + 62044, 62028, 62012, 61995, 61979, 61963, 61946, 61930, 61913, + 61897, 61880, 61864, 61847, 61831, 61814, 61797, 61780, 61764, + 61747, 61730, 61713, 61696, 61679, 61662, 61645, 61628, 61611, + 61594, 61577, 61559, 61542, 61525, 61507, 61490, 61473, 61455, + 61438, 61420, 61403, 61385, 61367, 61350, 61332, 61314, 61297, + 61279, 61261, 61243, 61225, 61207, 61189, 61171, 61153, 61135, + 61117, 61099, 61081, 61062, 61044, 61026, 61007, 60989, 60971, + 60952, 60934, 60915, 60897, 60878, 60859, 60841, 60822, 60803, + 60785, 60766, 60747, 60728, 60709, 60690, 60671, 60652, 60633, + 60614, 60595, 60576, 60556, 60537, 60518, 60499, 60479, 60460, + 60441, 60421, 60402, 60382, 60363, 60343, 60323, 60304, 60284, + 60264, 60244, 60225, 60205, 60185, 60165, 60145, 60125, 60105, + 60085, 60065, 60045, 60025, 60004, 59984, 59964, 59944, 59923, + 59903, 59883, 59862, 59842, 59821, 59801, 59780, 59759, 59739, + 59718, 59697, 59677, 59656, 59635, 59614, 59593, 59572, 59551, + 59530, 59509, 59488, 59467, 59446, 59425, 59404, 59382, 59361, + 59340, 59318, 59297, 59276, 59254, 59233, 59211, 59190, 59168, + 59146, 59125, 59103, 59081, 59059, 59038, 59016, 58994, 58972, + 58950, 58928, 58906, 58884, 58862, 58840, 58818, 58795, 58773, + 58751, 58729, 58706, 58684, 58662, 58639, 58617, 58594, 58572, + 58549, 58527, 58504, 58481, 58459, 58436, 58413, 58390, 58367, + 58345, 58322, 58299, 58276, 58253, 58230, 58207, 58183, 58160, + 58137, 58114, 58091, 58067, 58044, 58021, 57997, 57974, 57950, + 57927, 57903, 57880, 57856, 57833, 57809, 57785, 57762, 57738, + 57714, 57690, 57666, 57642, 57618, 57594, 57570, 57546, 57522, + 57498, 57474, 57450, 57426, 57402, 57377, 57353, 57329, 57304, + 57280, 57255, 57231, 57206, 57182, 57157, 57133, 57108, 57083, + 57059, 57034, 57009, 56984, 56959, 56935, 56910, 56885, 56860, + 56835, 56810, 56785, 56760, 56734, 56709, 56684, 56659, 56633, + 56608, 56583, 56557, 56532, 56507, 56481, 56456, 56430, 56404, + 56379, 56353, 56328, 56302, 56276, 56250, 56225, 56199, 56173, + 56147, 56121, 56095, 56069, 56043, 56017, 55991, 55965, 55938, + 55912, 55886, 55860, 55833, 55807, 55781, 55754, 55728, 55701, + 55675, 55648, 55622, 55595, 55569, 55542, 55515, 55489, 55462, + 55435, 55408, 55381, 55354, 55327, 55300, 55274, 55246, 55219, + 55192, 55165, 55138, 55111, 55084, 55056, 55029, 55002, 54974, + 54947, 54920, 54892, 54865, 54837, 54810, 54782, 54755, 54727, + 54699, 54672, 54644, 54616, 54588, 54560, 54533, 54505, 54477, + 54449, 54421, 54393, 54365, 54337, 54308, 54280, 54252, 54224, + 54196, 54167, 54139, 54111, 54082, 54054, 54026, 53997, 53969, + 53940, 53911, 53883, 53854, 53826, 53797, 53768, 53739, 53711, + 53682, 53653, 53624, 53595, 53566, 53537, 53508, 53479, 53450, + 53421, 53392, 53363, 53334, 53304, 53275, 53246, 53216, 53187, + 53158, 53128, 53099, 53069, 53040, 53010, 52981, 52951, 52922, + 52892, 52862, 52832, 52803, 52773, 52743, 52713, 52683, 52653, + 52624, 52594, 52564, 52534, 52503, 52473, 52443, 52413, 52383, + 52353, 52322, 52292, 52262, 52231, 52201, 52171, 52140, 52110, + 52079, 52049, 52018, 51988, 51957, 51926, 51896, 51865, 51834, + 51803, 51773, 51742, 51711, 51680, 51649, 51618, 51587, 51556, + 51525, 51494, 51463, 51432, 51401, 51369, 51338, 51307, 51276, + 51244, 51213, 51182, 51150, 51119, 51087, 51056, 51024, 50993, + 50961, 50929, 50898, 50866, 50834, 50803, 50771, 50739, 50707, + 50675, 50644, 50612, 50580, 50548, 50516, 50484, 50452, 50420, + 50387, 50355, 50323, 50291, 50259, 50226, 50194, 50162, 50129, + 50097, 50065, 50032, 50000, 49967, 49935, 49902, 49869, 49837, + 49804, 49771, 49739, 49706, 49673, 49640, 49608, 49575, 49542, + 49509, 49476, 49443, 49410, 49377, 49344, 49311, 49278, 49244, + 49211, 49178, 49145, 49112, 49078, 49045, 49012, 48978, 48945, + 48911, 48878, 48844, 48811, 48777, 48744, 48710, 48676, 48643, + 48609, 48575, 48542, 48508, 48474, 48440, 48406, 48372, 48338, + 48304, 48271, 48237, 48202, 48168, 48134, 48100, 48066, 48032, + 47998, 47963, 47929, 47895, 47860, 47826, 47792, 47757, 47723, + 47688, 47654, 47619, 47585, 47550, 47516, 47481, 47446, 47412, + 47377, 47342, 47308, 47273, 47238, 47203, 47168, 47133, 47098, + 47063, 47028, 46993, 46958, 46923, 46888, 46853, 46818, 46783, + 46747, 46712, 46677, 46642, 46606, 46571, 46536, 46500, 46465, + 46429, 46394, 46358, 46323, 46287, 46252, 46216, 46180, 46145, + 46109, 46073, 46037, 46002, 45966, 45930, 45894, 45858, 45822, + 45786, 45750, 45714, 45678, 45642, 45606, 45570, 45534, 45498, + 45462, 45425, 45389, 45353, 45316, 45280, 45244, 45207, 45171, + 45135, 45098, 45062, 45025, 44989, 44952, 44915, 44879, 44842, + 44806, 44769, 44732, 44695, 44659, 44622, 44585, 44548, 44511, + 44474, 44437, 44400, 44363, 44326, 44289, 44252, 44215, 44178, + 44141, 44104, 44067, 44029, 43992, 43955, 43918, 43880, 43843, + 43806, 43768, 43731, 43693, 43656, 43618, 43581, 43543, 43506, + 43468, 43430, 43393, 43355, 43317, 43280, 43242, 43204, 43166, + 43128, 43091, 43053, 43015, 42977, 42939, 42901, 42863, 42825, + 42787, 42749, 42711, 42672, 42634, 42596, 42558, 42520, 42481, + 42443, 42405, 42366, 42328, 42290, 42251, 42213, 42174, 42136, + 42097, 42059, 42020, 41982, 41943, 41904, 41866, 41827, 41788, + 41750, 41711, 41672, 41633, 41595, 41556, 41517, 41478, 41439, + 41400, 41361, 41322, 41283, 41244, 41205, 41166, 41127, 41088, + 41048, 41009, 40970, 40931, 40891, 40852, 40813, 40773, 40734, + 40695, 40655, 40616, 40576, 40537, 40497, 40458, 40418, 40379, + 40339, 40300, 40260, 40220, 40180, 40141, 40101, 40061, 40021, + 39982, 39942, 39902, 39862, 39822, 39782, 39742, 39702, 39662, + 39622, 39582, 39542, 39502, 39462, 39422, 39382, 39341, 39301, + 39261, 39221, 39180, 39140, 39100, 39059, 39019, 38979, 38938, + 38898, 38857, 38817, 38776, 38736, 38695, 38655, 38614, 38573, + 38533, 38492, 38451, 38411, 38370, 38329, 38288, 38248, 38207, + 38166, 38125, 38084, 38043, 38002, 37961, 37920, 37879, 37838, + 37797, 37756, 37715, 37674, 37633, 37592, 37551, 37509, 37468, + 37427, 37386, 37344, 37303, 37262, 37220, 37179, 37137, 37096, + 37055, 37013, 36972, 36930, 36889, 36847, 36805, 36764, 36722, + 36681, 36639, 36597, 36556, 36514, 36472, 36430, 36388, 36347, + 36305, 36263, 36221, 36179, 36137, 36095, 36053, 36011, 35969, + 35927, 35885, 35843, 35801, 35759, 35717, 35675, 35633, 35590, + 35548, 35506, 35464, 35421, 35379, 35337, 35294, 35252, 35210, + 35167, 35125, 35082, 35040, 34997, 34955, 34912, 34870, 34827, + 34785, 34742, 34699, 34657, 34614, 34571, 34529, 34486, 34443, + 34400, 34358, 34315, 34272, 34229, 34186, 34143, 34100, 34057, + 34015, 33972, 33929, 33886, 33843, 33799, 33756, 33713, 33670, + 33627, 33584, 33541, 33498, 33454, 33411, 33368, 33325, 33281, + 33238, 33195, 33151, 33108, 33065, 33021, 32978, 32934, 32891, + 32847, 32804, 32760, 32717, 32673, 32630, 32586, 32542, 32499, + 32455, 32411, 32368, 32324, 32280, 32236, 32193, 32149, 32105, + 32061, 32017, 31974, 31930, 31886, 31842, 31798, 31754, 31710, + 31666, 31622, 31578, 31534, 31490, 31446, 31402, 31357, 31313, + 31269, 31225, 31181, 31136, 31092, 31048, 31004, 30959, 30915, + 30871, 30826, 30782, 30738, 30693, 30649, 30604, 30560, 30515, + 30471, 30426, 30382, 30337, 30293, 30248, 30204, 30159, 30114, + 30070, 30025, 29980, 29936, 29891, 29846, 29801, 29757, 29712, + 29667, 29622, 29577, 29533, 29488, 29443, 29398, 29353, 29308, + 29263, 29218, 29173, 29128, 29083, 29038, 28993, 28948, 28903, + 28858, 28812, 28767, 28722, 28677, 28632, 28586, 28541, 28496, + 28451, 28405, 28360, 28315, 28269, 28224, 28179, 28133, 28088, + 28042, 27997, 27952, 27906, 27861, 27815, 27770, 27724, 27678, + 27633, 27587, 27542, 27496, 27450, 27405, 27359, 27313, 27268, + 27222, 27176, 27131, 27085, 27039, 26993, 26947, 26902, 26856, + 26810, 26764, 26718, 26672, 26626, 26580, 26534, 26488, 26442, + 26396, 26350, 26304, 26258, 26212, 26166, 26120, 26074, 26028, + 25982, 25936, 25889, 25843, 25797, 25751, 25705, 25658, 25612, + 25566, 25520, 25473, 25427, 25381, 25334, 25288, 25241, 25195, + 25149, 25102, 25056, 25009, 24963, 24916, 24870, 24823, 24777, + 24730, 24684, 24637, 24591, 24544, 24497, 24451, 24404, 24357, + 24311, 24264, 24217, 24171, 24124, 24077, 24030, 23984, 23937, + 23890, 23843, 23796, 23750, 23703, 23656, 23609, 23562, 23515, + 23468, 23421, 23374, 23327, 23280, 23233, 23186, 23139, 23092, + 23045, 22998, 22951, 22904, 22857, 22810, 22763, 22716, 22668, + 22621, 22574, 22527, 22480, 22433, 22385, 22338, 22291, 22243, + 22196, 22149, 22102, 22054, 22007, 21960, 21912, 21865, 21817, + 21770, 21723, 21675, 21628, 21580, 21533, 21485, 21438, 21390, + 21343, 21295, 21248, 21200, 21153, 21105, 21057, 21010, 20962, + 20915, 20867, 20819, 20772, 20724, 20676, 20629, 20581, 20533, + 20485, 20438, 20390, 20342, 20294, 20246, 20199, 20151, 20103, + 20055, 20007, 19959, 19912, 19864, 19816, 19768, 19720, 19672, + 19624, 19576, 19528, 19480, 19432, 19384, 19336, 19288, 19240, + 19192, 19144, 19096, 19048, 19000, 18951, 18903, 18855, 18807, + 18759, 18711, 18663, 18614, 18566, 18518, 18470, 18421, 18373, + 18325, 18277, 18228, 18180, 18132, 18084, 18035, 17987, 17939, + 17890, 17842, 17793, 17745, 17697, 17648, 17600, 17551, 17503, + 17455, 17406, 17358, 17309, 17261, 17212, 17164, 17115, 17067, + 17018, 16970, 16921, 16872, 16824, 16775, 16727, 16678, 16629, + 16581, 16532, 16484, 16435, 16386, 16338, 16289, 16240, 16191, + 16143, 16094, 16045, 15997, 15948, 15899, 15850, 15802, 15753, + 15704, 15655, 15606, 15557, 15509, 15460, 15411, 15362, 15313, + 15264, 15215, 15167, 15118, 15069, 15020, 14971, 14922, 14873, + 14824, 14775, 14726, 14677, 14628, 14579, 14530, 14481, 14432, + 14383, 14334, 14285, 14236, 14187, 14138, 14089, 14040, 13990, + 13941, 13892, 13843, 13794, 13745, 13696, 13646, 13597, 13548, + 13499, 13450, 13401, 13351, 13302, 13253, 13204, 13154, 13105, + 13056, 13007, 12957, 12908, 12859, 12810, 12760, 12711, 12662, + 12612, 12563, 12514, 12464, 12415, 12366, 12316, 12267, 12218, + 12168, 12119, 12069, 12020, 11970, 11921, 11872, 11822, 11773, + 11723, 11674, 11624, 11575, 11525, 11476, 11426, 11377, 11327, + 11278, 11228, 11179, 11129, 11080, 11030, 10981, 10931, 10882, + 10832, 10782, 10733, 10683, 10634, 10584, 10534, 10485, 10435, + 10386, 10336, 10286, 10237, 10187, 10137, 10088, 10038, 9988, + 9939, 9889, 9839, 9790, 9740, 9690, 9640, 9591, 9541, + 9491, 9442, 9392, 9342, 9292, 9243, 9193, 9143, 9093, + 9043, 8994, 8944, 8894, 8844, 8794, 8745, 8695, 8645, + 8595, 8545, 8496, 8446, 8396, 8346, 8296, 8246, 8196, + 8147, 8097, 8047, 7997, 7947, 7897, 7847, 7797, 7747, + 7697, 7648, 7598, 7548, 7498, 7448, 7398, 7348, 7298, + 7248, 7198, 7148, 7098, 7048, 6998, 6948, 6898, 6848, + 6798, 6748, 6698, 6648, 6598, 6548, 6498, 6448, 6398, + 6348, 6298, 6248, 6198, 6148, 6098, 6048, 5998, 5948, + 5898, 5848, 5798, 5748, 5697, 5647, 5597, 5547, 5497, + 5447, 5397, 5347, 5297, 5247, 5197, 5146, 5096, 5046, + 4996, 4946, 4896, 4846, 4796, 4745, 4695, 4645, 4595, + 4545, 4495, 4445, 4394, 4344, 4294, 4244, 4194, 4144, + 4093, 4043, 3993, 3943, 3893, 3843, 3792, 3742, 3692, + 3642, 3592, 3541, 3491, 3441, 3391, 3341, 3291, 3240, + 3190, 3140, 3090, 3039, 2989, 2939, 2889, 2839, 2788, + 2738, 2688, 2638, 2587, 2537, 2487, 2437, 2387, 2336, + 2286, 2236, 2186, 2135, 2085, 2035, 1985, 1934, 1884, + 1834, 1784, 1733, 1683, 1633, 1583, 1532, 1482, 1432, + 1382, 1331, 1281, 1231, 1181, 1130, 1080, 1030, 980, + 929, 879, 829, 779, 728, 678, 628, 578, 527, + 477, 427, 376, 326, 276, 226, 175, 125, 75, + 25, -25, -75, -125, -175, -226, -276, -326, -376, + -427, -477, -527, -578, -628, -678, -728, -779, -829, + -879, -929, -980, -1030, -1080, -1130, -1181, -1231, -1281, + -1331, -1382, -1432, -1482, -1532, -1583, -1633, -1683, -1733, + -1784, -1834, -1884, -1934, -1985, -2035, -2085, -2135, -2186, + -2236, -2286, -2336, -2387, -2437, -2487, -2537, -2588, -2638, + -2688, -2738, -2788, -2839, -2889, -2939, -2989, -3039, -3090, + -3140, -3190, -3240, -3291, -3341, -3391, -3441, -3491, -3541, + -3592, -3642, -3692, -3742, -3792, -3843, -3893, -3943, -3993, + -4043, -4093, -4144, -4194, -4244, -4294, -4344, -4394, -4445, + -4495, -4545, -4595, -4645, -4695, -4745, -4796, -4846, -4896, + -4946, -4996, -5046, -5096, -5146, -5197, -5247, -5297, -5347, + -5397, -5447, -5497, -5547, -5597, -5647, -5697, -5748, -5798, + -5848, -5898, -5948, -5998, -6048, -6098, -6148, -6198, -6248, + -6298, -6348, -6398, -6448, -6498, -6548, -6598, -6648, -6698, + -6748, -6798, -6848, -6898, -6948, -6998, -7048, -7098, -7148, + -7198, -7248, -7298, -7348, -7398, -7448, -7498, -7548, -7598, + -7648, -7697, -7747, -7797, -7847, -7897, -7947, -7997, -8047, + -8097, -8147, -8196, -8246, -8296, -8346, -8396, -8446, -8496, + -8545, -8595, -8645, -8695, -8745, -8794, -8844, -8894, -8944, + -8994, -9043, -9093, -9143, -9193, -9243, -9292, -9342, -9392, + -9442, -9491, -9541, -9591, -9640, -9690, -9740, -9790, -9839, + -9889, -9939, -9988, -10038, -10088, -10137, -10187, -10237, -10286, + -10336, -10386, -10435, -10485, -10534, -10584, -10634, -10683, -10733, + -10782, -10832, -10882, -10931, -10981, -11030, -11080, -11129, -11179, + -11228, -11278, -11327, -11377, -11426, -11476, -11525, -11575, -11624, + -11674, -11723, -11773, -11822, -11872, -11921, -11970, -12020, -12069, + -12119, -12168, -12218, -12267, -12316, -12366, -12415, -12464, -12514, + -12563, -12612, -12662, -12711, -12760, -12810, -12859, -12908, -12957, + -13007, -13056, -13105, -13154, -13204, -13253, -13302, -13351, -13401, + -13450, -13499, -13548, -13597, -13647, -13696, -13745, -13794, -13843, + -13892, -13941, -13990, -14040, -14089, -14138, -14187, -14236, -14285, + -14334, -14383, -14432, -14481, -14530, -14579, -14628, -14677, -14726, + -14775, -14824, -14873, -14922, -14971, -15020, -15069, -15118, -15167, + -15215, -15264, -15313, -15362, -15411, -15460, -15509, -15557, -15606, + -15655, -15704, -15753, -15802, -15850, -15899, -15948, -15997, -16045, + -16094, -16143, -16191, -16240, -16289, -16338, -16386, -16435, -16484, + -16532, -16581, -16629, -16678, -16727, -16775, -16824, -16872, -16921, + -16970, -17018, -17067, -17115, -17164, -17212, -17261, -17309, -17358, + -17406, -17455, -17503, -17551, -17600, -17648, -17697, -17745, -17793, + -17842, -17890, -17939, -17987, -18035, -18084, -18132, -18180, -18228, + -18277, -18325, -18373, -18421, -18470, -18518, -18566, -18614, -18663, + -18711, -18759, -18807, -18855, -18903, -18951, -19000, -19048, -19096, + -19144, -19192, -19240, -19288, -19336, -19384, -19432, -19480, -19528, + -19576, -19624, -19672, -19720, -19768, -19816, -19864, -19912, -19959, + -20007, -20055, -20103, -20151, -20199, -20246, -20294, -20342, -20390, + -20438, -20485, -20533, -20581, -20629, -20676, -20724, -20772, -20819, + -20867, -20915, -20962, -21010, -21057, -21105, -21153, -21200, -21248, + -21295, -21343, -21390, -21438, -21485, -21533, -21580, -21628, -21675, + -21723, -21770, -21817, -21865, -21912, -21960, -22007, -22054, -22102, + -22149, -22196, -22243, -22291, -22338, -22385, -22433, -22480, -22527, + -22574, -22621, -22668, -22716, -22763, -22810, -22857, -22904, -22951, + -22998, -23045, -23092, -23139, -23186, -23233, -23280, -23327, -23374, + -23421, -23468, -23515, -23562, -23609, -23656, -23703, -23750, -23796, + -23843, -23890, -23937, -23984, -24030, -24077, -24124, -24171, -24217, + -24264, -24311, -24357, -24404, -24451, -24497, -24544, -24591, -24637, + -24684, -24730, -24777, -24823, -24870, -24916, -24963, -25009, -25056, + -25102, -25149, -25195, -25241, -25288, -25334, -25381, -25427, -25473, + -25520, -25566, -25612, -25658, -25705, -25751, -25797, -25843, -25889, + -25936, -25982, -26028, -26074, -26120, -26166, -26212, -26258, -26304, + -26350, -26396, -26442, -26488, -26534, -26580, -26626, -26672, -26718, + -26764, -26810, -26856, -26902, -26947, -26993, -27039, -27085, -27131, + -27176, -27222, -27268, -27313, -27359, -27405, -27450, -27496, -27542, + -27587, -27633, -27678, -27724, -27770, -27815, -27861, -27906, -27952, + -27997, -28042, -28088, -28133, -28179, -28224, -28269, -28315, -28360, + -28405, -28451, -28496, -28541, -28586, -28632, -28677, -28722, -28767, + -28812, -28858, -28903, -28948, -28993, -29038, -29083, -29128, -29173, + -29218, -29263, -29308, -29353, -29398, -29443, -29488, -29533, -29577, + -29622, -29667, -29712, -29757, -29801, -29846, -29891, -29936, -29980, + -30025, -30070, -30114, -30159, -30204, -30248, -30293, -30337, -30382, + -30426, -30471, -30515, -30560, -30604, -30649, -30693, -30738, -30782, + -30826, -30871, -30915, -30959, -31004, -31048, -31092, -31136, -31181, + -31225, -31269, -31313, -31357, -31402, -31446, -31490, -31534, -31578, + -31622, -31666, -31710, -31754, -31798, -31842, -31886, -31930, -31974, + -32017, -32061, -32105, -32149, -32193, -32236, -32280, -32324, -32368, + -32411, -32455, -32499, -32542, -32586, -32630, -32673, -32717, -32760, + -32804, -32847, -32891, -32934, -32978, -33021, -33065, -33108, -33151, + -33195, -33238, -33281, -33325, -33368, -33411, -33454, -33498, -33541, + -33584, -33627, -33670, -33713, -33756, -33799, -33843, -33886, -33929, + -33972, -34015, -34057, -34100, -34143, -34186, -34229, -34272, -34315, + -34358, -34400, -34443, -34486, -34529, -34571, -34614, -34657, -34699, + -34742, -34785, -34827, -34870, -34912, -34955, -34997, -35040, -35082, + -35125, -35167, -35210, -35252, -35294, -35337, -35379, -35421, -35464, + -35506, -35548, -35590, -35633, -35675, -35717, -35759, -35801, -35843, + -35885, -35927, -35969, -36011, -36053, -36095, -36137, -36179, -36221, + -36263, -36305, -36347, -36388, -36430, -36472, -36514, -36555, -36597, + -36639, -36681, -36722, -36764, -36805, -36847, -36889, -36930, -36972, + -37013, -37055, -37096, -37137, -37179, -37220, -37262, -37303, -37344, + -37386, -37427, -37468, -37509, -37551, -37592, -37633, -37674, -37715, + -37756, -37797, -37838, -37879, -37920, -37961, -38002, -38043, -38084, + -38125, -38166, -38207, -38248, -38288, -38329, -38370, -38411, -38451, + -38492, -38533, -38573, -38614, -38655, -38695, -38736, -38776, -38817, + -38857, -38898, -38938, -38979, -39019, -39059, -39100, -39140, -39180, + -39221, -39261, -39301, -39341, -39382, -39422, -39462, -39502, -39542, + -39582, -39622, -39662, -39702, -39742, -39782, -39822, -39862, -39902, + -39942, -39982, -40021, -40061, -40101, -40141, -40180, -40220, -40260, + -40299, -40339, -40379, -40418, -40458, -40497, -40537, -40576, -40616, + -40655, -40695, -40734, -40773, -40813, -40852, -40891, -40931, -40970, + -41009, -41048, -41087, -41127, -41166, -41205, -41244, -41283, -41322, + -41361, -41400, -41439, -41478, -41517, -41556, -41595, -41633, -41672, + -41711, -41750, -41788, -41827, -41866, -41904, -41943, -41982, -42020, + -42059, -42097, -42136, -42174, -42213, -42251, -42290, -42328, -42366, + -42405, -42443, -42481, -42520, -42558, -42596, -42634, -42672, -42711, + -42749, -42787, -42825, -42863, -42901, -42939, -42977, -43015, -43053, + -43091, -43128, -43166, -43204, -43242, -43280, -43317, -43355, -43393, + -43430, -43468, -43506, -43543, -43581, -43618, -43656, -43693, -43731, + -43768, -43806, -43843, -43880, -43918, -43955, -43992, -44029, -44067, + -44104, -44141, -44178, -44215, -44252, -44289, -44326, -44363, -44400, + -44437, -44474, -44511, -44548, -44585, -44622, -44659, -44695, -44732, + -44769, -44806, -44842, -44879, -44915, -44952, -44989, -45025, -45062, + -45098, -45135, -45171, -45207, -45244, -45280, -45316, -45353, -45389, + -45425, -45462, -45498, -45534, -45570, -45606, -45642, -45678, -45714, + -45750, -45786, -45822, -45858, -45894, -45930, -45966, -46002, -46037, + -46073, -46109, -46145, -46180, -46216, -46252, -46287, -46323, -46358, + -46394, -46429, -46465, -46500, -46536, -46571, -46606, -46642, -46677, + -46712, -46747, -46783, -46818, -46853, -46888, -46923, -46958, -46993, + -47028, -47063, -47098, -47133, -47168, -47203, -47238, -47273, -47308, + -47342, -47377, -47412, -47446, -47481, -47516, -47550, -47585, -47619, + -47654, -47688, -47723, -47757, -47792, -47826, -47860, -47895, -47929, + -47963, -47998, -48032, -48066, -48100, -48134, -48168, -48202, -48236, + -48271, -48304, -48338, -48372, -48406, -48440, -48474, -48508, -48542, + -48575, -48609, -48643, -48676, -48710, -48744, -48777, -48811, -48844, + -48878, -48911, -48945, -48978, -49012, -49045, -49078, -49112, -49145, + -49178, -49211, -49244, -49278, -49311, -49344, -49377, -49410, -49443, + -49476, -49509, -49542, -49575, -49608, -49640, -49673, -49706, -49739, + -49771, -49804, -49837, -49869, -49902, -49935, -49967, -50000, -50032, + -50065, -50097, -50129, -50162, -50194, -50226, -50259, -50291, -50323, + -50355, -50387, -50420, -50452, -50484, -50516, -50548, -50580, -50612, + -50644, -50675, -50707, -50739, -50771, -50803, -50834, -50866, -50898, + -50929, -50961, -50993, -51024, -51056, -51087, -51119, -51150, -51182, + -51213, -51244, -51276, -51307, -51338, -51369, -51401, -51432, -51463, + -51494, -51525, -51556, -51587, -51618, -51649, -51680, -51711, -51742, + -51773, -51803, -51834, -51865, -51896, -51926, -51957, -51988, -52018, + -52049, -52079, -52110, -52140, -52171, -52201, -52231, -52262, -52292, + -52322, -52353, -52383, -52413, -52443, -52473, -52503, -52534, -52564, + -52594, -52624, -52653, -52683, -52713, -52743, -52773, -52803, -52832, + -52862, -52892, -52922, -52951, -52981, -53010, -53040, -53069, -53099, + -53128, -53158, -53187, -53216, -53246, -53275, -53304, -53334, -53363, + -53392, -53421, -53450, -53479, -53508, -53537, -53566, -53595, -53624, + -53653, -53682, -53711, -53739, -53768, -53797, -53826, -53854, -53883, + -53911, -53940, -53969, -53997, -54026, -54054, -54082, -54111, -54139, + -54167, -54196, -54224, -54252, -54280, -54308, -54337, -54365, -54393, + -54421, -54449, -54477, -54505, -54533, -54560, -54588, -54616, -54644, + -54672, -54699, -54727, -54755, -54782, -54810, -54837, -54865, -54892, + -54920, -54947, -54974, -55002, -55029, -55056, -55084, -55111, -55138, + -55165, -55192, -55219, -55246, -55274, -55300, -55327, -55354, -55381, + -55408, -55435, -55462, -55489, -55515, -55542, -55569, -55595, -55622, + -55648, -55675, -55701, -55728, -55754, -55781, -55807, -55833, -55860, + -55886, -55912, -55938, -55965, -55991, -56017, -56043, -56069, -56095, + -56121, -56147, -56173, -56199, -56225, -56250, -56276, -56302, -56328, + -56353, -56379, -56404, -56430, -56456, -56481, -56507, -56532, -56557, + -56583, -56608, -56633, -56659, -56684, -56709, -56734, -56760, -56785, + -56810, -56835, -56860, -56885, -56910, -56935, -56959, -56984, -57009, + -57034, -57059, -57083, -57108, -57133, -57157, -57182, -57206, -57231, + -57255, -57280, -57304, -57329, -57353, -57377, -57402, -57426, -57450, + -57474, -57498, -57522, -57546, -57570, -57594, -57618, -57642, -57666, + -57690, -57714, -57738, -57762, -57785, -57809, -57833, -57856, -57880, + -57903, -57927, -57950, -57974, -57997, -58021, -58044, -58067, -58091, + -58114, -58137, -58160, -58183, -58207, -58230, -58253, -58276, -58299, + -58322, -58345, -58367, -58390, -58413, -58436, -58459, -58481, -58504, + -58527, -58549, -58572, -58594, -58617, -58639, -58662, -58684, -58706, + -58729, -58751, -58773, -58795, -58818, -58840, -58862, -58884, -58906, + -58928, -58950, -58972, -58994, -59016, -59038, -59059, -59081, -59103, + -59125, -59146, -59168, -59190, -59211, -59233, -59254, -59276, -59297, + -59318, -59340, -59361, -59382, -59404, -59425, -59446, -59467, -59488, + -59509, -59530, -59551, -59572, -59593, -59614, -59635, -59656, -59677, + -59697, -59718, -59739, -59759, -59780, -59801, -59821, -59842, -59862, + -59883, -59903, -59923, -59944, -59964, -59984, -60004, -60025, -60045, + -60065, -60085, -60105, -60125, -60145, -60165, -60185, -60205, -60225, + -60244, -60264, -60284, -60304, -60323, -60343, -60363, -60382, -60402, + -60421, -60441, -60460, -60479, -60499, -60518, -60537, -60556, -60576, + -60595, -60614, -60633, -60652, -60671, -60690, -60709, -60728, -60747, + -60766, -60785, -60803, -60822, -60841, -60859, -60878, -60897, -60915, + -60934, -60952, -60971, -60989, -61007, -61026, -61044, -61062, -61081, + -61099, -61117, -61135, -61153, -61171, -61189, -61207, -61225, -61243, + -61261, -61279, -61297, -61314, -61332, -61350, -61367, -61385, -61403, + -61420, -61438, -61455, -61473, -61490, -61507, -61525, -61542, -61559, + -61577, -61594, -61611, -61628, -61645, -61662, -61679, -61696, -61713, + -61730, -61747, -61764, -61780, -61797, -61814, -61831, -61847, -61864, + -61880, -61897, -61913, -61930, -61946, -61963, -61979, -61995, -62012, + -62028, -62044, -62060, -62076, -62092, -62108, -62125, -62141, -62156, + -62172, -62188, -62204, -62220, -62236, -62251, -62267, -62283, -62298, + -62314, -62329, -62345, -62360, -62376, -62391, -62407, -62422, -62437, + -62453, -62468, -62483, -62498, -62513, -62528, -62543, -62558, -62573, + -62588, -62603, -62618, -62633, -62648, -62662, -62677, -62692, -62706, + -62721, -62735, -62750, -62764, -62779, -62793, -62808, -62822, -62836, + -62850, -62865, -62879, -62893, -62907, -62921, -62935, -62949, -62963, + -62977, -62991, -63005, -63019, -63032, -63046, -63060, -63074, -63087, + -63101, -63114, -63128, -63141, -63155, -63168, -63182, -63195, -63208, + -63221, -63235, -63248, -63261, -63274, -63287, -63300, -63313, -63326, + -63339, -63352, -63365, -63378, -63390, -63403, -63416, -63429, -63441, + -63454, -63466, -63479, -63491, -63504, -63516, -63528, -63541, -63553, + -63565, -63578, -63590, -63602, -63614, -63626, -63638, -63650, -63662, + -63674, -63686, -63698, -63709, -63721, -63733, -63745, -63756, -63768, + -63779, -63791, -63803, -63814, -63825, -63837, -63848, -63859, -63871, + -63882, -63893, -63904, -63915, -63927, -63938, -63949, -63960, -63971, + -63981, -63992, -64003, -64014, -64025, -64035, -64046, -64057, -64067, + -64078, -64088, -64099, -64109, -64120, -64130, -64140, -64151, -64161, + -64171, -64181, -64192, -64202, -64212, -64222, -64232, -64242, -64252, + -64261, -64271, -64281, -64291, -64301, -64310, -64320, -64330, -64339, + -64349, -64358, -64368, -64377, -64387, -64396, -64405, -64414, -64424, + -64433, -64442, -64451, -64460, -64469, -64478, -64487, -64496, -64505, + -64514, -64523, -64532, -64540, -64549, -64558, -64566, -64575, -64584, + -64592, -64601, -64609, -64617, -64626, -64634, -64642, -64651, -64659, + -64667, -64675, -64683, -64691, -64699, -64707, -64715, -64723, -64731, + -64739, -64747, -64754, -64762, -64770, -64777, -64785, -64793, -64800, + -64808, -64815, -64822, -64830, -64837, -64844, -64852, -64859, -64866, + -64873, -64880, -64887, -64895, -64902, -64908, -64915, -64922, -64929, + -64936, -64943, -64949, -64956, -64963, -64969, -64976, -64982, -64989, + -64995, -65002, -65008, -65015, -65021, -65027, -65033, -65040, -65046, + -65052, -65058, -65064, -65070, -65076, -65082, -65088, -65094, -65099, + -65105, -65111, -65117, -65122, -65128, -65133, -65139, -65144, -65150, + -65155, -65161, -65166, -65171, -65177, -65182, -65187, -65192, -65197, + -65202, -65207, -65212, -65217, -65222, -65227, -65232, -65237, -65242, + -65246, -65251, -65256, -65260, -65265, -65270, -65274, -65279, -65283, + -65287, -65292, -65296, -65300, -65305, -65309, -65313, -65317, -65321, + -65325, -65329, -65333, -65337, -65341, -65345, -65349, -65352, -65356, + -65360, -65363, -65367, -65371, -65374, -65378, -65381, -65385, -65388, + -65391, -65395, -65398, -65401, -65404, -65408, -65411, -65414, -65417, + -65420, -65423, -65426, -65429, -65431, -65434, -65437, -65440, -65442, + -65445, -65448, -65450, -65453, -65455, -65458, -65460, -65463, -65465, + -65467, -65470, -65472, -65474, -65476, -65478, -65480, -65482, -65484, + -65486, -65488, -65490, -65492, -65494, -65496, -65497, -65499, -65501, + -65502, -65504, -65505, -65507, -65508, -65510, -65511, -65513, -65514, + -65515, -65516, -65518, -65519, -65520, -65521, -65522, -65523, -65524, + -65525, -65526, -65527, -65527, -65528, -65529, -65530, -65530, -65531, + -65531, -65532, -65532, -65533, -65533, -65534, -65534, -65534, -65535, + -65535, -65535, -65535, -65535, -65535, -65535, -65535, -65535, -65535, + -65535, -65535, -65535, -65535, -65534, -65534, -65534, -65533, -65533, + -65532, -65532, -65531, -65531, -65530, -65530, -65529, -65528, -65527, + -65527, -65526, -65525, -65524, -65523, -65522, -65521, -65520, -65519, + -65518, -65516, -65515, -65514, -65513, -65511, -65510, -65508, -65507, + -65505, -65504, -65502, -65501, -65499, -65497, -65496, -65494, -65492, + -65490, -65488, -65486, -65484, -65482, -65480, -65478, -65476, -65474, + -65472, -65470, -65467, -65465, -65463, -65460, -65458, -65455, -65453, + -65450, -65448, -65445, -65442, -65440, -65437, -65434, -65431, -65429, + -65426, -65423, -65420, -65417, -65414, -65411, -65408, -65404, -65401, + -65398, -65395, -65391, -65388, -65385, -65381, -65378, -65374, -65371, + -65367, -65363, -65360, -65356, -65352, -65349, -65345, -65341, -65337, + -65333, -65329, -65325, -65321, -65317, -65313, -65309, -65305, -65300, + -65296, -65292, -65287, -65283, -65279, -65274, -65270, -65265, -65260, + -65256, -65251, -65246, -65242, -65237, -65232, -65227, -65222, -65217, + -65212, -65207, -65202, -65197, -65192, -65187, -65182, -65177, -65171, + -65166, -65161, -65155, -65150, -65144, -65139, -65133, -65128, -65122, + -65117, -65111, -65105, -65099, -65094, -65088, -65082, -65076, -65070, + -65064, -65058, -65052, -65046, -65040, -65033, -65027, -65021, -65015, + -65008, -65002, -64995, -64989, -64982, -64976, -64969, -64963, -64956, + -64949, -64943, -64936, -64929, -64922, -64915, -64908, -64902, -64895, + -64887, -64880, -64873, -64866, -64859, -64852, -64844, -64837, -64830, + -64822, -64815, -64808, -64800, -64793, -64785, -64777, -64770, -64762, + -64754, -64747, -64739, -64731, -64723, -64715, -64707, -64699, -64691, + -64683, -64675, -64667, -64659, -64651, -64642, -64634, -64626, -64617, + -64609, -64601, -64592, -64584, -64575, -64566, -64558, -64549, -64540, + -64532, -64523, -64514, -64505, -64496, -64487, -64478, -64469, -64460, + -64451, -64442, -64433, -64424, -64414, -64405, -64396, -64387, -64377, + -64368, -64358, -64349, -64339, -64330, -64320, -64310, -64301, -64291, + -64281, -64271, -64261, -64252, -64242, -64232, -64222, -64212, -64202, + -64192, -64181, -64171, -64161, -64151, -64140, -64130, -64120, -64109, + -64099, -64088, -64078, -64067, -64057, -64046, -64035, -64025, -64014, + -64003, -63992, -63981, -63971, -63960, -63949, -63938, -63927, -63915, + -63904, -63893, -63882, -63871, -63859, -63848, -63837, -63825, -63814, + -63803, -63791, -63779, -63768, -63756, -63745, -63733, -63721, -63709, + -63698, -63686, -63674, -63662, -63650, -63638, -63626, -63614, -63602, + -63590, -63578, -63565, -63553, -63541, -63528, -63516, -63504, -63491, + -63479, -63466, -63454, -63441, -63429, -63416, -63403, -63390, -63378, + -63365, -63352, -63339, -63326, -63313, -63300, -63287, -63274, -63261, + -63248, -63235, -63221, -63208, -63195, -63182, -63168, -63155, -63141, + -63128, -63114, -63101, -63087, -63074, -63060, -63046, -63032, -63019, + -63005, -62991, -62977, -62963, -62949, -62935, -62921, -62907, -62893, + -62879, -62865, -62850, -62836, -62822, -62808, -62793, -62779, -62764, + -62750, -62735, -62721, -62706, -62692, -62677, -62662, -62648, -62633, + -62618, -62603, -62588, -62573, -62558, -62543, -62528, -62513, -62498, + -62483, -62468, -62453, -62437, -62422, -62407, -62391, -62376, -62360, + -62345, -62329, -62314, -62298, -62283, -62267, -62251, -62236, -62220, + -62204, -62188, -62172, -62156, -62141, -62125, -62108, -62092, -62076, + -62060, -62044, -62028, -62012, -61995, -61979, -61963, -61946, -61930, + -61913, -61897, -61880, -61864, -61847, -61831, -61814, -61797, -61780, + -61764, -61747, -61730, -61713, -61696, -61679, -61662, -61645, -61628, + -61611, -61594, -61577, -61559, -61542, -61525, -61507, -61490, -61473, + -61455, -61438, -61420, -61403, -61385, -61367, -61350, -61332, -61314, + -61297, -61279, -61261, -61243, -61225, -61207, -61189, -61171, -61153, + -61135, -61117, -61099, -61081, -61062, -61044, -61026, -61007, -60989, + -60971, -60952, -60934, -60915, -60897, -60878, -60859, -60841, -60822, + -60803, -60785, -60766, -60747, -60728, -60709, -60690, -60671, -60652, + -60633, -60614, -60595, -60576, -60556, -60537, -60518, -60499, -60479, + -60460, -60441, -60421, -60402, -60382, -60363, -60343, -60323, -60304, + -60284, -60264, -60244, -60225, -60205, -60185, -60165, -60145, -60125, + -60105, -60085, -60065, -60045, -60025, -60004, -59984, -59964, -59944, + -59923, -59903, -59883, -59862, -59842, -59821, -59801, -59780, -59759, + -59739, -59718, -59697, -59677, -59656, -59635, -59614, -59593, -59572, + -59551, -59530, -59509, -59488, -59467, -59446, -59425, -59404, -59382, + -59361, -59340, -59318, -59297, -59276, -59254, -59233, -59211, -59189, + -59168, -59146, -59125, -59103, -59081, -59059, -59038, -59016, -58994, + -58972, -58950, -58928, -58906, -58884, -58862, -58840, -58818, -58795, + -58773, -58751, -58729, -58706, -58684, -58662, -58639, -58617, -58594, + -58572, -58549, -58527, -58504, -58481, -58459, -58436, -58413, -58390, + -58367, -58345, -58322, -58299, -58276, -58253, -58230, -58207, -58183, + -58160, -58137, -58114, -58091, -58067, -58044, -58021, -57997, -57974, + -57950, -57927, -57903, -57880, -57856, -57833, -57809, -57785, -57762, + -57738, -57714, -57690, -57666, -57642, -57618, -57594, -57570, -57546, + -57522, -57498, -57474, -57450, -57426, -57402, -57377, -57353, -57329, + -57304, -57280, -57255, -57231, -57206, -57182, -57157, -57133, -57108, + -57083, -57059, -57034, -57009, -56984, -56959, -56935, -56910, -56885, + -56860, -56835, -56810, -56785, -56760, -56734, -56709, -56684, -56659, + -56633, -56608, -56583, -56557, -56532, -56507, -56481, -56456, -56430, + -56404, -56379, -56353, -56328, -56302, -56276, -56250, -56225, -56199, + -56173, -56147, -56121, -56095, -56069, -56043, -56017, -55991, -55965, + -55938, -55912, -55886, -55860, -55833, -55807, -55781, -55754, -55728, + -55701, -55675, -55648, -55622, -55595, -55569, -55542, -55515, -55489, + -55462, -55435, -55408, -55381, -55354, -55327, -55300, -55274, -55246, + -55219, -55192, -55165, -55138, -55111, -55084, -55056, -55029, -55002, + -54974, -54947, -54920, -54892, -54865, -54837, -54810, -54782, -54755, + -54727, -54699, -54672, -54644, -54616, -54588, -54560, -54533, -54505, + -54477, -54449, -54421, -54393, -54365, -54337, -54308, -54280, -54252, + -54224, -54196, -54167, -54139, -54111, -54082, -54054, -54026, -53997, + -53969, -53940, -53911, -53883, -53854, -53826, -53797, -53768, -53739, + -53711, -53682, -53653, -53624, -53595, -53566, -53537, -53508, -53479, + -53450, -53421, -53392, -53363, -53334, -53304, -53275, -53246, -53216, + -53187, -53158, -53128, -53099, -53069, -53040, -53010, -52981, -52951, + -52922, -52892, -52862, -52832, -52803, -52773, -52743, -52713, -52683, + -52653, -52624, -52594, -52564, -52534, -52503, -52473, -52443, -52413, + -52383, -52353, -52322, -52292, -52262, -52231, -52201, -52171, -52140, + -52110, -52079, -52049, -52018, -51988, -51957, -51926, -51896, -51865, + -51834, -51803, -51773, -51742, -51711, -51680, -51649, -51618, -51587, + -51556, -51525, -51494, -51463, -51432, -51401, -51369, -51338, -51307, + -51276, -51244, -51213, -51182, -51150, -51119, -51087, -51056, -51024, + -50993, -50961, -50929, -50898, -50866, -50834, -50803, -50771, -50739, + -50707, -50675, -50644, -50612, -50580, -50548, -50516, -50484, -50452, + -50420, -50387, -50355, -50323, -50291, -50259, -50226, -50194, -50162, + -50129, -50097, -50065, -50032, -50000, -49967, -49935, -49902, -49869, + -49837, -49804, -49771, -49739, -49706, -49673, -49640, -49608, -49575, + -49542, -49509, -49476, -49443, -49410, -49377, -49344, -49311, -49278, + -49244, -49211, -49178, -49145, -49112, -49078, -49045, -49012, -48978, + -48945, -48911, -48878, -48844, -48811, -48777, -48744, -48710, -48676, + -48643, -48609, -48575, -48542, -48508, -48474, -48440, -48406, -48372, + -48338, -48305, -48271, -48237, -48202, -48168, -48134, -48100, -48066, + -48032, -47998, -47963, -47929, -47895, -47860, -47826, -47792, -47757, + -47723, -47688, -47654, -47619, -47585, -47550, -47516, -47481, -47446, + -47412, -47377, -47342, -47307, -47273, -47238, -47203, -47168, -47133, + -47098, -47063, -47028, -46993, -46958, -46923, -46888, -46853, -46818, + -46783, -46747, -46712, -46677, -46642, -46606, -46571, -46536, -46500, + -46465, -46429, -46394, -46358, -46323, -46287, -46251, -46216, -46180, + -46145, -46109, -46073, -46037, -46002, -45966, -45930, -45894, -45858, + -45822, -45786, -45750, -45714, -45678, -45642, -45606, -45570, -45534, + -45498, -45462, -45425, -45389, -45353, -45316, -45280, -45244, -45207, + -45171, -45135, -45098, -45062, -45025, -44989, -44952, -44915, -44879, + -44842, -44806, -44769, -44732, -44695, -44659, -44622, -44585, -44548, + -44511, -44474, -44437, -44400, -44363, -44326, -44289, -44252, -44215, + -44178, -44141, -44104, -44067, -44029, -43992, -43955, -43918, -43880, + -43843, -43806, -43768, -43731, -43693, -43656, -43618, -43581, -43543, + -43506, -43468, -43430, -43393, -43355, -43317, -43280, -43242, -43204, + -43166, -43128, -43091, -43053, -43015, -42977, -42939, -42901, -42863, + -42825, -42787, -42749, -42711, -42672, -42634, -42596, -42558, -42520, + -42481, -42443, -42405, -42366, -42328, -42290, -42251, -42213, -42174, + -42136, -42097, -42059, -42020, -41982, -41943, -41904, -41866, -41827, + -41788, -41750, -41711, -41672, -41633, -41595, -41556, -41517, -41478, + -41439, -41400, -41361, -41322, -41283, -41244, -41205, -41166, -41127, + -41087, -41048, -41009, -40970, -40931, -40891, -40852, -40813, -40773, + -40734, -40695, -40655, -40616, -40576, -40537, -40497, -40458, -40418, + -40379, -40339, -40299, -40260, -40220, -40180, -40141, -40101, -40061, + -40021, -39982, -39942, -39902, -39862, -39822, -39782, -39742, -39702, + -39662, -39622, -39582, -39542, -39502, -39462, -39422, -39382, -39341, + -39301, -39261, -39221, -39180, -39140, -39100, -39059, -39019, -38979, + -38938, -38898, -38857, -38817, -38776, -38736, -38695, -38655, -38614, + -38573, -38533, -38492, -38451, -38411, -38370, -38329, -38288, -38248, + -38207, -38166, -38125, -38084, -38043, -38002, -37961, -37920, -37879, + -37838, -37797, -37756, -37715, -37674, -37633, -37592, -37550, -37509, + -37468, -37427, -37386, -37344, -37303, -37262, -37220, -37179, -37137, + -37096, -37055, -37013, -36972, -36930, -36889, -36847, -36805, -36764, + -36722, -36681, -36639, -36597, -36556, -36514, -36472, -36430, -36388, + -36347, -36305, -36263, -36221, -36179, -36137, -36095, -36053, -36011, + -35969, -35927, -35885, -35843, -35801, -35759, -35717, -35675, -35633, + -35590, -35548, -35506, -35464, -35421, -35379, -35337, -35294, -35252, + -35210, -35167, -35125, -35082, -35040, -34997, -34955, -34912, -34870, + -34827, -34785, -34742, -34699, -34657, -34614, -34571, -34529, -34486, + -34443, -34400, -34358, -34315, -34272, -34229, -34186, -34143, -34100, + -34057, -34015, -33972, -33929, -33886, -33843, -33799, -33756, -33713, + -33670, -33627, -33584, -33541, -33498, -33454, -33411, -33368, -33325, + -33281, -33238, -33195, -33151, -33108, -33065, -33021, -32978, -32934, + -32891, -32847, -32804, -32760, -32717, -32673, -32630, -32586, -32542, + -32499, -32455, -32411, -32368, -32324, -32280, -32236, -32193, -32149, + -32105, -32061, -32017, -31974, -31930, -31886, -31842, -31798, -31754, + -31710, -31666, -31622, -31578, -31534, -31490, -31446, -31402, -31357, + -31313, -31269, -31225, -31181, -31136, -31092, -31048, -31004, -30959, + -30915, -30871, -30826, -30782, -30738, -30693, -30649, -30604, -30560, + -30515, -30471, -30426, -30382, -30337, -30293, -30248, -30204, -30159, + -30114, -30070, -30025, -29980, -29936, -29891, -29846, -29801, -29757, + -29712, -29667, -29622, -29577, -29533, -29488, -29443, -29398, -29353, + -29308, -29263, -29218, -29173, -29128, -29083, -29038, -28993, -28948, + -28903, -28858, -28812, -28767, -28722, -28677, -28632, -28586, -28541, + -28496, -28451, -28405, -28360, -28315, -28269, -28224, -28179, -28133, + -28088, -28042, -27997, -27952, -27906, -27861, -27815, -27770, -27724, + -27678, -27633, -27587, -27542, -27496, -27450, -27405, -27359, -27313, + -27268, -27222, -27176, -27131, -27085, -27039, -26993, -26947, -26902, + -26856, -26810, -26764, -26718, -26672, -26626, -26580, -26534, -26488, + -26442, -26396, -26350, -26304, -26258, -26212, -26166, -26120, -26074, + -26028, -25982, -25936, -25889, -25843, -25797, -25751, -25705, -25658, + -25612, -25566, -25520, -25473, -25427, -25381, -25334, -25288, -25241, + -25195, -25149, -25102, -25056, -25009, -24963, -24916, -24870, -24823, + -24777, -24730, -24684, -24637, -24591, -24544, -24497, -24451, -24404, + -24357, -24311, -24264, -24217, -24171, -24124, -24077, -24030, -23984, + -23937, -23890, -23843, -23796, -23750, -23703, -23656, -23609, -23562, + -23515, -23468, -23421, -23374, -23327, -23280, -23233, -23186, -23139, + -23092, -23045, -22998, -22951, -22904, -22857, -22810, -22763, -22716, + -22668, -22621, -22574, -22527, -22480, -22432, -22385, -22338, -22291, + -22243, -22196, -22149, -22102, -22054, -22007, -21960, -21912, -21865, + -21817, -21770, -21723, -21675, -21628, -21580, -21533, -21485, -21438, + -21390, -21343, -21295, -21248, -21200, -21153, -21105, -21057, -21010, + -20962, -20915, -20867, -20819, -20772, -20724, -20676, -20629, -20581, + -20533, -20485, -20438, -20390, -20342, -20294, -20246, -20199, -20151, + -20103, -20055, -20007, -19959, -19912, -19864, -19816, -19768, -19720, + -19672, -19624, -19576, -19528, -19480, -19432, -19384, -19336, -19288, + -19240, -19192, -19144, -19096, -19048, -19000, -18951, -18903, -18855, + -18807, -18759, -18711, -18663, -18614, -18566, -18518, -18470, -18421, + -18373, -18325, -18277, -18228, -18180, -18132, -18084, -18035, -17987, + -17939, -17890, -17842, -17793, -17745, -17697, -17648, -17600, -17551, + -17503, -17455, -17406, -17358, -17309, -17261, -17212, -17164, -17115, + -17067, -17018, -16970, -16921, -16872, -16824, -16775, -16727, -16678, + -16629, -16581, -16532, -16484, -16435, -16386, -16338, -16289, -16240, + -16191, -16143, -16094, -16045, -15997, -15948, -15899, -15850, -15802, + -15753, -15704, -15655, -15606, -15557, -15509, -15460, -15411, -15362, + -15313, -15264, -15215, -15167, -15118, -15069, -15020, -14971, -14922, + -14873, -14824, -14775, -14726, -14677, -14628, -14579, -14530, -14481, + -14432, -14383, -14334, -14285, -14236, -14187, -14138, -14089, -14040, + -13990, -13941, -13892, -13843, -13794, -13745, -13696, -13647, -13597, + -13548, -13499, -13450, -13401, -13351, -13302, -13253, -13204, -13154, + -13105, -13056, -13007, -12957, -12908, -12859, -12810, -12760, -12711, + -12662, -12612, -12563, -12514, -12464, -12415, -12366, -12316, -12267, + -12217, -12168, -12119, -12069, -12020, -11970, -11921, -11872, -11822, + -11773, -11723, -11674, -11624, -11575, -11525, -11476, -11426, -11377, + -11327, -11278, -11228, -11179, -11129, -11080, -11030, -10981, -10931, + -10882, -10832, -10782, -10733, -10683, -10634, -10584, -10534, -10485, + -10435, -10386, -10336, -10286, -10237, -10187, -10137, -10088, -10038, + -9988, -9939, -9889, -9839, -9790, -9740, -9690, -9640, -9591, + -9541, -9491, -9442, -9392, -9342, -9292, -9243, -9193, -9143, + -9093, -9043, -8994, -8944, -8894, -8844, -8794, -8745, -8695, + -8645, -8595, -8545, -8496, -8446, -8396, -8346, -8296, -8246, + -8196, -8147, -8097, -8047, -7997, -7947, -7897, -7847, -7797, + -7747, -7697, -7648, -7598, -7548, -7498, -7448, -7398, -7348, + -7298, -7248, -7198, -7148, -7098, -7048, -6998, -6948, -6898, + -6848, -6798, -6748, -6698, -6648, -6598, -6548, -6498, -6448, + -6398, -6348, -6298, -6248, -6198, -6148, -6098, -6048, -5998, + -5948, -5898, -5848, -5798, -5747, -5697, -5647, -5597, -5547, + -5497, -5447, -5397, -5347, -5297, -5247, -5197, -5146, -5096, + -5046, -4996, -4946, -4896, -4846, -4796, -4745, -4695, -4645, + -4595, -4545, -4495, -4445, -4394, -4344, -4294, -4244, -4194, + -4144, -4093, -4043, -3993, -3943, -3893, -3843, -3792, -3742, + -3692, -3642, -3592, -3541, -3491, -3441, -3391, -3341, -3291, + -3240, -3190, -3140, -3090, -3039, -2989, -2939, -2889, -2839, + -2788, -2738, -2688, -2638, -2588, -2537, -2487, -2437, -2387, + -2336, -2286, -2236, -2186, -2135, -2085, -2035, -1985, -1934, + -1884, -1834, -1784, -1733, -1683, -1633, -1583, -1532, -1482, + -1432, -1382, -1331, -1281, -1231, -1181, -1130, -1080, -1030, + -980, -929, -879, -829, -779, -728, -678, -628, -578, + -527, -477, -427, -376, -326, -276, -226, -175, -125, + -75, -25, 25, 75, 125, 175, 226, 276, 326, + 376, 427, 477, 527, 578, 628, 678, 728, 779, + 829, 879, 929, 980, 1030, 1080, 1130, 1181, 1231, + 1281, 1331, 1382, 1432, 1482, 1532, 1583, 1633, 1683, + 1733, 1784, 1834, 1884, 1934, 1985, 2035, 2085, 2135, + 2186, 2236, 2286, 2336, 2387, 2437, 2487, 2537, 2587, + 2638, 2688, 2738, 2788, 2839, 2889, 2939, 2989, 3039, + 3090, 3140, 3190, 3240, 3291, 3341, 3391, 3441, 3491, + 3542, 3592, 3642, 3692, 3742, 3792, 3843, 3893, 3943, + 3993, 4043, 4093, 4144, 4194, 4244, 4294, 4344, 4394, + 4445, 4495, 4545, 4595, 4645, 4695, 4745, 4796, 4846, + 4896, 4946, 4996, 5046, 5096, 5146, 5197, 5247, 5297, + 5347, 5397, 5447, 5497, 5547, 5597, 5647, 5697, 5747, + 5798, 5848, 5898, 5948, 5998, 6048, 6098, 6148, 6198, + 6248, 6298, 6348, 6398, 6448, 6498, 6548, 6598, 6648, + 6698, 6748, 6798, 6848, 6898, 6948, 6998, 7048, 7098, + 7148, 7198, 7248, 7298, 7348, 7398, 7448, 7498, 7548, + 7598, 7648, 7697, 7747, 7797, 7847, 7897, 7947, 7997, + 8047, 8097, 8147, 8196, 8246, 8296, 8346, 8396, 8446, + 8496, 8545, 8595, 8645, 8695, 8745, 8794, 8844, 8894, + 8944, 8994, 9043, 9093, 9143, 9193, 9243, 9292, 9342, + 9392, 9442, 9491, 9541, 9591, 9640, 9690, 9740, 9790, + 9839, 9889, 9939, 9988, 10038, 10088, 10137, 10187, 10237, + 10286, 10336, 10386, 10435, 10485, 10534, 10584, 10634, 10683, + 10733, 10782, 10832, 10882, 10931, 10981, 11030, 11080, 11129, + 11179, 11228, 11278, 11327, 11377, 11426, 11476, 11525, 11575, + 11624, 11674, 11723, 11773, 11822, 11872, 11921, 11970, 12020, + 12069, 12119, 12168, 12218, 12267, 12316, 12366, 12415, 12464, + 12514, 12563, 12612, 12662, 12711, 12760, 12810, 12859, 12908, + 12957, 13007, 13056, 13105, 13154, 13204, 13253, 13302, 13351, + 13401, 13450, 13499, 13548, 13597, 13647, 13696, 13745, 13794, + 13843, 13892, 13941, 13990, 14040, 14089, 14138, 14187, 14236, + 14285, 14334, 14383, 14432, 14481, 14530, 14579, 14628, 14677, + 14726, 14775, 14824, 14873, 14922, 14971, 15020, 15069, 15118, + 15167, 15215, 15264, 15313, 15362, 15411, 15460, 15509, 15557, + 15606, 15655, 15704, 15753, 15802, 15850, 15899, 15948, 15997, + 16045, 16094, 16143, 16191, 16240, 16289, 16338, 16386, 16435, + 16484, 16532, 16581, 16629, 16678, 16727, 16775, 16824, 16872, + 16921, 16970, 17018, 17067, 17115, 17164, 17212, 17261, 17309, + 17358, 17406, 17455, 17503, 17551, 17600, 17648, 17697, 17745, + 17793, 17842, 17890, 17939, 17987, 18035, 18084, 18132, 18180, + 18228, 18277, 18325, 18373, 18421, 18470, 18518, 18566, 18614, + 18663, 18711, 18759, 18807, 18855, 18903, 18951, 19000, 19048, + 19096, 19144, 19192, 19240, 19288, 19336, 19384, 19432, 19480, + 19528, 19576, 19624, 19672, 19720, 19768, 19816, 19864, 19912, + 19959, 20007, 20055, 20103, 20151, 20199, 20246, 20294, 20342, + 20390, 20438, 20485, 20533, 20581, 20629, 20676, 20724, 20772, + 20819, 20867, 20915, 20962, 21010, 21057, 21105, 21153, 21200, + 21248, 21295, 21343, 21390, 21438, 21485, 21533, 21580, 21628, + 21675, 21723, 21770, 21817, 21865, 21912, 21960, 22007, 22054, + 22102, 22149, 22196, 22243, 22291, 22338, 22385, 22432, 22480, + 22527, 22574, 22621, 22668, 22716, 22763, 22810, 22857, 22904, + 22951, 22998, 23045, 23092, 23139, 23186, 23233, 23280, 23327, + 23374, 23421, 23468, 23515, 23562, 23609, 23656, 23703, 23750, + 23796, 23843, 23890, 23937, 23984, 24030, 24077, 24124, 24171, + 24217, 24264, 24311, 24357, 24404, 24451, 24497, 24544, 24591, + 24637, 24684, 24730, 24777, 24823, 24870, 24916, 24963, 25009, + 25056, 25102, 25149, 25195, 25241, 25288, 25334, 25381, 25427, + 25473, 25520, 25566, 25612, 25658, 25705, 25751, 25797, 25843, + 25889, 25936, 25982, 26028, 26074, 26120, 26166, 26212, 26258, + 26304, 26350, 26396, 26442, 26488, 26534, 26580, 26626, 26672, + 26718, 26764, 26810, 26856, 26902, 26947, 26993, 27039, 27085, + 27131, 27176, 27222, 27268, 27313, 27359, 27405, 27450, 27496, + 27542, 27587, 27633, 27678, 27724, 27770, 27815, 27861, 27906, + 27952, 27997, 28042, 28088, 28133, 28179, 28224, 28269, 28315, + 28360, 28405, 28451, 28496, 28541, 28586, 28632, 28677, 28722, + 28767, 28812, 28858, 28903, 28948, 28993, 29038, 29083, 29128, + 29173, 29218, 29263, 29308, 29353, 29398, 29443, 29488, 29533, + 29577, 29622, 29667, 29712, 29757, 29801, 29846, 29891, 29936, + 29980, 30025, 30070, 30114, 30159, 30204, 30248, 30293, 30337, + 30382, 30427, 30471, 30516, 30560, 30604, 30649, 30693, 30738, + 30782, 30826, 30871, 30915, 30959, 31004, 31048, 31092, 31136, + 31181, 31225, 31269, 31313, 31357, 31402, 31446, 31490, 31534, + 31578, 31622, 31666, 31710, 31754, 31798, 31842, 31886, 31930, + 31974, 32017, 32061, 32105, 32149, 32193, 32236, 32280, 32324, + 32368, 32411, 32455, 32499, 32542, 32586, 32630, 32673, 32717, + 32760, 32804, 32847, 32891, 32934, 32978, 33021, 33065, 33108, + 33151, 33195, 33238, 33281, 33325, 33368, 33411, 33454, 33498, + 33541, 33584, 33627, 33670, 33713, 33756, 33799, 33843, 33886, + 33929, 33972, 34015, 34057, 34100, 34143, 34186, 34229, 34272, + 34315, 34358, 34400, 34443, 34486, 34529, 34571, 34614, 34657, + 34699, 34742, 34785, 34827, 34870, 34912, 34955, 34997, 35040, + 35082, 35125, 35167, 35210, 35252, 35294, 35337, 35379, 35421, + 35464, 35506, 35548, 35590, 35633, 35675, 35717, 35759, 35801, + 35843, 35885, 35927, 35969, 36011, 36053, 36095, 36137, 36179, + 36221, 36263, 36305, 36347, 36388, 36430, 36472, 36514, 36556, + 36597, 36639, 36681, 36722, 36764, 36805, 36847, 36889, 36930, + 36972, 37013, 37055, 37096, 37137, 37179, 37220, 37262, 37303, + 37344, 37386, 37427, 37468, 37509, 37551, 37592, 37633, 37674, + 37715, 37756, 37797, 37838, 37879, 37920, 37961, 38002, 38043, + 38084, 38125, 38166, 38207, 38248, 38288, 38329, 38370, 38411, + 38451, 38492, 38533, 38573, 38614, 38655, 38695, 38736, 38776, + 38817, 38857, 38898, 38938, 38979, 39019, 39059, 39100, 39140, + 39180, 39221, 39261, 39301, 39341, 39382, 39422, 39462, 39502, + 39542, 39582, 39622, 39662, 39702, 39742, 39782, 39822, 39862, + 39902, 39942, 39982, 40021, 40061, 40101, 40141, 40180, 40220, + 40260, 40299, 40339, 40379, 40418, 40458, 40497, 40537, 40576, + 40616, 40655, 40695, 40734, 40773, 40813, 40852, 40891, 40931, + 40970, 41009, 41048, 41087, 41127, 41166, 41205, 41244, 41283, + 41322, 41361, 41400, 41439, 41478, 41517, 41556, 41595, 41633, + 41672, 41711, 41750, 41788, 41827, 41866, 41904, 41943, 41982, + 42020, 42059, 42097, 42136, 42174, 42213, 42251, 42290, 42328, + 42366, 42405, 42443, 42481, 42520, 42558, 42596, 42634, 42672, + 42711, 42749, 42787, 42825, 42863, 42901, 42939, 42977, 43015, + 43053, 43091, 43128, 43166, 43204, 43242, 43280, 43317, 43355, + 43393, 43430, 43468, 43506, 43543, 43581, 43618, 43656, 43693, + 43731, 43768, 43806, 43843, 43880, 43918, 43955, 43992, 44029, + 44067, 44104, 44141, 44178, 44215, 44252, 44289, 44326, 44363, + 44400, 44437, 44474, 44511, 44548, 44585, 44622, 44659, 44695, + 44732, 44769, 44806, 44842, 44879, 44915, 44952, 44989, 45025, + 45062, 45098, 45135, 45171, 45207, 45244, 45280, 45316, 45353, + 45389, 45425, 45462, 45498, 45534, 45570, 45606, 45642, 45678, + 45714, 45750, 45786, 45822, 45858, 45894, 45930, 45966, 46002, + 46037, 46073, 46109, 46145, 46180, 46216, 46252, 46287, 46323, + 46358, 46394, 46429, 46465, 46500, 46536, 46571, 46606, 46642, + 46677, 46712, 46747, 46783, 46818, 46853, 46888, 46923, 46958, + 46993, 47028, 47063, 47098, 47133, 47168, 47203, 47238, 47273, + 47308, 47342, 47377, 47412, 47446, 47481, 47516, 47550, 47585, + 47619, 47654, 47688, 47723, 47757, 47792, 47826, 47861, 47895, + 47929, 47963, 47998, 48032, 48066, 48100, 48134, 48168, 48202, + 48237, 48271, 48305, 48338, 48372, 48406, 48440, 48474, 48508, + 48542, 48575, 48609, 48643, 48676, 48710, 48744, 48777, 48811, + 48844, 48878, 48911, 48945, 48978, 49012, 49045, 49078, 49112, + 49145, 49178, 49211, 49244, 49278, 49311, 49344, 49377, 49410, + 49443, 49476, 49509, 49542, 49575, 49608, 49640, 49673, 49706, + 49739, 49771, 49804, 49837, 49869, 49902, 49935, 49967, 50000, + 50032, 50064, 50097, 50129, 50162, 50194, 50226, 50259, 50291, + 50323, 50355, 50387, 50420, 50452, 50484, 50516, 50548, 50580, + 50612, 50644, 50675, 50707, 50739, 50771, 50803, 50834, 50866, + 50898, 50929, 50961, 50993, 51024, 51056, 51087, 51119, 51150, + 51182, 51213, 51244, 51276, 51307, 51338, 51369, 51401, 51432, + 51463, 51494, 51525, 51556, 51587, 51618, 51649, 51680, 51711, + 51742, 51773, 51803, 51834, 51865, 51896, 51926, 51957, 51988, + 52018, 52049, 52079, 52110, 52140, 52171, 52201, 52231, 52262, + 52292, 52322, 52353, 52383, 52413, 52443, 52473, 52503, 52534, + 52564, 52594, 52624, 52653, 52683, 52713, 52743, 52773, 52803, + 52832, 52862, 52892, 52922, 52951, 52981, 53010, 53040, 53069, + 53099, 53128, 53158, 53187, 53216, 53246, 53275, 53304, 53334, + 53363, 53392, 53421, 53450, 53479, 53508, 53537, 53566, 53595, + 53624, 53653, 53682, 53711, 53739, 53768, 53797, 53826, 53854, + 53883, 53912, 53940, 53969, 53997, 54026, 54054, 54082, 54111, + 54139, 54167, 54196, 54224, 54252, 54280, 54309, 54337, 54365, + 54393, 54421, 54449, 54477, 54505, 54533, 54560, 54588, 54616, + 54644, 54672, 54699, 54727, 54755, 54782, 54810, 54837, 54865, + 54892, 54920, 54947, 54974, 55002, 55029, 55056, 55084, 55111, + 55138, 55165, 55192, 55219, 55246, 55274, 55300, 55327, 55354, + 55381, 55408, 55435, 55462, 55489, 55515, 55542, 55569, 55595, + 55622, 55648, 55675, 55701, 55728, 55754, 55781, 55807, 55833, + 55860, 55886, 55912, 55938, 55965, 55991, 56017, 56043, 56069, + 56095, 56121, 56147, 56173, 56199, 56225, 56250, 56276, 56302, + 56328, 56353, 56379, 56404, 56430, 56456, 56481, 56507, 56532, + 56557, 56583, 56608, 56633, 56659, 56684, 56709, 56734, 56760, + 56785, 56810, 56835, 56860, 56885, 56910, 56935, 56959, 56984, + 57009, 57034, 57059, 57083, 57108, 57133, 57157, 57182, 57206, + 57231, 57255, 57280, 57304, 57329, 57353, 57377, 57402, 57426, + 57450, 57474, 57498, 57522, 57546, 57570, 57594, 57618, 57642, + 57666, 57690, 57714, 57738, 57762, 57785, 57809, 57833, 57856, + 57880, 57903, 57927, 57950, 57974, 57997, 58021, 58044, 58067, + 58091, 58114, 58137, 58160, 58183, 58207, 58230, 58253, 58276, + 58299, 58322, 58345, 58367, 58390, 58413, 58436, 58459, 58481, + 58504, 58527, 58549, 58572, 58594, 58617, 58639, 58662, 58684, + 58706, 58729, 58751, 58773, 58795, 58818, 58840, 58862, 58884, + 58906, 58928, 58950, 58972, 58994, 59016, 59038, 59059, 59081, + 59103, 59125, 59146, 59168, 59190, 59211, 59233, 59254, 59276, + 59297, 59318, 59340, 59361, 59382, 59404, 59425, 59446, 59467, + 59488, 59509, 59530, 59551, 59572, 59593, 59614, 59635, 59656, + 59677, 59697, 59718, 59739, 59759, 59780, 59801, 59821, 59842, + 59862, 59883, 59903, 59923, 59944, 59964, 59984, 60004, 60025, + 60045, 60065, 60085, 60105, 60125, 60145, 60165, 60185, 60205, + 60225, 60244, 60264, 60284, 60304, 60323, 60343, 60363, 60382, + 60402, 60421, 60441, 60460, 60479, 60499, 60518, 60537, 60556, + 60576, 60595, 60614, 60633, 60652, 60671, 60690, 60709, 60728, + 60747, 60766, 60785, 60803, 60822, 60841, 60859, 60878, 60897, + 60915, 60934, 60952, 60971, 60989, 61007, 61026, 61044, 61062, + 61081, 61099, 61117, 61135, 61153, 61171, 61189, 61207, 61225, + 61243, 61261, 61279, 61297, 61314, 61332, 61350, 61367, 61385, + 61403, 61420, 61438, 61455, 61473, 61490, 61507, 61525, 61542, + 61559, 61577, 61594, 61611, 61628, 61645, 61662, 61679, 61696, + 61713, 61730, 61747, 61764, 61780, 61797, 61814, 61831, 61847, + 61864, 61880, 61897, 61913, 61930, 61946, 61963, 61979, 61995, + 62012, 62028, 62044, 62060, 62076, 62092, 62108, 62125, 62141, + 62156, 62172, 62188, 62204, 62220, 62236, 62251, 62267, 62283, + 62298, 62314, 62329, 62345, 62360, 62376, 62391, 62407, 62422, + 62437, 62453, 62468, 62483, 62498, 62513, 62528, 62543, 62558, + 62573, 62588, 62603, 62618, 62633, 62648, 62662, 62677, 62692, + 62706, 62721, 62735, 62750, 62764, 62779, 62793, 62808, 62822, + 62836, 62850, 62865, 62879, 62893, 62907, 62921, 62935, 62949, + 62963, 62977, 62991, 63005, 63019, 63032, 63046, 63060, 63074, + 63087, 63101, 63114, 63128, 63141, 63155, 63168, 63182, 63195, + 63208, 63221, 63235, 63248, 63261, 63274, 63287, 63300, 63313, + 63326, 63339, 63352, 63365, 63378, 63390, 63403, 63416, 63429, + 63441, 63454, 63466, 63479, 63491, 63504, 63516, 63528, 63541, + 63553, 63565, 63578, 63590, 63602, 63614, 63626, 63638, 63650, + 63662, 63674, 63686, 63698, 63709, 63721, 63733, 63745, 63756, + 63768, 63779, 63791, 63803, 63814, 63825, 63837, 63848, 63859, + 63871, 63882, 63893, 63904, 63915, 63927, 63938, 63949, 63960, + 63971, 63981, 63992, 64003, 64014, 64025, 64035, 64046, 64057, + 64067, 64078, 64088, 64099, 64109, 64120, 64130, 64140, 64151, + 64161, 64171, 64181, 64192, 64202, 64212, 64222, 64232, 64242, + 64252, 64261, 64271, 64281, 64291, 64301, 64310, 64320, 64330, + 64339, 64349, 64358, 64368, 64377, 64387, 64396, 64405, 64414, + 64424, 64433, 64442, 64451, 64460, 64469, 64478, 64487, 64496, + 64505, 64514, 64523, 64532, 64540, 64549, 64558, 64566, 64575, + 64584, 64592, 64600, 64609, 64617, 64626, 64634, 64642, 64651, + 64659, 64667, 64675, 64683, 64691, 64699, 64707, 64715, 64723, + 64731, 64739, 64747, 64754, 64762, 64770, 64777, 64785, 64793, + 64800, 64808, 64815, 64822, 64830, 64837, 64844, 64852, 64859, + 64866, 64873, 64880, 64887, 64895, 64902, 64908, 64915, 64922, + 64929, 64936, 64943, 64949, 64956, 64963, 64969, 64976, 64982, + 64989, 64995, 65002, 65008, 65015, 65021, 65027, 65033, 65040, + 65046, 65052, 65058, 65064, 65070, 65076, 65082, 65088, 65094, + 65099, 65105, 65111, 65117, 65122, 65128, 65133, 65139, 65144, + 65150, 65155, 65161, 65166, 65171, 65177, 65182, 65187, 65192, + 65197, 65202, 65207, 65212, 65217, 65222, 65227, 65232, 65237, + 65242, 65246, 65251, 65256, 65260, 65265, 65270, 65274, 65279, + 65283, 65287, 65292, 65296, 65300, 65305, 65309, 65313, 65317, + 65321, 65325, 65329, 65333, 65337, 65341, 65345, 65349, 65352, + 65356, 65360, 65363, 65367, 65371, 65374, 65378, 65381, 65385, + 65388, 65391, 65395, 65398, 65401, 65404, 65408, 65411, 65414, + 65417, 65420, 65423, 65426, 65429, 65431, 65434, 65437, 65440, + 65442, 65445, 65448, 65450, 65453, 65455, 65458, 65460, 65463, + 65465, 65467, 65470, 65472, 65474, 65476, 65478, 65480, 65482, + 65484, 65486, 65488, 65490, 65492, 65494, 65496, 65497, 65499, + 65501, 65502, 65504, 65505, 65507, 65508, 65510, 65511, 65513, + 65514, 65515, 65516, 65518, 65519, 65520, 65521, 65522, 65523, + 65524, 65525, 65526, 65527, 65527, 65528, 65529, 65530, 65530, + 65531, 65531, 65532, 65532, 65533, 65533, 65534, 65534, 65534, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, +}; + +const fixed_t *finecosine = &finesine[FINEANGLES / 4]; + +const angle_t tantoangle[2049] = +{ + 0, 333772, 667544, 1001315, 1335086, 1668857, + 2002626, 2336395, 2670163, 3003929, 3337694, 3671457, + 4005219, 4338979, 4672736, 5006492, 5340245, 5673995, + 6007743, 6341488, 6675230, 7008968, 7342704, 7676435, + 8010164, 8343888, 8677609, 9011325, 9345037, 9678744, + 10012447, 10346145, 10679838, 11013526, 11347209, 11680887, + 12014558, 12348225, 12681885, 13015539, 13349187, 13682829, + 14016464, 14350092, 14683714, 15017328, 15350936, 15684536, + 16018129, 16351714, 16685291, 17018860, 17352422, 17685974, + 18019518, 18353054, 18686582, 19020100, 19353610, 19687110, + 20020600, 20354080, 20687552, 21021014, 21354466, 21687906, + 22021338, 22354758, 22688168, 23021568, 23354956, 23688332, + 24021698, 24355052, 24688396, 25021726, 25355046, 25688352, + 26021648, 26354930, 26688200, 27021456, 27354702, 27687932, + 28021150, 28354356, 28687548, 29020724, 29353888, 29687038, + 30020174, 30353296, 30686404, 31019496, 31352574, 31685636, + 32018684, 32351718, 32684734, 33017736, 33350722, 33683692, + 34016648, 34349584, 34682508, 35015412, 35348300, 35681172, + 36014028, 36346868, 36679688, 37012492, 37345276, 37678044, + 38010792, 38343524, 38676240, 39008936, 39341612, 39674272, + 40006912, 40339532, 40672132, 41004716, 41337276, 41669820, + 42002344, 42334848, 42667332, 42999796, 43332236, 43664660, + 43997060, 44329444, 44661800, 44994140, 45326456, 45658752, + 45991028, 46323280, 46655512, 46987720, 47319908, 47652072, + 47984212, 48316332, 48648428, 48980500, 49312548, 49644576, + 49976580, 50308556, 50640512, 50972444, 51304352, 51636236, + 51968096, 52299928, 52631740, 52963524, 53295284, 53627020, + 53958728, 54290412, 54622068, 54953704, 55285308, 55616888, + 55948444, 56279972, 56611472, 56942948, 57274396, 57605816, + 57937212, 58268576, 58599916, 58931228, 59262512, 59593768, + 59924992, 60256192, 60587364, 60918508, 61249620, 61580704, + 61911760, 62242788, 62573788, 62904756, 63235692, 63566604, + 63897480, 64228332, 64559148, 64889940, 65220696, 65551424, + 65882120, 66212788, 66543420, 66874024, 67204600, 67535136, + 67865648, 68196120, 68526568, 68856984, 69187360, 69517712, + 69848024, 70178304, 70508560, 70838776, 71168960, 71499112, + 71829224, 72159312, 72489360, 72819376, 73149360, 73479304, + 73809216, 74139096, 74468936, 74798744, 75128520, 75458264, + 75787968, 76117632, 76447264, 76776864, 77106424, 77435952, + 77765440, 78094888, 78424304, 78753688, 79083032, 79412336, + 79741608, 80070840, 80400032, 80729192, 81058312, 81387392, + 81716432, 82045440, 82374408, 82703336, 83032224, 83361080, + 83689896, 84018664, 84347400, 84676096, 85004760, 85333376, + 85661952, 85990488, 86318984, 86647448, 86975864, 87304240, + 87632576, 87960872, 88289128, 88617344, 88945520, 89273648, + 89601736, 89929792, 90257792, 90585760, 90913688, 91241568, + 91569408, 91897200, 92224960, 92552672, 92880336, 93207968, + 93535552, 93863088, 94190584, 94518040, 94845448, 95172816, + 95500136, 95827416, 96154648, 96481832, 96808976, 97136080, + 97463136, 97790144, 98117112, 98444032, 98770904, 99097736, + 99424520, 99751256, 100077944, 100404592, 100731192, 101057744, + 101384248, 101710712, 102037128, 102363488, 102689808, 103016080, + 103342312, 103668488, 103994616, 104320696, 104646736, 104972720, + 105298656, 105624552, 105950392, 106276184, 106601928, 106927624, + 107253272, 107578872, 107904416, 108229920, 108555368, 108880768, + 109206120, 109531416, 109856664, 110181872, 110507016, 110832120, + 111157168, 111482168, 111807112, 112132008, 112456856, 112781648, + 113106392, 113431080, 113755720, 114080312, 114404848, 114729328, + 115053760, 115378136, 115702464, 116026744, 116350960, 116675128, + 116999248, 117323312, 117647320, 117971272, 118295176, 118619024, + 118942816, 119266560, 119590248, 119913880, 120237456, 120560984, + 120884456, 121207864, 121531224, 121854528, 122177784, 122500976, + 122824112, 123147200, 123470224, 123793200, 124116120, 124438976, + 124761784, 125084528, 125407224, 125729856, 126052432, 126374960, + 126697424, 127019832, 127342184, 127664472, 127986712, 128308888, + 128631008, 128953072, 129275080, 129597024, 129918912, 130240744, + 130562520, 130884232, 131205888, 131527480, 131849016, 132170496, + 132491912, 132813272, 133134576, 133455816, 133776992, 134098120, + 134419184, 134740176, 135061120, 135382000, 135702816, 136023584, + 136344272, 136664912, 136985488, 137306016, 137626464, 137946864, + 138267184, 138587456, 138907664, 139227808, 139547904, 139867920, + 140187888, 140507776, 140827616, 141147392, 141467104, 141786752, + 142106336, 142425856, 142745312, 143064720, 143384048, 143703312, + 144022512, 144341664, 144660736, 144979744, 145298704, 145617584, + 145936400, 146255168, 146573856, 146892480, 147211040, 147529536, + 147847968, 148166336, 148484640, 148802880, 149121056, 149439152, + 149757200, 150075168, 150393072, 150710912, 151028688, 151346400, + 151664048, 151981616, 152299136, 152616576, 152933952, 153251264, + 153568496, 153885680, 154202784, 154519824, 154836784, 155153696, + 155470528, 155787296, 156104000, 156420624, 156737200, 157053696, + 157370112, 157686480, 158002768, 158318976, 158635136, 158951216, + 159267232, 159583168, 159899040, 160214848, 160530592, 160846256, + 161161840, 161477376, 161792832, 162108208, 162423520, 162738768, + 163053952, 163369040, 163684080, 163999040, 164313936, 164628752, + 164943504, 165258176, 165572784, 165887312, 166201776, 166516160, + 166830480, 167144736, 167458912, 167773008, 168087040, 168400992, + 168714880, 169028688, 169342432, 169656096, 169969696, 170283216, + 170596672, 170910032, 171223344, 171536576, 171849728, 172162800, + 172475808, 172788736, 173101600, 173414384, 173727104, 174039728, + 174352288, 174664784, 174977200, 175289536, 175601792, 175913984, + 176226096, 176538144, 176850096, 177161984, 177473792, 177785536, + 178097200, 178408784, 178720288, 179031728, 179343088, 179654368, + 179965568, 180276704, 180587744, 180898720, 181209616, 181520448, + 181831184, 182141856, 182452448, 182762960, 183073408, 183383760, + 183694048, 184004240, 184314368, 184624416, 184934400, 185244288, + 185554096, 185863840, 186173504, 186483072, 186792576, 187102000, + 187411344, 187720608, 188029808, 188338912, 188647936, 188956896, + 189265760, 189574560, 189883264, 190191904, 190500448, 190808928, + 191117312, 191425632, 191733872, 192042016, 192350096, 192658096, + 192966000, 193273840, 193581584, 193889264, 194196848, 194504352, + 194811792, 195119136, 195426400, 195733584, 196040688, 196347712, + 196654656, 196961520, 197268304, 197574992, 197881616, 198188144, + 198494592, 198800960, 199107248, 199413456, 199719584, 200025616, + 200331584, 200637456, 200943248, 201248960, 201554576, 201860128, + 202165584, 202470960, 202776256, 203081456, 203386592, 203691632, + 203996592, 204301472, 204606256, 204910976, 205215600, 205520144, + 205824592, 206128960, 206433248, 206737456, 207041584, 207345616, + 207649568, 207953424, 208257216, 208560912, 208864512, 209168048, + 209471488, 209774832, 210078112, 210381296, 210684384, 210987408, + 211290336, 211593184, 211895936, 212198608, 212501184, 212803680, + 213106096, 213408432, 213710672, 214012816, 214314880, 214616864, + 214918768, 215220576, 215522288, 215823920, 216125472, 216426928, + 216728304, 217029584, 217330784, 217631904, 217932928, 218233856, + 218534704, 218835472, 219136144, 219436720, 219737216, 220037632, + 220337952, 220638192, 220938336, 221238384, 221538352, 221838240, + 222138032, 222437728, 222737344, 223036880, 223336304, 223635664, + 223934912, 224234096, 224533168, 224832160, 225131072, 225429872, + 225728608, 226027232, 226325776, 226624240, 226922608, 227220880, + 227519056, 227817152, 228115168, 228413088, 228710912, 229008640, + 229306288, 229603840, 229901312, 230198688, 230495968, 230793152, + 231090256, 231387280, 231684192, 231981024, 232277760, 232574416, + 232870960, 233167440, 233463808, 233760096, 234056288, 234352384, + 234648384, 234944304, 235240128, 235535872, 235831504, 236127056, + 236422512, 236717888, 237013152, 237308336, 237603424, 237898416, + 238193328, 238488144, 238782864, 239077488, 239372016, 239666464, + 239960816, 240255072, 240549232, 240843312, 241137280, 241431168, + 241724960, 242018656, 242312256, 242605776, 242899200, 243192512, + 243485744, 243778896, 244071936, 244364880, 244657744, 244950496, + 245243168, 245535744, 245828224, 246120608, 246412912, 246705104, + 246997216, 247289216, 247581136, 247872960, 248164688, 248456320, + 248747856, 249039296, 249330640, 249621904, 249913056, 250204128, + 250495088, 250785968, 251076736, 251367424, 251658016, 251948512, + 252238912, 252529200, 252819408, 253109520, 253399536, 253689456, + 253979280, 254269008, 254558640, 254848176, 255137632, 255426976, + 255716224, 256005376, 256294432, 256583392, 256872256, 257161024, + 257449696, 257738272, 258026752, 258315136, 258603424, 258891600, + 259179696, 259467696, 259755600, 260043392, 260331104, 260618704, + 260906224, 261193632, 261480960, 261768176, 262055296, 262342320, + 262629248, 262916080, 263202816, 263489456, 263776000, 264062432, + 264348784, 264635024, 264921168, 265207216, 265493168, 265779024, + 266064784, 266350448, 266636000, 266921472, 267206832, 267492096, + 267777264, 268062336, 268347312, 268632192, 268916960, 269201632, + 269486208, 269770688, 270055072, 270339360, 270623552, 270907616, + 271191616, 271475488, 271759296, 272042976, 272326560, 272610048, + 272893440, 273176736, 273459936, 273743040, 274026048, 274308928, + 274591744, 274874432, 275157024, 275439520, 275721920, 276004224, + 276286432, 276568512, 276850528, 277132416, 277414240, 277695936, + 277977536, 278259040, 278540448, 278821728, 279102944, 279384032, + 279665056, 279945952, 280226752, 280507456, 280788064, 281068544, + 281348960, 281629248, 281909472, 282189568, 282469568, 282749440, + 283029248, 283308960, 283588544, 283868032, 284147424, 284426720, + 284705920, 284985024, 285264000, 285542912, 285821696, 286100384, + 286378976, 286657440, 286935840, 287214112, 287492320, 287770400, + 288048384, 288326240, 288604032, 288881696, 289159264, 289436768, + 289714112, 289991392, 290268576, 290545632, 290822592, 291099456, + 291376224, 291652896, 291929440, 292205888, 292482272, 292758528, + 293034656, 293310720, 293586656, 293862496, 294138240, 294413888, + 294689440, 294964864, 295240192, 295515424, 295790560, 296065600, + 296340512, 296615360, 296890080, 297164704, 297439200, 297713632, + 297987936, 298262144, 298536256, 298810240, 299084160, 299357952, + 299631648, 299905248, 300178720, 300452128, 300725408, 300998592, + 301271680, 301544640, 301817536, 302090304, 302362976, 302635520, + 302908000, 303180352, 303452608, 303724768, 303996800, 304268768, + 304540608, 304812320, 305083968, 305355520, 305626944, 305898272, + 306169472, 306440608, 306711616, 306982528, 307253344, 307524064, + 307794656, 308065152, 308335552, 308605856, 308876032, 309146112, + 309416096, 309685984, 309955744, 310225408, 310494976, 310764448, + 311033824, 311303072, 311572224, 311841280, 312110208, 312379040, + 312647776, 312916416, 313184960, 313453376, 313721696, 313989920, + 314258016, 314526016, 314793920, 315061728, 315329408, 315597024, + 315864512, 316131872, 316399168, 316666336, 316933408, 317200384, + 317467232, 317733984, 318000640, 318267200, 318533632, 318799968, + 319066208, 319332352, 319598368, 319864288, 320130112, 320395808, + 320661408, 320926912, 321192320, 321457632, 321722816, 321987904, + 322252864, 322517760, 322782528, 323047200, 323311744, 323576192, + 323840544, 324104800, 324368928, 324632992, 324896928, 325160736, + 325424448, 325688096, 325951584, 326215008, 326478304, 326741504, + 327004608, 327267584, 327530464, 327793248, 328055904, 328318496, + 328580960, 328843296, 329105568, 329367712, 329629760, 329891680, + 330153536, 330415264, 330676864, 330938400, 331199808, 331461120, + 331722304, 331983392, 332244384, 332505280, 332766048, 333026752, + 333287296, 333547776, 333808128, 334068384, 334328544, 334588576, + 334848512, 335108352, 335368064, 335627712, 335887200, 336146624, + 336405920, 336665120, 336924224, 337183200, 337442112, 337700864, + 337959552, 338218112, 338476576, 338734944, 338993184, 339251328, + 339509376, 339767296, 340025120, 340282848, 340540480, 340797984, + 341055392, 341312704, 341569888, 341826976, 342083968, 342340832, + 342597600, 342854272, 343110848, 343367296, 343623648, 343879904, + 344136032, 344392064, 344648000, 344903808, 345159520, 345415136, + 345670656, 345926048, 346181344, 346436512, 346691616, 346946592, + 347201440, 347456224, 347710880, 347965440, 348219872, 348474208, + 348728448, 348982592, 349236608, 349490528, 349744320, 349998048, + 350251648, 350505152, 350758528, 351011808, 351264992, 351518048, + 351771040, 352023872, 352276640, 352529280, 352781824, 353034272, + 353286592, 353538816, 353790944, 354042944, 354294880, 354546656, + 354798368, 355049952, 355301440, 355552800, 355804096, 356055264, + 356306304, 356557280, 356808128, 357058848, 357309504, 357560032, + 357810464, 358060768, 358311008, 358561088, 358811104, 359060992, + 359310784, 359560480, 359810048, 360059520, 360308896, 360558144, + 360807296, 361056352, 361305312, 361554144, 361802880, 362051488, + 362300032, 362548448, 362796736, 363044960, 363293056, 363541024, + 363788928, 364036704, 364284384, 364531936, 364779392, 365026752, + 365274016, 365521152, 365768192, 366015136, 366261952, 366508672, + 366755296, 367001792, 367248192, 367494496, 367740704, 367986784, + 368232768, 368478656, 368724416, 368970080, 369215648, 369461088, + 369706432, 369951680, 370196800, 370441824, 370686752, 370931584, + 371176288, 371420896, 371665408, 371909792, 372154080, 372398272, + 372642336, 372886304, 373130176, 373373952, 373617600, 373861152, + 374104608, 374347936, 374591168, 374834304, 375077312, 375320224, + 375563040, 375805760, 376048352, 376290848, 376533248, 376775520, + 377017696, 377259776, 377501728, 377743584, 377985344, 378227008, + 378468544, 378709984, 378951328, 379192544, 379433664, 379674688, + 379915584, 380156416, 380397088, 380637696, 380878176, 381118560, + 381358848, 381599040, 381839104, 382079072, 382318912, 382558656, + 382798304, 383037856, 383277280, 383516640, 383755840, 383994976, + 384233984, 384472896, 384711712, 384950400, 385188992, 385427488, + 385665888, 385904160, 386142336, 386380384, 386618368, 386856224, + 387093984, 387331616, 387569152, 387806592, 388043936, 388281152, + 388518272, 388755296, 388992224, 389229024, 389465728, 389702336, + 389938816, 390175200, 390411488, 390647680, 390883744, 391119712, + 391355584, 391591328, 391826976, 392062528, 392297984, 392533312, + 392768544, 393003680, 393238720, 393473632, 393708448, 393943168, + 394177760, 394412256, 394646656, 394880960, 395115136, 395349216, + 395583200, 395817088, 396050848, 396284512, 396518080, 396751520, + 396984864, 397218112, 397451264, 397684288, 397917248, 398150080, + 398382784, 398615424, 398847936, 399080320, 399312640, 399544832, + 399776928, 400008928, 400240832, 400472608, 400704288, 400935872, + 401167328, 401398720, 401629984, 401861120, 402092192, 402323136, + 402553984, 402784736, 403015360, 403245888, 403476320, 403706656, + 403936896, 404167008, 404397024, 404626944, 404856736, 405086432, + 405316032, 405545536, 405774912, 406004224, 406233408, 406462464, + 406691456, 406920320, 407149088, 407377760, 407606336, 407834784, + 408063136, 408291392, 408519520, 408747584, 408975520, 409203360, + 409431072, 409658720, 409886240, 410113664, 410340992, 410568192, + 410795296, 411022304, 411249216, 411476032, 411702720, 411929312, + 412155808, 412382176, 412608480, 412834656, 413060736, 413286720, + 413512576, 413738336, 413964000, 414189568, 414415040, 414640384, + 414865632, 415090784, 415315840, 415540800, 415765632, 415990368, + 416215008, 416439552, 416663968, 416888288, 417112512, 417336640, + 417560672, 417784576, 418008384, 418232096, 418455712, 418679200, + 418902624, 419125920, 419349120, 419572192, 419795200, 420018080, + 420240864, 420463552, 420686144, 420908608, 421130976, 421353280, + 421575424, 421797504, 422019488, 422241344, 422463104, 422684768, + 422906336, 423127776, 423349120, 423570400, 423791520, 424012576, + 424233536, 424454368, 424675104, 424895744, 425116288, 425336736, + 425557056, 425777280, 425997408, 426217440, 426437376, 426657184, + 426876928, 427096544, 427316064, 427535488, 427754784, 427974016, + 428193120, 428412128, 428631040, 428849856, 429068544, 429287168, + 429505664, 429724064, 429942368, 430160576, 430378656, 430596672, + 430814560, 431032352, 431250048, 431467616, 431685120, 431902496, + 432119808, 432336992, 432554080, 432771040, 432987936, 433204736, + 433421408, 433637984, 433854464, 434070848, 434287104, 434503296, + 434719360, 434935360, 435151232, 435367008, 435582656, 435798240, + 436013696, 436229088, 436444352, 436659520, 436874592, 437089568, + 437304416, 437519200, 437733856, 437948416, 438162880, 438377248, + 438591520, 438805696, 439019744, 439233728, 439447584, 439661344, + 439875008, 440088576, 440302048, 440515392, 440728672, 440941824, + 441154880, 441367872, 441580736, 441793472, 442006144, 442218720, + 442431168, 442643552, 442855808, 443067968, 443280032, 443492000, + 443703872, 443915648, 444127296, 444338880, 444550336, 444761696, + 444972992, 445184160, 445395232, 445606176, 445817056, 446027840, + 446238496, 446449088, 446659552, 446869920, 447080192, 447290400, + 447500448, 447710432, 447920320, 448130112, 448339776, 448549376, + 448758848, 448968224, 449177536, 449386720, 449595808, 449804800, + 450013664, 450222464, 450431168, 450639776, 450848256, 451056640, + 451264960, 451473152, 451681248, 451889248, 452097152, 452304960, + 452512672, 452720288, 452927808, 453135232, 453342528, 453549760, + 453756864, 453963904, 454170816, 454377632, 454584384, 454791008, + 454997536, 455203968, 455410304, 455616544, 455822688, 456028704, + 456234656, 456440512, 456646240, 456851904, 457057472, 457262912, + 457468256, 457673536, 457878688, 458083744, 458288736, 458493600, + 458698368, 458903040, 459107616, 459312096, 459516480, 459720768, + 459924960, 460129056, 460333056, 460536960, 460740736, 460944448, + 461148064, 461351584, 461554976, 461758304, 461961536, 462164640, + 462367680, 462570592, 462773440, 462976160, 463178816, 463381344, + 463583776, 463786144, 463988384, 464190560, 464392608, 464594560, + 464796448, 464998208, 465199872, 465401472, 465602944, 465804320, + 466005600, 466206816, 466407904, 466608896, 466809824, 467010624, + 467211328, 467411936, 467612480, 467812896, 468013216, 468213440, + 468413600, 468613632, 468813568, 469013440, 469213184, 469412832, + 469612416, 469811872, 470011232, 470210528, 470409696, 470608800, + 470807776, 471006688, 471205472, 471404192, 471602784, 471801312, + 471999712, 472198048, 472396288, 472594400, 472792448, 472990400, + 473188256, 473385984, 473583648, 473781216, 473978688, 474176064, + 474373344, 474570528, 474767616, 474964608, 475161504, 475358336, + 475555040, 475751648, 475948192, 476144608, 476340928, 476537184, + 476733312, 476929376, 477125344, 477321184, 477516960, 477712640, + 477908224, 478103712, 478299104, 478494400, 478689600, 478884704, + 479079744, 479274656, 479469504, 479664224, 479858880, 480053408, + 480247872, 480442240, 480636512, 480830656, 481024736, 481218752, + 481412640, 481606432, 481800128, 481993760, 482187264, 482380704, + 482574016, 482767264, 482960416, 483153472, 483346432, 483539296, + 483732064, 483924768, 484117344, 484309856, 484502240, 484694560, + 484886784, 485078912, 485270944, 485462880, 485654720, 485846464, + 486038144, 486229696, 486421184, 486612576, 486803840, 486995040, + 487186176, 487377184, 487568096, 487758912, 487949664, 488140320, + 488330880, 488521312, 488711712, 488901984, 489092160, 489282240, + 489472256, 489662176, 489851968, 490041696, 490231328, 490420896, + 490610336, 490799712, 490988960, 491178144, 491367232, 491556224, + 491745120, 491933920, 492122656, 492311264, 492499808, 492688256, + 492876608, 493064864, 493253056, 493441120, 493629120, 493817024, + 494004832, 494192544, 494380160, 494567712, 494755136, 494942496, + 495129760, 495316928, 495504000, 495691008, 495877888, 496064704, + 496251424, 496438048, 496624608, 496811040, 496997408, 497183680, + 497369856, 497555936, 497741920, 497927840, 498113632, 498299360, + 498484992, 498670560, 498856000, 499041376, 499226656, 499411840, + 499596928, 499781920, 499966848, 500151680, 500336416, 500521056, + 500705600, 500890080, 501074464, 501258752, 501442944, 501627040, + 501811072, 501995008, 502178848, 502362592, 502546240, 502729824, + 502913312, 503096704, 503280000, 503463232, 503646368, 503829408, + 504012352, 504195200, 504377984, 504560672, 504743264, 504925760, + 505108192, 505290496, 505472736, 505654912, 505836960, 506018944, + 506200832, 506382624, 506564320, 506745952, 506927488, 507108928, + 507290272, 507471552, 507652736, 507833824, 508014816, 508195744, + 508376576, 508557312, 508737952, 508918528, 509099008, 509279392, + 509459680, 509639904, 509820032, 510000064, 510180000, 510359872, + 510539648, 510719328, 510898944, 511078432, 511257856, 511437216, + 511616448, 511795616, 511974688, 512153664, 512332576, 512511392, + 512690112, 512868768, 513047296, 513225792, 513404160, 513582432, + 513760640, 513938784, 514116800, 514294752, 514472608, 514650368, + 514828064, 515005664, 515183168, 515360608, 515537952, 515715200, + 515892352, 516069440, 516246432, 516423328, 516600160, 516776896, + 516953536, 517130112, 517306592, 517482976, 517659264, 517835488, + 518011616, 518187680, 518363648, 518539520, 518715296, 518891008, + 519066624, 519242144, 519417600, 519592960, 519768256, 519943424, + 520118528, 520293568, 520468480, 520643328, 520818112, 520992800, + 521167392, 521341888, 521516320, 521690656, 521864896, 522039072, + 522213152, 522387168, 522561056, 522734912, 522908640, 523082304, + 523255872, 523429376, 523602784, 523776096, 523949312, 524122464, + 524295552, 524468512, 524641440, 524814240, 524986976, 525159616, + 525332192, 525504640, 525677056, 525849344, 526021568, 526193728, + 526365792, 526537760, 526709632, 526881440, 527053152, 527224800, + 527396352, 527567840, 527739200, 527910528, 528081728, 528252864, + 528423936, 528594880, 528765760, 528936576, 529107296, 529277920, + 529448480, 529618944, 529789344, 529959648, 530129856, 530300000, + 530470048, 530640000, 530809888, 530979712, 531149440, 531319072, + 531488608, 531658080, 531827488, 531996800, 532166016, 532335168, + 532504224, 532673184, 532842080, 533010912, 533179616, 533348288, + 533516832, 533685312, 533853728, 534022048, 534190272, 534358432, + 534526496, 534694496, 534862400, 535030240, 535197984, 535365632, + 535533216, 535700704, 535868128, 536035456, 536202720, 536369888, + 536536992, 536704000, 536870912, +}; + +/* Now where did these came from? */ + +const byte gammatable[5][256] = +{ + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255}, + + { + 2, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 20, + 21, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 36, 37, + 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 230, 231, 232, 233, 234, 235, 236, 237, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 245, 246, 247, 248, 249, 250, 251, 252, + 252, 253, 254, 255, + }, + + { + 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, + 30, 32, 33, 35, 36, 38, 39, 40, 42, 43, 45, 46, 47, 48, + 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, + 67, 68, 69, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 153, 154, 155, 156, 157, 158, 159, 160, 160, 161, 162, + 163, 164, 165, 166, 166, 167, 168, 169, 170, 171, 172, 172, 173, 174, + 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 183, 184, 185, 186, + 187, 188, 188, 189, 190, 191, 192, 193, 193, 194, 195, 196, 197, 197, + 198, 199, 200, 201, 201, 202, 203, 204, 205, 206, 206, 207, 208, 209, + 210, 210, 211, 212, 213, 213, 214, 215, 216, 217, 217, 218, 219, 220, + 221, 221, 222, 223, 224, 224, 225, 226, 227, 228, 228, 229, 230, 231, + 231, 232, 233, 234, 235, 235, 236, 237, 238, 238, 239, 240, 241, 241, + 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 250, 251, 251, 252, + 253, 254, 254, 255, + }, + + { + 8, 12, 16, 19, 22, 24, 27, 29, 31, 34, 36, 38, 40, 41, + 43, 45, 47, 49, 50, 52, 53, 55, 57, 58, 60, 61, 63, 64, + 65, 67, 68, 70, 71, 72, 74, 75, 76, 77, 79, 80, 81, 82, + 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, + 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 164, + 165, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 173, 174, 175, + 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184, 185, 186, + 186, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 195, 195, 196, + 197, 197, 198, 199, 200, 200, 201, 202, 202, 203, 204, 205, 205, 206, + 207, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 215, 216, + 216, 217, 218, 219, 219, 220, 221, 221, 222, 223, 223, 224, 225, 225, + 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, + 235, 236, 237, 237, 238, 238, 239, 240, 240, 241, 242, 242, 243, 244, + 244, 245, 246, 246, 247, 247, 248, 249, 249, 250, 251, 251, 252, 253, + 253, 254, 254, 255, + }, + + { + 16, 23, 28, 32, 36, 39, 42, 45, 48, 50, 53, 55, 57, 60, + 62, 64, 66, 68, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, + 86, 87, 89, 90, 92, 93, 94, 96, 97, 98, 100, 101, 102, 103, + 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 143, 144, 145, 146, + 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, + 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 166, 167, 168, 169, + 169, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, + 180, 180, 181, 182, 182, 183, 184, 184, 185, 186, 187, 187, 188, 189, + 189, 190, 191, 191, 192, 193, 193, 194, 195, 195, 196, 196, 197, 198, + 198, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 207, + 207, 208, 208, 209, 210, 210, 211, 211, 212, 213, 213, 214, 214, 215, + 216, 216, 217, 217, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, + 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, + 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, + 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, + 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, + 254, 254, 255, 255, + }, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* to get a global angle from cartesian coordinates, the coordinates are + * flipped until they are in the first octant of the coordinate system, then + * the y (<=x) is scaled and divided by x to get a tangent (slope) value + * which is looked up in the tantoangle[] table. The +1 size is to handle + * the case when x==y without additional checking. + */ + +int slope_div(unsigned int num, unsigned int den) +{ + unsigned answer; + + if (den < 512) + { + return SLOPERANGE; + } + else + { + answer = (num << 3) / (den >> 8); + + if (answer <= SLOPERANGE) + { + return answer; + } + else + { + return SLOPERANGE; + } + } +} diff --git a/games/NXDoom/src/tables.h b/games/NXDoom/src/tables.h new file mode 100644 index 000000000..1a30ee74e --- /dev/null +++ b/games/NXDoom/src/tables.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * apps/games/NXDoom/src/tables.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Lookup tables. + * Do not try to look them up :-). + * In the order of appearance: + * + * int finetangent[4096] - Tangens LUT. + * Should work with BAM fairly well (12 of 16bit, effectively, + * by shifting). + * + * int finesine[10240] - Sine lookup. + * Guess what, serves as cosine, too. + * Remarkable thing is, how to use BAMs with this? + * + * int tantoangle[2049] - ArcTan LUT, + * maps tan(angle) to angle fast. Gotta search. + * + * + * DESCRIPTION: + * System specific interface stuff. + * + ****************************************************************************/ + +#ifndef __TABLES__ +#define __TABLES__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +#include "m_fixed.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define FINEANGLES 8192 +#define FINEMASK (FINEANGLES - 1) + +/* 0x100000000 to 0x2000 */ + +#define ANGLETOFINESHIFT 19 + +/* Binary Angle Measurement, BAM. */ + +#define ANG45 0x20000000 +#define ANG90 0x40000000 +#define ANG180 0x80000000 +#define ANG270 0xc0000000 +#define ANG_MAX 0xffffffff + +#define ANG1 (ANG45 / 45) +#define ANG60 (ANG180 / 3) + +/* Heretic code uses this definition as though it represents one + * degree, but it is not! This is actually ~1.40 degrees. + */ + +#define ANG1_X 0x01000000 + +#define SLOPERANGE 2048 +#define SLOPEBITS 11 +#define DBITS (FRACBITS - SLOPEBITS) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef unsigned int angle_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Effective size is 10240. */ + +extern const fixed_t finesine[5 * FINEANGLES / 4]; + +/* Reuse data, is just PI/2 pahse shift. */ + +extern const fixed_t *finecosine; + +/* Effective size is 4096. */ + +extern const fixed_t finetangent[FINEANGLES / 2]; + +/* Gamma correction tables. */ + +extern const byte gammatable[5][256]; + +/* Effective size is 2049; + * The +1 size is to handle the case when x==y without additional checking. + */ + +extern const angle_t tantoangle[SLOPERANGE + 1]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Utility function, called by r_point_to_angle. */ + +int slope_div(unsigned int num, unsigned int den); + +#endif /* __TABLES__ */ diff --git a/games/NXDoom/src/v_diskicon.c b/games/NXDoom/src/v_diskicon.c new file mode 100644 index 000000000..755d281a9 --- /dev/null +++ b/games/NXDoom/src/v_diskicon.c @@ -0,0 +1,185 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_diskicon.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Disk load indicator. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "deh_str.h" +#include "doomtype.h" +#include "i_swap.h" +#include "i_video.h" +#include "m_argv.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "v_diskicon.h" + +#include + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Only display the disk icon if more then this much bytes have been read + * during the previous tic. + */ + +static const int diskicon_threshold = 20 * 1024; + +/* Two buffers: disk_data contains the data representing the disk icon + * (raw, not a patch_t) while saved_background is an equivalently-sized + * buffer where we save the background data while the disk is on screen. + */ + +static pixel_t *disk_data; +static pixel_t *saved_background; + +static int loading_disk_xoffs = 0; +static int loading_disk_yoffs = 0; + +/* Number of bytes read since the last call to v_draw_disk_icon(). */ + +static size_t recent_bytes_read = 0; +static boolean disk_drawn; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void copy_region(pixel_t *dest, int dest_pitch, pixel_t *src, + int src_pitch, int w, int h) +{ + pixel_t *s, *d; + int y; + + s = src; + d = dest; + for (y = 0; y < h; ++y) + { + memcpy(d, s, w * sizeof(*d)); + s += src_pitch; + d += dest_pitch; + } +} + +static void save_disk_data(const char *disk_lump, int xoffs, int yoffs) +{ + pixel_t *tmpscreen; + patch_t *disk; + + /* Allocate a complete temporary screen where we'll draw the patch. */ + + tmpscreen = z_malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen), + PU_STATIC, NULL); + memset(tmpscreen, 0, SCREENWIDTH * SCREENHEIGHT * sizeof(*tmpscreen)); + v_use_buffer(tmpscreen); + + /* Buffer where we'll save the disk data. */ + + if (disk_data != NULL) + { + z_free(disk_data); + disk_data = NULL; + } + + disk_data = z_malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*disk_data), + PU_STATIC, NULL); + + /* Draw the patch and save the result to disk_data. */ + + disk = w_cache_lump_name(disk_lump, PU_STATIC); + v_draw_patch(loading_disk_xoffs, loading_disk_yoffs, disk); + copy_region(disk_data, LOADING_DISK_W, + tmpscreen + yoffs * SCREENWIDTH + xoffs, SCREENWIDTH, + LOADING_DISK_W, LOADING_DISK_H); + w_release_lump_name(disk_lump); + + v_restore_buffer(); + z_free(tmpscreen); +} + +static pixel_t *disk_region_pointer(void) +{ + return i_video_buffer + loading_disk_yoffs * SCREENWIDTH + + loading_disk_xoffs; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void v_enable_loading_disk(const char *lump_name, int xoffs, int yoffs) +{ + loading_disk_xoffs = xoffs; + loading_disk_yoffs = yoffs; + + if (saved_background != NULL) + { + z_free(saved_background); + saved_background = NULL; + } + + saved_background = + z_malloc(LOADING_DISK_W * LOADING_DISK_H * sizeof(*saved_background), + PU_STATIC, NULL); + save_disk_data(lump_name, xoffs, yoffs); +} + +void v_begin_read(size_t nbytes) +{ + recent_bytes_read += nbytes; +} + +void v_draw_disk_icon(void) +{ + if (disk_data != NULL && recent_bytes_read > diskicon_threshold) + { + /* Save the background behind the disk before we draw it. */ + + copy_region(saved_background, LOADING_DISK_W, disk_region_pointer(), + SCREENWIDTH, LOADING_DISK_W, LOADING_DISK_H); + + /* Write the disk to the screen buffer. */ + + copy_region(disk_region_pointer(), SCREENWIDTH, disk_data, + LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); + disk_drawn = true; + } + + recent_bytes_read = 0; +} + +void v_restore_disk_background(void) +{ + if (disk_drawn) + { + /* Restore the background. */ + + copy_region(disk_region_pointer(), SCREENWIDTH, saved_background, + LOADING_DISK_W, LOADING_DISK_W, LOADING_DISK_H); + + disk_drawn = false; + } +} diff --git a/games/NXDoom/src/v_diskicon.h b/games/NXDoom/src/v_diskicon.h new file mode 100644 index 000000000..4bd9b54aa --- /dev/null +++ b/games/NXDoom/src/v_diskicon.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_diskicon.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Disk load indicator. + * + ****************************************************************************/ + +#ifndef __V_DISKICON__ +#define __V_DISKICON__ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Dimensions of the flashing "loading" disk icon */ + +#define LOADING_DISK_W 16 +#define LOADING_DISK_H 16 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void v_enable_loading_disk(const char *lump_name, int xoffs, + int yoffs); +extern void v_begin_read(size_t nbytes); +extern void v_draw_disk_icon(void); +extern void v_restore_disk_background(void); + +#endif /* __V_DISKICON__ */ diff --git a/games/NXDoom/src/v_patch.h b/games/NXDoom/src/v_patch.h new file mode 100644 index 000000000..5bfac559c --- /dev/null +++ b/games/NXDoom/src/v_patch.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_patch.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + ****************************************************************************/ + +#ifndef V_PATCH_H +#define V_PATCH_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Patches. + * A patch holds one or more columns. + * Patches are used for sprites and all masked pictures, + * and we compose textures from the TEXTURE1/2 lists + * of patches. + */ + +begin_packed_struct struct patch_t +{ + short width; /* bounding box size */ + short height; + short leftoffset; /* pixels to the left of origin */ + short topoffset; /* pixels below the origin */ + int columnofs[8]; /* only [width] used the [0] is &columnofs[width] */ +} end_packed_struct; + +typedef struct patch_t patch_t; + +/* posts are runs of non masked source pixels */ + +begin_packed_struct struct post_t +{ + byte topdelta; /* -1 is the last post in a column */ + byte length; /* length data bytes follows */ +} end_packed_struct; + +typedef struct post_t post_t; + +/* column_t is a list of 0 or more post_t, (byte)-1 terminated */ + +typedef post_t column_t; + +#endif /* V_PATCH_H */ diff --git a/games/NXDoom/src/v_video.c b/games/NXDoom/src/v_video.c new file mode 100644 index 000000000..55f455cce --- /dev/null +++ b/games/NXDoom/src/v_video.c @@ -0,0 +1,881 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_video.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 1993-2008 Raven Software + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Gamma correction LUT stuff. + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "i_system.h" + +#include "doomtype.h" + +#include "deh_str.h" +#include "i_input.h" +#include "i_swap.h" +#include "i_video.h" +#include "m_bbox.h" +#include "m_misc.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MOUSE_SPEED_BOX_WIDTH 120 +#define MOUSE_SPEED_BOX_HEIGHT 9 +#define MOUSE_SPEED_BOX_X (SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH - 10) +#define MOUSE_SPEED_BOX_Y 15 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* SCREEN SHOTS */ + +begin_packed_struct struct pcx_t +{ + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + + unsigned short xmin; + unsigned short ymin; + unsigned short xmax; + unsigned short ymax; + + unsigned short hres; + unsigned short vres; + + unsigned char palette[48]; + + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + + char filler[58]; + unsigned char data; /* unbounded */ +} end_packed_struct; + +typedef struct pcx_t pcx_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The screen buffer that the v_video.c code draws to. */ + +static pixel_t *dest_screen = NULL; + +/* haleyjd 08/28/10: clipping callback function for patches. + * This is needed for Chocolate Strife, which clips patches to the screen. + */ + +static vpatchclipfunc_t patchclip_callback = NULL; + +/* Highest seen mouse turn speed. We scale the range of the thermometer + * according to this value, so that it never exceeds the range. Initially + * this is set to a 1:1 setting where 1 pixel = 1 unit of speed. + */ + +static int max_seen_speed = MOUSE_SPEED_BOX_WIDTH - 1; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Blending table used for fuzzpatch, etc. Only used in Heretic/Hexen */ + +byte *tinttable = NULL; + +/* villsa [STRIFE] Blending table used for Strife */ + +byte *xlatab = NULL; + +int dirtybox[4]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void draw_accelerating_box(int speed) +{ + int red; + int white; + int yellow; + int original_speed; + int redline_x; + int linelen; + + red = i_get_palette_index(0xff, 0x00, 0x00); + white = i_get_palette_index(0xff, 0xff, 0xff); + yellow = i_get_palette_index(0xff, 0xff, 0x00); + + /* Calculate the position of the red threshold line when calibrating + * acceleration. This is 1/3 of the way along the box. + */ + + redline_x = MOUSE_SPEED_BOX_WIDTH / 3; + + if (speed >= mouse_threshold) + { + /* Undo acceleration and get back the original mouse speed */ + + original_speed = speed - mouse_threshold; + original_speed = (int)(original_speed / mouse_acceleration); + original_speed += mouse_threshold; + + linelen = (original_speed * redline_x) / mouse_threshold; + } + else + { + linelen = (speed * redline_x) / mouse_threshold; + } + + /* Horizontal "thermometer" */ + + if (linelen > MOUSE_SPEED_BOX_WIDTH - 1) + { + linelen = MOUSE_SPEED_BOX_WIDTH - 1; + } + + if (linelen < redline_x) + { + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + linelen, white); + } + else + { + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + redline_x, white); + v_draw_horiz_line(MOUSE_SPEED_BOX_X + redline_x, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, + linelen - redline_x, yellow); + } + + /* Draw acceleration threshold line */ + + v_draw_vert_line(MOUSE_SPEED_BOX_X + redline_x, MOUSE_SPEED_BOX_Y + 1, + MOUSE_SPEED_BOX_HEIGHT - 2, red); +} + +static void draw_non_accelerating_box(int speed) +{ + int white; + int linelen; + + white = i_get_palette_index(0xff, 0xff, 0xff); + + if (speed > max_seen_speed) + { + max_seen_speed = speed; + } + + /* Draw horizontal "thermometer": */ + + linelen = speed * (MOUSE_SPEED_BOX_WIDTH - 1) / max_seen_speed; + + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, + MOUSE_SPEED_BOX_Y + MOUSE_SPEED_BOX_HEIGHT / 2, linelen, + white); +} + +static void write_pcx_file(char *filename, pixel_t *data, int width, + int height, byte *palette) +{ + int i; + int length; + pcx_t *pcx; + byte *pack; + + pcx = z_malloc(width * height * 2 + 1000, PU_STATIC, NULL); + + pcx->manufacturer = 0x0a; /* PCX id */ + pcx->version = 5; /* 256 color */ + pcx->encoding = 1; /* uncompressed */ + pcx->bits_per_pixel = 8; /* 256 color */ + pcx->xmin = 0; + pcx->ymin = 0; + pcx->xmax = SHORT(width - 1); + pcx->ymax = SHORT(height - 1); + pcx->hres = SHORT(1); + pcx->vres = SHORT(1); + memset(pcx->palette, 0, sizeof(pcx->palette)); + pcx->reserved = 0; /* PCX spec: reserved byte must be zero */ + pcx->color_planes = 1; /* chunky image */ + pcx->bytes_per_line = SHORT(width); + pcx->palette_type = SHORT(2); /* not a grey scale */ + memset(pcx->filler, 0, sizeof(pcx->filler)); + + /* pack the image */ + + pack = &pcx->data; + + for (i = 0; i < width * height; i++) + { + if ((*data & 0xc0) != 0xc0) + *pack++ = *data++; + else + { + *pack++ = 0xc1; + *pack++ = *data++; + } + } + + /* write the palette */ + + *pack++ = 0x0c; /* palette ID byte */ + for (i = 0; i < 768; i++) + *pack++ = *palette++; + + /* write output file */ + + length = pack - (byte *)pcx; + m_write_file(filename, pcx, length); + + z_free(pcx); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void v_mark_rect(int x, int y, int width, int height) +{ + /* If we are temporarily using an alternate screen, do not affect the + * update box. + */ + + if (dest_screen == i_video_buffer) + { + m_add_to_box(dirtybox, x, y); + m_add_to_box(dirtybox, x + width - 1, y + height - 1); + } +} + +void v_copy_rect(int srcx, int srcy, pixel_t *source, int width, int height, + int destx, int desty) +{ + pixel_t *src; + pixel_t *dest; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (srcx < 0 || srcx + width > SCREENWIDTH || srcy < 0 || + srcy + height > SCREENHEIGHT || destx < 0 || + destx + width > SCREENWIDTH || desty < 0 || + desty + height > SCREENHEIGHT) + { + i_error("Bad v_copy_rect"); + } +#endif + + v_mark_rect(destx, desty, width, height); + + src = source + SCREENWIDTH * srcy + srcx; + dest = dest_screen + SCREENWIDTH * desty + destx; + + for (; height > 0; height--) + { + memcpy(dest, src, width * sizeof(*dest)); + src += SCREENWIDTH; + dest += SCREENWIDTH; + } +} + +/* v_set_patch_clip_callback + * + * haleyjd 08/28/10: Added for Strife support. + * By calling this function, you can setup runtime error checking for patch + * clipping. Strife never caused errors by drawing patches partway + * off-screen. + * + * Some versions of vanilla DOOM also behaved differently than the default + * implementation, so this could possibly be extended to those as well for + * accurate emulation. + */ + +void v_set_patch_clip_callback(vpatchclipfunc_t func) +{ + patchclip_callback = func; +} + +/* V_DrawPatch + * Masks a column based masked pic to the screen. + */ + +void v_draw_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + /* haleyjd 08/28/10: Strife needs silent error checking here. */ + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad V_DrawPatch"); + } +#endif + + v_mark_rect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_patch_flipped + * Masks a column based masked pic to the screen. + * Flips horizontally, e.g. to mirror face. + */ + +void v_draw_patch_flipped(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + /* haleyjd 08/28/10: Strife needs silent error checking here. */ + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_patch_flipped"); + } +#endif + + v_mark_rect(x, y, SHORT(patch->width), SHORT(patch->height)); + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + + for (; col < w; x++, col++, desttop++) + { + column = + (column_t *)((byte *)patch + LONG(patch->columnofs[w - 1 - col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = *source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_patch_direct + * Draws directly to the screen on the pc. + */ + +void v_draw_patch_direct(int x, int y, patch_t *patch) +{ + v_draw_patch(x, y, patch); +} + +/* v_draw_tl_patch + * Masks a column based translucent masked pic to the screen. + */ + +void v_draw_tl_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_tl_patch"); + } + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + for (; col < w; x++, col++, desttop++) + { + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = tinttable[*dest + ((*source++) << 8)]; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_xla_patch + * villsa [STRIFE] Masks a column based translucent masked pic to the screen. + */ + +void v_draw_xla_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop, *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (patchclip_callback) + { + if (!patchclip_callback(patch, x, y)) return; + } + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + for (; col < w; x++, col++, desttop++) + { + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = xlatab[*dest + ((*source) << 8)]; + source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_alt_tl_patch + * Masks a column based translucent masked pic to the screen. + */ + +void v_draw_alt_tl_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_alt_tl_patch"); + } + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + + w = SHORT(patch->width); + for (; col < w; x++, col++, desttop++) + { + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest = tinttable[((*dest) << 8) + *source++]; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* v_draw_shadowed_patch + * Masks a column based masked pic to the screen. + */ + +void v_draw_shadowed_patch(int x, int y, patch_t *patch) +{ + int count; + int col; + column_t *column; + pixel_t *desttop; + pixel_t *dest; + byte *source; + pixel_t *desttop2; + pixel_t *dest2; + int w; + + y -= SHORT(patch->topoffset); + x -= SHORT(patch->leftoffset); + + if (x < 0 || x + SHORT(patch->width) > SCREENWIDTH || y < 0 || + y + SHORT(patch->height) > SCREENHEIGHT) + { + i_error("Bad v_draw_shadowed_patch"); + } + + col = 0; + desttop = dest_screen + y * SCREENWIDTH + x; + desttop2 = dest_screen + (y + 2) * SCREENWIDTH + x + 2; + + w = SHORT(patch->width); + for (; col < w; x++, col++, desttop++, desttop2++) + { + column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); + + /* step through the posts in a column */ + + while (column->topdelta != 0xff) + { + source = (byte *)column + 3; + dest = desttop + column->topdelta * SCREENWIDTH; + dest2 = desttop2 + column->topdelta * SCREENWIDTH; + count = column->length; + + while (count--) + { + *dest2 = tinttable[((*dest2) << 8)]; + dest2 += SCREENWIDTH; + *dest = *source++; + dest += SCREENWIDTH; + } + + column = (column_t *)((byte *)column + column->length + 4); + } + } +} + +/* Load tint table from TINTTAB lump. */ + +void v_load_tint_table(void) +{ + tinttable = w_cache_lump_name("TINTTAB", PU_STATIC); +} + +/* v_load_xla_table + * villsa [STRIFE] Load xla table from XLATAB lump. + */ + +void v_load_xla_table(void) +{ + xlatab = w_cache_lump_name("XLATAB", PU_STATIC); +} + +/* v_draw_block + * Draw a linear block of pixels into the view buffer. + */ + +void v_draw_block(int x, int y, int width, int height, pixel_t *src) +{ + pixel_t *dest; + +#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK + if (x < 0 || x + width > SCREENWIDTH || y < 0 || y + height > SCREENHEIGHT) + { + i_error("Bad v_draw_block"); + } +#endif + + v_mark_rect(x, y, width, height); + + dest = dest_screen + y * SCREENWIDTH + x; + + while (height--) + { + memcpy(dest, src, width * sizeof(*dest)); + src += width; + dest += SCREENWIDTH; + } +} + +void v_draw_filled_box(int x, int y, int w, int h, int c) +{ + pixel_t *buf, *buf1; + int x1; + int y1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (y1 = 0; y1 < h; ++y1) + { + buf1 = buf; + + for (x1 = 0; x1 < w; ++x1) + { + *buf1++ = c; + } + + buf += SCREENWIDTH; + } +} + +void v_draw_horiz_line(int x, int y, int w, int c) +{ + pixel_t *buf; + int x1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (x1 = 0; x1 < w; ++x1) + { + *buf++ = c; + } +} + +void v_draw_vert_line(int x, int y, int h, int c) +{ + pixel_t *buf; + int y1; + + buf = i_video_buffer + SCREENWIDTH * y + x; + + for (y1 = 0; y1 < h; ++y1) + { + *buf = c; + buf += SCREENWIDTH; + } +} + +void v_draw_box(int x, int y, int w, int h, int c) +{ + v_draw_horiz_line(x, y, w, c); + v_draw_horiz_line(x, y + h - 1, w, c); + v_draw_vert_line(x, y, h, c); + v_draw_vert_line(x + w - 1, y, h, c); +} + +/* Draw a "raw" screen (lump containing raw data to blit directly + * to the screen) + */ + +void v_draw_raw_screen(pixel_t *raw) +{ + memcpy(dest_screen, raw, + SCREENWIDTH * SCREENHEIGHT * sizeof(*dest_screen)); +} + +void v_init(void) +{ + /* no-op! + * There used to be separate screens that could be drawn to; these are + * now handled in the upper layers. + */ +} + +/* Set the buffer that the code draws to. */ + +void v_use_buffer(pixel_t *buffer) +{ + dest_screen = buffer; +} + +/* Restore screen buffer to the i_video screen buffer. */ + +void v_restore_buffer(void) +{ + dest_screen = i_video_buffer; +} + +void v_screenshot(const char *format) +{ + int i; + char lbmname[16]; /* haleyjd 20110213: BUG FIX - 12 is too small! */ + const char *ext; + + /* find a file name to save it to */ + + ext = "pcx"; + + for (i = 0; i <= 99; i++) + { + snprintf(lbmname, sizeof(lbmname), format, i, ext); + + if (!m_file_exists(lbmname)) + { + break; /* file doesn't exist */ + } + } + + if (i == 100) + { + i_error("v_screenshot: Couldn't create a PCX"); + } + + /* save the pcx file */ + + write_pcx_file(lbmname, i_video_buffer, SCREENWIDTH, SCREENHEIGHT, + w_cache_lump_name(("PLAYPAL"), PU_CACHE)); +} + +void v_draw_mouse_speed_box(int speed) +{ + int bgcolor; + int bordercolor; + int black; + + /* If the mouse is turned off, don't draw the box at all. */ + + if (!usemouse) + { + return; + } + + /* Get palette indices for colors for widget. These depend on the + * palette of the game being played. + */ + + bgcolor = i_get_palette_index(0x77, 0x77, 0x77); + bordercolor = i_get_palette_index(0x55, 0x55, 0x55); + black = i_get_palette_index(0x00, 0x00, 0x00); + + /* Calculate box position */ + + v_draw_filled_box(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, + MOUSE_SPEED_BOX_WIDTH, MOUSE_SPEED_BOX_HEIGHT, bgcolor); + v_draw_box(MOUSE_SPEED_BOX_X, MOUSE_SPEED_BOX_Y, MOUSE_SPEED_BOX_WIDTH, + MOUSE_SPEED_BOX_HEIGHT, bordercolor); + v_draw_horiz_line(MOUSE_SPEED_BOX_X + 1, MOUSE_SPEED_BOX_Y + 4, + MOUSE_SPEED_BOX_WIDTH - 2, black); + + /* If acceleration is used, draw a box that helps to calibrate the + * threshold point. + */ + + if (mouse_threshold > 0 && fabs(mouse_acceleration - 1) > 0.01) + { + draw_accelerating_box(speed); + } + else + { + draw_non_accelerating_box(speed); + } +} diff --git a/games/NXDoom/src/v_video.h b/games/NXDoom/src/v_video.h new file mode 100644 index 000000000..07ac56730 --- /dev/null +++ b/games/NXDoom/src/v_video.h @@ -0,0 +1,181 @@ +/**************************************************************************** + * apps/games/NXDoom/src/v_video.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Gamma correction LUT. + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + ****************************************************************************/ + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/* Needed because we are referring to patches. */ + +#include "v_patch.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CENTERY (SCREENHEIGHT / 2) + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int dirtybox[4]; + +extern byte *tinttable; + +/* haleyjd 08/28/10: implemented for Strife support + * haleyjd 08/28/10: Patch clipping callback, implemented to support Choco + * Strife. + */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef boolean (*vpatchclipfunc_t)(patch_t *, int, int); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void v_set_patch_clip_callback(vpatchclipfunc_t func); + +/**************************************************************************** + * Name: v_init + * + * Description: + * Allocates buffer screens, call before r_init. + * + ****************************************************************************/ + +void v_init(void); + +/**************************************************************************** + * Name: v_copy_rect + * + * Description: + * Draw a block from the specified source screen to the screen. + * + ****************************************************************************/ + +void v_copy_rect(int srcx, int srcy, pixel_t *source, int width, int height, + int destx, int desty); + +void v_draw_patch(int x, int y, patch_t *patch); +void v_draw_patch_flipped(int x, int y, patch_t *patch); +void v_draw_tl_patch(int x, int y, patch_t *patch); +void v_draw_alt_tl_patch(int x, int y, patch_t *patch); +void v_draw_shadowed_patch(int x, int y, patch_t *patch); +void v_draw_xla_patch(int x, int y, patch_t *patch); /* villsa [STRIFE] */ +void v_draw_patch_direct(int x, int y, patch_t *patch); + +/**************************************************************************** + * Name: v_draw_block + * + * Description: + * Draw a linear block of pixels into the view buffer. + * + ****************************************************************************/ + +void v_draw_block(int x, int y, int width, int height, pixel_t *src); + +void v_mark_rect(int x, int y, int width, int height); + +void v_draw_filled_box(int x, int y, int w, int h, int c); +void v_draw_horiz_line(int x, int y, int w, int c); +void v_draw_vert_line(int x, int y, int h, int c); +void v_draw_box(int x, int y, int w, int h, int c); + +/**************************************************************************** + * Name: v_draw_raw_screen + * + * Description: + * Draw a raw screen lump + * + ****************************************************************************/ + +void v_draw_raw_screen(pixel_t *raw); + +/**************************************************************************** + * Name: v_use_buffer + * + * Description: + * Temporarily switch to using a different buffer to draw graphics, etc. + * + ****************************************************************************/ + +void v_use_buffer(pixel_t *buffer); + +/**************************************************************************** + * Name: v_restore_buffer + * + * Description: + * Return to using the normal screen buffer to draw graphics. + * + ****************************************************************************/ + +void v_restore_buffer(void); + +/**************************************************************************** + * Name: v_screenshot + * + * Description: + * Save a screenshot of the current screen to a file, named in the format + * described in the string passed to the function, eg. "DOOM%02i.pcx" + * + ****************************************************************************/ + +void v_screenshot(const char *format); + +/**************************************************************************** + * Name: v_load_tint_table + * + * Description: + * Load the lookup table for translucency calculations from the TINTTAB + * lump. + * + ****************************************************************************/ + +void v_load_tint_table(void); + +/**************************************************************************** + * Name: v_load_xla_table + * + * Description: + * villsa [STRIFE] + * Load the lookup table for translucency calculations from the XLATAB lump. + * + ****************************************************************************/ + +void v_load_xla_table(void); + +void v_draw_mouse_speed_box(int speed); + +#endif diff --git a/games/NXDoom/src/w_checksum.c b/games/NXDoom/src/w_checksum.c new file mode 100644 index 000000000..28dbf8d16 --- /dev/null +++ b/games/NXDoom/src/w_checksum.c @@ -0,0 +1,113 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_checksum.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Generate a checksum of the WAD directory. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "i_system.h" +#include "m_misc.h" +#include "sha1.h" +#include "w_checksum.h" +#include "w_wad.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static wad_file_t **g_open_wadfiles = NULL; +static int g_num_open_wadfiles = 0; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int get_file_number(wad_file_t *handle) +{ + int i; + int result; + + for (i = 0; i < g_num_open_wadfiles; ++i) + { + if (g_open_wadfiles[i] == handle) + { + return i; + } + } + + /* Not found in list. This is a new file we haven't seen yet. + * Allocate another slot for this file. + */ + + g_open_wadfiles = i_realloc(g_open_wadfiles, + sizeof(wad_file_t *) * (g_num_open_wadfiles + 1)); + g_open_wadfiles[g_num_open_wadfiles] = handle; + + result = g_num_open_wadfiles; + ++g_num_open_wadfiles; + + return result; +} + +static void checksum_add_lump(SHA1_CTX *sha1_context, lumpinfo_t *lump) +{ + char buf[9]; + int fileno; + + fileno = get_file_number(lump->wad_file); + + m_str_copy(buf, lump->name, sizeof(buf)); + sha1_updatestring(sha1_context, buf); + sha1_updateint32(sha1_context, fileno); + sha1_updateint32(sha1_context, lump->position); + sha1_updateint32(sha1_context, lump->size); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void w_checksum(sha1_digest_t digest) +{ + SHA1_CTX sha1_context; + unsigned int i; + + sha1init(&sha1_context); + + g_num_open_wadfiles = 0; + + /* Go through each entry in the WAD directory, adding information + * about each entry to the SHA1 hash. + */ + + for (i = 0; i < numlumps; ++i) + { + checksum_add_lump(&sha1_context, lumpinfo[i]); + } + + sha1final(digest, &sha1_context); +} diff --git a/games/NXDoom/src/w_checksum.h b/games/NXDoom/src/w_checksum.h new file mode 100644 index 000000000..316239b70 --- /dev/null +++ b/games/NXDoom/src/w_checksum.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_checksum.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Generate a checksum of the WAD directory. + * + ****************************************************************************/ + +#ifndef W_CHECKSUM_H +#define W_CHECKSUM_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +extern void w_checksum(sha1_digest_t digest); + +#endif /* W_CHECKSUM_H */ diff --git a/games/NXDoom/src/w_file.c b/games/NXDoom/src/w_file.c new file mode 100644 index 000000000..38b4d9574 --- /dev/null +++ b/games/NXDoom/src/w_file.c @@ -0,0 +1,92 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "config.h" + +#include "doomtype.h" +#include "m_argv.h" + +#include "w_file.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static wad_file_class_t *wad_file_classes[] = +{ + &stdc_wad_file, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +wad_file_t *w_open_file(const char *path) +{ + wad_file_t *result; + int i; + + /* @category obscure + * + * Use the OS's virtual memory subsystem to map WAD files + * directly into memory. + */ + + if (!m_check_parm("-mmap")) + { + return stdc_wad_file.open_file(path); + } + + /* Try all classes in order until we find one that works */ + + result = NULL; + + for (i = 0; i < arrlen(wad_file_classes); ++i) + { + result = wad_file_classes[i]->open_file(path); + + if (result != NULL) + { + break; + } + } + + return result; +} + +void w_close_file(wad_file_t *wad) +{ + wad->file_class->close_file(wad); +} + +size_t w_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len) +{ + return wad->file_class->read(wad, offset, buffer, buffer_len); +} diff --git a/games/NXDoom/src/w_file.h b/games/NXDoom/src/w_file.h new file mode 100644 index 000000000..32ac34222 --- /dev/null +++ b/games/NXDoom/src/w_file.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +#ifndef __W_FILE__ +#define __W_FILE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "doomtype.h" +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct _wad_file_s wad_file_t; + +typedef struct +{ + /* Open a file for reading. */ + + wad_file_t *(*open_file)(const char *path); + + /* Close the specified file. */ + + void (*close_file)(wad_file_t *file); + + /* Read data from the specified position in the file into the provided + * buffer. Returns the number of bytes read. + */ + + size_t (*read)(wad_file_t *file, unsigned int offset, void *buffer, + size_t buffer_len); +} wad_file_class_t; + +struct _wad_file_s +{ + /* Class of this file. */ + + wad_file_class_t *file_class; + + /* If this is NULL, the file cannot be mapped into memory. If this is + * non-NULL, it is a pointer to the mapped file. + */ + + byte *mapped; + + /* Length of the file, in bytes. */ + + unsigned int length; + + /* File's location on disk. */ + + const char *path; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern wad_file_class_t stdc_wad_file; + +#ifdef HAVE_MMAP +extern wad_file_class_t posix_wad_file; +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Open the specified file. Returns a pointer to a new wad_file_t + * handle for the WAD file, or NULL if it could not be opened. + */ + +wad_file_t *w_open_file(const char *path); + +/* Close the specified WAD file. */ + +void w_close_file(wad_file_t *wad); + +/* Read data from the specified file into the provided buffer. The + * data is read from the specified offset from the start of the file. + * Returns the number of bytes read. + */ + +size_t w_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len); + +#endif /* __W_FILE__ */ diff --git a/games/NXDoom/src/w_file_stdc.c b/games/NXDoom/src/w_file_stdc.c new file mode 100644 index 000000000..361bf2060 --- /dev/null +++ b/games/NXDoom/src/w_file_stdc.c @@ -0,0 +1,123 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_file_stdc.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "m_misc.h" +#include "w_file.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + wad_file_t wad; + FILE *fstream; +} stdc_wad_file_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static wad_file_t *w_stdc_openfile(const char *path); +static void w_stdc_closefile(wad_file_t *wad); +static size_t w_stdc_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +wad_file_class_t stdc_wad_file = +{ + w_stdc_openfile, + w_stdc_closefile, + w_stdc_read, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static wad_file_t *w_stdc_openfile(const char *path) +{ + stdc_wad_file_t *result; + FILE *fstream; + + fstream = fopen(path, "rb"); + + if (fstream == NULL) + { + return NULL; + } + + /* Create a new stdc_wad_file_t to hold the file handle. */ + + result = z_malloc(sizeof(stdc_wad_file_t), PU_STATIC, 0); + result->wad.file_class = &stdc_wad_file; + result->wad.mapped = NULL; + result->wad.length = m_file_length(fstream); + result->wad.path = m_string_duplicate(path); + result->fstream = fstream; + + return &result->wad; +} + +static void w_stdc_closefile(wad_file_t *wad) +{ + stdc_wad_file_t *stdc_wad; + + stdc_wad = (stdc_wad_file_t *)wad; + + fclose(stdc_wad->fstream); + z_free(stdc_wad); +} + +/* Read data from the specified position in the file into the + * provided buffer. Returns the number of bytes read. + */ + +static size_t w_stdc_read(wad_file_t *wad, unsigned int offset, void *buffer, + size_t buffer_len) +{ + stdc_wad_file_t *stdc_wad; + size_t result; + + stdc_wad = (stdc_wad_file_t *)wad; + + /* Jump to the specified position in the file. */ + + fseek(stdc_wad->fstream, offset, SEEK_SET); + + /* Read into the buffer. */ + + result = fread(buffer, 1, buffer_len, stdc_wad->fstream); + + return result; +} diff --git a/games/NXDoom/src/w_main.c b/games/NXDoom/src/w_main.c new file mode 100644 index 000000000..ce1176547 --- /dev/null +++ b/games/NXDoom/src/w_main.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_main.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Common code to parse command line, identifying WAD files to load. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "config.h" +#include "d_iwad.h" +#include "i_glob.h" +#include "i_system.h" +#include "m_argv.h" +#include "w_main.h" +#include "w_merge.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct uniquelump +{ + gamemission_t mission; + const char *lumpname; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Lump names that are unique to particular game types. This lets us check + * the user is not trying to play with the wrong executable, eg. + * chocolate-doom -iwad hexen.wad. + */ + +static const struct uniquelump g_unique_lumps[] = +{ + {doom, "POSSA1"}, + {heretic, "IMPXA1"}, + {hexen, "ETTNA1"}, + {strife, "AGRDA1"}, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Parse the command line, merging WAD files that are sppecified. + * Returns true if at least one file was added. + */ + +boolean w_parse_command_line(void) +{ + boolean modifiedgame = false; + int p; + + /* Merged PWADs are loaded first, because they are supposed to be + * modified IWADs. + */ + + /* @arg + * @category mod + * + * Simulates the behavior of deutex's -merge option, merging a PWAD + * into the main IWAD. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-merge", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging %s\n", filename); + w_merge_file(filename); + free(filename); + } + } + + /* NWT-style merging: */ + + /* NWT's -merge option: */ + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -merge option. Multiple files + * may be specified. + */ + + p = m_check_parm_with_args("-nwtmerge", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" performing NWT-style merge of %s\n", filename); + w_nwt_dash_merge(filename); + free(filename); + } + } + + /* Add flats */ + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -af option, merging flats into + * the main IWAD directory. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-af", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging flats from %s\n", filename); + w_nwt_merge_file(filename, W_NWT_MERGE_FLATS); + free(filename); + } + } + + /* @arg + * @category mod + * + * Simulates the behavior of NWT's -as option, merging sprites + * into the main IWAD directory. Multiple files may be specified. + */ + + p = m_check_parm_with_args("-as", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging sprites from %s\n", filename); + w_nwt_merge_file(filename, W_NWT_MERGE_SPRITES); + free(filename); + } + } + + /* @arg + * @category mod + * + * Equivalent to "-af -as ". + */ + + p = m_check_parm_with_args("-aa", 1); + + if (p > 0) + { + for (p = p + 1; p < myargc && myargv[p][0] != '-'; ++p) + { + char *filename; + + modifiedgame = true; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" merging sprites and flats from %s\n", filename); + w_nwt_merge_file(filename, + W_NWT_MERGE_SPRITES | W_NWT_MERGE_FLATS); + free(filename); + } + } + + /* @arg + * @vanilla + * + * Load the specified PWAD files. Each succeeding argument is + * treated as a PWAD file name until one starts with a dash or the + * argument list is exhausted. + */ + + p = m_check_parm_with_args("-file", 1); + if (p) + { + /* the params after p are wadfile/lump names, + * until end of params or another - preceded param + */ + + modifiedgame = true; /* homebrew levels */ + while (++p != myargc && myargv[p][0] != '-') + { + char *filename; + + filename = d_try_find_wad_by_name(myargv[p]); + + printf(" adding %s\n", filename); + w_add_file(filename); + free(filename); + } + } + + return modifiedgame; +} + +/* Load all WAD files from the given directory. */ + +void w_auto_load_wads(const char *path) +{ + glob_t *glob; + const char *filename; + + glob = i_start_multi_glob(path, GLOB_FLAG_NOCASE | GLOB_FLAG_SORTED, + "*.wad", "*.lmp", NULL); + for (; ; ) + { + filename = i_next_glob(glob); + if (filename == NULL) + { + break; + } + + printf(" [autoload] merging %s\n", filename); + w_merge_file(filename); + } + + i_end_glob(glob); +} + +void w_check_correct_iwad(gamemission_t mission) +{ + int i; + lumpindex_t lumpnum; + + for (i = 0; i < arrlen(g_unique_lumps); ++i) + { + if (mission != g_unique_lumps[i].mission) + { + lumpnum = w_check_num_for_name(g_unique_lumps[i].lumpname); + + if (lumpnum >= 0) + { + i_error( + "\nYou are trying to use a %s IWAD file with " + "the %s%s binary.\nThis isn't going to work.\n" + "You probably want to use the %s%s binary.", + d_suggest_game_name( + g_unique_lumps[i].mission, indetermined), + PROGRAM_PREFIX, d_game_mission_string(mission), + PROGRAM_PREFIX, + d_game_mission_string(g_unique_lumps[i].mission)); + } + } + } +} diff --git a/games/NXDoom/src/w_main.h b/games/NXDoom/src/w_main.h new file mode 100644 index 000000000..beab3f29b --- /dev/null +++ b/games/NXDoom/src/w_main.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_main.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Common code to parse command line, identifying WAD files to load. + * + ****************************************************************************/ + +#ifndef W_MAIN_H +#define W_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "d_mode.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +boolean w_parse_command_line(void); +void w_check_correct_iwad(gamemission_t mission); + +/* Autoload all .wad files from the given directory: */ + +void w_auto_load_wads(const char *path); + +#endif /* W_MAIN_H */ diff --git a/games/NXDoom/src/w_merge.c b/games/NXDoom/src/w_merge.c new file mode 100644 index 000000000..88a05b4bd --- /dev/null +++ b/games/NXDoom/src/w_merge.c @@ -0,0 +1,764 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_merge.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Handles merging of PWADs, similar to deutex's -merge option + * + * Ideally this should work exactly the same as in deutex, but trying to + * read the deutex source code made my brain hurt. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_misc.h" +#include "w_merge.h" +#include "w_wad.h" +#include "z_zone.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef enum +{ + SECTION_NORMAL, + SECTION_FLATS, + SECTION_SPRITES, +} section_t; + +typedef struct +{ + lumpinfo_t **lumps; + int numlumps; +} searchlist_t; + +typedef struct +{ + char sprname[4]; + char frame; + lumpinfo_t *angle_lumps[8]; +} sprite_frame_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static searchlist_t iwad; +static searchlist_t iwad_sprites; +static searchlist_t pwad; + +static searchlist_t iwad_flats; +static searchlist_t pwad_sprites; +static searchlist_t pwad_flats; + +/* lumps with these sprites must be replaced in the IWAD */ + +static sprite_frame_t *sprite_frames; +static int num_sprite_frames; +static int sprite_frames_alloced; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Search in a list to find a lump with a particular name + * Linear search (slow!) + * + * Returns -1 if not found + */ + +static int find_in_list(searchlist_t *list, const char *name) +{ + int i; + + for (i = 0; i < list->numlumps; ++i) + { + if (!strncasecmp(list->lumps[i]->name, name, 8)) return i; + } + + return -1; +} + +static boolean setup_list(searchlist_t *list, searchlist_t *src_list, + const char *startname, const char *endname, + const char *startname2, const char *endname2) +{ + int startlump; + int endlump; + + list->numlumps = 0; + startlump = find_in_list(src_list, startname); + + if (startname2 != NULL && startlump < 0) + { + startlump = find_in_list(src_list, startname2); + } + + if (startlump >= 0) + { + endlump = find_in_list(src_list, endname); + + if (endname2 != NULL && endlump < 0) + { + endlump = find_in_list(src_list, endname2); + } + + if (endlump > startlump) + { + list->lumps = src_list->lumps + startlump + 1; + list->numlumps = endlump - startlump - 1; + return true; + } + } + + return false; +} + +/* Sets up the sprite/flat search lists */ + +static void setup_lists(void) +{ + /* IWAD */ + + if (!setup_list(&iwad_flats, &iwad, "F_START", "F_END", NULL, NULL)) + { + i_error("Flats section not found in IWAD"); + } + + if (!setup_list(&iwad_sprites, &iwad, "S_START", "S_END", NULL, NULL)) + + { + i_error("Sprites section not found in IWAD"); + } + + /* PWAD */ + + setup_list(&pwad_flats, &pwad, "F_START", "F_END", "FF_START", "FF_END"); + setup_list(&pwad_sprites, &pwad, "S_START", "S_END", "SS_START", "SS_END"); +} + +/* Initialize the replace list */ + +static void init_sprite_list(void) +{ + if (sprite_frames == NULL) + { + sprite_frames_alloced = 128; + sprite_frames = z_malloc( + sizeof(*sprite_frames) * sprite_frames_alloced, + PU_STATIC, NULL + ); + } + + num_sprite_frames = 0; +} + +static boolean valid_sprite_lump_name(char *name) +{ + if (name[0] == '\0' || name[1] == '\0' || name[2] == '\0' || + name[3] == '\0') + { + return false; + } + + /* First frame: */ + + if (name[4] == '\0' || !isdigit(name[5])) + { + return false; + } + + /* Second frame (optional): */ + + if (name[6] != '\0' && !isdigit(name[7])) + { + return false; + } + + return true; +} + +static sprite_frame_t *find_sprite_frame(char *name, int frame) +{ + sprite_frame_t *result; + int i; + + /* Search the list and try to find the frame */ + + for (i = 0; i < num_sprite_frames; ++i) + { + sprite_frame_t *cur = &sprite_frames[i]; + + if (!strncasecmp(cur->sprname, name, 4) && cur->frame == frame) + { + return cur; + } + } + + /* Not found in list; Need to add to the list */ + + /* Grow list? */ + + if (num_sprite_frames >= sprite_frames_alloced) + { + sprite_frame_t *newframes; + + newframes = z_malloc( + sprite_frames_alloced * 2 * sizeof(*sprite_frames), + PU_STATIC, NULL + ); + memcpy(newframes, sprite_frames, + sprite_frames_alloced * sizeof(*sprite_frames)); + z_free(sprite_frames); + sprite_frames_alloced *= 2; + sprite_frames = newframes; + } + + /* Add to end of list */ + + result = &sprite_frames[num_sprite_frames]; + memcpy(result->sprname, name, 4); + result->frame = frame; + + for (i = 0; i < 8; ++i) + result->angle_lumps[i] = NULL; + + ++num_sprite_frames; + + return result; +} + +/* Check if sprite lump is needed in the new wad */ + +static boolean sprite_lump_needed(lumpinfo_t *lump) +{ + sprite_frame_t *sprite; + int angle_num; + int i; + + if (!valid_sprite_lump_name(lump->name)) + { + return true; + } + + /* check the first frame */ + + sprite = find_sprite_frame(lump->name, lump->name[4]); + angle_num = lump->name[5] - '0'; + + if (angle_num == 0) + { + /* must check all frames */ + + for (i = 0; i < 8; ++i) + { + if (sprite->angle_lumps[i] == lump) return true; + } + } + else + { + /* check if this lump is being used for this frame */ + + if (sprite->angle_lumps[angle_num - 1] == lump) return true; + } + + /* second frame if any */ + + /* no second frame? */ + + if (lump->name[6] == '\0') return false; + + sprite = find_sprite_frame(lump->name, lump->name[6]); + angle_num = lump->name[7] - '0'; + + if (angle_num == 0) + { + /* must check all frames */ + + for (i = 0; i < 8; ++i) + { + if (sprite->angle_lumps[i] == lump) return true; + } + } + else + { + /* check if this lump is being used for this frame */ + + if (sprite->angle_lumps[angle_num - 1] == lump) return true; + } + + return false; +} + +static void add_sprite_lump(lumpinfo_t *lump) +{ + sprite_frame_t *sprite; + int angle_num; + int i; + + if (!valid_sprite_lump_name(lump->name)) + { + return; + } + + /* first angle */ + + sprite = find_sprite_frame(lump->name, lump->name[4]); + angle_num = lump->name[5] - '0'; + + if (angle_num == 0) + { + for (i = 0; i < 8; ++i) + sprite->angle_lumps[i] = lump; + } + else + { + sprite->angle_lumps[angle_num - 1] = lump; + } + + /* second angle */ + + /* no second angle? */ + + if (lump->name[6] == '\0') return; + + sprite = find_sprite_frame(lump->name, lump->name[6]); + angle_num = lump->name[7] - '0'; + + if (angle_num == 0) + { + for (i = 0; i < 8; ++i) + sprite->angle_lumps[i] = lump; + } + else + { + sprite->angle_lumps[angle_num - 1] = lump; + } +} + +/* Generate the list. Run at the start, before merging */ + +static void generate_sprite_list(void) +{ + int i; + + init_sprite_list(); + + /* Add all sprites from the IWAD */ + + for (i = 0; i < iwad_sprites.numlumps; ++i) + { + add_sprite_lump(iwad_sprites.lumps[i]); + } + + /* Add all sprites from the PWAD + * (replaces IWAD sprites) + */ + + for (i = 0; i < pwad_sprites.numlumps; ++i) + { + add_sprite_lump(pwad_sprites.lumps[i]); + } +} + +/* Perform the merge. + * + * The merge code creates a new lumpinfo list, adding entries from the + * IWAD first followed by the PWAD. + * + * For the IWAD: + * * Flats are added. If a flat with the same name is in the PWAD, + * it is ignored(deleted). At the end of the section, all flats in the + * PWAD are inserted. This is consistent with the behavior of + * deutex/deusf. + * * Sprites are added. The "replace list" is generated before the merge + * from the list of sprites in the PWAD. Any sprites in the IWAD found + * to match the replace list are removed. At the end of the section, + * the sprites from the PWAD are inserted. + * + * For the PWAD: + * * All Sprites and Flats are ignored, with the assumption they have + * already been merged into the IWAD's sections. + */ + +static void do_merge(void) +{ + section_t current_section; + lumpinfo_t **newlumps; + int num_newlumps; + int lumpindex; + int i; + int n; + + /* Can't ever have more lumps than we already have */ + + newlumps = calloc(numlumps, sizeof(lumpinfo_t *)); + num_newlumps = 0; + + /* Add IWAD lumps */ + + current_section = SECTION_NORMAL; + + for (i = 0; i < iwad.numlumps; ++i) + { + lumpinfo_t *lump = iwad.lumps[i]; + + switch (current_section) + { + case SECTION_NORMAL: + if (!strncasecmp(lump->name, "F_START", 8)) + { + current_section = SECTION_FLATS; + } + else if (!strncasecmp(lump->name, "S_START", 8)) + { + current_section = SECTION_SPRITES; + } + + newlumps[num_newlumps++] = lump; + + break; + + case SECTION_FLATS: + + /* Have we reached the end of the section? */ + + if (!strncasecmp(lump->name, "F_END", 8)) + { + /* Add all new flats from the PWAD to the end + * of the section + */ + + for (n = 0; n < pwad_flats.numlumps; ++n) + { + newlumps[num_newlumps++] = pwad_flats.lumps[n]; + } + + newlumps[num_newlumps++] = lump; + + /* back to normal reading */ + + current_section = SECTION_NORMAL; + } + else + { + /* If there is a flat in the PWAD with the same name, + * do not add it now. All PWAD flats are added to the + * end of the section. Otherwise, if it is only in the + * IWAD, add it now + */ + + lumpindex = find_in_list(&pwad_flats, lump->name); + + if (lumpindex < 0) + { + newlumps[num_newlumps++] = lump; + } + } + + break; + + case SECTION_SPRITES: + + /* Have we reached the end of the section? */ + + if (!strncasecmp(lump->name, "S_END", 8)) + { + /* add all the PWAD sprites */ + + for (n = 0; n < pwad_sprites.numlumps; ++n) + { + if (sprite_lump_needed(pwad_sprites.lumps[n])) + { + newlumps[num_newlumps++] = pwad_sprites.lumps[n]; + } + } + + /* copy the ending */ + + newlumps[num_newlumps++] = lump; + + /* back to normal reading */ + + current_section = SECTION_NORMAL; + } + else + { + /* Is this lump holding a sprite to be replaced in the + * PWAD? If so, wait until the end to add it. + */ + + if (sprite_lump_needed(lump)) + { + newlumps[num_newlumps++] = lump; + } + } + + break; + } + } + + /* Add PWAD lumps */ + + current_section = SECTION_NORMAL; + + for (i = 0; i < pwad.numlumps; ++i) + { + lumpinfo_t *lump = pwad.lumps[i]; + + switch (current_section) + { + case SECTION_NORMAL: + if (!strncasecmp(lump->name, "F_START", 8) || + !strncasecmp(lump->name, "FF_START", 8)) + { + current_section = SECTION_FLATS; + } + else if (!strncasecmp(lump->name, "S_START", 8) || + !strncasecmp(lump->name, "SS_START", 8)) + { + current_section = SECTION_SPRITES; + } + else + { + /* Don't include the headers of sections */ + + newlumps[num_newlumps++] = lump; + } + break; + + case SECTION_FLATS: + + /* PWAD flats are ignored (already merged) */ + + if (!strncasecmp(lump->name, "FF_END", 8) || + !strncasecmp(lump->name, "F_END", 8)) + { + /* end of section */ + + current_section = SECTION_NORMAL; + } + break; + + case SECTION_SPRITES: + + /* PWAD sprites are ignored (already merged) */ + + if (!strncasecmp(lump->name, "SS_END", 8) || + !strncasecmp(lump->name, "S_END", 8)) + { + /* end of section */ + + current_section = SECTION_NORMAL; + } + break; + } + } + + /* Switch to the new lumpinfo, and free the old one */ + + free(lumpinfo); + lumpinfo = newlumps; + numlumps = num_newlumps; +} + +/* Replace lumps in the given list with lumps from the PWAD */ + +static void w_nwt_add_lumps(searchlist_t *list) +{ + int i; + + /* Go through the IWAD list given, replacing lumps with lumps of + * the same name from the PWAD + */ + + for (i = 0; i < list->numlumps; ++i) + { + int index; + + index = find_in_list(&pwad, list->lumps[i]->name); + + if (index > 0) + { + memcpy(list->lumps[i], pwad.lumps[index], sizeof(lumpinfo_t)); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void w_print_directory(void) +{ + unsigned int i; + unsigned int n; + + /* debug */ + + for (i = 0; i < numlumps; ++i) + { + for (n = 0; n < 8 && lumpinfo[i]->name[n] != '\0'; ++n) + putchar(lumpinfo[i]->name[n]); + putchar('\n'); + } +} + +/* Merge in a file by name */ + +void w_merge_file(const char *filename) +{ + int old_numlumps; + + old_numlumps = numlumps; + + /* Load PWAD */ + + if (w_add_file(filename) == NULL) return; + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Generate list of sprites to be replaced by the PWAD */ + + generate_sprite_list(); + + /* Perform the merge */ + + do_merge(); +} + +/* Merge sprites and flats in the way NWT does with its -af and -as + * command-line options. + */ + +void w_nwt_merge_file(const char *filename, int flags) +{ + int old_numlumps; + + old_numlumps = numlumps; + + /* Load PWAD */ + + if (w_add_file(filename) == NULL) return; + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Merge in flats? */ + + if (flags & W_NWT_MERGE_FLATS) + { + w_nwt_add_lumps(&iwad_flats); + } + + /* Sprites? */ + + if (flags & W_NWT_MERGE_SPRITES) + { + w_nwt_add_lumps(&iwad_sprites); + } + + /* Discard the PWAD */ + + numlumps = old_numlumps; +} + +/* Simulates the NWT -merge command line parameter. What this does is load + * a PWAD, then search the IWAD sprites, removing any sprite lumps that also + * exist in the PWAD. + */ + +void w_nwt_dash_merge(const char *filename) +{ + wad_file_t *wad_file; + int old_numlumps; + int i; + + old_numlumps = numlumps; + + /* Load PWAD */ + + wad_file = w_add_file(filename); + + if (wad_file == NULL) + { + return; + } + + /* IWAD is at the start, PWAD was appended to the end */ + + iwad.lumps = lumpinfo; + iwad.numlumps = old_numlumps; + + pwad.lumps = lumpinfo + old_numlumps; + pwad.numlumps = numlumps - old_numlumps; + + /* Setup sprite/flat lists */ + + setup_lists(); + + /* Search through the IWAD sprites list. */ + + for (i = 0; i < iwad_sprites.numlumps; ++i) + { + if (find_in_list(&pwad, iwad_sprites.lumps[i]->name) >= 0) + { + /* Replace this entry with an empty string. This is what + * nwt -merge does. + */ + + m_str_copy(iwad_sprites.lumps[i]->name, "", 8); + } + } + + /* Discard PWAD + * The PWAD must now be added in again with -file. + */ + + numlumps = old_numlumps; + + w_close_file(wad_file); +} diff --git a/games/NXDoom/src/w_merge.h b/games/NXDoom/src/w_merge.h new file mode 100644 index 000000000..ce580afc5 --- /dev/null +++ b/games/NXDoom/src/w_merge.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_merge.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Handles merging of PWADs, similar to deutex's -merge option + * + * Ideally this should work exactly the same as in deutex, but trying to + * read the deutex source code made my brain hurt. + * + ****************************************************************************/ + +#ifndef W_MERGE_H +#define W_MERGE_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define W_NWT_MERGE_SPRITES 0x1 +#define W_NWT_MERGE_FLATS 0x2 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Add a new WAD and merge it into the main directory */ + +void w_merge_file(const char *filename); + +/* NWT-style merging */ + +void w_nwt_merge_file(const char *filename, int flags); + +/* Acts the same as NWT's "-merge" option. */ + +void w_nwt_dash_merge(const char *filename); + +/* Debug function that prints the WAD directory. */ + +void w_print_directory(void); + +#endif /* W_MERGE_H */ diff --git a/games/NXDoom/src/w_wad.c b/games/NXDoom/src/w_wad.c new file mode 100644 index 000000000..88c75ada5 --- /dev/null +++ b/games/NXDoom/src/w_wad.c @@ -0,0 +1,663 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_wad.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Handles WAD file header, directory, lump I/O. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomtype.h" + +#include "i_swap.h" +#include "i_system.h" +#include "i_video.h" +#include "m_misc.h" +#include "v_diskicon.h" +#include "z_zone.h" + +#include "w_wad.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +begin_packed_struct struct wadinfo_t +{ + /* Should be "IWAD" or "PWAD". */ + + char identification[4]; + int numlumps; + int infotableofs; +} end_packed_struct; + +typedef struct wadinfo_t wadinfo_t; + +begin_packed_struct struct filelump_t +{ + int filepos; + int size; + char name[8]; +} end_packed_struct; + +typedef struct filelump_t filelump_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Hash table for fast lookups */ + +static lumpindex_t *lumphash; + +/* Variables for the reload hack: filename of the PWAD to reload, and the + * lumps from WADs before the reload file, so we can resent numlumps and + * load the file again. + */ + +static wad_file_t *reloadhandle = NULL; +static lumpinfo_t *reloadlumps = NULL; +static char *reloadname = NULL; +static int reloadlump = -1; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Location of each lump on disk. */ + +lumpinfo_t **lumpinfo; +unsigned int numlumps = 0; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Hash function used for lump names. */ + +unsigned int w_lump_name_hash(const char *s) +{ + /* This is the djb2 string hash function, modded to work on strings + * that have a maximum length of 8. + */ + + unsigned int result = 5381; + unsigned int i; + + for (i = 0; i < 8 && s[i] != '\0'; ++i) + { + result = ((result << 5) ^ result) ^ toupper(s[i]); + } + + return result; +} + +/* LUMP BASED ROUTINES. */ + +/* w_add_file + * + * All files are optional, but at least one file must be found (PWAD, if all + * required lumps are present). + * + * Files with a .wad extension are wadlink files with multiple lumps. + * + * Other files are single lumps with the base filename for the lump name. + */ + +wad_file_t *w_add_file(const char *filename) +{ + wadinfo_t header; + lumpindex_t i; + wad_file_t *wad_file; + int length; + int startlump; + filelump_t *fileinfo; + filelump_t *filerover; + lumpinfo_t *filelumps; + int numfilelumps; + + /* If the filename begins with a ~, it indicates that we should use the + * reload hack. + */ + + if (filename[0] == '~') + { + if (reloadname != NULL) + { + i_error("Prefixing a WAD filename with '~' indicates that the " + "WAD should be reloaded\n" + "on each level restart, for use by level authors for " + "rapid development. You\n" + "can only reload one WAD file, and it must be the last " + "file in the -file list."); + } + + reloadname = strdup(filename); + reloadlump = numlumps; + ++filename; + } + + /* Open the file and add to directory */ + + wad_file = w_open_file(filename); + + if (wad_file == NULL) + { + printf(" couldn't open %s\n", filename); + return NULL; + } + + if (strcasecmp(filename + strlen(filename) - 3, "wad")) + { + /* single lump file */ + + /* fraggle: Swap the filepos and size here. The WAD directory + * parsing code expects a little-endian directory, so will swap + * them back. Effectively we're constructing a "fake WAD directory" + * here, as it would appear on disk. + */ + + fileinfo = z_malloc(sizeof(filelump_t), PU_STATIC, 0); + fileinfo->filepos = LONG(0); + fileinfo->size = LONG(wad_file->length); + + /* Name the lump after the base of the filename (without the + * extension). + */ + + m_extract_file_base(filename, fileinfo->name); + numfilelumps = 1; + } + else + { + /* WAD file */ + + w_read(wad_file, 0, &header, sizeof(header)); + + if (strncmp(header.identification, "IWAD", 4)) + { + /* Homebrew levels? */ + + if (strncmp(header.identification, "PWAD", 4)) + { + w_close_file(wad_file); + i_error("Wad file %s doesn't have IWAD " + "or PWAD id\n", + filename); + } + + /* ???modifiedgame = true; */ + } + + header.numlumps = LONG(header.numlumps); + + /* Vanilla Doom doesn't like WADs with more than 4046 lumps + * https://www.doomworld.com/vb/post/1010985 + */ + + if (!strncmp(header.identification, "PWAD", 4) && + header.numlumps > 4046) + { + w_close_file(wad_file); + i_error("Error: Vanilla limit for lumps in a WAD is 4046, " + "PWAD %s has %d", + filename, header.numlumps); + } + + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps * sizeof(filelump_t); + fileinfo = z_malloc(length, PU_STATIC, 0); + + w_read(wad_file, header.infotableofs, fileinfo, length); + numfilelumps = header.numlumps; + } + + /* Increase size of numlumps array to accommodate the new file. */ + + filelumps = calloc(numfilelumps, sizeof(lumpinfo_t)); + if (filelumps == NULL) + { + w_close_file(wad_file); + i_error("Failed to allocate array for lumps from new file."); + } + + startlump = numlumps; + numlumps += numfilelumps; + lumpinfo = i_realloc(lumpinfo, numlumps * sizeof(lumpinfo_t *)); + filerover = fileinfo; + + for (i = startlump; i < numlumps; ++i) + { + lumpinfo_t *lump_p = &filelumps[i - startlump]; + lump_p->wad_file = wad_file; + lump_p->position = LONG(filerover->filepos); + lump_p->size = LONG(filerover->size); + lump_p->cache = NULL; + strncpy(lump_p->name, filerover->name, 8); + lumpinfo[i] = lump_p; + + ++filerover; + } + + z_free(fileinfo); + + if (lumphash != NULL) + { + z_free(lumphash); + lumphash = NULL; + } + + /* If this is the reload file, we need to save some details about the + * file so that we can close it later on when we do a reload. + */ + + if (reloadname) + { + reloadhandle = wad_file; + reloadlumps = filelumps; + } + + return wad_file; +} + +/* w_check_num_for_name + * Returns -1 if name not found. + */ + +lumpindex_t w_check_num_for_name(const char *name) +{ + lumpindex_t i; + + /* Do we have a hash table yet? */ + + if (lumphash != NULL) + { + int hash; + + /* We do! Excellent. */ + + hash = w_lump_name_hash(name) % numlumps; + + for (i = lumphash[hash]; i != -1; i = lumpinfo[i]->next) + { + if (!strncasecmp(lumpinfo[i]->name, name, 8)) + { + return i; + } + } + } + else + { + /* We don't have a hash table generate yet. Linear search :-( + * + * scan backwards so patch lump files take precedence + */ + + for (i = numlumps - 1; i >= 0; --i) + { + if (!strncasecmp(lumpinfo[i]->name, name, 8)) + { + return i; + } + } + } + + /* TFB. Not found. */ + + return -1; +} + +/* w_get_num_for_name + * Calls w_check_num_for_name, but bombs out if not found. + */ + +lumpindex_t w_get_num_for_name(const char *name) +{ + lumpindex_t i; + + i = w_check_num_for_name(name); + + if (i < 0) + { + i_error("w_get_num_for_name: %s not found!", name); + } + + return i; +} + +/* w_lump_length + * Returns the buffer size needed to load the given lump. + */ + +int w_lump_length(lumpindex_t lump) +{ + if (lump >= numlumps) + { + i_error("w_lump_length: %i >= numlumps", lump); + } + + return lumpinfo[lump]->size; +} + +/* w_read_lump + * Loads the lump into the given buffer, + * which must be >= w_lump_length(). + */ + +void w_read_lump(lumpindex_t lump, void *dest) +{ + int c; + lumpinfo_t *l; + + if (lump >= numlumps) + { + i_error("w_read_lump: %i >= numlumps", lump); + } + + l = lumpinfo[lump]; + + v_begin_read(l->size); + + c = w_read(l->wad_file, l->position, dest, l->size); + + if (c < l->size) + { + i_error("w_read_lump: only read %i of %i on lump %i", + c, l->size, lump); + } +} + +/* w_cache_lump_num + * + * Load a lump into memory and return a pointer to a buffer containing + * the lump data. + * + * 'tag' is the type of zone memory buffer to allocate for the lump + * (usually PU_STATIC or PU_CACHE). If the lump is loaded as + * PU_STATIC, it should be released back using w_release_lump_num + * when no longer needed (do not use z_change_tag). + */ + +void *w_cache_lump_num(lumpindex_t lumpnum, int tag) +{ + byte *result; + lumpinfo_t *lump; + + if ((unsigned)lumpnum >= numlumps) + { + i_error("w_cache_lump_num: %i >= numlumps", lumpnum); + } + + lump = lumpinfo[lumpnum]; + + /* Get the pointer to return. If the lump is in a memory-mapped + * file, we can just return a pointer to within the memory-mapped + * region. If the lump is in an ordinary file, we may already + * have it cached; otherwise, load it into memory. + */ + + if (lump->wad_file->mapped != NULL) + { + /* Memory mapped file, return from the mmapped region. */ + + result = lump->wad_file->mapped + lump->position; + } + else if (lump->cache != NULL) + { + /* Already cached, so just switch the zone tag. */ + + result = lump->cache; + z_change_tag(lump->cache, tag); + } + else + { + /* Not yet loaded, so load it now */ + + lump->cache = z_malloc(w_lump_length(lumpnum), tag, &lump->cache); + w_read_lump(lumpnum, lump->cache); + result = lump->cache; + } + + return result; +} + +/* w_cache_lump_name */ + +void *w_cache_lump_name(const char *name, int tag) +{ + return w_cache_lump_num(w_get_num_for_name(name), tag); +} + +/* Release a lump back to the cache, so that it can be reused later + * without having to read from disk again, or alternatively, discarded + * if we run out of memory. + * + * Back in Vanilla Doom, this was just done using z_change_tag + * directly, but now that we have WAD mmap, things are a bit more + * complicated ... + */ + +void w_release_lump_num(lumpindex_t lumpnum) +{ + lumpinfo_t *lump; + + if ((unsigned)lumpnum >= numlumps) + { + i_error("w_release_lump_num: %i >= numlumps", lumpnum); + } + + lump = lumpinfo[lumpnum]; + + if (lump->wad_file->mapped != NULL) + { + /* Memory-mapped file, so nothing needs to be done here. */ + } + else + { + z_change_tag(lump->cache, PU_CACHE); + } +} + +void w_release_lump_name(const char *name) +{ + w_release_lump_num(w_get_num_for_name(name)); +} + +#if 0 + +/* w_profile */ + +int info[2500][10]; +int profilecount; + +void w_profile(void) +{ + int i; + memblock_t *block; + void *ptr; + char ch; + FILE *f; + int j; + char name[9]; + + for (i = 0; i < numlumps; i++) + { + ptr = lumpinfo[i].cache; + if (!ptr) + { + ch = ' '; + continue; + } + else + { + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + if (block->tag < PU_PURGELEVEL) + ch = 'S'; + else + ch = 'P'; + } + + info[i][profilecount] = ch; + } + + profilecount++; + + f = fopen("waddump.txt", "w"); + name[8] = 0; + + for (i = 0; i < numlumps; i++) + { + memcpy(name, lumpinfo[i].name, 8); + + for (j = 0; j < 8; j++) + { + if (!name[j]) + { + break; + } + } + + for (; j < 8; j++) + { + name[j] = ' '; + } + + fprintf(f, "%s ", name); + + for (j = 0; j < profilecount; j++) + { + fprintf(f, " %c", info[i][j]); + } + + fprintf(f, "\n"); + } + + fclose(f); +} +#endif + +/* Generate a hash table for fast lookups */ + +void w_generate_hash_table(void) +{ + lumpindex_t i; + + /* Free the old hash table, if there is one: */ + + if (lumphash != NULL) + { + z_free(lumphash); + } + + /* Generate hash table */ + + if (numlumps > 0) + { + lumphash = z_malloc(sizeof(lumpindex_t) * numlumps, PU_STATIC, NULL); + + for (i = 0; i < numlumps; ++i) + { + lumphash[i] = -1; + } + + for (i = 0; i < numlumps; ++i) + { + unsigned int hash; + + hash = w_lump_name_hash(lumpinfo[i]->name) % numlumps; + + /* Hook into the hash table */ + + lumpinfo[i]->next = lumphash[hash]; + lumphash[hash] = i; + } + } + + /* All done! */ +} + +/* The Doom reload hack. The idea here is that if you give a WAD file to + * -file prefixed with the ~ hack, that WAD file will be reloaded each time a + * new level is loaded. This lets you use a level editor in parallel and make + * incremental changes to the level you're working on without having to + * restart the game after every change. But: the reload feature is a fragile + * hack... + */ + +void w_reload(void) +{ + char *filename; + lumpindex_t i; + + if (reloadname == NULL) + { + return; + } + + /* We must free any lumps being cached from the PWAD we're about to reload: + */ + + for (i = reloadlump; i < numlumps; ++i) + { + if (lumpinfo[i]->cache != NULL) + { + z_free(lumpinfo[i]->cache); + } + } + + /* Reset numlumps to remove the reload WAD file: */ + + numlumps = reloadlump; + + /* Now reload the WAD file. */ + + filename = reloadname; + + w_close_file(reloadhandle); + free(reloadlumps); + + reloadname = NULL; + reloadlump = -1; + reloadhandle = NULL; + w_add_file(filename); + free(filename); + + /* The WAD directory has changed, so we have to regenerate the + * fast lookup hashtable: + */ + + w_generate_hash_table(); +} + +const char *w_wad_name_for_lump(const lumpinfo_t *lump) +{ + return m_base_name(lump->wad_file->path); +} + +boolean w_is_iwad_lump(const lumpinfo_t *lump) +{ + return lump->wad_file == lumpinfo[0]->wad_file; +} diff --git a/games/NXDoom/src/w_wad.h b/games/NXDoom/src/w_wad.h new file mode 100644 index 000000000..867ac5add --- /dev/null +++ b/games/NXDoom/src/w_wad.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * apps/games/NXDoom/src/w_wad.h + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * WAD I/O functions. + * + ****************************************************************************/ + +#ifndef __W_WAD__ +#define __W_WAD__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "w_file.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* WADFILE I/O related stuff. */ + +typedef struct lumpinfo_s lumpinfo_t; +typedef int lumpindex_t; + +struct lumpinfo_s +{ + char name[8]; + wad_file_t *wad_file; + int position; + int size; + void *cache; + + /* Used for hash table lookups */ + + lumpindex_t next; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern lumpinfo_t **lumpinfo; +extern unsigned int numlumps; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +wad_file_t *w_add_file(const char *filename); +void w_reload(void); + +lumpindex_t w_check_num_for_name(const char *name); +lumpindex_t w_get_num_for_name(const char *name); + +int w_lump_length(lumpindex_t lump); +void w_read_lump(lumpindex_t lump, void *dest); + +void *w_cache_lump_num(lumpindex_t lump, int tag); +void *w_cache_lump_name(const char *name, int tag); + +void w_generate_hash_table(void); + +extern unsigned int w_lump_name_hash(const char *s); + +void w_release_lump_num(lumpindex_t lump); +void w_release_lump_name(const char *name); + +const char *w_wad_name_for_lump(const lumpinfo_t *lump); +boolean w_is_iwad_lump(const lumpinfo_t *lump); + +#endif /* __W_WAD_H__ */ diff --git a/games/NXDoom/src/z_native.c b/games/NXDoom/src/z_native.c new file mode 100644 index 000000000..3d962bbd0 --- /dev/null +++ b/games/NXDoom/src/z_native.c @@ -0,0 +1,556 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_native.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * This is an implementation of the zone memory API which + * uses native calls to malloc() and free(). + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomtype.h" +#include "i_system.h" +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ZONEID 0x1d4a11 + +#ifdef TESTING +#define malloc test_malloc +#define free test_free +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct memblock_s memblock_t; + +struct memblock_s +{ + int id; /* = ZONEID */ + int tag; + int size; + void **user; + memblock_t *prev; + memblock_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Linked list of allocated blocks for each tag type */ + +static memblock_t *allocated_blocks[PU_NUM_TAGS]; + +#ifdef TESTING +static int test_malloced = 0; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: z_insert_block + * + * Description: + * Add a block into the linked list for its type. + * + ****************************************************************************/ + +static void z_insert_block(memblock_t *block) +{ + block->prev = NULL; + block->next = allocated_blocks[block->tag]; + allocated_blocks[block->tag] = block; + + if (block->next != NULL) + { + block->next->prev = block; + } +} + +/**************************************************************************** + * Name: z_insert_block + * + * Description: + * Remove a block from its linked list. + * + ****************************************************************************/ + +static void z_remove_block(memblock_t *block) +{ + /* Unlink from list */ + + if (block->prev == NULL) + { + /* Start of list */ + + allocated_blocks[block->tag] = block->next; + } + else + { + if (block->prev->next != block) + { + i_error("Z_RemoveBlock: Doubly-linked list corrupted!"); + } + + block->prev->next = block->next; + } + + if (block->next != NULL) + { + if (block->next->prev != block) + { + i_error("Z_RemoveBlock: Doubly-linked list corrupted!"); + } + + block->next->prev = block->prev; + } +} + +/**************************************************************************** + * Name: clear_cache + * + * Description: + * Empty data from the cache list to allocate enough data of the size + * required. + * + * Return: + * Returns true if any blocks were freed. + * + ****************************************************************************/ + +static boolean clear_cache(int size) +{ + memblock_t *block; + memblock_t *next_block; + int remaining; + + block = allocated_blocks[PU_CACHE]; + + if (block == NULL) + { + /* Cache is already empty. */ + + return false; + } + + /* Search to the end of the PU_CACHE list. The blocks at the end + * of the list are the ones that have been free for longer and + * are more likely to be unneeded now. + */ + + while (block->next != NULL) + { + block = block->next; + } + + /* Search backwards through the list freeing blocks until we have + * freed the amount of memory required. + */ + + remaining = size; + + while (remaining > 0) + { + if (block == NULL) + { + break; /* No blocks left to free; we've done our best. */ + } + + next_block = block->prev; + + z_remove_block(block); + + remaining -= block->size; + + if (block->user) + { + *block->user = NULL; + } + + free(block); + + block = next_block; + } + + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef TESTING +void *test_malloc(size_t size) +{ + int *result; + + if (test_malloced + size > 2 * 1024 * 1024) + { + return NULL; + } + + test_malloced += size; + + result = malloc(size + sizeof(int)); + + *result = size; + + return result + 1; +} + +void test_free(void *data) +{ + int *i; + + i = ((int *)data) - 1; + + test_malloced -= *i; + + free(i); +} +#endif /* #ifdef TESTING */ + +/**************************************************************************** + * Name: z_init + ****************************************************************************/ + +void z_init(void) +{ + memset(allocated_blocks, 0, sizeof(allocated_blocks)); + printf("zone memory: Using native C allocator.\n"); +} + +/**************************************************************************** + * Name: z_free + ****************************************************************************/ + +void z_free(void *ptr) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_free: freed a pointer without ZONEID"); + } + + if (block->tag != PU_FREE && block->user != NULL) + { + /* clear the user's mark */ + + *block->user = NULL; + } + + z_remove_block(block); + + /* Free back to system */ + + free(block); +} + +/**************************************************************************** + * Name: z_malloc + * + * Description: + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * Return: + * The translated key code. + * + ****************************************************************************/ + +void *z_malloc(int size, int tag, void *user) +{ + memblock_t *newblock; + unsigned char *data; + void *result; + + if (tag < 0 || tag >= PU_NUM_TAGS || tag == PU_FREE) + { + i_error("z_malloc: attempted to allocate a block with an invalid " + "tag: %i", + tag); + } + + if (user == NULL && tag >= PU_PURGELEVEL) + { + i_error("z_malloc: an owner is required for purgeable blocks"); + } + + /* Malloc a block of the required size */ + + newblock = NULL; + + while (newblock == NULL) + { + newblock = (memblock_t *)malloc(sizeof(memblock_t) + size); + + if (newblock == NULL) + { + if (!clear_cache(sizeof(memblock_t) + size)) + { + i_error("z_malloc: failed on allocation of %i bytes", size); + } + } + } + + newblock->tag = tag; + + /* Hook into the linked list for this tag type */ + + newblock->id = ZONEID; + newblock->user = user; + newblock->size = size; + + z_insert_block(newblock); + + data = (unsigned char *)newblock; + result = data + sizeof(memblock_t); + + if (user != NULL) + { + *newblock->user = result; + } + + return result; +} + +/**************************************************************************** + * Name: z_free_tags + ****************************************************************************/ + +void z_free_tags(int lowtag, int hightag) +{ + int i; + + for (i = lowtag; i <= hightag; ++i) + { + memblock_t *block; + memblock_t *next; + + /* Free all in this chain */ + + for (block = allocated_blocks[i]; block != NULL; ) + { + next = block->next; + + /* Free this block */ + + if (block->user != NULL) + { + *block->user = NULL; + } + + free(block); + + /* Jump to the next in the chain */ + + block = next; + } + + /* This chain is empty now */ + + allocated_blocks[i] = NULL; + } +} + +/**************************************************************************** + * Name: z_dump_heap + ****************************************************************************/ + +void z_dump_heap(int lowtag, int hightag) +{ + /* WARN: broken */ + +#if 0 + memblock_t *block; + + printf("zone size: %i location: %p\n", mainzone->size, mainzone); + + printf("tag range: %i to %i\n", lowtag, hightag); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + printf("block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + /* all blocks have been hit */ + + break; + } + + if ((byte *)block + block->size != (byte *)block->next) + printf("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + printf("ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + printf("ERROR: two consecutive free blocks\n"); + } +#endif +} + +/**************************************************************************** + * Name: z_file_dump_heap + ****************************************************************************/ + +void z_file_dump_heap(FILE *f) +{ + /* WARN: broken */ +#if 0 + memblock_t *block; + + fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + fprintf(f, "block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + /* all blocks have been hit */ + + break; + } + + if ((byte *)block + block->size != (byte *)block->next) + fprintf(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + fprintf(f, "ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + fprintf(f, "ERROR: two consecutive free blocks\n"); + } +#endif +} + +/**************************************************************************** + * Name: z_check_heap + ****************************************************************************/ + +void z_check_heap(void) +{ + memblock_t *block; + memblock_t *prev; + int i; + + /* Check all chains */ + + for (i = 0; i < PU_NUM_TAGS; ++i) + { + prev = NULL; + + for (block = allocated_blocks[i]; block != NULL; block = block->next) + { + if (block->id != ZONEID) + { + i_error("z_check_heap: Block without a ZONEID!"); + } + + if (block->prev != prev) + { + i_error("z_check_heap: Doubly-linked list corrupted!"); + } + + prev = block; + } + } +} + +/**************************************************************************** + * Name: z_change_tag2 + ****************************************************************************/ + +void z_change_tag2(void *ptr, int tag, const char *file, int line) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + i_error("%s:%i: z_change_tag: block without a ZONEID!", file, line); + + if (tag >= PU_PURGELEVEL && block->user == NULL) + i_error("%s:%i: z_change_tag: an owner is required " + "for purgeable blocks", + file, line); + + /* Remove the block from its current list, and rehook it into + * its new list. + */ + + z_remove_block(block); + block->tag = tag; + z_insert_block(block); +} + +void z_change_user(void *ptr, void **user) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_change_user: Tried to change user for invalid block!"); + } + + block->user = user; + *user = ptr; +} + +/**************************************************************************** + * Name: z_free_memory + ****************************************************************************/ + +int z_free_memory(void) +{ + return -1; /* Limited by the system?? */ +} + +/**************************************************************************** + * Name: z_zone_size + ****************************************************************************/ + +unsigned int z_zone_size(void) +{ + return 0; +} diff --git a/games/NXDoom/src/z_zone.c b/games/NXDoom/src/z_zone.c new file mode 100644 index 000000000..f5274e334 --- /dev/null +++ b/games/NXDoom/src/z_zone.c @@ -0,0 +1,557 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_zone.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "doomtype.h" +#include "i_system.h" +#include "m_argv.h" + +#include "z_zone.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ZONE MEMORY ALLOCATION + * + * There is never any space between memblocks, + * and there will never be two contiguous free memblocks. + * The rover can be left pointing at a non-empty block. + * + * It is of no value to free a cacheable block, + * because it will get overwritten automatically if needed. + */ + +#define MEM_ALIGN sizeof(void *) +#define ZONEID 0x1d4a11 + +#define MINFRAGMENT 64 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct memblock_s +{ + int size; /* including the header and possibly tiny fragments */ + void **user; + int tag; /* PU_FREE if this is free */ + int id; /* should be ZONEID */ + struct memblock_s *next; + struct memblock_s *prev; +} memblock_t; + +typedef struct +{ + /* total bytes malloced, including header */ + + int size; + + /* start / end cap for linked list */ + + memblock_t blocklist; + + memblock_t *rover; +} memzone_t; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static memzone_t *mainzone; +static boolean zero_on_free; +static boolean scan_on_free; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: scan_for_block + * + * Description: + * Scan the zone heap for pointers within the specified range, and warn + * about any remaining pointers. + * + ****************************************************************************/ + +static void scan_for_block(void *start, void *end) +{ + memblock_t *block; + void **mem; + int i; + int len; + int tag; + + block = mainzone->blocklist.next; + + while (block->next != &mainzone->blocklist) + { + tag = block->tag; + + if (tag == PU_STATIC || tag == PU_LEVEL || tag == PU_LEVSPEC) + { + /* Scan for pointers on the assumption that pointers are aligned + * on word boundaries (word size depending on pointer size): + */ + + mem = (void **)((byte *)block + sizeof(memblock_t)); + len = (block->size - sizeof(memblock_t)) / sizeof(void *); + + for (i = 0; i < len; ++i) + { + if (start <= mem[i] && mem[i] <= end) + { + fprintf(stderr, + "%p has dangling pointer into freed block " + "%p (%p -> %p)\n", + mem, start, &mem[i], mem[i]); + } + } + } + + block = block->next; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: z_init + ****************************************************************************/ + +void z_init(void) +{ + memblock_t *block; + int size; + + mainzone = (memzone_t *)i_zone_base(&size); + mainzone->size = size; + + /* set the entire zone to one free block */ + + mainzone->blocklist.next = mainzone->blocklist.prev = block = + (memblock_t *)((byte *)mainzone + sizeof(memzone_t)); + + mainzone->blocklist.user = (void *)mainzone; + mainzone->blocklist.tag = PU_STATIC; + mainzone->rover = block; + + block->prev = block->next = &mainzone->blocklist; + + block->tag = PU_FREE; /* free block */ + + block->size = mainzone->size - sizeof(memzone_t); + + /* [Deliberately undocumented] + * Zone memory debugging flag. If set, memory is zeroed after it is freed + * to deliberately break any code that attempts to use it after free. + */ + + zero_on_free = m_parm_exists("-zonezero"); + + /* [Deliberately undocumented] + * Zone memory debugging flag. If set, each time memory is freed, the zone + * heap is scanned to look for remaining pointers to the freed block. + */ + + scan_on_free = m_parm_exists("-zonescan"); +} + +/**************************************************************************** + * Name: z_free + ****************************************************************************/ + +void z_free(void *ptr) +{ + memblock_t *block; + memblock_t *other; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) i_error("z_free: freed a pointer without ZONEID"); + + if (block->tag != PU_FREE && block->user != NULL) + { + *block->user = 0; /* clear the user's mark */ + } + + /* mark as free */ + + block->tag = PU_FREE; + block->user = NULL; + block->id = 0; + + /* If the -zonezero flag is provided, we zero out the block on free + * to break code that depends on reading freed memory. + */ + + if (zero_on_free) + { + memset(ptr, 0, block->size - sizeof(memblock_t)); + } + + if (scan_on_free) + { + scan_for_block(ptr, (byte *)ptr + block->size - sizeof(memblock_t)); + } + + other = block->prev; + + if (other->tag == PU_FREE) + { + /* merge with previous free block */ + + other->size += block->size; + other->next = block->next; + other->next->prev = other; + + if (block == mainzone->rover) mainzone->rover = other; + + block = other; + } + + other = block->next; + if (other->tag == PU_FREE) + { + /* merge with previous free block */ + + block->size += other->size; + block->next = other->next; + block->next->prev = block; + + if (other == mainzone->rover) mainzone->rover = block; + } +} + +/**************************************************************************** + * Name: z_malloc + * + * Description: + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + ****************************************************************************/ + +void *z_malloc(int size, int tag, void *user) +{ + int extra; + memblock_t *start; + memblock_t *rover; + memblock_t *newblock; + memblock_t *base; + void *result; + + size = (size + MEM_ALIGN - 1) & ~(MEM_ALIGN - 1); + + /* scan through the block list, looking for the first free block of + * sufficient size, throwing out any purgeable blocks along the way. + */ + + /* account for size of block header */ + + size += sizeof(memblock_t); + + /* if there is a free block behind the rover, back up over them */ + + base = mainzone->rover; + + if (base->prev->tag == PU_FREE) base = base->prev; + + rover = base; + start = base->prev; + + do + { + if (rover == start) + { + /* scanned all the way around the list */ + + i_error("z_malloc: failed on allocation of %i bytes", size); + } + + if (rover->tag != PU_FREE) + { + if (rover->tag < PU_PURGELEVEL) + { + /* hit a block that can't be purged, so move base past it */ + + base = rover = rover->next; + } + else + { + /* free the rover block (adding the size to base) the rover can + * be the base block + */ + + base = base->prev; + z_free((byte *)rover + sizeof(memblock_t)); + base = base->next; + rover = base->next; + } + } + else + { + rover = rover->next; + } + } + while (base->tag != PU_FREE || base->size < size); + + /* found a block big enough */ + + extra = base->size - size; + + if (extra > MINFRAGMENT) + { + /* there will be a free fragment after the allocated block */ + + newblock = (memblock_t *)((byte *)base + size); + newblock->size = extra; + + newblock->tag = PU_FREE; + newblock->user = NULL; + newblock->prev = base; + newblock->next = base->next; + newblock->next->prev = newblock; + + base->next = newblock; + base->size = size; + } + + if (user == NULL && tag >= PU_PURGELEVEL) + i_error("z_malloc: an owner is required for purgeable blocks"); + + base->user = user; + base->tag = tag; + + result = (void *)((byte *)base + sizeof(memblock_t)); + + if (base->user) + { + *base->user = result; + } + + /* next allocation will start looking here */ + + mainzone->rover = base->next; + + base->id = ZONEID; + + return result; +} + +/**************************************************************************** + * Name: z_free_tags + ****************************************************************************/ + +void z_free_tags(int lowtag, int hightag) +{ + memblock_t *block; + memblock_t *next; + + for (block = mainzone->blocklist.next; block != &mainzone->blocklist; + block = next) + { + /* get link before freeing */ + + next = block->next; + + /* free block? */ + + if (block->tag == PU_FREE) continue; + + if (block->tag >= lowtag && block->tag <= hightag) + z_free((byte *)block + sizeof(memblock_t)); + } +} + +/**************************************************************************** + * Name: z_dump_heap + * + * Description: + * Note: TFileDumpHeap( stdout ) ? + * + ****************************************************************************/ + +void z_dump_heap(int lowtag, int hightag) +{ + memblock_t *block; + + printf("zone size: %i location: %p\n", mainzone->size, mainzone); + + printf("tag range: %i to %i\n", lowtag, hightag); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->tag >= lowtag && block->tag <= hightag) + printf("block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + printf("ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + printf("ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + printf("ERROR: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_file_dump_heap + ****************************************************************************/ + +void z_file_dump_heap(FILE *f) +{ + memblock_t *block; + + fprintf(f, "zone size: %i location: %p\n", mainzone->size, mainzone); + + for (block = mainzone->blocklist.next; ; block = block->next) + { + fprintf(f, "block:%p size:%7i user:%p tag:%3i\n", block, + block->size, block->user, block->tag); + + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + fprintf(f, "ERROR: block size does not touch the next block\n"); + + if (block->next->prev != block) + fprintf(f, "ERROR: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + fprintf(f, "ERROR: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_check_heap + ****************************************************************************/ + +void z_check_heap(void) +{ + memblock_t *block; + + for (block = mainzone->blocklist.next; ; block = block->next) + { + if (block->next == &mainzone->blocklist) + { + break; /* all blocks have been hit */ + } + + if ((byte *)block + block->size != (byte *)block->next) + i_error("z_check_heap: block size does not touch the next block\n"); + + if (block->next->prev != block) + i_error("z_check_heap: next block doesn't have proper back link\n"); + + if (block->tag == PU_FREE && block->next->tag == PU_FREE) + i_error("z_check_heap: two consecutive free blocks\n"); + } +} + +/**************************************************************************** + * Name: z_change_tag2 + ****************************************************************************/ + +void z_change_tag2(void *ptr, int tag, const char *file, int line) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + i_error("%s:%i: z_change_tag: block without a ZONEID!", file, line); + + if (tag >= PU_PURGELEVEL && block->user == NULL) + i_error("%s:%i: z_change_tag: an owner is required " + "for purgeable blocks", + file, line); + + block->tag = tag; +} + +/**************************************************************************** + * Name: z_change_user + ****************************************************************************/ + +void z_change_user(void *ptr, void **user) +{ + memblock_t *block; + + block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); + + if (block->id != ZONEID) + { + i_error("z_change_user: Tried to change user for invalid block!"); + } + + block->user = user; + *user = ptr; +} + +/**************************************************************************** + * Name: z_free_memory + ****************************************************************************/ + +int z_free_memory(void) +{ + memblock_t *block; + int free; + + free = 0; + + for (block = mainzone->blocklist.next; block != &mainzone->blocklist; + block = block->next) + { + if (block->tag == PU_FREE || block->tag >= PU_PURGELEVEL) + free += block->size; + } + + return free; +} + +/**************************************************************************** + * Name: z_zone_size + ****************************************************************************/ + +unsigned int z_zone_size(void) +{ + return mainzone->size; +} diff --git a/games/NXDoom/src/z_zone.h b/games/NXDoom/src/z_zone.h new file mode 100644 index 000000000..4df6bb3fb --- /dev/null +++ b/games/NXDoom/src/z_zone.h @@ -0,0 +1,89 @@ +/**************************************************************************** + * apps/games/NXDoom/src/z_zone.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Zone Memory Allocation, perhaps NeXT ObjectiveC inspired. + * + * Remark: this was the only stuff that, according to John Carmack, might + * have been useful for Quake. + * + ****************************************************************************/ + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* This is used to get the local FILE:LINE info from CPP prior to really call + * the function in question. + */ + +#define z_change_tag(p, t) z_change_tag2((p), (t), __FILE__, __LINE__) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* ZONE MEMORY + * PU - purge tags. + */ + +enum +{ + PU_STATIC = 1, /* static entire execution time */ + PU_SOUND, /* static while playing */ + PU_MUSIC, /* static while playing */ + PU_FREE, /* a free block */ + PU_LEVEL, /* static until level exited */ + PU_LEVSPEC, /* a special thinker in a level */ + + /* Tags >= PU_PURGELEVEL are purgeable whenever needed. */ + + PU_PURGELEVEL, + PU_CACHE, + + /* Total number of different tag types */ + + PU_NUM_TAGS +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void z_init(void); +void *z_malloc(int size, int tag, void *ptr); +void z_free(void *ptr); +void z_free_tags(int lowtag, int hightag); +void z_dump_heap(int lowtag, int hightag); +void z_file_dump_heap(FILE *f); +void z_check_heap(void); +void z_change_tag2(void *ptr, int tag, const char *file, int line); +void z_change_user(void *ptr, void **user); +int z_free_memory(void); +unsigned int z_zone_size(void); + +#endif /* __Z_ZONE__ */ diff --git a/games/NXDoom/textscreen/.gitignore b/games/NXDoom/textscreen/.gitignore new file mode 100644 index 000000000..3be8ec0c7 --- /dev/null +++ b/games/NXDoom/textscreen/.gitignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.deps +*.a +tags +TAGS diff --git a/games/NXDoom/textscreen/fonts/README b/games/NXDoom/textscreen/fonts/README new file mode 100644 index 000000000..a9b9b1b24 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/README @@ -0,0 +1,38 @@ + +This directory contains the fonts used by libtextscreen: + + * normal.png is the standard DOS font, from the DOSbox project. + Copyright (C) 2002-2004 The DOSBox Team + + * small.png contains a miniature, half-size font for low-resolution + displays. This is based on the Atari-Small font by Tom Fine. The + original font was standard ASCII only; this has been extended to the + full Extended ASCII range with scaled-down versions of the full-size + DOS font. Original copyright notice: + + Copyright (c) 1999, Thomas A. Fine + + License to copy, modify, and distribute for both commercial and + non-commercial use is herby granted, provided this notice + is preserved. + + Email to my last name at head.cfa.harvard.edu + http://hea-www.harvard.edu/~fine/ + + * large.png is a scaled-up version of normal.png with extra detail + added; this is for use on modern high-resolution ("retina") displays. + +All modifications and enhancements to the above fonts are: + + Copyright (C) 2005-2016 Simon Howard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + diff --git a/games/NXDoom/textscreen/fonts/codepage.h b/games/NXDoom/textscreen/fonts/codepage.h new file mode 100644 index 000000000..f506c90e4 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/codepage.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/codepage.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2017 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains a #define macro mapping from the characters + * in the CP437 code page to the equivalent Unicode characters. It + * therefore defines which Unicode characters can be represented on + * a text screen using such a code page. + * + * If you're changing the textscreen font to show a different code + * page, you should update the mapping in this file to match. The + * Wikipedia pages for DOS code pages are a good place to look for + * this information. + * + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CODE_PAGE_TO_UNICODE \ + { \ + /* CP437 control codes: */ \ + \ + 0x0000, 0x263a, 0x263b, 0x2665, /* 00 - 0f */ \ + 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25cb, 0x25d9, \ + 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, \ + \ + 0x25ba, 0x25c4, 0x2195, 0x203c, /* 10 - 1f */ \ + 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, \ + 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, \ + \ + /* Standard ASCII range: */ \ + \ + 0x0020, 0x0021, 0x0022, 0x0023, /* 20 - 2f */ \ + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, \ + 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, \ + \ + 0x0030, 0x0031, 0x0032, 0x0033, /* 30 - 3f */ \ + 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, \ + 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, \ + \ + 0x0040, 0x0041, 0x0042, 0x0043, /* 40 - 4f */ \ + 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, \ + 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, \ + \ + 0x0050, 0x0051, 0x0052, 0x0053, /* 50 - 5f */ \ + 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, \ + 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, \ + \ + 0x0060, 0x0061, 0x0062, 0x0063, /* 60 - 6f */ \ + 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, \ + 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, \ + \ + 0x0070, 0x0071, 0x0072, 0x0073, /* 70 - 7f */ \ + 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, \ + 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, \ + \ + /* CP437 Extended ASCII range: */ \ + \ + 0x00c7, 0x00fc, 0x00e9, 0x00e2, /* 80-8f */ \ + 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, \ + 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, \ + \ + 0x00c9, 0x00e6, 0x00c6, 0x00f4, /* 90-9f */ \ + 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, \ + 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, \ + \ + 0x00e1, 0x00ed, 0x00f3, 0x00fa, /* a0-af */ \ + 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, \ + 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, \ + \ + 0x2591, 0x2592, 0x2593, 0x2502, /* b0-bf */ \ + 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, \ + 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, \ + \ + 0x2514, 0x2534, 0x252c, 0x251c, /* c0-cf */ \ + 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, \ + 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, \ + \ + 0x2568, 0x2564, 0x2565, 0x2559, /* d0-df */ \ + 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, \ + 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, \ + \ + 0x03b1, 0x00df, 0x0393, 0x03c0, /* e0-ef */ \ + 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, \ + 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, \ + \ + 0x2261, 0x00b1, 0x2265, 0x2264, /* f0-ff */ \ + 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, \ + 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0, \ + } diff --git a/games/NXDoom/textscreen/fonts/convert-font b/games/NXDoom/textscreen/fonts/convert-font new file mode 100644 index 000000000..b3d828cc5 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/convert-font @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright(C) 2016 Simon Howard +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# +# Converts images into libtextscreen fonts. +# + +import sys +import os +import re + +GRID_COLUMNS = 16 +GRID_ROWS = 16 + +try: + import Image +except ImportError: + try: + from PIL import Image + except ImportError: + print("WARNING: Could not update %s. " + "Please install the Python Imaging library or Pillow." + % sys.argv[3]) + sys.exit(0) + + +def generate_font_data(filename): + """Read font data from the given file. + + Args: + filename: Image file to read. + Returns: + Tuple containing: + (width, height) dimensions of each font character. + Array of byte data for font. + """ + im = Image.open(filename) + width, height = im.size + assert (width % GRID_COLUMNS) == 0 + assert (height % GRID_ROWS) == 0 + + char_w, char_h = width // GRID_COLUMNS, height // GRID_ROWS + font_data_len = (width * height) // 8 + font_data = [0] * font_data_len + + for y in range(height): + for x in range(width): + px = im.getpixel((x, y)) + if px > (127, 127, 127): + char_x, char_y = x // char_w, y // char_h + x1, y1 = x % char_w, y % char_h + bit_index = ( + (char_y * GRID_COLUMNS + char_x) * (char_w * char_h) + + (y1 * char_w) + x1) + font_data[bit_index // 8] |= 1 << (bit_index % 8) + + return (char_w, char_h), font_data + +def convert_image(font_prefix, filename, output_filename): + """Convert the given image to a text output file. + + Args: + font_prefix: Prefix string to attach to constants. + filename: Input image file to read. + output_filename: Header file to write. + """ + dimensions, data = generate_font_data(filename) + + with open(output_filename, "w") as outfile: + outfile.write("/* Font data generated from %s; do not edit.\n" + " Please see textscreen/fonts/README for copyright\n" + " information. */\n\n" % filename) + outfile.write("static const uint8_t %s_font_data[] =\n{\n" % + font_prefix) + for index, b in enumerate(data): + if (index % 8) == 0: + outfile.write(" ") + outfile.write("0x%02x," % b) + if ((index + 1) % 8) == 0: + outfile.write("\n") + else: + outfile.write(" ") + outfile.write("};\n") + + outfile.write("\n") + outfile.write("static const txt_font_t %s_font =\n{\n" % font_prefix) + outfile.write(" \"%s\", %s_font_data, %d, %d,\n" % ( + font_prefix, font_prefix, + dimensions[0], dimensions[1])) + outfile.write("};\n") + +convert_image(sys.argv[1], sys.argv[2], sys.argv[3]) + diff --git a/games/NXDoom/textscreen/fonts/large.h b/games/NXDoom/textscreen/fonts/large.h new file mode 100644 index 000000000..a58692d91 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/large.h @@ -0,0 +1,1391 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/large.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from large.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t large_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfe, 0x7f, 0x07, 0xe0, 0x03, 0xc0, 0x33, 0xcc, 0x33, 0xcc, + 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xf3, 0xcf, 0xf3, 0xcf, + 0xe3, 0xc7, 0xc3, 0xc3, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, + 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xf3, 0xcf, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0e, 0x3e, 0x1f, 0xff, 0x3f, 0xff, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xf8, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, + 0xf0, 0x03, 0xf8, 0x07, 0xfc, 0x0f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x3f, + 0xfe, 0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0x3e, 0x7c, 0x3f, 0xfc, + 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3e, 0x7c, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfe, 0x7f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xdc, 0x3b, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x1c, 0x38, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x1c, 0x38, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x07, 0xe0, 0xc3, 0xc3, 0xe3, 0xc7, 0xf3, 0xcf, 0xf3, 0xcf, + 0xf3, 0xcf, 0xf3, 0xcf, 0xe3, 0xc7, 0xc3, 0xc3, 0x07, 0xe0, 0x0f, 0xf0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x80, 0x37, 0xc0, 0x33, 0xe0, 0x31, 0xf0, 0x30, 0xfc, 0x03, 0xfe, 0x07, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xfe, 0x07, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x7f, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfc, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, + 0xfc, 0xff, 0xfc, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, + 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf8, 0x3c, 0xfc, + 0x3e, 0xfc, 0x3f, 0xfc, 0x3f, 0x7c, 0x3f, 0x38, 0x1f, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc3, 0xc3, 0xcf, 0xf3, + 0xfc, 0x3f, 0xf0, 0x0f, 0x3f, 0xfc, 0x3f, 0xfc, 0xf0, 0x0f, 0xfc, 0x3f, + 0xcf, 0xf3, 0xc3, 0xc3, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, + 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0xff, 0x00, + 0xff, 0x01, 0xff, 0x03, 0xff, 0x07, 0xff, 0x07, 0xff, 0x03, 0xff, 0x01, + 0xff, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x3e, 0x00, 0x3f, + 0x80, 0x3f, 0xc0, 0x3f, 0xe0, 0x3f, 0xf0, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, + 0xf0, 0x3f, 0xe0, 0x3f, 0xc0, 0x3f, 0x80, 0x3f, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x30, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, + 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x3c, + 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xfe, 0xff, 0xc7, 0xf3, 0xc7, 0xf3, + 0xc7, 0xf3, 0xc7, 0xf3, 0xc7, 0xf3, 0xc7, 0xf3, 0xfe, 0xf3, 0xfc, 0xf3, + 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, + 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x38, + 0x3e, 0x00, 0xfc, 0x00, 0xf8, 0x03, 0xf0, 0x07, 0x38, 0x0f, 0x1c, 0x1e, + 0x0e, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x1c, 0x1e, 0x0e, 0x3c, 0x07, + 0xf8, 0x03, 0xf0, 0x03, 0xc0, 0x0f, 0x00, 0x1f, 0x07, 0x3e, 0x0f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0x80, 0x01, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, + 0xff, 0x1f, 0xff, 0x1f, 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x1c, 0x00, 0xfe, 0x3f, 0xfe, 0x3f, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x04, 0x30, 0x0c, 0x38, 0x1c, 0x3c, 0x3c, + 0xfe, 0x7f, 0xfe, 0x7f, 0x3c, 0x3c, 0x38, 0x1c, 0x30, 0x0c, 0x20, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xe0, 0x01, + 0xf0, 0x03, 0xf0, 0x03, 0xf8, 0x07, 0xf8, 0x07, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0xfe, 0x1f, 0xfe, 0x1f, 0xfc, 0x0f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, + 0xf0, 0x03, 0xf0, 0x03, 0xe0, 0x01, 0xe0, 0x01, 0xc0, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xe0, 0x07, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x1c, 0x30, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x07, 0x38, 0x07, + 0x38, 0x07, 0x38, 0x07, 0xff, 0x3f, 0xff, 0x3f, 0x38, 0x07, 0x38, 0x07, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0xff, 0x3f, 0xff, 0x3f, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x38, 0x0f, 0x30, 0x0f, 0x30, 0x0f, 0x00, 0x0f, 0x00, + 0xfe, 0x0f, 0xfc, 0x1f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x03, 0x3c, 0x03, 0x3c, 0x07, 0x3c, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x0f, 0x38, + 0x0f, 0x3c, 0x06, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x18, 0x07, 0x3c, + 0x03, 0x3c, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x07, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x38, 0x07, 0xf0, 0x03, 0xf0, 0x03, 0xfc, 0x30, 0xfe, 0x38, + 0xcf, 0x1f, 0xcf, 0x1f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3f, 0xfc, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x80, 0x0f, 0xc0, 0x03, 0xe0, 0x01, 0xe0, 0x00, 0xe0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x01, 0xc0, 0x03, + 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x01, 0xc0, 0x03, 0x80, 0x07, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, + 0x80, 0x07, 0xc0, 0x03, 0xf0, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x38, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xf0, 0x0f, + 0x78, 0x1e, 0x3c, 0x3c, 0x1c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xfc, 0x3f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x30, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, + 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, + 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x1f, + 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x8f, 0xf1, 0xcf, 0xf3, 0xcf, 0xf3, 0x8f, 0xf1, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0xf8, 0x1f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x03, 0xc0, 0x03, 0xe0, 0x03, 0xf0, 0x03, 0xf8, 0x03, 0xfc, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xfc, 0x0f, 0x0e, 0x1c, 0x07, 0x3c, + 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0f, 0x30, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x1f, 0x3e, 0x0f, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x0f, 0xc0, 0x0f, 0xe0, 0x0f, 0xf0, 0x0f, + 0x78, 0x0f, 0x3c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x0f, 0xff, 0x1f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x03, + 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0xff, 0x0f, 0xff, 0x1f, 0x1f, 0x3e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, + 0xe0, 0x01, 0xe0, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x1f, 0x3e, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xfe, 0x3f, 0xfc, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0xfc, 0x07, 0xfc, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x00, + 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, + 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3e, 0x00, 0x1f, 0x80, 0x0f, + 0xc0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x8f, 0x3f, 0xcf, 0x3f, 0xcf, 0x3f, 0xcf, 0x3f, + 0xcf, 0x3f, 0xcf, 0x3f, 0xcf, 0x1f, 0x8f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, + 0xfe, 0x0f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0xf8, 0x07, + 0x3c, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0x0f, + 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, + 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0xff, 0x0f, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x38, 0x1e, 0x30, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x1e, 0x30, 0x1e, 0x38, 0x3c, 0x3c, 0x78, 0x1e, + 0xf0, 0x0f, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 0xff, 0x03, 0xbc, 0x07, 0x3c, 0x0f, + 0x3c, 0x1e, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0x3c, 0x1e, + 0x3c, 0x0f, 0xbc, 0x07, 0xff, 0x03, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x20, 0x3c, 0x03, 0x3c, 0x03, + 0xfc, 0x03, 0xfc, 0x03, 0x3c, 0x03, 0x3c, 0x03, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x20, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x30, 0x3c, 0x20, + 0x3c, 0x03, 0x3c, 0x03, 0xfc, 0x03, 0xfc, 0x03, 0x3c, 0x03, 0x3c, 0x03, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x78, 0x1c, 0x3c, 0x38, + 0x1e, 0x30, 0x0e, 0x30, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0xcf, 0x3f, 0xcf, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x3c, 0x1e, 0x3c, + 0x3c, 0x3c, 0x78, 0x3e, 0xf0, 0x37, 0xe0, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0xfe, 0x07, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3c, 0x3f, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, 0x3c, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x03, 0xfc, 0x07, 0x3c, 0x0f, 0x3c, 0x1e, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3f, 0x3c, 0x3f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x20, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf3, 0x8f, 0xf1, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x0f, 0x3c, + 0x1f, 0x3c, 0x3f, 0x3c, 0x7f, 0x3c, 0xff, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0xcf, 0x3f, 0x8f, 0x3f, 0x0f, 0x3f, 0x0f, 0x3e, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0xff, 0x0f, 0x3c, 0x1e, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, 0xfc, 0x0f, 0xfc, 0x07, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xcf, 0x3c, 0xcf, 0x3c, 0xcf, 0x3f, 0xcf, 0x3f, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x07, 0xff, 0x0f, 0x3c, 0x1c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, + 0x3c, 0x3c, 0x3c, 0x1c, 0xfc, 0x0f, 0xfc, 0x07, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x38, + 0x3f, 0x38, 0x3f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x00, 0x3c, 0x00, 0xf8, 0x00, 0xf0, 0x03, + 0xc0, 0x0f, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1f, 0x3e, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xcf, 0xf3, 0xc7, 0xe3, 0xc3, 0xc3, 0xc1, 0x83, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x8f, 0xf1, 0x8f, 0xf1, 0xcf, 0xf3, 0xcf, 0xf3, + 0xef, 0xf7, 0xef, 0xf7, 0x7e, 0x7e, 0x7e, 0x7e, 0x3c, 0x3c, 0x3c, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, + 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x0f, 0xf0, 0x07, 0x78, 0x03, 0x3c, 0x01, 0x1e, 0x00, 0x0f, 0x80, 0x07, + 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, + 0x0f, 0x80, 0x0f, 0xc0, 0x0f, 0xe0, 0x0f, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0xfc, 0x00, 0xfc, 0x00, + 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x03, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x1c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x1c, 0x3c, 0x1c, 0xfc, 0x0f, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1c, 0x0f, 0x3c, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1e, 0x1c, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0xf0, 0x0f, 0xf8, 0x0f, 0x3c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x1f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x3c, 0x0e, 0x3c, 0x0c, 0x3c, 0x0c, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x38, 0xfc, 0x3d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0f, + 0xfc, 0x0f, 0xf8, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x07, 0x0f, 0x0e, 0x0f, + 0xfc, 0x03, 0xf8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x0f, 0xbc, 0x1f, 0xfc, 0x3c, 0x7c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3f, 0x3c, 0x3f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, + 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x38, 0x3c, 0x1c, 0x3c, 0x0e, 0x3c, 0x07, + 0xbc, 0x03, 0xfc, 0x01, 0xfc, 0x01, 0xfc, 0x03, 0x3c, 0x07, 0x3c, 0x0e, + 0x3c, 0x1c, 0x3c, 0x38, 0x3f, 0x38, 0x3f, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x3c, 0x7f, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc7, 0x07, 0xef, 0x0f, 0x3c, 0x1c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc7, 0x07, 0xef, 0x0f, 0x7c, 0x1e, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1e, + 0xfc, 0x0f, 0xfc, 0x07, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x38, 0xfc, 0x3d, 0x1e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x1e, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0xc0, 0x3f, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x07, 0xcf, 0x0f, + 0xfc, 0x1c, 0x7c, 0x38, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0x3e, 0x00, 0xfc, 0x00, + 0xf0, 0x03, 0xc0, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xff, 0x0f, 0xff, 0x0f, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x38, 0xe0, 0x3c, 0xc0, 0x0f, 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, + 0x0f, 0xf0, 0x0f, 0xf0, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, + 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, + 0x3c, 0x3c, 0x78, 0x1e, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0x78, 0x1e, 0x3c, 0x3c, 0x1e, 0x78, 0x0f, 0xf0, 0x07, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x3c, + 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, + 0xff, 0x03, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x1f, 0x07, 0x0e, 0x03, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x30, 0x07, 0x38, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3f, + 0x80, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xfc, 0x00, 0xfc, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x00, 0x3f, 0x00, 0x3f, + 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x03, + 0xc0, 0x03, 0xc0, 0x01, 0xfc, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, + 0xce, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x07, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x1c, 0x38, + 0x0e, 0x30, 0x0f, 0x30, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x30, 0x0e, 0x30, 0x1c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x0f, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x1c, + 0xfc, 0x0f, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, + 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, + 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x1c, 0x0e, 0x1c, 0x0e, 0xf0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, + 0xf8, 0x0f, 0xfc, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x9e, 0x0f, 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x3c, 0x38, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x0f, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x1c, + 0xf0, 0x0f, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, + 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, + 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x38, 0x1f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0x70, 0x0e, 0x38, 0x1c, 0x1c, 0x38, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, + 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x1c, 0x0e, 0x1c, 0x0e, 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0xc0, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x38, 0x3c, 0x30, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x30, 0x3c, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x1e, 0x38, 0x3f, 0xf0, 0x73, 0xe0, 0xf3, 0xc0, 0xf3, 0xc0, 0x73, + 0xf8, 0x3f, 0xfc, 0x1f, 0xce, 0x03, 0xc7, 0x03, 0xc7, 0x07, 0xce, 0x1e, + 0xfc, 0xfc, 0x78, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0xf0, 0x3f, 0x38, 0x0f, 0x1c, 0x0f, + 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0x3f, 0xff, 0x3f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x3f, 0x0f, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, + 0x1c, 0x0e, 0x0e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, + 0x0e, 0x07, 0x07, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, 0xfe, 0x3d, 0xfc, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, + 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0e, 0x3c, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0x3c, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x07, 0xfc, 0x0f, 0x0e, 0x1c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x1c, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0e, 0x1c, + 0xfc, 0x0f, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf8, 0x1f, 0xfc, 0x3f, + 0x0e, 0x70, 0x07, 0xe0, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x07, 0xe0, 0x0e, 0x70, 0xfc, 0x3f, 0xf8, 0x1f, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x3c, 0x0e, + 0x3c, 0x0c, 0x3c, 0x0c, 0x3c, 0x00, 0x3c, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3f, 0x38, 0x3f, 0x1c, 0xff, 0x0f, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xe0, 0x0e, 0x70, 0x1c, 0x38, 0x38, 0x1c, 0x70, 0x0e, 0xe0, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x0f, 0xff, 0x1f, 0x3c, 0x3c, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x3c, + 0xfc, 0x1f, 0xfc, 0x0f, 0x3c, 0x30, 0x3c, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0xff, 0xf8, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x3f, 0x80, 0x77, 0xc0, 0xe3, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc7, 0x03, 0xee, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, + 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x01, 0xfc, 0x03, 0x00, 0x07, 0x00, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f, + 0x1e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x9e, 0x0f, + 0xfc, 0x3c, 0x78, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xfc, 0x0f, + 0x1e, 0x1e, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x1e, 0x1e, 0xfc, 0x0f, 0xf8, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, + 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x8f, 0x0f, + 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, + 0x00, 0x00, 0x00, 0x00, 0xc7, 0x07, 0xef, 0x0f, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x3c, 0x0f, 0x3c, 0x1f, 0x3c, 0x3f, 0x3c, 0x7f, 0x3c, 0xff, 0x3c, + 0xff, 0x3d, 0xef, 0x3f, 0xcf, 0x3f, 0x8f, 0x3f, 0x0f, 0x3f, 0x0f, 0x3e, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0xf0, 0x0f, + 0x38, 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x38, 0x0f, 0xf0, 0x3f, 0xe0, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x1c, 0x0e, 0x38, 0x07, + 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x0f, 0x3c, 0xfe, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x20, 0x0f, 0x30, 0x0f, 0x38, 0x0f, 0x1c, 0x0f, 0x0e, 0x0f, 0x07, + 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x3e, 0x07, 0x7f, 0x83, 0xf3, 0xc1, 0xf1, 0x00, 0x38, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x07, 0x80, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x20, 0x0f, 0x30, 0x0f, 0x38, 0x0f, 0x1c, + 0x0f, 0x0e, 0x0f, 0x07, 0x80, 0x03, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, + 0x38, 0x3c, 0x1c, 0x3e, 0x0e, 0x3f, 0x87, 0x3d, 0xc3, 0x3c, 0x61, 0x3c, + 0xf0, 0x3f, 0xf0, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0x78, 0x1e, + 0x3c, 0x0f, 0x9e, 0x07, 0xcf, 0x03, 0xcf, 0x03, 0x9e, 0x07, 0x3c, 0x0f, + 0x78, 0x1e, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcf, 0x03, 0x9e, 0x07, 0x3c, 0x0f, 0x78, 0x1e, 0xf0, 0x3c, 0xf0, 0x3c, + 0x78, 0x1e, 0x3c, 0x0f, 0x9e, 0x07, 0xcf, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, + 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0xc0, 0xc0, 0xc0, 0x0c, 0x0c, 0x0c, 0x0c, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0x33, 0x33, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xcc, + 0x33, 0x33, 0x33, 0x33, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, + 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, + 0xfc, 0xfc, 0xfc, 0xfc, 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, + 0xcf, 0xcf, 0xcf, 0xcf, 0xfc, 0xfc, 0xfc, 0xfc, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0x3c, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xff, 0x3c, 0xff, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x3c, 0x00, 0x3c, 0xff, 0x3c, 0xff, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3c, 0xff, 0x3c, 0x00, 0x3c, 0x00, 0x3c, + 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x03, 0xff, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x00, 0xf0, 0x00, + 0xf0, 0xff, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xfc, 0xf0, 0xfc, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0xfc, 0xf0, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xfc, 0xff, 0xfc, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0xff, 0xf0, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0xff, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xc0, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0xf0, 0xff, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xff, 0xff, 0xff, 0xff, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, 0xf0, 0x3c, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, + 0xc0, 0x03, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xc0, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, + 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x3c, 0xfe, 0x3e, 0xcf, 0x1f, 0x87, 0x0f, + 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, + 0x87, 0x0f, 0xcf, 0x1f, 0xfe, 0x3d, 0xfc, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xfc, 0x03, + 0x0e, 0x07, 0x0e, 0x07, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07, 0x8f, 0x07, + 0xcf, 0x03, 0xcf, 0x03, 0x8f, 0x07, 0x0f, 0x0f, 0x0f, 0x0e, 0x0f, 0x1c, + 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x1c, 0x0f, 0x1e, 0x0f, 0x0f, 0x0f, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x3f, 0xff, 0x3f, 0x0f, 0x38, 0x1e, 0x30, 0x3c, 0x00, 0x78, 0x00, + 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x78, 0x00, 0x3c, 0x00, 0x1e, 0x30, 0x0f, 0x38, 0xff, 0x3f, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x3f, 0xfc, 0x3f, 0xce, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, + 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0x87, 0x03, 0xce, 0x01, + 0xfc, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x1c, 0x3c, 0x1e, 0xfc, 0x0f, 0xfc, 0x07, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0xc7, 0x07, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xe0, 0x07, 0xf0, 0x0f, 0x38, 0x1c, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, + 0x1c, 0x38, 0x38, 0x1c, 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0xc0, 0x03, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0xf0, 0x03, + 0x38, 0x07, 0x1c, 0x0e, 0x0e, 0x1c, 0x0e, 0x1c, 0x07, 0x38, 0x07, 0x38, + 0xff, 0x3f, 0xff, 0x3f, 0x07, 0x38, 0x07, 0x38, 0x0e, 0x1c, 0x0e, 0x1c, + 0x1c, 0x0e, 0x38, 0x07, 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xf8, 0x07, + 0x3c, 0x0f, 0x1e, 0x1e, 0x0f, 0x3c, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, + 0x07, 0x38, 0x07, 0x38, 0x0e, 0x1c, 0x1e, 0x1e, 0x1c, 0x0e, 0x3c, 0x0f, + 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x38, 0x07, 0x3f, 0x3f, 0x3f, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0xc0, 0x3f, 0xe0, 0x00, 0xe0, 0x00, 0xc0, 0x01, 0x80, 0x03, + 0x00, 0x07, 0x00, 0x0f, 0xe0, 0x1f, 0xf0, 0x3f, 0x38, 0x3c, 0x1c, 0x38, + 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x38, 0x1c, + 0xf0, 0x0f, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xfc, 0x3f, 0xce, 0xf3, 0xcf, 0xf3, + 0xcf, 0xf3, 0xcf, 0xf3, 0xcf, 0xf3, 0xce, 0xf3, 0xfc, 0x3f, 0xf8, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0xf8, 0x1f, 0xfc, 0x3f, + 0x0e, 0x77, 0x8f, 0xf3, 0xcf, 0xf3, 0xef, 0xf1, 0xff, 0xf0, 0xff, 0x70, + 0xfc, 0x3f, 0xfc, 0x1f, 0x1c, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0xc0, 0x0f, 0xe0, 0x01, 0xf0, 0x00, 0x78, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0x3c, 0x00, 0x3c, 0x00, + 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xe0, 0x01, + 0xc0, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x1f, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, + 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xfc, 0x3f, 0xfc, 0x3f, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0xf0, 0x00, + 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x00, 0x0f, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, + 0x78, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xe0, 0x01, 0xc0, 0x03, 0x80, 0x07, + 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x80, 0x7f, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, 0xc0, 0xf3, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, 0xcf, 0x03, + 0xcf, 0x03, 0xcf, 0x03, 0xfe, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0xc0, 0x03, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x38, 0xfc, 0x3c, 0xcf, 0x0f, 0x87, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x01, 0xf0, 0x03, 0x38, 0x07, 0x1c, 0x0e, 0x1c, 0x0e, 0x38, 0x07, + 0xf0, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, + 0x07, 0x0f, 0x0f, 0x0f, 0x1e, 0x0f, 0x3c, 0x0f, 0x78, 0x0f, 0xf0, 0x0f, + 0xe0, 0x0f, 0xc0, 0x0f, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcf, 0x03, 0xef, 0x07, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, 0x3c, 0x0f, + 0x3c, 0x0f, 0x3c, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0xce, 0x01, 0xc7, 0x03, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00, + 0x0e, 0x03, 0x07, 0x03, 0xff, 0x03, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, + 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t large_font = +{ + "large", + large_font_data, + 16, + 32, +}; diff --git a/games/NXDoom/textscreen/fonts/large.png b/games/NXDoom/textscreen/fonts/large.png new file mode 100644 index 000000000..3827ab599 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/large.png differ diff --git a/games/NXDoom/textscreen/fonts/normal.h b/games/NXDoom/textscreen/fonts/normal.h new file mode 100644 index 000000000..4ecaf3787 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/normal.h @@ -0,0 +1,367 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/normal.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from normal.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t normal_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, + 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, + 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x7f, + 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, + 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x78, 0x70, + 0x58, 0x4c, 0x1e, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xcc, 0xfc, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0e, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xe7, 0x67, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x7f, 0x1f, + 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x70, + 0x78, 0x7c, 0x7f, 0x7c, 0x78, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xdb, + 0xdb, 0xdb, 0xde, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x06, 0x1c, 0x36, 0x63, 0x63, 0x36, 0x1c, 0x30, 0x63, + 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x7f, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x7f, 0x06, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x3e, 0x3e, 0x7f, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x3e, 0x3e, + 0x1c, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, + 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x3e, 0x63, 0x43, 0x03, 0x3e, 0x60, 0x60, 0x61, 0x63, 0x3e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, 0x18, + 0x0c, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, + 0x36, 0x1c, 0x6e, 0x3b, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x0c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x1e, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x60, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x38, 0x3c, 0x36, 0x33, 0x7f, + 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, + 0x03, 0x03, 0x3f, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x06, 0x03, 0x03, 0x3f, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x60, 0x60, 0x30, 0x18, + 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x63, 0x63, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x60, 0x60, 0x30, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x30, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x7b, 0x7b, + 0x7b, 0x3b, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, + 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, + 0x03, 0x43, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x36, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x46, 0x66, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, + 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0x43, 0x03, 0x03, 0x7b, 0x63, 0x63, 0x66, 0x5c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x67, 0x66, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x66, 0x67, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, + 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, + 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7b, 0x3e, + 0x30, 0x70, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x36, + 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, + 0x63, 0x06, 0x1c, 0x30, 0x60, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, + 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x61, 0x30, 0x18, 0x0c, 0x06, 0x83, 0xc3, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x03, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x1e, 0x36, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x03, 0x03, 0x03, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x30, 0x30, 0x3c, 0x36, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x3e, 0x30, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x36, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x07, 0x06, + 0x06, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0xff, 0xdb, + 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x6e, 0x66, 0x06, 0x06, 0x06, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x06, + 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0c, + 0x0c, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, + 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x33, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, + 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, + 0x63, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0x43, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x30, 0x60, 0x3e, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x00, 0x3e, 0x63, 0x7f, + 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, + 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x1c, + 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x30, 0x60, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x7f, + 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, + 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, + 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x1c, 0x00, + 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x0c, 0x06, 0x00, 0x7f, 0x66, 0x06, 0x3e, 0x06, 0x06, 0x66, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, + 0x7e, 0x1b, 0x3b, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x36, + 0x33, 0x33, 0x7f, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, + 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x1e, 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x00, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, + 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1e, 0x00, + 0x00, 0x63, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0xc3, 0x03, 0x03, 0x03, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x67, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, + 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x66, 0x66, + 0x3e, 0x46, 0x66, 0xf6, 0x66, 0x66, 0x66, 0xcf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x1e, 0x30, 0x3e, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, + 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0c, 0x06, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, + 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x3b, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x36, 0x36, 0x7c, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, + 0x1c, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x06, 0x03, 0x63, 0x63, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, + 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x06, 0x73, 0xd9, 0x60, + 0x30, 0xf8, 0x00, 0x00, 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, + 0x66, 0x73, 0x69, 0x7c, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x36, 0x1b, 0x36, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x36, 0x6c, + 0x36, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x22, 0x88, 0x22, + 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, + 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, + 0xaa, 0x55, 0xaa, 0x55, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, + 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6e, 0x3b, 0x1b, 0x1b, 0x1b, 0x3b, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x33, 0x33, 0x33, 0x1b, 0x33, 0x63, 0x63, 0x63, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x63, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7f, 0x63, 0x06, 0x0c, 0x18, 0x0c, 0x06, 0x63, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, + 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x36, 0x36, 0x36, 0x36, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x66, + 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x60, 0x7e, 0xdb, 0xdb, 0xcf, 0x7e, 0x06, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0c, 0x06, 0x06, 0x3e, 0x06, + 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0xd8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x00, + 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x36, 0x36, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x37, 0x36, 0x36, 0x3c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1b, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x0c, 0x06, 0x13, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t normal_font = +{ + "normal", + normal_font_data, + 8, + 16, +}; diff --git a/games/NXDoom/textscreen/fonts/normal.png b/games/NXDoom/textscreen/fonts/normal.png new file mode 100644 index 000000000..884bb98d4 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/normal.png differ diff --git a/games/NXDoom/textscreen/fonts/small.h b/games/NXDoom/textscreen/fonts/small.h new file mode 100644 index 000000000..b7dd28d22 --- /dev/null +++ b/games/NXDoom/textscreen/fonts/small.h @@ -0,0 +1,111 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/fonts/small.h + * + * SPDX-License-Identifer: GPLv2 + * + * Font data generated from small.png; do not edit. Please see + * textscreen/fonts/README for copyright information. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +static const uint8_t small_font_data[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x4e, 0x00, 0xf6, 0xf5, 0xb1, 0x6f, + 0x00, 0xea, 0x04, 0x00, 0x00, 0xe4, 0x04, 0x00, 0x40, 0xaa, 0xe4, 0x00, + 0x40, 0xee, 0xe4, 0x00, 0x00, 0x60, 0x06, 0x00, 0xff, 0x9f, 0xf9, 0xff, + 0x00, 0x96, 0x69, 0x00, 0xff, 0x69, 0x96, 0xff, 0xe0, 0x74, 0x75, 0x00, + 0x60, 0x69, 0x6f, 0x06, 0xc0, 0x44, 0x74, 0x03, 0xe0, 0xaa, 0xba, 0x03, + 0x22, 0x27, 0x27, 0x22, 0x10, 0x73, 0x13, 0x00, 0x80, 0xec, 0x8c, 0x00, + 0x40, 0x4e, 0xe4, 0x04, 0xa0, 0xaa, 0xa0, 0x00, 0xf0, 0xb9, 0xaa, 0x00, + 0x16, 0x96, 0x86, 0x06, 0x00, 0x00, 0xff, 0x00, 0x40, 0x4e, 0xe4, 0xf4, + 0x40, 0x4e, 0x44, 0x04, 0x40, 0x44, 0xe4, 0x04, 0x20, 0xf4, 0x24, 0x00, + 0x40, 0xf2, 0x42, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0xf5, 0x05, 0x00, + 0x00, 0x72, 0x0f, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x22, 0x02, 0x02, 0x50, 0x05, 0x00, 0x00, 0x50, 0x57, 0x75, 0x05, + 0x22, 0x25, 0x54, 0x22, 0x50, 0x24, 0x12, 0x05, 0x52, 0x52, 0x35, 0x06, + 0x20, 0x02, 0x00, 0x00, 0x40, 0x22, 0x22, 0x04, 0x10, 0x22, 0x22, 0x01, + 0x50, 0x72, 0x52, 0x00, 0x20, 0x72, 0x22, 0x00, 0x00, 0x00, 0x20, 0x12, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x20, 0x02, 0x40, 0x24, 0x12, 0x01, + 0x20, 0x75, 0x55, 0x02, 0x20, 0x23, 0x22, 0x07, 0x20, 0x45, 0x12, 0x07, + 0x70, 0x24, 0x54, 0x02, 0x40, 0x56, 0x47, 0x04, 0x70, 0x31, 0x54, 0x02, + 0x60, 0x31, 0x55, 0x02, 0x70, 0x44, 0x22, 0x02, 0x20, 0x25, 0x55, 0x02, + 0x20, 0x55, 0x46, 0x03, 0x00, 0x02, 0x20, 0x00, 0x00, 0x02, 0x20, 0x01, + 0x00, 0x24, 0x21, 0x04, 0x00, 0x07, 0x07, 0x00, 0x00, 0x21, 0x24, 0x01, + 0x20, 0x45, 0x02, 0x02, 0x20, 0x55, 0x11, 0x06, 0x20, 0x55, 0x57, 0x05, + 0x30, 0x35, 0x55, 0x03, 0x20, 0x15, 0x51, 0x02, 0x30, 0x55, 0x55, 0x03, + 0x70, 0x71, 0x11, 0x07, 0x70, 0x71, 0x11, 0x01, 0x20, 0x15, 0x55, 0x02, + 0x50, 0x75, 0x55, 0x05, 0x70, 0x22, 0x22, 0x07, 0x40, 0x44, 0x54, 0x02, + 0x50, 0x35, 0x55, 0x05, 0x10, 0x11, 0x11, 0x07, 0x50, 0x57, 0x55, 0x05, + 0x40, 0x75, 0x57, 0x01, 0x20, 0x55, 0x55, 0x02, 0x30, 0x55, 0x13, 0x01, + 0x20, 0x55, 0x35, 0x06, 0x30, 0x55, 0x53, 0x05, 0x20, 0x25, 0x54, 0x02, + 0x70, 0x22, 0x22, 0x02, 0x50, 0x55, 0x55, 0x07, 0x50, 0x55, 0x55, 0x02, + 0x50, 0x55, 0x75, 0x05, 0x50, 0x25, 0x55, 0x05, 0x50, 0x25, 0x22, 0x02, + 0x70, 0x24, 0x12, 0x07, 0x60, 0x22, 0x22, 0x06, 0x10, 0x21, 0x42, 0x04, + 0x30, 0x22, 0x22, 0x03, 0x20, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0x10, 0x02, 0x00, 0x00, 0x00, 0x43, 0x56, 0x06, 0x10, 0x31, 0x55, 0x03, + 0x00, 0x16, 0x11, 0x06, 0x40, 0x64, 0x55, 0x06, 0x00, 0x52, 0x17, 0x06, + 0x40, 0x72, 0x22, 0x02, 0x00, 0x56, 0x65, 0x34, 0x10, 0x31, 0x55, 0x05, + 0x20, 0x30, 0x22, 0x07, 0x40, 0x60, 0x44, 0x34, 0x10, 0x51, 0x53, 0x05, + 0x20, 0x22, 0x22, 0x02, 0x00, 0x75, 0x55, 0x05, 0x00, 0x53, 0x55, 0x05, + 0x00, 0x52, 0x55, 0x02, 0x00, 0x53, 0x35, 0x11, 0x00, 0x56, 0x65, 0x44, + 0x00, 0x16, 0x11, 0x01, 0x00, 0x16, 0x42, 0x03, 0x20, 0x72, 0x22, 0x02, + 0x00, 0x55, 0x55, 0x07, 0x00, 0x55, 0x55, 0x02, 0x00, 0x55, 0x75, 0x05, + 0x00, 0x55, 0x52, 0x05, 0x00, 0x55, 0x65, 0x34, 0x00, 0x47, 0x12, 0x07, + 0x24, 0x12, 0x22, 0x04, 0x22, 0x02, 0x22, 0x02, 0x21, 0x42, 0x22, 0x01, + 0xa0, 0x05, 0x00, 0x00, 0x20, 0x55, 0x75, 0x00, 0x20, 0x15, 0x51, 0x32, + 0x05, 0x55, 0x55, 0x07, 0x12, 0x52, 0x17, 0x06, 0xa4, 0x43, 0x56, 0x06, + 0x05, 0x43, 0x56, 0x06, 0x42, 0x43, 0x56, 0x06, 0x52, 0x42, 0x56, 0x06, + 0x00, 0x16, 0x11, 0x36, 0x52, 0x52, 0x17, 0x06, 0x05, 0x52, 0x17, 0x06, + 0x42, 0x52, 0x17, 0x06, 0x50, 0x30, 0x22, 0x07, 0x52, 0x30, 0x22, 0x07, + 0x42, 0x30, 0x22, 0x07, 0x05, 0x52, 0x57, 0x05, 0x52, 0x52, 0x57, 0x05, + 0x24, 0x17, 0x13, 0x07, 0x00, 0xad, 0x5e, 0x0e, 0xe0, 0xf5, 0x55, 0x0d, + 0x52, 0x20, 0x55, 0x02, 0x05, 0x52, 0x55, 0x02, 0x42, 0x52, 0x55, 0x02, + 0x52, 0x50, 0x55, 0x07, 0x42, 0x50, 0x55, 0x07, 0x05, 0x55, 0x65, 0x34, + 0x05, 0x52, 0x55, 0x02, 0x05, 0x55, 0x55, 0x07, 0x20, 0x16, 0x61, 0x02, + 0x2c, 0x72, 0x22, 0x0f, 0x50, 0x25, 0x27, 0x02, 0x30, 0x35, 0xd5, 0x05, + 0x48, 0xe4, 0x54, 0x02, 0x24, 0x43, 0x56, 0x06, 0x24, 0x30, 0x22, 0x07, + 0x12, 0x52, 0x55, 0x02, 0x24, 0x50, 0x55, 0x07, 0x5a, 0x30, 0x55, 0x05, + 0x5a, 0x90, 0xdb, 0x09, 0x56, 0x0e, 0x0f, 0x00, 0x52, 0x02, 0x07, 0x00, + 0x20, 0x20, 0x51, 0x02, 0x00, 0x70, 0x11, 0x00, 0x00, 0xe0, 0x88, 0x00, + 0x11, 0x25, 0x8d, 0xc4, 0x11, 0x25, 0xa1, 0x8e, 0x20, 0x20, 0x22, 0x02, + 0x00, 0xa0, 0xa5, 0x00, 0x00, 0x50, 0x5a, 0x00, 0x28, 0x28, 0x28, 0x28, + 0x5a, 0x5a, 0x5a, 0x5a, 0xeb, 0xeb, 0xeb, 0xeb, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x74, 0x44, 0x44, 0x44, 0x47, 0x47, 0x44, 0xaa, 0xba, 0xaa, 0xaa, + 0x00, 0xf0, 0xaa, 0xaa, 0x00, 0x47, 0x47, 0x44, 0xaa, 0x8b, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x8f, 0xab, 0xaa, 0xaa, 0x8b, 0x0f, 0x00, + 0xaa, 0xfa, 0x00, 0x00, 0x44, 0x47, 0x07, 0x00, 0x00, 0x70, 0x44, 0x44, + 0x44, 0xc4, 0x00, 0x00, 0x44, 0xf4, 0x00, 0x00, 0x00, 0xf0, 0x44, 0x44, + 0x44, 0xc4, 0x44, 0x44, 0x00, 0xf0, 0x00, 0x00, 0x44, 0xf4, 0x44, 0x44, + 0x44, 0x4c, 0x4c, 0x44, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0x0e, 0x00, + 0x00, 0x2e, 0xaa, 0xaa, 0xaa, 0x0b, 0x0f, 0x00, 0x00, 0x0f, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x0f, 0x0f, 0x00, 0xaa, 0x0b, 0xab, 0xaa, + 0x44, 0x0f, 0x0f, 0x00, 0xaa, 0xfa, 0x00, 0x00, 0x00, 0x0f, 0x4f, 0x44, + 0x00, 0xf0, 0xaa, 0xaa, 0xaa, 0xea, 0x00, 0x00, 0x44, 0x4c, 0x0c, 0x00, + 0x00, 0x4c, 0x4c, 0x44, 0x00, 0xe0, 0xaa, 0xaa, 0xaa, 0xfa, 0xaa, 0xaa, + 0x44, 0x4f, 0x4f, 0x44, 0x44, 0x74, 0x00, 0x00, 0x00, 0xc0, 0x44, 0x44, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, + 0xcc, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xa0, 0x55, 0x0a, + 0x30, 0x35, 0x95, 0x05, 0xf0, 0x19, 0x11, 0x01, 0x00, 0x5f, 0x55, 0x05, + 0x00, 0x17, 0x12, 0x07, 0x00, 0x5e, 0x55, 0x02, 0x00, 0xaa, 0x2e, 0x01, + 0x00, 0x5a, 0x44, 0x04, 0x70, 0x52, 0x25, 0x07, 0x20, 0x75, 0x55, 0x02, + 0x60, 0x99, 0x66, 0x0f, 0x60, 0x21, 0x55, 0x02, 0x00, 0xd6, 0x6b, 0x00, + 0x80, 0x9f, 0xf9, 0x01, 0x60, 0x71, 0x11, 0x06, 0x20, 0x55, 0x55, 0x05, + 0x70, 0x70, 0x70, 0x00, 0x20, 0x27, 0x70, 0x00, 0x10, 0x42, 0x12, 0x07, + 0x40, 0x12, 0x42, 0x07, 0x80, 0x44, 0x44, 0x44, 0x44, 0x44, 0x25, 0x00, + 0x00, 0x02, 0x07, 0x02, 0xa0, 0x05, 0x5a, 0x00, 0x52, 0x02, 0x00, 0x00, + 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x4c, 0x54, 0x46, 0x00, + 0x57, 0x55, 0x00, 0x00, 0x43, 0x72, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +static const txt_font_t small_font = +{ + "small", + small_font_data, + 4, + 8, +}; diff --git a/games/NXDoom/textscreen/fonts/small.png b/games/NXDoom/textscreen/fonts/small.png new file mode 100644 index 000000000..155797fe6 Binary files /dev/null and b/games/NXDoom/textscreen/fonts/small.png differ diff --git a/games/NXDoom/textscreen/textscreen.h b/games/NXDoom/textscreen/textscreen.h new file mode 100644 index 000000000..40824da68 --- /dev/null +++ b/games/NXDoom/textscreen/textscreen.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/textscreen.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TEXTSCREEN_H +#define TEXTSCREEN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "../src/doomkeys.h" +#include "txt_main.h" + +#include "txt_button.h" +#include "txt_checkbox.h" +#include "txt_conditional.h" +#include "txt_desktop.h" +#include "txt_dropdown.h" +#include "txt_fileselect.h" +#include "txt_inputbox.h" +#include "txt_label.h" +#include "txt_radiobutton.h" +#include "txt_scrollpane.h" +#include "txt_separator.h" +#include "txt_spinctrl.h" +#include "txt_strut.h" +#include "txt_table.h" +#include "txt_widget.h" +#include "txt_window.h" +#include "txt_window_action.h" + +#ifdef __cplusplus +} +#endif + +#endif /* TEXTSCREEN_H */ diff --git a/games/NXDoom/textscreen/txt_button.c b/games/NXDoom/textscreen/txt_button.c new file mode 100644 index 000000000..0eadb71a4 --- /dev/null +++ b/games/NXDoom/textscreen/txt_button.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_button.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_button.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_button_size_calc(TXT_UNCAST_ARG(button)); +static void txt_button_drawer(TXT_UNCAST_ARG(button)); +static void txt_button_destructor(TXT_UNCAST_ARG(button)); +static int txt_button_keypress(TXT_UNCAST_ARG(button), int key); +static void txt_button_mousepress(TXT_UNCAST_ARG(button), int x, int y, + int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_button_class = +{ + txt_always_selectable, + txt_button_size_calc, + txt_button_drawer, + txt_button_keypress, + txt_button_destructor, + txt_button_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_button_size_calc(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + + button->widget.w = txt_utf8_strlen(button->label); + button->widget.h = 1; +} + +static void txt_button_drawer(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + int i; + int w; + + w = button->widget.w; + + txt_set_widget_bg(button); + + txt_draw_string(button->label); + + for (i = txt_utf8_strlen(button->label); i < w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_button_destructor(TXT_UNCAST_ARG(button)) +{ + TXT_CAST_ARG(txt_button_t, button); + + free(button->label); +} + +static int txt_button_keypress(TXT_UNCAST_ARG(button), int key) +{ + TXT_CAST_ARG(txt_button_t, button); + + if (key == KEY_ENTER) + { + txt_emit_signal(button, "pressed"); + return 1; + } + + return 0; +} + +static void txt_button_mousepress(TXT_UNCAST_ARG(button), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_button_t, button); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_button_keypress(button, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_button_label(txt_button_t *button, const char *label) +{ + free(button->label); + button->label = strdup(label); +} + +txt_button_t *txt_new_button(const char *label) +{ + txt_button_t *button; + + button = malloc(sizeof(txt_button_t)); + + txt_init_widget(button, &txt_button_class); + button->label = strdup(label); + + return button; +} + +/* Button with a callback set automatically */ + +txt_button_t *txt_new_button2(const char *label, txt_widget_signal_f func, + void *user_data) +{ + txt_button_t *button; + + button = txt_new_button(label); + + txt_signal_connect(button, "pressed", func, user_data); + + return button; +} diff --git a/games/NXDoom/textscreen/txt_button.h b/games/NXDoom/textscreen/txt_button.h new file mode 100644 index 000000000..1982e894a --- /dev/null +++ b/games/NXDoom/textscreen/txt_button.h @@ -0,0 +1,80 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_button.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_BUTTON_H +#define TXT_BUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Button widget. + * + * A button is a widget that can be selected to perform some action. + * When a button is pressed, it emits the "pressed" signal. + */ + +struct txt_button_s +{ + txt_widget_t widget; + char *label; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new button widget. + * + * @param label The label to use on the new button (UTF-8 format). + * @return Pointer to the new button widget. + */ + +txt_button_t *txt_new_button(const char *label); + +/** + * Create a new button widget, binding the "pressed" signal to a + * specified callback function. + * + * @param label The label to use on the new button (UTF-8 format). + * @param func The callback function to invoke. + * @param user_data User-specified pointer to pass to the callback. + * @return Pointer to the new button widget. + */ + +txt_button_t *txt_new_button2(const char *label, txt_widget_signal_f func, + void *user_data); + +/** + * Change the label used on a button. + * + * @param button The button. + * @param label The new label (UTF-8 format). + */ + +void txt_set_button_label(txt_button_t *button, const char *label); + +#endif /* TXT_BUTTON_H */ diff --git a/games/NXDoom/textscreen/txt_checkbox.c b/games/NXDoom/textscreen/txt_checkbox.c new file mode 100644 index 000000000..cd467ff3b --- /dev/null +++ b/games/NXDoom/textscreen/txt_checkbox.c @@ -0,0 +1,174 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_checkbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_checkbox.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_checkbox_size_calc(TXT_UNCAST_ARG(checkbox)); +static void txt_checkbox_drawer(TXT_UNCAST_ARG(checkbox)); +static void txt_checkbox_destructor(TXT_UNCAST_ARG(checkbox)); +static int txt_check_box_keypress(TXT_UNCAST_ARG(checkbox), int key); +static void txt_checkbox_mousepress(TXT_UNCAST_ARG(checkbox), int x, int y, + int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_checkbox_class = +{ + txt_always_selectable, + txt_checkbox_size_calc, + txt_checkbox_drawer, + txt_check_box_keypress, + txt_checkbox_destructor, + txt_checkbox_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_checkbox_size_calc(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + /* Minimum width is the string length + right-side space for padding */ + + checkbox->widget.w = txt_utf8_strlen(checkbox->label) + 5; + checkbox->widget.h = 1; +} + +static void txt_checkbox_drawer(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + txt_saved_colors_t colors; + int i; + int w; + + w = checkbox->widget.w; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("("); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + if ((*checkbox->variable != 0) ^ checkbox->inverted) + { + txt_draw_code_page_string("\x07"); + } + else + { + txt_draw_string(" "); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + txt_draw_string(") "); + + txt_restore_colours(&colors); + txt_set_widget_bg(checkbox); + txt_draw_string(checkbox->label); + + for (i = txt_utf8_strlen(checkbox->label); i < w - 4; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_checkbox_destructor(TXT_UNCAST_ARG(checkbox)) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + free(checkbox->label); +} + +static int txt_check_box_keypress(TXT_UNCAST_ARG(checkbox), int key) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + if (key == KEY_ENTER || key == ' ') + { + *checkbox->variable = !*checkbox->variable; + txt_emit_signal(checkbox, "changed"); + return 1; + } + + return 0; +} + +static void txt_checkbox_mousepress(TXT_UNCAST_ARG(checkbox), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_checkbox_t, checkbox); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_check_box_keypress(checkbox, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_checkbox_t *txt_new_check_box(const char *label, int *variable) +{ + txt_checkbox_t *checkbox; + + checkbox = malloc(sizeof(txt_checkbox_t)); + + txt_init_widget(checkbox, &txt_checkbox_class); + checkbox->label = strdup(label); + checkbox->variable = variable; + checkbox->inverted = 0; + + return checkbox; +} + +txt_checkbox_t *txt_new_inverted_checkbox(const char *label, int *variable) +{ + txt_checkbox_t *result; + + result = txt_new_check_box(label, variable); + result->inverted = 1; + + return result; +} diff --git a/games/NXDoom/textscreen/txt_checkbox.h b/games/NXDoom/textscreen/txt_checkbox.h new file mode 100644 index 000000000..b988f2cf1 --- /dev/null +++ b/games/NXDoom/textscreen/txt_checkbox.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_checkbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_CHECKBOX_H +#define TXT_CHECKBOX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Checkbox widget. + * + * A checkbox is used to control boolean values that may be either on + * or off. The widget has a label that is displayed to the right of + * the checkbox indicator. The widget tracks an integer variable; + * if the variable is non-zero, the checkbox is checked, while if it + * is zero, the checkbox is unchecked. It is also possible to + * create "inverted" checkboxes where this logic is reversed. + * + * When a checkbox is changed, it emits the "changed" signal. + */ + +struct txt_checkbox_s +{ + txt_widget_t widget; + char *label; + int *variable; + int inverted; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new checkbox. + * + * @param label The label for the new checkbox (UTF-8 format). + * @param variable Pointer to the variable containing this checkbox's + * value. + * @return Pointer to the new checkbox. + */ + +txt_checkbox_t *txt_new_check_box(const char *label, int *variable); + +/** + * Create a new inverted checkbox. + * + * An inverted checkbox displays the opposite of a normal checkbox; + * where it would be checked, it appears unchecked, and vice-versa. + * + * @param label The label for the new checkbox (UTF-8 format). + * @param variable Pointer to the variable containing this checkbox's + * value. + * @return Pointer to the new checkbox. + */ + +txt_checkbox_t *txt_new_inverted_checkbox(const char *label, int *variable); + +#endif /* TXT_CHECKBOX_H */ diff --git a/games/NXDoom/textscreen/txt_conditional.c b/games/NXDoom/textscreen/txt_conditional.c new file mode 100644 index 000000000..182711915 --- /dev/null +++ b/games/NXDoom/textscreen/txt_conditional.c @@ -0,0 +1,189 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_conditional.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2016 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_conditional.h" +#include "txt_strut.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct txt_conditional_s +{ + txt_widget_t widget; + int *var; + int expected_value; + txt_widget_t *child; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int condition_true(txt_conditional_t *conditional) +{ + return *conditional->var == conditional->expected_value; +} + +static int txt_cond_selectable(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + return condition_true(conditional) && + txt_selectable_widget(conditional->child); +} + +static void txt_cond_size_calc(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (!condition_true(conditional)) + { + conditional->widget.w = 0; + conditional->widget.h = 0; + } + else + { + txt_calc_widget_size(conditional->child); + conditional->widget.w = conditional->child->w; + conditional->widget.h = conditional->child->h; + } +} + +static void txt_cond_layout(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + conditional->child->x = conditional->widget.x; + conditional->child->y = conditional->widget.y; + txt_layout_widget(conditional->child); + } +} + +static void txt_cond_drawer(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_draw_widget(conditional->child); + } +} + +static void txt_cond_destructor(TXT_UNCAST_ARG(conditional)) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + txt_destroy_widget(conditional->child); +} + +static void txt_cond_focused(TXT_UNCAST_ARG(conditional), int focused) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_set_widget_focus(conditional->child, focused); + } +} + +static int txt_cond_key_press(TXT_UNCAST_ARG(conditional), int key) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + return txt_widget_key_press(conditional->child, key); + } + + return 0; +} + +static void txt_cond_mouse_press(TXT_UNCAST_ARG(conditional), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_conditional_t, conditional); + + if (condition_true(conditional)) + { + txt_widget_mouse_press(conditional->child, x, y, b); + } +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_conditional_class = +{ + txt_cond_selectable, txt_cond_size_calc, txt_cond_drawer, + txt_cond_key_press, txt_cond_destructor, txt_cond_mouse_press, + txt_cond_layout, txt_cond_focused, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_conditional_t *txt_new_conidtional(int *var, int expected_value, + TXT_UNCAST_ARG(child)) +{ + TXT_CAST_ARG(txt_widget_t, child); + txt_conditional_t *conditional; + + conditional = malloc(sizeof(txt_conditional_t)); + + txt_init_widget(conditional, &txt_conditional_class); + conditional->var = var; + conditional->expected_value = expected_value; + conditional->child = child; + + child->parent = &conditional->widget; + + return conditional; +} + +/* "Static" conditional that returns an empty strut if the given static + * value is false. Kind of like a conditional but we only evaluate it at + * creation time. + */ + +txt_widget_t *txt_if(int conditional, TXT_UNCAST_ARG(child)) +{ + TXT_CAST_ARG(txt_widget_t, child); + + if (conditional) + { + return child; + } + else + { + txt_strut_t *nullwidget; + txt_destroy_widget(child); + nullwidget = txt_new_strut(0, 0); + return &nullwidget->widget; + } +} diff --git a/games/NXDoom/textscreen/txt_conditional.h b/games/NXDoom/textscreen/txt_conditional.h new file mode 100644 index 000000000..691c891e4 --- /dev/null +++ b/games/NXDoom/textscreen/txt_conditional.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_conditional.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2016 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_CONDITIONAL_H +#define TXT_CONDITIONAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/** + * @file txt_conditional.h + * + * Conditional widget. + */ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * Conditional widget. + * + * A conditional widget contains another widget, and conditionally + * shows or hides it based on the value of a variable. + */ + +typedef struct txt_conditional_s txt_conditional_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new conditional widget. + * + * @param var The variable to check. + * @param expected_value If the variable has this value, the widget is + * shown. + * @param child The inner widget to show or hide. + * @return Pointer to the new conditional widget. + */ + +txt_conditional_t *txt_new_conidtional(int *var, int expected_value, + TXT_UNCAST_ARG(child)); + +/** + * Return the given child widget if the given boolean condition is true. + * + * If the condition is not true, the child widget is destroyed and a dummy + * "null" widget is returned that shows nothing. + * + * @param condition Boolean condition - true or false value. + * @param child Widget to conditionally return. + * @return Either child (if condition is true) or a null + * widget. + */ + +txt_widget_t *txt_if(int condition, TXT_UNCAST_ARG(child)); + +#endif /* #ifndef TXT_CONDITIONAL_H */ diff --git a/games/NXDoom/textscreen/txt_desktop.c b/games/NXDoom/textscreen/txt_desktop.c new file mode 100644 index 000000000..3b45a0eb5 --- /dev/null +++ b/games/NXDoom/textscreen/txt_desktop.c @@ -0,0 +1,420 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_desktop.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_window.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HELP_KEY KEY_F1 +#define MAXWINDOWS 128 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static char *g_desktop_title; +static txt_window_t *g_all_windows[MAXWINDOWS]; +static int g_num_windows = 0; +static int g_main_loop_running = 0; + +static txt_idle_callback_f g_periodic_callback = NULL; +static void *g_periodic_callback_data; +static unsigned int g_periodic_callback_period; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_exit_mainloop(void) +{ + g_main_loop_running = 0; +} + +#if 0 /* UNUSED */ +static int txt_raise_window(txt_window_t *window) +{ + int i; + + for (i = 0; i < g_num_windows - 1; ++i) + { + if (g_all_windows[i] == window) + { + g_all_windows[i] = g_all_windows[i + 1]; + g_all_windows[i + 1] = window; + + if (i == g_num_windows - 2) + { + txt_set_window_focus(g_all_windows[i], 0); + txt_set_window_focus(window, 1); + } + + return 1; + } + } + + /* Window not in the list, or at the end of the list (top) already. */ + + return 0; +} +#endif + +static void draw_desktop_background(const char *title) +{ + int i; + unsigned char *screendata; + unsigned char *p; + + screendata = txt_get_screen_data(); + + /* Fill the screen with gradient characters */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + *p++ = 0xb1; + *p++ = TXT_COLOR_GREY | (TXT_COLOR_BLUE << 4); + } + + /* Draw the top and bottom banners */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + p = screendata + (TXT_SCREEN_H - 1) * TXT_SCREEN_W * 2; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + /* Print the title */ + + txt_goto_xy(0, 0); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_putchar(' '); + txt_puts(title); +} + +static void draw_help_indicator(void) +{ + char keybuf[10]; + int fgcolor; + int x; + int y; + + txt_get_key_description(HELP_KEY, keybuf, sizeof(keybuf)); + + txt_get_mouse_position(&x, &y); + + if (y == 0 && x >= TXT_SCREEN_W - 9) + { + fgcolor = TXT_COLOR_GREY; + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + fgcolor = TXT_COLOR_BLACK; + txt_bgcolour(TXT_COLOR_GREY, 0); + } + + txt_goto_xy(TXT_SCREEN_W - 9, 0); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_draw_string(" "); + txt_draw_string(keybuf); + + txt_fgcolour(fgcolor); + txt_draw_string("=Help "); +} + +static void desktop_input_event(int c) +{ + txt_window_t *active_window; + int x; + int y; + + switch (c) + { + case TXT_MOUSE_LEFT: + txt_get_mouse_position(&x, &y); + + /* Clicking the top-right of the screen is equivalent + * to pressing the help key. + */ + + if (y == 0 && x >= TXT_SCREEN_W - 9) + { + desktop_input_event(HELP_KEY); + } + break; + + case HELP_KEY: + active_window = txt_get_active_window(); + if (active_window != NULL) + { + txt_open_window_help_url(active_window); + } + break; + + default: + break; + } +} + +#if 0 /* UNUSED */ +static void txt_draw_ascii_table(void) +{ + unsigned char *screendata; + char buf[10]; + int x; + int y; + int n; + + screendata = txt_get_screen_data(); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_bgcolour(TXT_COLOR_BLACK, 0); + + for (y = 0; y < 16; ++y) + { + for (x = 0; x < 16; ++x) + { + n = y * 16 + x; + + txt_goto_xy(x * 5, y); + txt_snprintf(buf, sizeof(buf), "%02x ", n); + txt_puts(buf); + + /* Write the character directly to the screen memory buffer: */ + + screendata[(y * TXT_SCREEN_W + x * 5 + 3) * 2] = n; + } + } + + txt_update_screen(); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_add_desktop_window(txt_window_t *win) +{ + /* Previously-top window loses focus: */ + + if (g_num_windows > 0) + { + txt_set_window_focus(g_all_windows[g_num_windows - 1], 0); + } + + g_all_windows[g_num_windows] = win; + ++g_num_windows; + + /* New window gains focus: */ + + txt_set_window_focus(win, 1); +} + +void txt_remove_desktop_window(txt_window_t *win) +{ + int from; + int to; + + /* Window must lose focus if it's being removed: */ + + txt_set_window_focus(win, 0); + + for (from = 0, to = 0; from < g_num_windows; ++from) + { + if (g_all_windows[from] != win) + { + g_all_windows[to] = g_all_windows[from]; + ++to; + } + } + + g_num_windows = to; + + /* Top window gains focus: */ + + if (g_num_windows > 0) + { + txt_set_window_focus(g_all_windows[g_num_windows - 1], 1); + } +} + +txt_window_t *txt_get_active_window(void) +{ + if (g_num_windows == 0) + { + return NULL; + } + + return g_all_windows[g_num_windows - 1]; +} + +int txt_lower_window(txt_window_t *window) +{ + int i; + + for (i = 0; i < g_num_windows - 1; ++i) + { + if (g_all_windows[i + 1] == window) + { + g_all_windows[i + 1] = g_all_windows[i]; + g_all_windows[i] = window; + + if (i == g_num_windows - 2) + { + txt_set_window_focus(window, 0); + txt_set_window_focus(g_all_windows[i + 1], 1); + } + + return 1; + } + } + + /* Window not in the list, or at the start of the list (bottom) already. */ + + return 0; +} + +void txt_set_desktop_title(const char *title) +{ + free(g_desktop_title); + g_desktop_title = strdup(title); + txt_set_window_title(title); +} + +void txt_draw_desktop(void) +{ + txt_window_t *active_window; + const char *title; + int i; + + txt_init_clip_area(); + + if (g_desktop_title == NULL) + title = ""; + else + title = g_desktop_title; + + draw_desktop_background(title); + + active_window = txt_get_active_window(); + if (active_window != NULL && active_window->help_url != NULL) + { + draw_help_indicator(); + } + + for (i = 0; i < g_num_windows; ++i) + { + txt_draw_window(g_all_windows[i]); + } + + txt_update_screen(); +} + +/* Fallback function to handle key/mouse events that are not handled by + * the active window. + */ + +void txt_dispatch_events(void) +{ + txt_window_t *active_window; + int c; + + while ((c = txt_getchar()) > 0) + { + active_window = txt_get_active_window(); + + if (active_window != NULL && !txt_window_keypress(active_window, c)) + { + desktop_input_event(c); + } + } +} + +void txt_set_periodic_callback(txt_idle_callback_f callback, void *user_data, + unsigned int period) +{ + g_periodic_callback = callback; + g_periodic_callback_data = user_data; + g_periodic_callback_period = period; +} + +void txt_gui_mainloop(void) +{ + g_main_loop_running = 1; + + while (g_main_loop_running) + { + txt_dispatch_events(); + + /* After the last window is closed, exit the loop */ + + if (g_num_windows <= 0) + { + txt_exit_mainloop(); + continue; + } + + txt_draw_desktop(); + + /* TXT_DrawASCIITable(); */ + + if (g_periodic_callback == NULL) + { + txt_sleep(0); + } + else + { + txt_sleep(g_periodic_callback_period); + + g_periodic_callback(g_periodic_callback_data); + } + } +} diff --git a/games/NXDoom/textscreen/txt_desktop.h b/games/NXDoom/textscreen/txt_desktop.h new file mode 100644 index 000000000..faae8c834 --- /dev/null +++ b/games/NXDoom/textscreen/txt_desktop.h @@ -0,0 +1,99 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_desktop.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_DESKTOP_H +#define TXT_DESKTOP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_window.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*txt_idle_callback_f)(void *user_data); + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_add_desktop_window(txt_window_t *win); +void txt_remove_desktop_window(txt_window_t *win); +void txt_draw_desktop(void); +void txt_dispatch_events(void); +void txt_draw_window(txt_window_t *window); +void txt_set_window_focus(txt_window_t *window, int focused); +int txt_window_keypress(txt_window_t *window, int c); + +/** + * Set the title displayed at the top of the screen. + * + * @param title The title to display (UTF-8 format). + */ + +void txt_set_desktop_title(const char *title); + +/** + * Start the main event loop. At least one window must have been + * opened prior to running this function. When no windows are left + * open, the event loop exits. + * + * It is possible to trigger an exit from this function using the + * @ref TXT_ExitMainLoop function. + */ + +void txt_gui_mainloop(void); + +/** + * Get the top window on the desktop that is currently receiving + * inputs. + * + * @return The active window, or NULL if no windows are present. + */ + +txt_window_t *txt_get_active_window(void); + +/** + * Set a callback function to be invoked periodically by the main + * loop code. + * + * @param callback The callback to invoke, or NULL to cancel + * an existing callback. + * @param user_data Extra data to pass to the callback. + * @param period Maximum time between invoking each callback. + * eg. a value of 200 will cause the callback + * to be invoked at least once every 200ms. + */ + +void txt_set_periodic_callback(txt_idle_callback_f callback, void *user_data, + unsigned int period); +/** + * Lower the z-position of the given window relative to other windows. + * + * @param window The window to make inactive. + * @return Non-zero if the window was lowered successfully, + * or zero if the window could not be lowered further. + */ + +int txt_lower_window(txt_window_t *window); + +#endif /* TXT_DESKTOP_H */ diff --git a/games/NXDoom/textscreen/txt_dropdown.c b/games/NXDoom/textscreen/txt_dropdown.c new file mode 100644 index 000000000..da19a8efd --- /dev/null +++ b/games/NXDoom/textscreen/txt_dropdown.c @@ -0,0 +1,343 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_dropdown.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_button.h" +#include "txt_dropdown.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + txt_window_t *window; + txt_dropdown_list_t *list; + int item; +} callback_data_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_dropdown_list_size_calc(TXT_UNCAST_ARG(list)); +static void txt_dropdown_list_drawer(TXT_UNCAST_ARG(list)); +static void txt_dropdown_list_destructor(TXT_UNCAST_ARG(list)); +static int txt_dropdown_lis_keypress(TXT_UNCAST_ARG(list), int key); +static void txt_dropdown_list_mousepress(TXT_UNCAST_ARG(list), int x, int y, + int b); +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_dropdown_list_class = +{ + txt_always_selectable, + txt_dropdown_list_size_calc, + txt_dropdown_list_drawer, + txt_dropdown_lis_keypress, + txt_dropdown_list_destructor, + txt_dropdown_list_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Check if the selected value for a list is valid */ + +static int valid_selection(txt_dropdown_list_t *list) +{ + return *list->variable >= 0 && *list->variable < list->num_values; +} + +/* Calculate the Y position for the selector window */ + +static int selector_window_y(txt_dropdown_list_t *list) +{ + int result; + + if (valid_selection(list)) + { + result = list->widget.y - 1 - *list->variable; + } + else + { + result = list->widget.y - 1 - (list->num_values / 2); + } + + /* Keep dropdown inside the screen. */ + + if (result < 1) + { + result = 1; + } + else if (result + list->num_values > (TXT_SCREEN_H - 3)) + { + result = TXT_SCREEN_H - list->num_values - 3; + } + + return result; +} + +/* Called when a button in the selector window is pressed */ + +static void item_selected(TXT_UNCAST_ARG(button), + TXT_UNCAST_ARG(callback_data)) +{ + TXT_CAST_ARG(callback_data_t, callback_data); + + /* Set the variable */ + + *callback_data->list->variable = callback_data->item; + + txt_emit_signal(callback_data->list, "changed"); + + /* Close the window */ + + txt_close_window(callback_data->window); +} + +/* Free callback data when the window is closed */ + +static void free_callback_data(TXT_UNCAST_ARG(list), + TXT_UNCAST_ARG(callback_data)) +{ + TXT_CAST_ARG(callback_data_t, callback_data); + free(callback_data); +} + +/* Catch presses of escape and close the window. */ + +static int selector_window_listener(txt_window_t *window, int key, + void *user_data) +{ + if (key == KEY_ESCAPE) + { + txt_close_window(window); + return 1; + } + + return 0; +} + +static int selector_mouse_listener(txt_window_t *window, int x, int y, int b, + void *unused) +{ + txt_widget_t *win; + + win = (txt_widget_t *)window; + + if (x < win->x || x > win->x + win->w || y < win->y || y > win->y + win->h) + { + txt_close_window(window); + return 1; + } + + return 0; +} + +/* Open the dropdown list window to select an item */ + +static void open_selector_window(txt_dropdown_list_t *list) +{ + txt_window_t *window; + int i; + + /* Open a simple window with no title bar or action buttons. */ + + window = txt_new_window(NULL); + + txt_set_window_action(window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(window, TXT_HORIZ_CENTER, NULL); + txt_set_window_action(window, TXT_HORIZ_RIGHT, NULL); + + /* Position the window so that the currently selected item appears + * over the top of the list widget. + */ + + txt_set_window_position(window, TXT_HORIZ_LEFT, TXT_VERT_TOP, + list->widget.x - 2, selector_window_y(list)); + + /* Add a button to the window for each option in the list. */ + + for (i = 0; i < list->num_values; ++i) + { + txt_button_t *button; + callback_data_t *data; + + button = txt_new_button(list->values[i]); + + txt_add_widget(window, button); + + /* Callback struct */ + + data = malloc(sizeof(callback_data_t)); + data->list = list; + data->window = window; + data->item = i; + + /* When the button is pressed, invoke the button press callback */ + + txt_signal_connect(button, "pressed", item_selected, data); + + /* When the window is closed, free back the callback struct */ + + txt_signal_connect(window, "closed", free_callback_data, data); + + /* Is this the currently-selected value? If so, select the button + * in the window as the default. + */ + + if (i == *list->variable) + { + txt_select_widget(window, button); + } + } + + /* Catch presses of escape in this window and close it. */ + + txt_set_key_listener(window, selector_window_listener, NULL); + txt_set_mouse_listener(window, selector_mouse_listener, NULL); +} + +static int dropdown_list_width(txt_dropdown_list_t *list) +{ + int i; + int result; + + /* Find the maximum string width */ + + result = 0; + + for (i = 0; i < list->num_values; ++i) + { + int w = txt_utf8_strlen(list->values[i]); + if (w > result) + { + result = w; + } + } + + return result; +} + +static void txt_dropdown_list_size_calc(TXT_UNCAST_ARG(list)) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + list->widget.w = dropdown_list_width(list); + list->widget.h = 1; +} + +static void txt_dropdown_list_drawer(TXT_UNCAST_ARG(list)) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + unsigned int i; + const char *str; + + /* Set bg/fg text colors. */ + + txt_set_widget_bg(list); + + /* Select a string to draw from the list, if the current value is + * in range. Otherwise fall back to a default. + */ + + if (valid_selection(list)) + { + str = list->values[*list->variable]; + } + else + { + str = "???"; + } + + /* Draw the string and fill to the end with spaces */ + + txt_draw_string(str); + + for (i = txt_utf8_strlen(str); i < list->widget.w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_dropdown_list_destructor(TXT_UNCAST_ARG(list)) +{ +} + +static int txt_dropdown_lis_keypress(TXT_UNCAST_ARG(list), int key) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + if (key == KEY_ENTER) + { + open_selector_window(list); + return 1; + } + + return 0; +} + +static void txt_dropdown_list_mousepress(TXT_UNCAST_ARG(list), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_dropdown_list_t, list); + + /* Left mouse click does the same as selecting and pressing enter */ + + if (b == TXT_MOUSE_LEFT) + { + txt_dropdown_lis_keypress(list, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_dropdown_list_t *txt_new_dropdown_list(int *variable, + const char **values, int num_values) +{ + txt_dropdown_list_t *list; + + list = malloc(sizeof(txt_dropdown_list_t)); + + txt_init_widget(list, &txt_dropdown_list_class); + list->variable = variable; + list->values = values; + list->num_values = num_values; + + return list; +} diff --git a/games/NXDoom/textscreen/txt_dropdown.h b/games/NXDoom/textscreen/txt_dropdown.h new file mode 100644 index 000000000..08286325b --- /dev/null +++ b/games/NXDoom/textscreen/txt_dropdown.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_dropdown.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_DROPDOWN_H +#define TXT_DROPDOWN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Dropdown list widget. + * + * A dropdown list allows the user to select from a list of values, + * which appears when the list is selected. + * + * When the value of a dropdown list is changed, the "changed" signal + * is emitted. + */ + +struct txt_dropdown_list_s +{ + txt_widget_t widget; + int *variable; + const char **values; + int num_values; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new dropdown list widget. + * + * The parameters specify a list of string labels, and a pointer to an + * integer variable. The variable contains the current "value" of the + * list, as an index within the list of labels. + * + * @param variable Pointer to the variable containing the + * list's value. + * @param values Pointer to an array of strings containing + * the labels to use for the list (UTF-8 format). + * @param num_values The number of variables in the list. + */ + +txt_dropdown_list_t *txt_new_dropdown_list(int *variable, + const char **values, int num_values); + +#endif /* TXT_DROPDOWN_H */ diff --git a/games/NXDoom/textscreen/txt_fileselect.c b/games/NXDoom/textscreen/txt_fileselect.c new file mode 100644 index 000000000..b6b6067cd --- /dev/null +++ b/games/NXDoom/textscreen/txt_fileselect.c @@ -0,0 +1,361 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_fileselect.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Routines for selecting files. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_fileselect.h" +#include "txt_gui.h" +#include "txt_inputbox.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_widget.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Linux version: invoke the Zenity command line program to pop up a + * dialog box. This avoids adding Gtk+ as a compile dependency. + */ + +#define ZENITY_BINARY "/usr/bin/zenity" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct txt_fileselect_s +{ + txt_widget_t widget; + txt_inputbox_t *inputbox; + int size; + const char *prompt; + const char **extensions; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_file_select_size_calc(TXT_UNCAST_ARG(fileselect)); +static void txt_file_select_drawer(TXT_UNCAST_ARG(fileselect)); +static int txt_file_select_keypress(TXT_UNCAST_ARG(fileselect), int key); +static void txt_file_select_destructor(TXT_UNCAST_ARG(fileselect)); +static void txt_file_select_mousepress(TXT_UNCAST_ARG(fileselect), int x, + int y, int b); +static void txt_file_select_focused(TXT_UNCAST_ARG(fileselect), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Dummy value to select a directory. */ + +const char *TXT_DIRECTORY[] = +{ + "__directory__", + NULL, +}; + +txt_widget_class_t txt_fileselect_class = +{ + txt_always_selectable, + txt_file_select_size_calc, + txt_file_select_drawer, + txt_file_select_keypress, + txt_file_select_destructor, + txt_file_select_mousepress, + NULL, + txt_file_select_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#if 0 /* UNUSED */ +static char *exec_read_output(char **argv) +{ + char *result; + int completed; + int pid; + int status; + int result_len; + int pipefd[2]; + + if (pipe(pipefd) != 0) + { + return NULL; + } + + pid = fork(); + + if (pid == 0) + { + dup2(pipefd[1], fileno(stdout)); + execv(argv[0], argv); + exit(-1); + } + + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + + /* Read program output into 'result' string. + * Wait until the program has completed and (if it was successful) + * a full line has been read. + */ + + result = NULL; + result_len = 0; + completed = 0; + + while (!completed || + (status == 0 && (result == NULL || strchr(result, '\n') == NULL))) + { + char buf[64]; + int bytes; + + if (!completed && waitpid(pid, &status, WNOHANG) != 0) + { + completed = 1; + } + + bytes = read(pipefd[0], buf, sizeof(buf)); + + if (bytes < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + { + status = -1; + break; + } + } + else + { + char *new_result = realloc(result, result_len + bytes + 1); + if (new_result == NULL) + { + break; + } + + result = new_result; + memcpy(result + result_len, buf, bytes); + result_len += bytes; + result[result_len] = '\0'; + } + + usleep(100 * 1000); + txt_sleep(1); + txt_update_screen(); + } + + close(pipefd[0]); + close(pipefd[1]); + + /* Must have a success exit code. */ + + if (WEXITSTATUS(status) != 0) + { + free(result); + result = NULL; + } + + /* Strip off newline from the end. */ + + if (result != NULL && result[result_len - 1] == '\n') + { + result[result_len - 1] = '\0'; + } + + return result; +} + +static unsigned int num_extensions(const char **extensions) +{ + unsigned int result = 0; + + if (extensions != NULL) + { + for (result = 0; extensions[result] != NULL; ++result) + ; + } + + return result; +} + +/* ExpandExtension + * given an extension (like wad) + * return a pointer to a string that is a case-insensitive + * pattern representation (like [Ww][Aa][Dd]) + */ + +static char *expand_extension(const char *orig) +{ + int oldlen; + int newlen; + int i; + char *c; + char *newext = NULL; + + oldlen = strlen(orig); + newlen = oldlen * 4; /* pathological case: 'w' => '[Ww]' */ + newext = malloc(newlen + 1); + + if (newext == NULL) + { + return NULL; + } + + c = newext; + + for (i = 0; i < oldlen; ++i) + { + if (isalpha(orig[i])) + { + *c++ = '['; + *c++ = tolower(orig[i]); + *c++ = toupper(orig[i]); + *c++ = ']'; + } + else + { + *c++ = orig[i]; + } + } + + *c = '\0'; + return newext; +} + +static char *txt_select_file(const char *window_title, + const char **extensions) +{ + return NULL; +} +#endif + +static void txt_file_select_size_calc(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + /* Calculate widget size, but override the width to always + * be the configured size. + */ + + txt_calc_widget_size(fileselect->inputbox); + fileselect->widget.w = fileselect->size; + fileselect->widget.h = fileselect->inputbox->widget.h; +} + +static void txt_file_select_drawer(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + /* Input box widget inherits all the properties of the + * file selector. + */ + + fileselect->inputbox->widget.x = fileselect->widget.x + 2; + fileselect->inputbox->widget.y = fileselect->widget.y; + fileselect->inputbox->widget.w = fileselect->widget.w - 2; + fileselect->inputbox->widget.h = fileselect->widget.h; + + /* Triple bar symbol gives a distinguishing look to the file selector. */ + + txt_draw_code_page_string("\xf0 "); + txt_bgcolour(TXT_COLOR_BLACK, 0); + txt_draw_widget(fileselect->inputbox); +} + +static void txt_file_select_destructor(TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_destroy_widget(fileselect->inputbox); +} + +static int txt_file_select_keypress(TXT_UNCAST_ARG(fileselect), int key) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + return txt_widget_key_press(fileselect->inputbox, key); +} + +static void txt_file_select_mousepress(TXT_UNCAST_ARG(fileselect), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + txt_widget_mouse_press(fileselect->inputbox, x, y, b); +} + +static void txt_file_select_focused(TXT_UNCAST_ARG(fileselect), int focused) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_set_widget_focus(fileselect->inputbox, focused); +} + +/* If the (inner) inputbox widget is changed, emit a change to the + * outer (fileselect) widget. + */ + +static void input_box_changed(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(fileselect)) +{ + TXT_CAST_ARG(txt_fileselect_t, fileselect); + + txt_emit_signal(&fileselect->widget, "changed"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_fileselect_t *txt_new_file_selector(char **variable, int size, + const char *prompt, + const char **extensions) +{ + txt_fileselect_t *fileselect; + + fileselect = malloc(sizeof(txt_fileselect_t)); + txt_init_widget(fileselect, &txt_fileselect_class); + fileselect->inputbox = txt_new_input_box(variable, 1024); + fileselect->inputbox->widget.parent = &fileselect->widget; + fileselect->size = size; + fileselect->prompt = prompt; + fileselect->extensions = extensions; + + txt_signal_connect(fileselect->inputbox, "changed", input_box_changed, + fileselect); + + return fileselect; +} diff --git a/games/NXDoom/textscreen/txt_fileselect.h b/games/NXDoom/textscreen/txt_fileselect.h new file mode 100644 index 000000000..a8bc7eb5c --- /dev/null +++ b/games/NXDoom/textscreen/txt_fileselect.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_fileselect.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Routines for selecting files, and the txt_fileselect_t widget. + * + ****************************************************************************/ + +#ifndef TXT_FILESELECT_H +#define TXT_FILESELECT_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * File selection widget. + * + * A file selection widget resembles an input box (@ref txt_inputbox_t) + * but opens a file selector dialog box when clicked. + */ + +typedef struct txt_fileselect_s txt_fileselect_t; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/** + * Special value to use for 'extensions' that selects a directory + * instead of a file. + */ + +extern const char *TXT_DIRECTORY[]; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new txt_fileselect_t widget. + * + * @param variable Pointer to a char * variable in which the selected + * file should be stored (UTF-8 format). + * @param size Width of the file selector widget in characters. + * @param prompt Pointer to a string containing a prompt to display + * in the file selection window. + * @param extensions NULL-terminated list of filename extensions that + * can be used for this widget, or @ref TXT_DIRECTORY + * to select directories. + */ + +txt_fileselect_t *txt_new_file_selector(char **variable, int size, + const char *prompt, + const char **extensions); + +#endif /* TXT_FILESELECT_H */ diff --git a/games/NXDoom/textscreen/txt_font.h b/games/NXDoom/textscreen/txt_font.h new file mode 100644 index 000000000..9815f8378 --- /dev/null +++ b/games/NXDoom/textscreen/txt_font.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_font.h + * + * SPDX-License-Identifer: GPLv2 + * + ****************************************************************************/ + +#ifndef __TEXTSCREEN_FONT_H__ +#define __TEXTSCREEN_FONT_H__ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + const char *name; + const uint8_t *data; + unsigned int w; + unsigned int h; +} txt_font_t; + +#endif /* __TEXTSCREEN_FONT_H__ */ diff --git a/games/NXDoom/textscreen/txt_gui.c b/games/NXDoom/textscreen/txt_gui.c new file mode 100644 index 000000000..9df99e62d --- /dev/null +++ b/games/NXDoom/textscreen/txt_gui.c @@ -0,0 +1,519 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_gui.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VALID_X(x) ((x) >= g_cliparea->x1 && (x) < g_cliparea->x2) +#define VALID_Y(y) ((y) >= g_cliparea->y1 && (y) < g_cliparea->y2) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct txt_cliparea_s txt_cliparea_t; + +struct txt_cliparea_s +{ + int x1; + int x2; + int y1; + int y2; + txt_cliparea_t *next; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Array of border characters for drawing windows. The array looks like this: + * + * +-++ + * | || + * +-++ + * +-++ + */ + +static const int g_borders[4][4] = +{ + {0xda, 0xc4, 0xc2, 0xbf}, + {0xb3, ' ', 0xb3, 0xb3}, + {0xc3, 0xc4, 0xc5, 0xb4}, + {0xc0, 0xc4, 0xc1, 0xd9}, +}; + +static txt_cliparea_t *g_cliparea = NULL; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void txt_draw_desktop_background(const char *title) +{ + int i; + unsigned char *screendata; + unsigned char *p; + + screendata = txt_get_screen_data(); + + /* Fill the screen with gradient characters */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + *p++ = 0xb1; + *p++ = TXT_COLOR_GREY | (TXT_COLOR_BLUE << 4); + } + + /* Draw the top and bottom banners */ + + p = screendata; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + p = screendata + (TXT_SCREEN_H - 1) * TXT_SCREEN_W * 2; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = TXT_COLOR_BLACK | (TXT_COLOR_GREY << 4); + } + + /* Print the title */ + + txt_goto_xy(0, 0); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_draw_string(" "); + txt_draw_string(title); +} +#endif + +static void txt_draw_shadow(int x, int y, int w, int h) +{ + unsigned char *screendata; + unsigned char *p; + int x1; + int y1; + + screendata = txt_get_screen_data(); + + for (y1 = y; y1 < y + h; ++y1) + { + p = screendata + (y1 * TXT_SCREEN_W + x) * 2; + + for (x1 = x; x1 < x + w; ++x1) + { + if (VALID_X(x1) && VALID_Y(y1)) + { + p[1] = TXT_COLOR_DARK_GREY; + } + + p += 2; + } + } +} + +static void put_unicode_char(unsigned int c) +{ + int d; + + /* Treat control characters specially. */ + + if (c == '\n' || c == '\b') + { + txt_putchar(c); + return; + } + + /* Map Unicode character into the symbol used to represent it in this + * code page. For unrepresentable characters, print a fallback instead. + * Note that we use txt_put_symbol() here because we just want to do a + * raw write into the screen buffer. + */ + + d = txt_unicode_character(c); + + if (d >= 0) + { + txt_put_symbol(d); + } + else + { + txt_put_symbol('\xa8'); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_draw_window_frame(const char *title, int x, int y, int w, int h) +{ + txt_saved_colors_t colors; + int x1; + int y1; + int bx; + int by; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + for (y1 = y; y1 < y + h; ++y1) + { + /* Select the appropriate row and column in the borders + * array to pick the appropriate character to draw at + * this location. + * + * Draw a horizontal line on the third line down, so we + * draw a box around the title. + */ + + by = y1 == y ? 0 + : y1 == y + 2 && title != NULL ? 2 + : y1 == y + h - 1 ? 3 + : 1; + + for (x1 = x; x1 < x + w; ++x1) + { + bx = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; + + if (VALID_X(x1) && VALID_Y(y1)) + { + txt_goto_xy(x1, y1); + txt_putchar(g_borders[by][bx]); + } + } + } + + /* Draw the title */ + + if (title != NULL) + { + txt_goto_xy(x + 1, y + 1); + txt_bgcolour(TXT_COLOR_GREY, 0); + txt_fgcolour(TXT_COLOR_BLUE); + + for (x1 = 0; x1 < w - 2; ++x1) + { + txt_draw_string(" "); + } + + txt_goto_xy(x + (w - txt_utf8_strlen(title)) / 2, y + 1); + txt_draw_string(title); + } + + /* Draw the window's shadow. */ + + txt_draw_shadow(x + 2, y + h, w, 1); + txt_draw_shadow(x + w, y + 1, 2, h); + + txt_restore_colours(&colors); +} + +void txt_draw_separator(int x, int y, int w) +{ + txt_saved_colors_t colors; + unsigned char *data; + int x1; + int b; + + data = txt_get_screen_data(); + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + if (!VALID_Y(y)) + { + return; + } + + data += (y * TXT_SCREEN_W + x) * 2; + + for (x1 = x; x1 < x + w; ++x1) + { + txt_goto_xy(x1, y); + + b = x1 == x ? 0 : x1 == x + w - 1 ? 3 : 1; + + if (VALID_X(x1)) + { + /* Read the current value from the screen + * Check that it matches what the window should look like if + * there is no separator, then apply the separator + */ + + if (*data == g_borders[1][b]) + { + txt_putchar(g_borders[2][b]); + } + } + + data += 2; + } + + txt_restore_colours(&colors); +} + +/* Alternative to txt_draw_string() where the argument is a "code page + * string" - characters are in native code page format and not UTF-8. + */ + +void txt_draw_code_page_string(const char *s) +{ + int x; + int y; + int x1; + const char *p; + + txt_get_xy(&x, &y); + + if (VALID_Y(y)) + { + x1 = x; + + for (p = s; *p != '\0'; ++p) + { + if (VALID_X(x1)) + { + txt_goto_xy(x1, y); + txt_putchar(*p); + } + + x1 += 1; + } + } + + txt_goto_xy(x + strlen(s), y); +} + +void txt_draw_string(const char *s) +{ + int x; + int y; + int x1; + const char *p; + unsigned int c; + + txt_get_xy(&x, &y); + + if (VALID_Y(y)) + { + x1 = x; + + for (p = s; *p != '\0'; ) + { + c = txt_decode_utf8(&p); + + if (c == 0) + { + break; + } + + if (VALID_X(x1)) + { + txt_goto_xy(x1, y); + put_unicode_char(c); + } + + x1 += 1; + } + } + + txt_goto_xy(x + txt_utf8_strlen(s), y); +} + +void txt_draw_horiz_scrollbar(int x, int y, int w, int cursor, int range) +{ + txt_saved_colors_t colors; + int x1; + int cursor_x; + + if (!VALID_Y(y)) + { + return; + } + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_goto_xy(x, y); + txt_putchar('\x1b'); + + cursor_x = x + 1; + + if (range > 0) + { + cursor_x += (cursor * (w - 3)) / range; + } + + if (cursor_x > x + w - 2) + { + cursor_x = x + w - 2; + } + + for (x1 = x + 1; x1 < x + w - 1; ++x1) + { + if (VALID_X(x1)) + { + if (x1 == cursor_x) + { + txt_putchar('\xdb'); + } + else + { + txt_putchar('\xb1'); + } + } + } + + txt_putchar('\x1a'); + txt_restore_colours(&colors); +} + +void txt_draw_vert_scrollbar(int x, int y, int h, int cursor, int range) +{ + txt_saved_colors_t colors; + int y1; + int cursor_y; + + if (!VALID_X(x)) + { + return; + } + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BLACK); + txt_bgcolour(TXT_COLOR_GREY, 0); + + txt_goto_xy(x, y); + txt_putchar('\x18'); + + cursor_y = y + 1; + + if (cursor_y > y + h - 2) + { + cursor_y = y + h - 2; + } + + if (range > 0) + { + cursor_y += (cursor * (h - 3)) / range; + } + + for (y1 = y + 1; y1 < y + h - 1; ++y1) + { + if (VALID_Y(y1)) + { + txt_goto_xy(x, y1); + + if (y1 == cursor_y) + { + txt_putchar('\xdb'); + } + else + { + txt_putchar('\xb1'); + } + } + } + + txt_goto_xy(x, y + h - 1); + txt_putchar('\x19'); + txt_restore_colours(&colors); +} + +void txt_init_clip_area(void) +{ + if (g_cliparea == NULL) + { + g_cliparea = malloc(sizeof(txt_cliparea_t)); + g_cliparea->x1 = 0; + g_cliparea->x2 = TXT_SCREEN_W; + g_cliparea->y1 = 0; + g_cliparea->y2 = TXT_SCREEN_H; + g_cliparea->next = NULL; + } +} + +void txt_push_clip_area(int x1, int x2, int y1, int y2) +{ + txt_cliparea_t *newarea; + + newarea = malloc(sizeof(txt_cliparea_t)); + + /* Set the new clip area to the intersection of the old + * area and the new one. + */ + + newarea->x1 = g_cliparea->x1; + newarea->x2 = g_cliparea->x2; + newarea->y1 = g_cliparea->y1; + newarea->y2 = g_cliparea->y2; + + if (x1 > newarea->x1) newarea->x1 = x1; + if (x2 < newarea->x2) newarea->x2 = x2; + if (y1 > newarea->y1) newarea->y1 = y1; + if (y2 < newarea->y2) newarea->y2 = y2; + +#if 0 + printf("New scrollable area: %i,%i-%i,%i\n", x1, y1, x2, y2); +#endif + + /* Hook into the list */ + + newarea->next = g_cliparea; + g_cliparea = newarea; +} + +void txt_pop_clip_area(void) +{ + txt_cliparea_t *next_cliparea; + + /* Never pop the last entry */ + + if (g_cliparea->next == NULL) return; + + /* Unlink the last entry and delete */ + + next_cliparea = g_cliparea->next; + free(g_cliparea); + g_cliparea = next_cliparea; +} diff --git a/games/NXDoom/textscreen/txt_gui.h b/games/NXDoom/textscreen/txt_gui.h new file mode 100644 index 000000000..3dfe0f59a --- /dev/null +++ b/games/NXDoom/textscreen/txt_gui.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_gui.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_GUI_H +#define TXT_GUI_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TXT_INACTIVE_WINDOW_BACKGROUND TXT_COLOR_BLACK +#define TXT_ACTIVE_WINDOW_BACKGROUND TXT_COLOR_BLUE +#define TXT_HOVER_BACKGROUND TXT_COLOR_CYAN + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_draw_window_frame(const char *title, int x, int y, int w, int h); +void txt_draw_separator(int x, int y, int w); +void txt_draw_code_page_string(const char *s); +void txt_draw_string(const char *s); + +void txt_draw_horiz_scrollbar(int x, int y, int w, int cursor, int range); +void txt_draw_vert_scrollbar(int x, int y, int h, int cursor, int range); + +void txt_init_clip_area(void); +void txt_push_clip_area(int x1, int x2, int y1, int y2); +void txt_pop_clip_area(void); + +#endif /* TXT_GUI_H */ diff --git a/games/NXDoom/textscreen/txt_inputbox.c b/games/NXDoom/textscreen/txt_inputbox.c new file mode 100644 index 000000000..17801687a --- /dev/null +++ b/games/NXDoom/textscreen/txt_inputbox.c @@ -0,0 +1,387 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_inputbox.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_inputbox.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_input_box_size_calc(TXT_UNCAST_ARG(inputbox)); +static void txt_input_box_drawer(TXT_UNCAST_ARG(inputbox)); +static int txt_input_box_keypress(TXT_UNCAST_ARG(inputbox), int key); +static void txt_input_box_destructor(TXT_UNCAST_ARG(inputbox)); +static void txt_input_box_mousepress(TXT_UNCAST_ARG(inputbox), int x, int y, + int b); +static void txt_input_box_focused(TXT_UNCAST_ARG(inputbox), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_inputbox_class; +txt_widget_class_t txt_int_inputbox_class; + +txt_widget_class_t txt_inputbox_class = +{ + txt_always_selectable, + txt_input_box_size_calc, + txt_input_box_drawer, + txt_input_box_keypress, + txt_input_box_destructor, + txt_input_box_mousepress, + NULL, + txt_input_box_focused, +}; + +txt_widget_class_t txt_int_inputbox_class = +{ + txt_always_selectable, + txt_input_box_size_calc, + txt_input_box_drawer, + txt_input_box_keypress, + txt_input_box_destructor, + txt_input_box_mousepress, + NULL, + txt_input_box_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void set_buffer_from_value(txt_inputbox_t *inputbox) +{ + if (inputbox->widget.widget_class == &txt_inputbox_class) + { + char **value = (char **)inputbox->value; + + if (*value != NULL) + { + txt_string_copy(inputbox->buffer, *value, + strnlen(*value, inputbox->buffer_len) + 1); + } + else + { + txt_string_copy(inputbox->buffer, "", inputbox->buffer_len); + } + } + else if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + int *value = (int *)inputbox->value; + txt_snprintf(inputbox->buffer, inputbox->buffer_len, "%i", *value); + } +} + +static void start_editing(txt_inputbox_t *inputbox) +{ + /* Integer input boxes start from an empty buffer: */ + + if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + txt_string_copy(inputbox->buffer, "", inputbox->buffer_len); + } + else + { + set_buffer_from_value(inputbox); + } + + /* Switch to text input mode so we get shifted input. */ + + txt_set_input_mode(TXT_INPUT_TEXT); + inputbox->editing = 1; +} + +static void stop_editing(txt_inputbox_t *inputbox) +{ + if (inputbox->editing) + { + /* Switch back to normal input mode. */ + + txt_set_input_mode(TXT_INPUT_NORMAL); + inputbox->editing = 0; + } +} + +static void finish_editing(txt_inputbox_t *inputbox) +{ + if (!inputbox->editing) + { + return; + } + + /* Save the new value back to the variable. */ + + if (inputbox->widget.widget_class == &txt_inputbox_class) + { + free(*((char **)inputbox->value)); + *((char **)inputbox->value) = strdup(inputbox->buffer); + } + else if (inputbox->widget.widget_class == &txt_int_inputbox_class) + { + *((int *)inputbox->value) = atoi(inputbox->buffer); + } + + txt_emit_signal(&inputbox->widget, "changed"); + + stop_editing(inputbox); +} + +static void txt_input_box_size_calc(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + /* Enough space for the box + cursor */ + + inputbox->widget.w = inputbox->size + 1; + inputbox->widget.h = 1; +} + +static void txt_input_box_drawer(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + int focused; + int i; + int chars; + int w; + + focused = inputbox->widget.focused; + w = inputbox->widget.w; + + /* Select the background color based on whether we are currently + * editing, and if not, whether the widget is focused. + */ + + if (inputbox->editing && focused) + { + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + txt_set_widget_bg(inputbox); + } + + if (!inputbox->editing) + { + /* If not editing, use the current value from inputbox->value. */ + + set_buffer_from_value(inputbox); + } + + /* If string size exceeds the widget's width, show only the end. */ + + if (txt_utf8_strlen(inputbox->buffer) > w - 1) + { + txt_draw_code_page_string("\xae"); + txt_draw_string(txt_utf8_skip_chars( + inputbox->buffer, txt_utf8_strlen(inputbox->buffer) - w + 2)); + chars = w - 1; + } + else + { + txt_draw_string(inputbox->buffer); + chars = txt_utf8_strlen(inputbox->buffer); + } + + if (chars < w && inputbox->editing && focused) + { + txt_bgcolour(TXT_COLOR_BLACK, 1); + txt_draw_string("_"); + ++chars; + } + + for (i = chars; i < w; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_input_box_destructor(TXT_UNCAST_ARG(inputbox)) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + stop_editing(inputbox); + free(inputbox->buffer); +} + +static void backspace(txt_inputbox_t *inputbox) +{ + unsigned int len; + char *p; + + len = txt_utf8_strlen(inputbox->buffer); + + if (len > 0) + { + p = txt_utf8_skip_chars(inputbox->buffer, len - 1); + *p = '\0'; + } +} + +static void add_character(txt_inputbox_t *inputbox, int key) +{ + char *end; + char *p; + + if (txt_utf8_strlen(inputbox->buffer) < inputbox->size) + { + /* Add character to the buffer */ + + end = inputbox->buffer + strlen(inputbox->buffer); + p = txt_encode_utf8(end, key); + *p = '\0'; + } +} + +static int txt_input_box_keypress(TXT_UNCAST_ARG(inputbox), int key) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + unsigned int c; + + if (!inputbox->editing) + { + if (key == KEY_ENTER) + { + start_editing(inputbox); + return 1; + } + + /* Backspace or delete erases the contents of the box. */ + + if ((key == KEY_DEL || key == KEY_BACKSPACE) && + inputbox->widget.widget_class == &txt_inputbox_class) + { + free(*((char **)inputbox->value)); + *((char **)inputbox->value) = strdup(""); + } + + return 0; + } + + if (key == KEY_ENTER) + { + finish_editing(inputbox); + } + + if (key == KEY_ESCAPE) + { + stop_editing(inputbox); + } + + if (key == KEY_BACKSPACE) + { + backspace(inputbox); + } + + c = TXT_KEY_TO_UNICODE(key); + + /* Add character to the buffer, but only if it's a printable character + * that we can represent on the screen. + */ + + if (isprint(c) || (c >= 128 && txt_unicode_character(c) >= 0)) + { + add_character(inputbox, c); + } + + return 1; +} + +static void txt_input_box_mousepress(TXT_UNCAST_ARG(inputbox), int x, int y, + int b) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + if (b == TXT_MOUSE_LEFT) + { + /* Make mouse clicks start editing the box */ + + if (!inputbox->editing) + { + /* Send a simulated keypress to start editing */ + + txt_widget_key_press(inputbox, KEY_ENTER); + } + } +} + +static void txt_input_box_focused(TXT_UNCAST_ARG(inputbox), int focused) +{ + TXT_CAST_ARG(txt_inputbox_t, inputbox); + + /* Stop editing when we lose focus. */ + + if (inputbox->editing && !focused) + { + finish_editing(inputbox); + } +} + +static txt_inputbox_t *new_input_box(txt_widget_class_t *widget_class, + void *value, int size) +{ + txt_inputbox_t *inputbox; + + inputbox = malloc(sizeof(txt_inputbox_t)); + + txt_init_widget(inputbox, widget_class); + inputbox->value = value; + inputbox->size = size; + + /* 'size' is the maximum number of characters that can be entered, + * but for a UTF-8 string, each character can take up to four + * characters. + */ + + inputbox->buffer_len = size * 4 + 1; + inputbox->buffer = malloc(inputbox->buffer_len); + inputbox->editing = 0; + + return inputbox; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_inputbox_t *txt_new_input_box(char **value, int size) +{ + return new_input_box(&txt_inputbox_class, value, size); +} + +txt_inputbox_t *txt_new_int_input_box(int *value, int size) +{ + return new_input_box(&txt_int_inputbox_class, value, size); +} diff --git a/games/NXDoom/textscreen/txt_inputbox.h b/games/NXDoom/textscreen/txt_inputbox.h new file mode 100644 index 000000000..077df013e --- /dev/null +++ b/games/NXDoom/textscreen/txt_inputbox.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_inputbox.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_INPUTBOX_H +#define TXT_INPUTBOX_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Input box widget. + * + * An input box is a widget that displays a value, which can be + * selected to enter a new value. + * + * Input box widgets can be of an integer or string type. + */ + +struct txt_inputbox_s +{ + txt_widget_t widget; + char *buffer; + size_t buffer_len; + unsigned int size; + int editing; + void *value; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new input box widget for controlling a string value. + * + * @param value Pointer to a string variable that contains + * a pointer to the current value of the + * input box. The value should be allocated + * dynamically; when the string is changed it + * will be freed and the variable set to point + * to the new string value. String will be in + * UTF-8 format. + * @param size Width of the input box, in characters. + * @return Pointer to the new input box widget. + */ + +txt_inputbox_t *txt_new_input_box(char **value, int size); + +/** + * Create a new input box widget for controlling an integer value. + * + * @param value Pointer to an integer variable containing + * the value of the input box. + * @param size Width of the input box, in characters. + * @return Pointer to the new input box widget. + */ + +txt_inputbox_t *txt_new_int_input_box(int *value, int size); + +#endif /* TXT_INPUTBOX_H */ diff --git a/games/NXDoom/textscreen/txt_io.c b/games/NXDoom/textscreen/txt_io.c new file mode 100644 index 000000000..f8127f17d --- /dev/null +++ b/games/NXDoom/textscreen/txt_io.c @@ -0,0 +1,197 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_io.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Text mode I/O functions, similar to C stdio + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_io.h" +#include "txt_main.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int g_cur_x = 0; +static int g_cur_y = 0; +static txt_color_t g_fgcolour = TXT_COLOR_GREY; +static txt_color_t g_bgcolour = TXT_COLOR_BLACK; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#if 0 /* UNUSED */ +static void txt_clear_screen(void) +{ + unsigned char *screen; + int i; + + screen = txt_get_screen_data(); + + for (i = 0; i < TXT_SCREEN_W * TXT_SCREEN_H; ++i) + { + screen[i * 2] = ' '; + screen[i * 2 + 1] = (bgcolor << 4) | fgcolor; + } + + cur_x = 0; + cur_y = 0; +} +#endif + +static void new_line(unsigned char *screendata) +{ + int i; + unsigned char *p; + + g_cur_x = 0; + ++g_cur_y; + + if (g_cur_y >= TXT_SCREEN_H) + { + /* Scroll the screen up */ + + g_cur_y = TXT_SCREEN_H - 1; + + memmove(screendata, screendata + TXT_SCREEN_W * 2, + TXT_SCREEN_W * 2 * (TXT_SCREEN_H - 1)); + + /* Clear the bottom line */ + + p = screendata + (TXT_SCREEN_H - 1) * 2 * TXT_SCREEN_W; + + for (i = 0; i < TXT_SCREEN_W; ++i) + { + *p++ = ' '; + *p++ = g_fgcolour | (g_bgcolour << 4); + } + } +} + +static void put_symbol(unsigned char *screendata, int c) +{ + unsigned char *p; + + p = screendata + g_cur_y * TXT_SCREEN_W * 2 + g_cur_x * 2; + + /* Add a new character to the buffer */ + + p[0] = c; + p[1] = g_fgcolour | (g_bgcolour << 4); + + ++g_cur_x; + + if (g_cur_x >= TXT_SCREEN_W) + { + new_line(screendata); + } +} + +static void put_char(unsigned char *screendata, int c) +{ + switch (c) + { + case '\n': + new_line(screendata); + break; + + case '\b': + + /* backspace */ + + --g_cur_x; + if (g_cur_x < 0) g_cur_x = 0; + break; + + default: + put_symbol(screendata, c); + break; + } +} + +/* "Blind" version of txt_putchar() below which doesn't do any interpretation + * of control signals. Just write a particular symbol to the screen buffer. + */ + +void txt_put_symbol(int c) +{ + put_symbol(txt_get_screen_data(), c); +} + +void txt_putchar(int c) +{ + put_char(txt_get_screen_data(), c); +} + +void txt_puts(const char *s) +{ + unsigned char *screen; + const char *p; + + screen = txt_get_screen_data(); + + for (p = s; *p != '\0'; ++p) + { + put_char(screen, *p); + } + + put_char(screen, '\n'); +} + +void txt_goto_xy(int x, int y) +{ + g_cur_x = x; + g_cur_y = y; +} + +void txt_get_xy(int *x, int *y) +{ + *x = g_cur_x; + *y = g_cur_y; +} + +void txt_fgcolour(txt_color_t color) +{ + g_fgcolour = color; +} + +void txt_bgcolour(int color, int blinking) +{ + g_bgcolour = color; + if (blinking) g_bgcolour |= TXT_COLOR_BLINKING; +} + +void txt_save_colours(txt_saved_colors_t *save) +{ + save->bgcolor = g_bgcolour; + save->fgcolor = g_fgcolour; +} + +void txt_restore_colours(txt_saved_colors_t *save) +{ + g_bgcolour = save->bgcolor; + g_fgcolour = save->fgcolor; +} diff --git a/games/NXDoom/textscreen/txt_io.h b/games/NXDoom/textscreen/txt_io.h new file mode 100644 index 000000000..7d91b885b --- /dev/null +++ b/games/NXDoom/textscreen/txt_io.h @@ -0,0 +1,55 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_io.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_IO_H +#define TXT_IO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_main.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef struct +{ + int bgcolor; + int fgcolor; +} txt_saved_colors_t; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_put_symbol(int c); +void txt_putchar(int c); +void txt_puts(const char *s); +void txt_goto_xy(int x, int y); +void txt_get_xy(int *x, int *y); +void txt_fgcolour(txt_color_t color); +void txt_bgcolour(int color, int blinking); +void txt_save_colours(txt_saved_colors_t *save); +void txt_restore_colours(txt_saved_colors_t *save); + +#endif /* TXT_IO_H */ diff --git a/games/NXDoom/textscreen/txt_label.c b/games/NXDoom/textscreen/txt_label.c new file mode 100644 index 000000000..c943b1b50 --- /dev/null +++ b/games/NXDoom/textscreen/txt_label.c @@ -0,0 +1,234 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_label.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_label.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_label_size_calc(TXT_UNCAST_ARG(label)); +static void txt_label_drawer(TXT_UNCAST_ARG(label)); +static void txt_label_destructor(TXT_UNCAST_ARG(label)); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_label_class = +{ + txt_never_selectable, + txt_label_size_calc, + txt_label_drawer, + NULL, + txt_label_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_label_size_calc(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + + label->widget.w = label->w; + label->widget.h = label->h; +} + +static void txt_label_drawer(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + unsigned int x; + unsigned int y; + int origin_x; + int origin_y; + unsigned int align_indent = 0; + unsigned int w; + unsigned int sw; + + w = label->widget.w; + + if (label->bgcolor >= 0) + { + txt_bgcolour(label->bgcolor, 0); + } + if (label->fgcolor >= 0) + { + txt_fgcolour(label->fgcolor); + } + + txt_get_xy(&origin_x, &origin_y); + + for (y = 0; y < label->h; ++y) + { + /* Calculate the amount to indent this line due to the align + * setting + */ + + sw = txt_utf8_strlen(label->lines[y]); + switch (label->widget.align) + { + case TXT_HORIZ_LEFT: + align_indent = 0; + break; + case TXT_HORIZ_CENTER: + align_indent = (label->w - sw) / 2; + break; + case TXT_HORIZ_RIGHT: + align_indent = label->w - sw; + break; + } + + /* Draw this line */ + + txt_goto_xy(origin_x, origin_y + y); + + /* Gap at the start */ + + for (x = 0; x < align_indent; ++x) + { + txt_draw_string(" "); + } + + /* The string itself */ + + txt_draw_string(label->lines[y]); + x += sw; + + /* Gap at the end */ + + for (; x < w; ++x) + { + txt_draw_string(" "); + } + } +} + +static void txt_label_destructor(TXT_UNCAST_ARG(label)) +{ + TXT_CAST_ARG(txt_label_t, label); + + free(label->label); + free(label->lines); +} + +#if 0 /* UNUSED */ +static void txt_set_bg_colour(txt_label_t *label, txt_color_t color) +{ + label->bgcolor = color; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_label(txt_label_t *label, const char *value) +{ + char *p; + unsigned int y; + + /* Free back the old label */ + + free(label->label); + free(label->lines); + + /* Set the new value */ + + label->label = strdup(value); + + /* Work out how many lines in this label */ + + label->h = 1; + + for (p = label->label; *p != '\0'; ++p) + { + if (*p == '\n') + { + ++label->h; + } + } + + /* Split into lines */ + + label->lines = malloc(sizeof(char *) * label->h); + label->lines[0] = label->label; + y = 1; + + for (p = label->label; *p != '\0'; ++p) + { + if (*p == '\n') + { + label->lines[y] = p + 1; + *p = '\0'; + ++y; + } + } + + label->w = 0; + + for (y = 0; y < label->h; ++y) + { + unsigned int line_len; + + line_len = txt_utf8_strlen(label->lines[y]); + + if (line_len > label->w) label->w = line_len; + } +} + +txt_label_t *txt_new_label(const char *text) +{ + txt_label_t *label; + + label = malloc(sizeof(txt_label_t)); + + txt_init_widget(label, &txt_label_class); + label->label = NULL; + label->lines = NULL; + + /* Default colors */ + + label->bgcolor = -1; + label->fgcolor = -1; + + txt_set_label(label, text); + + return label; +} + +void txt_set_fg_colour(txt_label_t *label, txt_color_t color) +{ + label->fgcolor = color; +} diff --git a/games/NXDoom/textscreen/txt_label.h b/games/NXDoom/textscreen/txt_label.h new file mode 100644 index 000000000..f7f1b1033 --- /dev/null +++ b/games/NXDoom/textscreen/txt_label.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_label.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_LABEL_H +#define TXT_LABEL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_main.h" +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * Label widget. + * + * A label widget does nothing except show a text label. + */ + +typedef struct txt_label_s txt_label_t; + +struct txt_label_s +{ + txt_widget_t widget; + char *label; + char **lines; + unsigned int w; + unsigned int h; + int fgcolor; + int bgcolor; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new label widget. + * + * @param label String to display in the widget (UTF-8 format). + * @return Pointer to the new label widget. + */ + +txt_label_t *txt_new_label(const char *label); + +/** + * Set the string displayed in a label widget. + * + * @param label The widget. + * @param value The string to display (UTF-8 format). + */ + +void txt_set_label(txt_label_t *label, const char *value); + +/** + * Set the foreground color of a label widget. + * + * @param label The widget. + * @param color The foreground color to use. + */ + +void txt_set_fg_colour(txt_label_t *label, txt_color_t color); + +#endif /* TXT_LABEL_H */ diff --git a/games/NXDoom/textscreen/txt_main.h b/games/NXDoom/textscreen/txt_main.h new file mode 100644 index 000000000..34a92bde8 --- /dev/null +++ b/games/NXDoom/textscreen/txt_main.h @@ -0,0 +1,255 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_main.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Base interface that abstracts the text mode screen. + * + ****************************************************************************/ + +#ifndef TXT_MAIN_H +#define TXT_MAIN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/* For the moment, txt_sdl.c is the only implementation of the base + * text mode screen API: + */ + +#include "txt_sdl.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* textscreen key values: + * Key values are difficult because we have to support multiple conflicting + * address spaces. + * First, Doom's key constants use 0-127 as ASCII and extra values from + * 128-255 to represent special keys. Second, mouse buttons are represented + * as buttons. Finally, we want to be able to support Unicode. + * + * So we define different ranges: + * 0-255: Doom key constants, including ASCII. + * 256-511: Mouse buttons and other reserved. + * >=512: Unicode values greater than 127 are offset up into this range. + + * Special keypress values that correspond to mouse button clicks + */ + +#define TXT_MOUSE_BASE 256 +#define TXT_MOUSE_LEFT (TXT_MOUSE_BASE + 0) +#define TXT_MOUSE_RIGHT (TXT_MOUSE_BASE + 1) +#define TXT_MOUSE_MIDDLE (TXT_MOUSE_BASE + 2) +#define TXT_MOUSE_SCROLLUP (TXT_MOUSE_BASE + 3) +#define TXT_MOUSE_SCROLLDOWN (TXT_MOUSE_BASE + 4) +#define TXT_MOUSE_X1 (TXT_MOUSE_BASE + 5) +#define TXT_MOUSE_X2 (TXT_MOUSE_BASE + 6) +#define TXT_MAX_MOUSE_BUTTONS 16 + +#define TXT_KEY_TO_MOUSE_BUTTON(x) \ + ((x) >= TXT_MOUSE_BASE && (x) < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS \ + ? (x) - TXT_MOUSE_BASE \ + : -1) + +/* Unicode offset. Unicode values from 128 onwards are offset up into + * this range, so TXT_UNICODE_BASE = Unicode character #128, and so on. + */ + +#define TXT_UNICODE_BASE 512 + +/* Convert a key value to a Unicode character: */ + +#define TXT_KEY_TO_UNICODE(x) \ + ((x) < 128 ? (x) \ + : (x) >= TXT_UNICODE_BASE ? ((x) - TXT_UNICODE_BASE + 128) \ + : 0) + +/* Convert a Unicode character to a key value: */ + +#define TXT_UNICODE_TO_KEY(u) \ + ((u) < 128 ? (u) : ((u) - 128 + TXT_UNICODE_BASE)) + +/* Screen size */ + +#define TXT_SCREEN_W 80 +#define TXT_SCREEN_H 25 + +#define TXT_COLOR_BLINKING (1 << 3) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + TXT_COLOR_BLACK, + TXT_COLOR_BLUE, + TXT_COLOR_GREEN, + TXT_COLOR_CYAN, + TXT_COLOR_RED, + TXT_COLOR_MAGENTA, + TXT_COLOR_BROWN, + TXT_COLOR_GREY, + TXT_COLOR_DARK_GREY, + TXT_COLOR_BRIGHT_BLUE, + TXT_COLOR_BRIGHT_GREEN, + TXT_COLOR_BRIGHT_CYAN, + TXT_COLOR_BRIGHT_RED, + TXT_COLOR_BRIGHT_MAGENTA, + TXT_COLOR_YELLOW, + TXT_COLOR_BRIGHT_WHITE, +} txt_color_t; + +/* Modifier keys. */ + +typedef enum +{ + TXT_MOD_SHIFT, + TXT_MOD_CTRL, + TXT_MOD_ALT, + TXT_NUM_MODIFIERS +} txt_modifier_t; + +/* Due to the way the SDL API works, we provide different ways of configuring + * how we read input events, each of which is useful in different scenarios. + */ + +typedef enum +{ + /* "Localized" output that takes software keyboard layout into account, + * but key shifting has no effect. + */ + + TXT_INPUT_NORMAL, + + /* "Raw" input; the keys correspond to physical keyboard layout and + * software keyboard layout has no effect. + */ + + TXT_INPUT_RAW, + + /* Used for full text input. Events are fully shifted and localized. + * However, not all keyboard keys will generate input. + * Setting this mode may activate the on-screen keyboard, depending on + * device and OS. + */ + + TXT_INPUT_TEXT, +} txt_input_mode_t; + +#ifdef __GNUC__ + +#define PRINTF_ATTR(fmt, first) __attribute__((format(printf, fmt, first))) + +#else /* __GNUC__ */ + +#define PRINTF_ATTR(fmt, first) + +#endif /* __GNUC__ */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialize the screen + * Returns 1 if successful, 0 if failed. + */ + +int txt_init(void); + +/* Shut down text mode emulation */ + +void txt_shutdown(void); + +/* Get a pointer to the buffer containing the raw screen data. */ + +unsigned char *txt_get_screen_data(void); + +/* Update an area of the screen */ + +void txt_update_screen_area(int x, int y, int w, int h); + +/* Update the whole screen */ + +void txt_update_screen(void); + +/* Set the RGB value for a particular entry in the color palette: */ + +void txt_set_colour(txt_color_t color, int r, int g, int b); + +/* Read a character from the keyboard */ + +int txt_getchar(void); + +/* Given a Unicode character, get a character that can be used to represent + * it on the code page being displayed on the screen. If the character cannot + * be represented, this returns -1. + */ + +int txt_unicode_character(unsigned int c); + +/* Read the current state of modifier keys that are held down. */ + +int txt_get_modifier_state(txt_modifier_t mod); + +/* Provides a short description of a key code, placing into the provided + * buffer. Note that the key is assumed to represent a physical key on the + * keyboard (like that returned by TXT_INPUT_RAW), and the resulting string + * takes keyboard layout into consideration. For example, + * txt_get_key_description('q') on a French keyboard returns "A". + * The contents of the filled buffer will be in UTF-8 format, but will never + * contain characters which can't be shown on the screen. + */ + +void txt_get_key_description(int key, char *buf, size_t buf_len); + +/* Retrieve the current position of the mouse */ + +void txt_get_mouse_position(int *x, int *y); + +/* Sleep until an event is received or the screen needs updating + * Optional timeout in ms (timeout == 0 : sleep forever) + */ + +void txt_sleep(int timeout); + +/* Change mode for text input. */ + +void txt_set_input_mode(txt_input_mode_t mode); + +/* Set the window title of the window containing the text mode screen */ + +void txt_set_window_title(const char *title); + +/* Safe string copy. */ + +void txt_string_copy(char *dest, const char *src, size_t dest_len); + +/* Safe version of vsnprintf(). */ + +int txt_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args); + +/* Safe version of snprintf(). */ + +int txt_snprintf(char *buf, size_t buf_len, const char *s, ...) + PRINTF_ATTR(3, 4); + +#endif /* TXT_MAIN_H */ diff --git a/games/NXDoom/textscreen/txt_radiobutton.c b/games/NXDoom/textscreen/txt_radiobutton.c new file mode 100644 index 000000000..b13c617c9 --- /dev/null +++ b/games/NXDoom/textscreen/txt_radiobutton.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_radiobutton.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_radiobutton.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_radio_button_size_calc(TXT_UNCAST_ARG(radiobutton)); +static void txt_radio_button_drawer(TXT_UNCAST_ARG(radiobutton)); +static void txt_radio_button_destructor(TXT_UNCAST_ARG(radiobutton)); +static int txt_radio_button_keypress(TXT_UNCAST_ARG(radiobutton), int key); +static void txt_radio_button_mousepress(TXT_UNCAST_ARG(radiobutton), int x, + int y, int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_radiobutton_class = +{ + txt_always_selectable, + txt_radio_button_size_calc, + txt_radio_button_drawer, + txt_radio_button_keypress, + txt_radio_button_destructor, + txt_radio_button_mousepress, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_radio_button_size_calc(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + /* Minimum width is the string length + right-side spaces for padding */ + + radiobutton->widget.w = txt_utf8_strlen(radiobutton->label) + 5; + radiobutton->widget.h = 1; +} + +static void txt_radio_button_drawer(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + txt_saved_colors_t colors; + int i; + int w; + + w = radiobutton->widget.w; + + txt_save_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("("); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + if (*radiobutton->variable == radiobutton->value) + { + txt_draw_code_page_string("\x07"); + } + else + { + txt_draw_string(" "); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + + txt_draw_string(") "); + + txt_restore_colours(&colors); + txt_set_widget_bg(radiobutton); + + txt_draw_string(radiobutton->label); + + for (i = txt_utf8_strlen(radiobutton->label); i < w - 5; ++i) + { + txt_draw_string(" "); + } +} + +static void txt_radio_button_destructor(TXT_UNCAST_ARG(radiobutton)) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + free(radiobutton->label); +} + +static int txt_radio_button_keypress(TXT_UNCAST_ARG(radiobutton), int key) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + if (key == KEY_ENTER || key == ' ') + { + if (*radiobutton->variable != radiobutton->value) + { + *radiobutton->variable = radiobutton->value; + txt_emit_signal(radiobutton, "selected"); + } + + return 1; + } + + return 0; +} + +static void txt_radio_button_mousepress(TXT_UNCAST_ARG(radiobutton), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_radiobutton_t, radiobutton); + + if (b == TXT_MOUSE_LEFT) + { + /* Equivalent to pressing enter */ + + txt_radio_button_keypress(radiobutton, KEY_ENTER); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_radiobutton_t *txt_new_radio_button(const char *label, int *variable, + int value) +{ + txt_radiobutton_t *radiobutton; + + radiobutton = malloc(sizeof(txt_radiobutton_t)); + + txt_init_widget(radiobutton, &txt_radiobutton_class); + radiobutton->label = strdup(label); + radiobutton->variable = variable; + radiobutton->value = value; + + return radiobutton; +} + +#if 0 /* UNUSED */ +static void txt_set_radio_button_label(txt_radiobutton_t *radiobutton, + const char *value) +{ + free(radiobutton->label); + radiobutton->label = strdup(value); +} +#endif diff --git a/games/NXDoom/textscreen/txt_radiobutton.h b/games/NXDoom/textscreen/txt_radiobutton.h new file mode 100644 index 000000000..f5cafb6c8 --- /dev/null +++ b/games/NXDoom/textscreen/txt_radiobutton.h @@ -0,0 +1,77 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_radiobutton.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_RADIOBUTTON_H +#define TXT_RADIOBUTTON_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* A radio button widget. + * + * Radio buttons are typically used in groups, to allow a value to be + * selected from a range of options. Each radio button corresponds + * to a particular option that may be selected. A radio button + * has an indicator to indicate whether it is the currently-selected + * value, and a text label. + * + * Internally, a radio button tracks an integer variable that may take + * a range of different values. Each radio button has a particular + * value associated with it; if the variable is equal to that value, + * the radio button appears selected. If a radio button is pressed + * by the user through the GUI, the variable is set to its value. + * + * When a radio button is selected, the "selected" signal is emitted. + */ + +struct txt_radiobutton_s +{ + txt_widget_t widget; + char *label; + int *variable; + int value; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new radio button widget. + * + * @param label The label to display next to the radio button + * (UTF-8 format). + * @param variable Pointer to the variable tracking whether this + * radio button is selected. + * @param value If the variable is equal to this value, the + * radio button appears selected. + * @return Pointer to the new radio button widget. + */ + +txt_radiobutton_t *txt_new_radio_button(const char *label, int *variable, + int value); + +#endif /* TXT_RADIOBUTTON_H */ diff --git a/games/NXDoom/textscreen/txt_scrollpane.c b/games/NXDoom/textscreen/txt_scrollpane.c new file mode 100644 index 000000000..4fa3b7184 --- /dev/null +++ b/games/NXDoom/textscreen/txt_scrollpane.c @@ -0,0 +1,638 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_scrollpane.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_scrollpane.h" +#include "txt_table.h" + +#include "doomkeys.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SCROLLBAR_VERTICAL (1 << 0) +#define SCROLLBAR_HORIZONTAL (1 << 1) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int txt_scroll_pane_selectable(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_size_calc(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_drawer(TXT_UNCAST_ARG(scrollpane)); +static int txt_scroll_pane_keypress(TXT_UNCAST_ARG(scrollpane), int key); +static void txt_scroll_pane_destructor(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_mousepress(TXT_UNCAST_ARG(scrollpane), int x, + int y, int b); +static void txt_scroll_pane_layout(TXT_UNCAST_ARG(scrollpane)); +static void txt_scroll_pane_focus(TXT_UNCAST_ARG(scrollpane), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_scrollpane_class = +{ + txt_scroll_pane_selectable, txt_scroll_pane_size_calc, + txt_scroll_pane_drawer, txt_scroll_pane_keypress, + txt_scroll_pane_destructor, txt_scroll_pane_mousepress, + txt_scroll_pane_layout, txt_scroll_pane_focus, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int full_width(txt_scrollpane_t *scrollpane) +{ + if (scrollpane->child != NULL) + { + return scrollpane->child->w; + } + else + { + return 0; + } +} + +static int full_height(txt_scrollpane_t *scrollpane) +{ + if (scrollpane->child != NULL) + { + return scrollpane->child->h; + } + else + { + return 0; + } +} + +/* Calculate which scroll bars the pane needs. */ + +static int needs_scroll_bars(txt_scrollpane_t *scrollpane) +{ + int result; + + result = 0; + + if (full_width(scrollpane) > scrollpane->w) + { + result |= SCROLLBAR_HORIZONTAL; + } + + if (full_height(scrollpane) > scrollpane->h) + { + result |= SCROLLBAR_VERTICAL; + } + + return result; +} + +/* If a scrollbar isn't needed, the scroll position is reset. */ + +static void sanity_check_scrollbars(txt_scrollpane_t *scrollpane) +{ + int scrollbars; + int max_x; + int max_y; + + scrollbars = needs_scroll_bars(scrollpane); + + if ((scrollbars & SCROLLBAR_HORIZONTAL) == 0) + { + scrollpane->x = 0; + } + + if ((scrollbars & SCROLLBAR_VERTICAL) == 0) + { + scrollpane->y = 0; + } + + max_x = full_width(scrollpane) - scrollpane->w; + max_y = full_height(scrollpane) - scrollpane->h; + + if (scrollpane->x < 0) + { + scrollpane->x = 0; + } + else if (scrollpane->x > max_x) + { + scrollpane->x = max_x; + } + + if (scrollpane->y < 0) + { + scrollpane->y = 0; + } + else if (scrollpane->y > max_y) + { + scrollpane->y = max_y; + } +} + +static void txt_scroll_pane_size_calc(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int scrollbars; + + if (scrollpane->child != NULL) + { + txt_calc_widget_size(scrollpane->child); + } + + /* Expand as necessary (to ensure that no scrollbars are needed)? */ + + if (scrollpane->expand_w) + { + scrollpane->w = full_width(scrollpane); + } + + if (scrollpane->expand_h) + { + scrollpane->h = full_height(scrollpane); + } + + scrollpane->widget.w = scrollpane->w; + scrollpane->widget.h = scrollpane->h; + + /* If we have scroll bars, we need to expand slightly to + * accommodate them. Eg. if we have a vertical scrollbar, we + * need to be an extra character wide. + */ + + scrollbars = needs_scroll_bars(scrollpane); + + if (scrollbars & SCROLLBAR_HORIZONTAL) + { + ++scrollpane->widget.h; + } + + if (scrollbars & SCROLLBAR_VERTICAL) + { + ++scrollpane->widget.w; + } + + if (scrollpane->child != NULL) + { + if (scrollpane->child->w < scrollpane->w) + { + scrollpane->child->w = scrollpane->w; + } + + if (scrollpane->child->h < scrollpane->h) + { + scrollpane->child->h = scrollpane->h; + } + } +} + +static void txt_scroll_pane_drawer(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int x1; + int y1; + int x2; + int y2; + int scrollbars; + + /* We set a clipping area of the scroll pane. */ + + x1 = scrollpane->widget.x; + y1 = scrollpane->widget.y; + x2 = x1 + scrollpane->w; + y2 = y1 + scrollpane->h; + + scrollbars = needs_scroll_bars(scrollpane); + + if (scrollbars & SCROLLBAR_HORIZONTAL) + { + txt_draw_horiz_scrollbar(x1, y1 + scrollpane->h, scrollpane->w, + scrollpane->x, + full_width(scrollpane) - scrollpane->w); + } + + if (scrollbars & SCROLLBAR_VERTICAL) + { + txt_draw_vert_scrollbar(x1 + scrollpane->w, y1, scrollpane->h, + scrollpane->y, + full_height(scrollpane) - scrollpane->h); + } + + txt_push_clip_area(x1, x2, y1, y2); + + /* Draw the child widget */ + + if (scrollpane->child != NULL) + { + txt_draw_widget(scrollpane->child); + } + + /* Restore old clipping area. */ + + txt_pop_clip_area(); +} + +static void txt_scroll_pane_destructor(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + if (scrollpane->child != NULL) + { + txt_destroy_widget(scrollpane->child); + } +} + +static void txt_scroll_pane_focus(TXT_UNCAST_ARG(scrollpane), int focused) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + /* Whether the child is focused depends only on whether the scroll pane + * itself is focused. Pass through focus to the child. + */ + + if (scrollpane->child != NULL) + { + txt_set_widget_focus(scrollpane->child, focused); + } +} + +/* Hack for tables - when browsing a table inside a scroll pane, + * automatically scroll the window to show the newly-selected + * item. + */ + +static void show_selected_widget(txt_scrollpane_t *scrollpane) +{ + txt_widget_t *selected; + + selected = txt_get_selected_widget(scrollpane->child); + + /* Scroll up or down? */ + + if (selected->y <= scrollpane->widget.y) + { + scrollpane->y -= scrollpane->widget.y - selected->y; + } + else if ((signed)(selected->y + selected->h) > + (signed)(scrollpane->widget.y + scrollpane->h)) + { + scrollpane->y += (selected->y + selected->h) - + (scrollpane->widget.y + scrollpane->h); + } + + /* Scroll left or right? */ + + if (selected->x <= scrollpane->widget.x) + { + scrollpane->x -= scrollpane->widget.x - selected->x; + } + else if ((signed)(selected->x + selected->w) > + (signed)(scrollpane->widget.x + scrollpane->w)) + { + scrollpane->x += (selected->x + selected->w) - + (scrollpane->widget.x + scrollpane->w); + } +} + +/* Another hack for tables - when scrolling in 'pages', the normal key press + * event does not provide children with enough information to know how far + * to move their selection to reach a new page. This function does so. + * Note that it *only* affects scrolling in pages, not with arrows! + * A side-effect of this, rather than 'pulling' the selection to fit within + * the new page, is that we will jump straight over ranges of unselectable + * items longer than a page, but that is also true of arrow-key scrolling. + * The other unfortunate effect of doing things this way is that page keys + * have no effect on tables _not_ in scrollpanes: not even home/end. + */ + +static int page_selected_widget(txt_scrollpane_t *scrollpane, int key) +{ + int pagex = 0; /* No page left/right yet, but some keyboards have them */ + int pagey = 0; + + /* Subtract one from the absolute page distance as this is slightly more + * intuitive: a page down first jumps to the bottom of the current page, + * then proceeds to scroll onwards. + */ + + switch (key) + { + case KEY_PGUP: + pagey = 1 - scrollpane->h; + break; + + case KEY_PGDN: + pagey = scrollpane->h - 1; + break; + + default: /* We shouldn't even be in this function */ + return 0; + } + + if (scrollpane->child->widget_class == &txt_table_class) + { + return txt_page_table(scrollpane->child, pagex, pagey); + } + + return 0; +} + +/* Interpret arrow key presses as scroll commands */ + +static int interpret_scroll_key(txt_scrollpane_t *scrollpane, int key) +{ + int maxy; + + switch (key) + { + case KEY_UPARROW: + if (scrollpane->y > 0) + { + --scrollpane->y; + return 1; + } + break; + + case KEY_DOWNARROW: + if (scrollpane->y < full_height(scrollpane) - scrollpane->h) + { + ++scrollpane->y; + return 1; + } + break; + + case KEY_LEFTARROW: + if (scrollpane->x > 0) + { + --scrollpane->x; + return 1; + } + break; + + case KEY_RIGHTARROW: + if (scrollpane->x < full_width(scrollpane) - scrollpane->w) + { + ++scrollpane->x; + return 1; + } + break; + + case KEY_PGUP: + if (scrollpane->y > 0) + { + scrollpane->y -= scrollpane->h; + if (scrollpane->y < 0) + { + scrollpane->y = 0; + } + + return 1; + } + break; + + case KEY_PGDN: + maxy = full_height(scrollpane) - scrollpane->h; + if (scrollpane->y < maxy) + { + scrollpane->y += scrollpane->h; + if (scrollpane->y > maxy) + { + scrollpane->y = maxy; + } + + return 1; + } + break; + + default: + break; + } + + return 0; +} + +static int txt_scroll_pane_keypress(TXT_UNCAST_ARG(scrollpane), int key) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int result; + + result = 0; + + if (scrollpane->child != NULL) + { + result = txt_widget_key_press(scrollpane->child, key); + + /* Gross hack - if we're scrolling in a menu with the keyboard, + * automatically move the scroll pane to show the new + * selected item. + */ + + if ((key == KEY_UPARROW || key == KEY_DOWNARROW || + key == KEY_LEFTARROW || key == KEY_RIGHTARROW || + key == KEY_PGUP || key == KEY_PGDN || key == KEY_TAB) && + scrollpane->child->widget_class == &txt_table_class) + { + if (page_selected_widget(scrollpane, key)) + { + result = 1; + } + + show_selected_widget(scrollpane); + } + + /* If the child widget didn't use the keypress, we can see + * if it can be interpreted as a scrolling command. + */ + + if (result == 0) + { + result = interpret_scroll_key(scrollpane, key); + } + } + + return result; +} + +static void txt_scroll_pane_mousepress(TXT_UNCAST_ARG(scrollpane), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + int scrollbars; + int rel_x; + int rel_y; + + scrollbars = needs_scroll_bars(scrollpane); + + if (b == TXT_MOUSE_SCROLLUP) + { + if (scrollbars & SCROLLBAR_VERTICAL) + { + scrollpane->y -= 3; + } + else if (scrollbars & SCROLLBAR_HORIZONTAL) + { + scrollpane->x -= 3; + } + + return; + } + else if (b == TXT_MOUSE_SCROLLDOWN) + { + if (scrollbars & SCROLLBAR_VERTICAL) + { + scrollpane->y += 3; + } + else if (scrollbars & SCROLLBAR_HORIZONTAL) + { + scrollpane->x += 3; + } + + return; + } + + rel_x = x - scrollpane->widget.x; + rel_y = y - scrollpane->widget.y; + + /* Click on the horizontal scrollbar? */ + + if ((scrollbars & SCROLLBAR_HORIZONTAL) && rel_y == scrollpane->h) + { + if (rel_x == 0) + { + --scrollpane->x; + } + else if (rel_x == scrollpane->w - 1) + { + ++scrollpane->x; + } + else + { + int range = full_width(scrollpane) - scrollpane->w; + int bar_max = scrollpane->w - 3; + + scrollpane->x = ((rel_x - 1) * range + bar_max - 1) / bar_max; + } + + return; + } + + /* Click on the vertical scrollbar? */ + + if ((scrollbars & SCROLLBAR_VERTICAL) && rel_x == scrollpane->w) + { + if (rel_y == 0) + { + --scrollpane->y; + } + else if (rel_y == scrollpane->h - 1) + { + ++scrollpane->y; + } + else + { + int range = full_height(scrollpane) - scrollpane->h; + int bar_max = scrollpane->h - 3; + + scrollpane->y = ((rel_y - 1) * range + bar_max - 1) / bar_max; + } + + return; + } + + if (scrollpane->child != NULL) + { + txt_widget_mouse_press(scrollpane->child, x, y, b); + } +} + +static void txt_scroll_pane_layout(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + sanity_check_scrollbars(scrollpane); + + /* The child widget takes the same position as the scroll pane + * itself, but is offset by the scroll position. + */ + + if (scrollpane->child != NULL) + { + scrollpane->child->x = scrollpane->widget.x - scrollpane->x; + scrollpane->child->y = scrollpane->widget.y - scrollpane->y; + + txt_layout_widget(scrollpane->child); + } +} + +static int txt_scroll_pane_selectable(TXT_UNCAST_ARG(scrollpane)) +{ + TXT_CAST_ARG(txt_scrollpane_t, scrollpane); + + /* If scroll bars are displayed, the scroll pane must be selectable + * so that we can use the arrow keys to scroll around. + */ + + if (needs_scroll_bars(scrollpane)) + { + return 1; + } + + /* Otherwise, whether this is selectable depends on the child widget. */ + + return txt_selectable_widget(scrollpane->child); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_scrollpane_t *txt_new_scrollpane(int w, int h, TXT_UNCAST_ARG(target)) +{ + TXT_CAST_ARG(txt_widget_t, target); + txt_scrollpane_t *scrollpane; + + scrollpane = malloc(sizeof(txt_scrollpane_t)); + txt_init_widget(scrollpane, &txt_scrollpane_class); + scrollpane->w = w; + scrollpane->h = h; + scrollpane->x = 0; + scrollpane->y = 0; + scrollpane->child = target; + scrollpane->expand_w = w <= 0; + scrollpane->expand_h = h <= 0; + + /* Set parent pointer for inner widget. */ + + target->parent = &scrollpane->widget; + + return scrollpane; +} diff --git a/games/NXDoom/textscreen/txt_scrollpane.h b/games/NXDoom/textscreen/txt_scrollpane.h new file mode 100644 index 000000000..831a014d9 --- /dev/null +++ b/games/NXDoom/textscreen/txt_scrollpane.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_scrollpane.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + ****************************************************************************/ + +#ifndef TXT_SCROLLPANE_H +#define TXT_SCROLLPANE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Scrollable pane widget. + * + * A scrollable pane widget is a widget that contains another widget + * that is larger than it. Scroll bars appear on the side to allow + * different areas of the contained widget to be seen. + */ + +struct txt_scrollpane_s +{ + txt_widget_t widget; + int w; + int h; + int x; + int y; + int expand_w; + int expand_h; + txt_widget_t *child; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new scroll pane widget. + * + * @param w Width of the scroll pane, in characters. + * @param h Height of the scroll pane, in lines. + * @param target The target widget that the scroll pane will + * contain. + * @return Pointer to the new scroll pane widget. + */ + +txt_scrollpane_t *txt_new_scrollpane(int w, int h, TXT_UNCAST_ARG(target)); + +#endif /* TXT_SCROLLPANE_H */ diff --git a/games/NXDoom/textscreen/txt_sdl.c b/games/NXDoom/textscreen/txt_sdl.c new file mode 100644 index 000000000..c531f6cbe --- /dev/null +++ b/games/NXDoom/textscreen/txt_sdl.c @@ -0,0 +1,264 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_sdl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_main.h" +#include "txt_sdl.h" +#include "txt_utf8.h" + +/* Fonts: */ + +#include "txt_font.h" +#include "fonts/codepage.h" +#include "fonts/large.h" +#include "fonts/normal.h" +#include "fonts/small.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Time between character blinks in ms */ + +#define BLINK_PERIOD 250 + +/* XXX: duplicate from doomtype.h */ + +#define arrlen(array) (sizeof(array) / sizeof(*array)) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static unsigned char *screendata; + +/* Unicode key mapping; see codepage.h. */ + +static const short g_code_page_to_unicode[] = CODE_PAGE_TO_UNICODE; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Searches the desktop screen buffer to determine whether there are any + * blinking characters. + */ + +#if 0 /* UNUSED */ +static int txt_has_blinking_chars(void) +{ + int x; + int y; + unsigned char *p; + + /* Check all characters in screen buffer */ + + for (y = 0; y < TXT_SCREEN_H; ++y) + { + for (x = 0; x < TXT_SCREEN_W; ++x) + { + p = &screendata[(y * TXT_SCREEN_W + x) * 2]; + + if (p[1] & 0x80) + { + return 1; /* This character is blinking */ + } + } + } + + /* None found */ + + return 0; +} + +static void txt_string_concat(char *dest, const char *src, size_t dest_len) +{ + size_t offset; + + offset = strlen(dest); + if (offset > dest_len) + { + offset = dest_len; + } + + txt_string_copy(dest + offset, src, dest_len - offset); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Initialize text mode screen + * + * Returns 1 if successful, 0 if an error occurred + */ + +int txt_init(void) +{ + return 1; +} + +void txt_shutdown(void) +{ +} + +void txt_set_colour(txt_color_t color, int r, int g, int b) +{ +} + +unsigned char *txt_get_screen_data(void) +{ + return screendata; +} + +void txt_update_screen_area(int x, int y, int w, int h) +{ +} + +void txt_update_screen(void) +{ + txt_update_screen_area(0, 0, TXT_SCREEN_W, TXT_SCREEN_H); +} + +void txt_get_mouse_position(int *x, int *y) +{ +} + +signed int txt_getchar(void) +{ + return -1; +} + +int txt_get_modifier_state(txt_modifier_t mod) +{ + return 0; +} + +int txt_unicode_character(unsigned int c) +{ + unsigned int i; + + /* Check the code page mapping to see if this character maps + * to anything. + */ + + for (i = 0; i < arrlen(g_code_page_to_unicode); ++i) + { + if (g_code_page_to_unicode[i] == c) + { + return i; + } + } + + return -1; +} + +void txt_get_key_description(int key, char *buf, size_t buf_len) +{ +} + +/* Sleeps until an event is received, the screen needs to be redrawn, + * or until timeout expires (if timeout != 0) + */ + +void txt_sleep(int timeout) +{ +} + +void txt_set_input_mode(txt_input_mode_t mode) +{ +} + +void txt_set_window_title(const char *title) +{ +} + +void txt_sdl_set_event_callback(void *user_data) +{ +} + +/* Safe string functions. */ + +void txt_string_copy(char *dest, const char *src, size_t dest_len) +{ + if (dest_len < 1) + { + return; + } + + dest[dest_len - 1] = '\0'; + strncpy(dest, src, dest_len - 1); +} + +/* Safe, portable vsnprintf(). */ + +int txt_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args) +{ + int result; + + if (buf_len < 1) + { + return 0; + } + + /* Windows (and other OSes?) has a vsnprintf() that doesn't always + * append a trailing \0. So we must do it, and write into a buffer + * that is one byte shorter; otherwise this function is unsafe. + */ + + result = vsnprintf(buf, buf_len, s, args); + + /* If truncated, change the final char in the buffer to a \0. + * A negative result indicates a truncated buffer on Windows. + */ + + if (result < 0 || result >= buf_len) + { + buf[buf_len - 1] = '\0'; + result = buf_len - 1; + } + + return result; +} + +/* Safe, portable snprintf(). */ + +int txt_snprintf(char *buf, size_t buf_len, const char *s, ...) +{ + va_list args; + int result; + va_start(args, s); + result = txt_vsnprintf(buf, buf_len, s, args); + va_end(args); + return result; +} diff --git a/games/NXDoom/textscreen/txt_sdl.h b/games/NXDoom/textscreen/txt_sdl.h new file mode 100644 index 000000000..1de6af329 --- /dev/null +++ b/games/NXDoom/textscreen/txt_sdl.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_sdl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Text mode emulation in SDL + * + ****************************************************************************/ + +#ifndef TXT_SDL_H +#define TXT_SDL_H + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Set a callback function to call in the SDL event loop. Useful for + * intercepting events. Pass callback=NULL to clear an existing + * callback function. + * user_data is a void pointer to be passed to the callback function. + */ + +void txt_sdl_set_event_callback(void *user_data); + +#endif /* TXT_SDL_H */ diff --git a/games/NXDoom/textscreen/txt_separator.c b/games/NXDoom/textscreen/txt_separator.c new file mode 100644 index 000000000..fc0e1f6eb --- /dev/null +++ b/games/NXDoom/textscreen/txt_separator.c @@ -0,0 +1,145 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_separator.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_separator_size_calc(TXT_UNCAST_ARG(separator)); +static void txt_separator_drawer(TXT_UNCAST_ARG(separator)); +static void txt_separator_destructor(TXT_UNCAST_ARG(separator)); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_separator_class = +{ + txt_never_selectable, + txt_separator_size_calc, + txt_separator_drawer, + NULL, + txt_separator_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_separator_size_calc(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + + if (separator->label != NULL) + { + /* Minimum width is the string length + two spaces for padding */ + + separator->widget.w = txt_utf8_strlen(separator->label) + 2; + } + else + { + separator->widget.w = 0; + } + + separator->widget.h = 1; +} + +static void txt_separator_drawer(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + int x; + int y; + int w; + + w = separator->widget.w; + + txt_get_xy(&x, &y); + + /* Draw separator. Go back one character and draw two extra + * to overlap the window borders. + */ + + txt_draw_separator(x - 2, y, w + 4); + + if (separator->label != NULL) + { + txt_goto_xy(x, y); + + txt_fgcolour(TXT_COLOR_BRIGHT_GREEN); + txt_draw_string(" "); + txt_draw_string(separator->label); + txt_draw_string(" "); + } +} + +static void txt_separator_destructor(TXT_UNCAST_ARG(separator)) +{ + TXT_CAST_ARG(txt_separator_t, separator); + + free(separator->label); +} + +static void txt_set_separator_label(txt_separator_t *separator, + const char *label) +{ + free(separator->label); + + if (label != NULL) + { + separator->label = strdup(label); + } + else + { + separator->label = NULL; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_separator_t *txt_new_separator(const char *label) +{ + txt_separator_t *separator; + + separator = malloc(sizeof(txt_separator_t)); + + txt_init_widget(separator, &txt_separator_class); + + separator->label = NULL; + txt_set_separator_label(separator, label); + + return separator; +} diff --git a/games/NXDoom/textscreen/txt_separator.h b/games/NXDoom/textscreen/txt_separator.h new file mode 100644 index 000000000..79be2f248 --- /dev/null +++ b/games/NXDoom/textscreen/txt_separator.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_separator.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_SEPARATOR_H +#define TXT_SEPARATOR_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Horizontal separator. + * + * A horizontal separator appears as a horizontal line divider across + * the length of the window in which it is added. An optional label + * allows the separator to be used as a section divider for grouping + * related controls. + */ + +struct txt_separator_s +{ + txt_widget_t widget; + char *label; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern txt_widget_class_t txt_separator_class; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new horizontal separator widget. + * + * @param label Label to display on the separator (UTF-8 format). + * If this is set to NULL, no label is displayed. + * @return The new separator widget. + */ + +txt_separator_t *txt_new_separator(const char *label); + +#endif /* TXT_SEPARATOR_H */ diff --git a/games/NXDoom/textscreen/txt_spinctrl.c b/games/NXDoom/textscreen/txt_spinctrl.c new file mode 100644 index 000000000..2838cb49b --- /dev/null +++ b/games/NXDoom/textscreen/txt_spinctrl.c @@ -0,0 +1,460 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_spinctrl.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_spinctrl.h" +#include "txt_utf8.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_spin_control_size_calc(TXT_UNCAST_ARG(spincontrol)); +static void txt_spin_control_drawer(TXT_UNCAST_ARG(spincontrol)); +static int txt_spincontrol_keypress(TXT_UNCAST_ARG(spincontrol), int key); +static void txt_spin_control_destructor(TXT_UNCAST_ARG(spincontrol)); +static void txt_spincontrol_mousepress(TXT_UNCAST_ARG(spincontrol), int x, + int y, int b); +static void txt_spincontrol_focused(TXT_UNCAST_ARG(spincontrol), + int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_spincontrol_class = +{ + txt_always_selectable, + txt_spin_control_size_calc, + txt_spin_control_drawer, + txt_spincontrol_keypress, + txt_spin_control_destructor, + txt_spincontrol_mousepress, + NULL, + txt_spincontrol_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Generate the format string to be used for displaying floats */ + +static void float_format_string(float step, char *buf, size_t buf_len) +{ + int precision; + + precision = (int)ceil(-log(step) / log(10)); + + if (precision > 0) + { + txt_snprintf(buf, buf_len, "%%.%if", precision); + } + else + { + txt_string_copy(buf, "%.1f", buf_len); + } +} + +/* Number of characters needed to represent a character */ + +static unsigned int int_width(int val) +{ + char buf[25]; + + txt_snprintf(buf, sizeof(buf), "%i", val); + + return strlen(buf); +} + +static unsigned int float_width(float val, float step) +{ + unsigned int precision; + unsigned int result; + + /* Calculate the width of the int value */ + + result = int_width((int)val); + + /* Add a decimal part if the precision specifies it */ + + precision = (unsigned int)ceil(-log(step) / log(10)); + + if (precision > 0) + { + result += precision + 1; + } + + return result; +} + +/* Returns the minimum width of the input box */ + +static unsigned int spin_control_width(txt_spincontrol_t *spincontrol) +{ + unsigned int minw; + unsigned int maxw; + + switch (spincontrol->type) + { + case TXT_SPINCONTROL_FLOAT: + minw = float_width(spincontrol->min.f, spincontrol->step.f); + maxw = float_width(spincontrol->max.f, spincontrol->step.f); + break; + + default: + case TXT_SPINCONTROL_INT: + minw = int_width(spincontrol->min.i); + maxw = int_width(spincontrol->max.i); + break; + } + + /* Choose the wider of the two values. Add one so that there is always + * space for the cursor when editing. + */ + + if (minw > maxw) + { + return minw; + } + else + { + return maxw; + } +} + +static void txt_spin_control_size_calc(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + spincontrol->widget.w = spin_control_width(spincontrol) + 5; + spincontrol->widget.h = 1; +} + +static void set_buffer(txt_spincontrol_t *spincontrol) +{ + char format[25]; + + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + txt_snprintf(spincontrol->buffer, spincontrol->buffer_len, "%i", + spincontrol->value->i); + break; + + case TXT_SPINCONTROL_FLOAT: + float_format_string(spincontrol->step.f, format, sizeof(format)); + txt_snprintf(spincontrol->buffer, spincontrol->buffer_len, format, + spincontrol->value->f); + break; + } +} + +static void txt_spin_control_drawer(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + unsigned int i; + unsigned int padding; + txt_saved_colors_t colors; + int bw; + int focused; + + focused = spincontrol->widget.focused; + + txt_save_colours(&colors); + + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_code_page_string("\x1b "); + + txt_restore_colours(&colors); + + /* Choose background color */ + + if (focused && spincontrol->editing) + { + txt_bgcolour(TXT_COLOR_BLACK, 0); + } + else + { + txt_set_widget_bg(spincontrol); + } + + if (!spincontrol->editing) + { + set_buffer(spincontrol); + } + + i = 0; + + bw = txt_utf8_strlen(spincontrol->buffer); + padding = spincontrol->widget.w - bw - 4; + + while (i < padding) + { + txt_draw_string(" "); + ++i; + } + + txt_draw_string(spincontrol->buffer); + i += bw; + + while (i < spincontrol->widget.w - 4) + { + txt_draw_string(" "); + ++i; + } + + txt_restore_colours(&colors); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_code_page_string(" \x1a"); +} + +static void txt_spin_control_destructor(TXT_UNCAST_ARG(spincontrol)) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + free(spincontrol->buffer); +} + +static void add_character(txt_spincontrol_t *spincontrol, int key) +{ + if (txt_utf8_strlen(spincontrol->buffer) < + spin_control_width(spincontrol) && + strlen(spincontrol->buffer) < spincontrol->buffer_len - 2) + { + spincontrol->buffer[strlen(spincontrol->buffer) + 1] = '\0'; + spincontrol->buffer[strlen(spincontrol->buffer)] = key; + } +} + +static void backspace(txt_spincontrol_t *spincontrol) +{ + if (txt_utf8_strlen(spincontrol->buffer) > 0) + { + spincontrol->buffer[strlen(spincontrol->buffer) - 1] = '\0'; + } +} + +static void enforce_limits(txt_spincontrol_t *spincontrol) +{ + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + if (spincontrol->value->i > spincontrol->max.i) + spincontrol->value->i = spincontrol->max.i; + else if (spincontrol->value->i < spincontrol->min.i) + spincontrol->value->i = spincontrol->min.i; + break; + + case TXT_SPINCONTROL_FLOAT: + if (spincontrol->value->f > spincontrol->max.f) + spincontrol->value->f = spincontrol->max.f; + else if (spincontrol->value->f < spincontrol->min.f) + spincontrol->value->f = spincontrol->min.f; + break; + } +} + +static void finish_editing(txt_spincontrol_t *spincontrol) +{ + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i = atoi(spincontrol->buffer); + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f = (float)atof(spincontrol->buffer); + break; + } + + spincontrol->editing = 0; + enforce_limits(spincontrol); +} + +static int txt_spincontrol_keypress(TXT_UNCAST_ARG(spincontrol), int key) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + /* Enter to enter edit mode */ + + if (spincontrol->editing) + { + if (key == KEY_ENTER) + { + finish_editing(spincontrol); + return 1; + } + + if (key == KEY_ESCAPE) + { + /* Abort without saving value */ + + spincontrol->editing = 0; + return 1; + } + + if (isdigit(key) || key == '-' || key == '.') + { + add_character(spincontrol, key); + return 1; + } + + if (key == KEY_BACKSPACE) + { + backspace(spincontrol); + return 1; + } + } + else + { + /* Non-editing mode */ + + if (key == KEY_ENTER) + { + spincontrol->editing = 1; + txt_string_copy(spincontrol->buffer, "", spincontrol->buffer_len); + return 1; + } + + if (key == KEY_LEFTARROW) + { + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i -= spincontrol->step.i; + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f -= spincontrol->step.f; + break; + } + + enforce_limits(spincontrol); + + return 1; + } + + if (key == KEY_RIGHTARROW) + { + switch (spincontrol->type) + { + case TXT_SPINCONTROL_INT: + spincontrol->value->i += spincontrol->step.i; + break; + + case TXT_SPINCONTROL_FLOAT: + spincontrol->value->f += spincontrol->step.f; + break; + } + + enforce_limits(spincontrol); + + return 1; + } + } + + return 0; +} + +static void txt_spincontrol_mousepress(TXT_UNCAST_ARG(spincontrol), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + unsigned int rel_x; + + rel_x = x - spincontrol->widget.x; + + if (rel_x < 2) + { + txt_spincontrol_keypress(spincontrol, KEY_LEFTARROW); + } + else if (rel_x >= spincontrol->widget.w - 2) + { + txt_spincontrol_keypress(spincontrol, KEY_RIGHTARROW); + } +} + +static void txt_spincontrol_focused(TXT_UNCAST_ARG(spincontrol), int focused) +{ + TXT_CAST_ARG(txt_spincontrol_t, spincontrol); + + finish_editing(spincontrol); +} + +static txt_spincontrol_t *txt_base_spincontrol(void) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = malloc(sizeof(txt_spincontrol_t)); + + txt_init_widget(spincontrol, &txt_spincontrol_class); + spincontrol->buffer_len = 25; + spincontrol->buffer = malloc(spincontrol->buffer_len); + txt_string_copy(spincontrol->buffer, "", spincontrol->buffer_len); + spincontrol->editing = 0; + + return spincontrol; +} + +txt_spincontrol_t *txt_newspin_control(int *value, int min, int max) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = txt_base_spincontrol(); + spincontrol->type = TXT_SPINCONTROL_INT; + spincontrol->value = (void *)value; + spincontrol->min.i = min; + spincontrol->max.i = max; + spincontrol->step.i = 1; + set_buffer(spincontrol); + + return spincontrol; +} + +txt_spincontrol_t *txt_new_float_spincontrol(float *value, float min, + float max) +{ + txt_spincontrol_t *spincontrol; + + spincontrol = txt_base_spincontrol(); + spincontrol->type = TXT_SPINCONTROL_FLOAT; + spincontrol->value = (void *)value; + spincontrol->min.f = min; + spincontrol->max.f = max; + spincontrol->step.f = 0.1f; + set_buffer(spincontrol); + + return spincontrol; +} diff --git a/games/NXDoom/textscreen/txt_spinctrl.h b/games/NXDoom/textscreen/txt_spinctrl.h new file mode 100644 index 000000000..d6f663ed0 --- /dev/null +++ b/games/NXDoom/textscreen/txt_spinctrl.h @@ -0,0 +1,94 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_spinctrl.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_SPINCONTROL_H +#define TXT_SPINCONTROL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef enum +{ + TXT_SPINCONTROL_INT, + TXT_SPINCONTROL_FLOAT, +} txt_spincontrol_type_t; + +union _float_n_int +{ + float f; + int i; +}; + +/* Spin control widget. + * + * A spin control widget works as an input box that can be used to + * set numeric values, but also has buttons that allow its value + * to be increased or decreased. + */ + +struct txt_spincontrol_s +{ + txt_widget_t widget; + txt_spincontrol_type_t type; + union _float_n_int min; + union _float_n_int max; + union _float_n_int *value; + union _float_n_int step; + int editing; + char *buffer; + size_t buffer_len; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new spin control widget tracking an integer value. + * + * @param value Pointer to the variable containing the value + * displayed in the widget. + * @param min Minimum value that may be set. + * @param max Maximum value that may be set. + * @return Pointer to the new spin control widget. + */ + +txt_spincontrol_t *txt_newspin_control(int *value, int min, int max); + +/** + * Create a new spin control widget tracking a float value. + * + * @param value Pointer to the variable containing the value + * displayed in the widget. + * @param min Minimum value that may be set. + * @param max Maximum value that may be set. + * @return Pointer to the new spin control widget. + */ + +txt_spincontrol_t *txt_new_float_spincontrol(float *value, float min, + float max); + +#endif /* TXT_SPINCONTROL_H */ diff --git a/games/NXDoom/textscreen/txt_strut.c b/games/NXDoom/textscreen/txt_strut.c new file mode 100644 index 000000000..e1da6647e --- /dev/null +++ b/games/NXDoom/textscreen/txt_strut.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_strut.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "doomkeys.h" + +#include "txt_io.h" +#include "txt_main.h" +#include "txt_strut.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +static void txt_strut_size_calc(TXT_UNCAST_ARG(strut)); +static void txt_strut_drawer(TXT_UNCAST_ARG(strut)); +static void txt_strut_destructor(TXT_UNCAST_ARG(strut)); +static int txt_strut_keypress(TXT_UNCAST_ARG(strut), int key); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_strut_class = +{ + txt_never_selectable, + txt_strut_size_calc, + txt_strut_drawer, + txt_strut_keypress, + txt_strut_destructor, + NULL, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_strut_size_calc(TXT_UNCAST_ARG(strut)) +{ + TXT_CAST_ARG(txt_strut_t, strut); + + /* Minimum width is the string length + two spaces for padding */ + + strut->widget.w = strut->width; + strut->widget.h = strut->height; +} + +static void txt_strut_drawer(TXT_UNCAST_ARG(strut)) +{ + /* Nothing is drawn for a strut. */ +} + +static void txt_strut_destructor(TXT_UNCAST_ARG(strut)) +{ +} + +static int txt_strut_keypress(TXT_UNCAST_ARG(strut), int key) +{ + return 0; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +txt_strut_t *txt_new_strut(int width, int height) +{ + txt_strut_t *strut; + + strut = malloc(sizeof(txt_strut_t)); + + txt_init_widget(strut, &txt_strut_class); + strut->width = width; + strut->height = height; + + return strut; +} diff --git a/games/NXDoom/textscreen/txt_strut.h b/games/NXDoom/textscreen/txt_strut.h new file mode 100644 index 000000000..7d988fb03 --- /dev/null +++ b/games/NXDoom/textscreen/txt_strut.h @@ -0,0 +1,60 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_strut.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_STRUT_H +#define TXT_STRUT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Strut widget. + * + * A strut is a widget that takes up a fixed amount of space. It can + * be visualised as a transparent box. Struts are used to provide + * spacing between widgets. + */ + +struct txt_strut_s +{ + txt_widget_t widget; + int width; + int height; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new strut. + * + * @param width Width of the strut, in characters. + * @param height Height of the strut, in characters. + */ + +txt_strut_t *txt_new_strut(int width, int height); + +#endif /* TXT_STRUT_H */ diff --git a/games/NXDoom/textscreen/txt_table.c b/games/NXDoom/textscreen/txt_table.c new file mode 100644 index 000000000..3d77e3731 --- /dev/null +++ b/games/NXDoom/textscreen/txt_table.c @@ -0,0 +1,1330 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_table.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_strut.h" +#include "txt_table.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int txt_table_selectable(TXT_UNCAST_ARG(table)); +static void txt_calc_table_size(TXT_UNCAST_ARG(table)); +static void txt_table_drawer(TXT_UNCAST_ARG(table)); +static int txt_table_keypress(TXT_UNCAST_ARG(table), int key); +static void txt_table_destructor(TXT_UNCAST_ARG(table)); +static void txt_table_mouse_press(TXT_UNCAST_ARG(table), int x, + int y, int b); +static void txt_table_layout(TXT_UNCAST_ARG(table)); +static void txt_table_focused(TXT_UNCAST_ARG(table), int focused); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_t txt_table_overflow_right; +txt_widget_t txt_table_overflow_down; +txt_widget_t txt_table_eol; +txt_widget_t txt_table_empty; + +txt_widget_class_t txt_table_class = +{ + txt_table_selectable, txt_calc_table_size, txt_table_drawer, + txt_table_keypress, txt_table_destructor, txt_table_mouse_press, + txt_table_layout, txt_table_focused, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Returns true if the given widget in the table's widgets[] array refers + * to an actual widget - not NULL, or one of the special overflow pointers. + */ + +static int is_actual_widget(txt_widget_t *widget) +{ + return widget != NULL && widget != &txt_table_overflow_right && + widget != &txt_table_overflow_down; +} + +static void txt_table_destructor(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + + txt_clear_table(table); +} + +static int table_rows(txt_table_t *table) +{ + return (table->num_widgets + table->columns - 1) / table->columns; +} + +/* Most widgets occupy just one cell of a table, but if the special + * overflow constants are used, they can occupy multiple cells. + * This function figures out for a widget in a given cell, which + * cells it should actually occupy (always a rectangle). + */ + +static void cell_overflowed_size(txt_table_t *table, int x, int y, int *w, + int *h) +{ + txt_widget_t *widget; + int x1; + int y1; + + if (!is_actual_widget(table->widgets[y * table->columns + x])) + { + *w = 0; + *h = 0; + return; + } + + *w = table->columns - x; + *h = 0; + for (y1 = y; y1 < table_rows(table); ++y1) + { + /* Every overflow cell must point to either (x, y) or another + * overflow cell. This means the first in every row must be + * txt_table_overflow_down. + */ + + if (y1 * table->columns + x >= table->num_widgets) + { + break; + } + + widget = table->widgets[y1 * table->columns + x]; + + if (y1 != y && widget != &txt_table_overflow_down) + { + break; + } + + for (x1 = x + 1; x1 < x + *w; ++x1) + { + if (y1 * table->columns + x1 >= table->num_widgets) + { + break; + } + + /* Can be either type of overflow, except on the first row. + * Otherwise we impose a limit on the width. + */ + + widget = table->widgets[y1 * table->columns + x1]; + if (widget != &txt_table_overflow_right && + (widget != &txt_table_overflow_down || y1 == y)) + { + *w = x1 - x; + break; + } + } + + ++*h; + } +} + +static int is_overflowing_cell(txt_table_t *table, int x, int y) +{ + int w; + int h; + cell_overflowed_size(table, x, y, &w, &h); + return w > 1 || h > 1; +} + +/* Using the given column/row size tables, calculate the size of the given + * widget, storing the result in (w, h). + */ + +static void calculate_widget_dimensions(txt_table_t *table, int x, int y, + unsigned int *column_widths, + unsigned int *row_heights, + unsigned int *w, unsigned int *h) +{ + int cell_w; + int cell_h; + int x1; + int y1; + + /* Find which cells this widget occupies. */ + + cell_overflowed_size(table, x, y, &cell_w, &cell_h); + + /* Add up column / row widths / heights to get the actual dimensions. */ + + *w = 0; + for (x1 = x; x1 < x + cell_w; ++x1) + { + *w += column_widths[x1]; + } + + *h = 0; + for (y1 = y; y1 < y + cell_h; ++y1) + { + *h += row_heights[y1]; + } +} + +static void calc_row_col_sizes(txt_table_t *table, unsigned int *row_heights, + unsigned int *col_widths) +{ + int x; + int y; + int rows; + txt_widget_t *widget; + + rows = table_rows(table); + + memset(col_widths, 0, sizeof(int) * table->columns); + + for (y = 0; y < rows; ++y) + { + row_heights[y] = 0; + + for (x = 0; x < table->columns; ++x) + { + if (y * table->columns + x >= table->num_widgets) break; + + widget = table->widgets[y * table->columns + x]; + + if (is_actual_widget(widget)) + { + txt_calc_widget_size(widget); + } + + /* In the first pass we ignore overflowing cells. */ + + if (is_overflowing_cell(table, x, y)) + { + continue; + } + + /* NULL represents an empty spacer */ + + if (is_actual_widget(widget)) + { + if (widget->h > row_heights[y]) row_heights[y] = widget->h; + if (widget->w > col_widths[x]) col_widths[x] = widget->w; + } + } + } + + /* In the second pass, we go through again and process overflowing + * widgets, to ensure that they will fit. + */ + + for (y = 0; y < rows; ++y) + { + for (x = 0; x < table->columns; ++x) + { + unsigned int w; + unsigned int h; + + if (y * table->columns + x >= table->num_widgets) break; + + widget = table->widgets[y * table->columns + x]; + if (!is_actual_widget(widget)) + { + continue; + } + + /* Expand column width and row heights as needed. */ + + calculate_widget_dimensions(table, x, y, col_widths, row_heights, + &w, &h); + if (w < widget->w) + { + col_widths[x] += widget->w - w; + } + + if (h < widget->h) + { + row_heights[y] += widget->h - h; + } + } + } +} + +static void txt_calc_table_size(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + int x; + int y; + int rows; + + rows = table_rows(table); + + row_heights = malloc(sizeof(int) * rows); + column_widths = malloc(sizeof(int) * table->columns); + + calc_row_col_sizes(table, row_heights, column_widths); + + table->widget.w = 0; + + for (x = 0; x < table->columns; ++x) + { + table->widget.w += column_widths[x]; + } + + table->widget.h = 0; + + for (y = 0; y < rows; ++y) + { + table->widget.h += row_heights[y]; + } + + free(row_heights); + free(column_widths); +} + +static void fill_row_to_end(txt_table_t *table) +{ + while ((table->num_widgets % table->columns) != 0) + { + txt_add_widget(table, &txt_table_overflow_right); + } +} + +static int selectable_cell(txt_table_t *table, int x, int y) +{ + txt_widget_t *widget; + int i; + + if (x < 0 || x >= table->columns) + { + return 0; + } + + i = y * table->columns + x; + + if (i >= 0 && i < table->num_widgets) + { + widget = table->widgets[i]; + return is_actual_widget(widget) && txt_selectable_widget(widget) && + widget->visible; + } + + return 0; +} + +/* Tries to locate a selectable widget in the given row, returning + * the column number of the first column available or -1 if none are + * available in the given row. + * + * Starts from start_col, then searches nearby columns. + */ + +static int find_selectable_column(txt_table_t *table, int row, int start_col) +{ + int x; + + for (x = 0; x < table->columns; ++x) + { + /* Search to the right */ + + if (selectable_cell(table, start_col + x, row)) + { + return start_col + x; + } + + /* Search to the left */ + + if (selectable_cell(table, start_col - x, row)) + { + return start_col - x; + } + } + + /* None available */ + + return -1; +} + +/* Change the selected widget. */ + +static void change_selection(txt_table_t *table, int x, int y) +{ + txt_widget_t *cur_widget; + txt_widget_t *new_widget; + int i; + + /* No change? */ + + if (x == table->selected_x && y == table->selected_y) + { + return; + } + + /* Unfocus current widget: */ + + i = table->selected_y * table->columns + table->selected_x; + + if (i < table->num_widgets) + { + cur_widget = table->widgets[i]; + + if (table->widget.focused && is_actual_widget(cur_widget)) + { + txt_set_widget_focus(cur_widget, 0); + } + } + + /* Focus new widget. */ + + new_widget = table->widgets[y * table->columns + x]; + + table->selected_x = x; + table->selected_y = y; + + if (table->widget.focused && new_widget != NULL) + { + txt_set_widget_focus(new_widget, 1); + } +} + +static int txt_table_keypress(TXT_UNCAST_ARG(table), int key) +{ + TXT_CAST_ARG(txt_table_t, table); + int selected; + int rows; + + rows = table_rows(table); + + /* Send to the currently selected widget first */ + + selected = table->selected_y * table->columns + table->selected_x; + + if (selected >= 0 && selected < table->num_widgets) + { + if (is_actual_widget(table->widgets[selected]) && + txt_selectable_widget(table->widgets[selected]) && + txt_widget_key_press(table->widgets[selected], key)) + { + return 1; + } + } + + if (key == KEY_TAB) + { + int dir; + int i; + + dir = txt_get_modifier_state(TXT_MOD_SHIFT) ? -1 : 1; + + /* Cycle through all widgets until we find one that can be selected. */ + + for (i = table->selected_y * table->columns + table->selected_x + dir; + i >= 0 && i < table->num_widgets; i += dir) + { + if (is_actual_widget(table->widgets[i]) && + txt_selectable_widget(table->widgets[i])) + { + change_selection(table, + i % table->columns, i / table->columns); + return 1; + } + } + + return 0; + } + + if (key == KEY_DOWNARROW) + { + int new_x; + int new_y; + + /* Move cursor down to the next selectable widget */ + + for (new_y = table->selected_y + 1; new_y < rows; ++new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! */ + + change_selection(table, new_x, new_y); + + return 1; + } + } + } + + if (key == KEY_UPARROW) + { + int new_x; + int new_y; + + /* Move cursor up to the next selectable widget */ + + for (new_y = table->selected_y - 1; new_y >= 0; --new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! */ + + change_selection(table, new_x, new_y); + + return 1; + } + } + } + + if (key == KEY_LEFTARROW) + { + int new_x; + + /* Move cursor left */ + + for (new_x = table->selected_x - 1; new_x >= 0; --new_x) + { + if (selectable_cell(table, new_x, table->selected_y)) + { + /* Found a selectable widget! */ + + change_selection(table, new_x, table->selected_y); + + return 1; + } + } + } + + if (key == KEY_RIGHTARROW) + { + int new_x; + + /* Move cursor left */ + + for (new_x = table->selected_x + 1; new_x < table->columns; ++new_x) + { + if (selectable_cell(table, new_x, table->selected_y)) + { + /* Found a selectable widget! */ + + change_selection(table, new_x, table->selected_y); + + return 1; + } + } + } + + return 0; +} + +/* Check the currently selected widget in the table is valid. */ + +static void check_valid_selection(txt_table_t *table) +{ + int rows; + int new_x; + int new_y; + + rows = table_rows(table); + + for (new_y = table->selected_y; new_y < rows; ++new_y) + { + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable column. */ + + change_selection(table, new_x, new_y); + + break; + } + } +} + +static void layout_cell(txt_table_t *table, int x, int y, int draw_x, + int draw_y) +{ + txt_widget_t *widget; + int col_width; + + widget = table->widgets[y * table->columns + x]; + + col_width = widget->w; + + /* Adjust x position based on alignment property */ + + switch (widget->align) + { + case TXT_HORIZ_LEFT: + widget->w = col_width; + break; + + case TXT_HORIZ_CENTER: + txt_calc_widget_size(widget); + + /* Separators are always drawn left-aligned. */ + + if (widget->widget_class != &txt_separator_class) + { + draw_x += (col_width - widget->w) / 2; + } + + break; + + case TXT_HORIZ_RIGHT: + txt_calc_widget_size(widget); + + if (widget->widget_class != &txt_separator_class) + { + draw_x += col_width - widget->w; + } + break; + } + + /* Set the position for this widget */ + + widget->x = draw_x; + widget->y = draw_y; + + /* Recursively lay out any widgets contained in the widget */ + + txt_layout_widget(widget); +} + +static void txt_table_layout(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + txt_widget_t *widget; + int draw_x; + int draw_y; + int x; + int y; + int i; + int rows; + + /* Work out the column widths and row heights */ + + rows = table_rows(table); + + column_widths = malloc(sizeof(int) * table->columns); + row_heights = malloc(sizeof(int) * rows); + + calc_row_col_sizes(table, row_heights, column_widths); + + /* If this table only has one column, expand column size to fit + * the display width. Ensures that separators reach the window edges + * when drawing windows. + */ + + if (table->columns == 1) + { + column_widths[0] = table->widget.w; + } + + /* Draw all cells */ + + draw_y = table->widget.y; + + for (y = 0; y < rows; ++y) + { + draw_x = table->widget.x; + + for (x = 0; x < table->columns; ++x) + { + i = y * table->columns + x; + + if (i >= table->num_widgets) break; + + widget = table->widgets[i]; + + if (is_actual_widget(widget)) + { + calculate_widget_dimensions(table, x, y, column_widths, + row_heights, &widget->w, + &widget->h); + layout_cell(table, x, y, draw_x, draw_y); + } + + draw_x += column_widths[x]; + } + + draw_y += row_heights[y]; + } + + free(row_heights); + free(column_widths); +} + +static void txt_table_drawer(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *widget; + int i; + + /* Check the table's current selection points at something valid before + * drawing. + */ + + check_valid_selection(table); + + /* Draw all cells */ + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + + if (is_actual_widget(widget)) + { + txt_goto_xy(widget->x, widget->y); + txt_draw_widget(widget); + } + } +} + +/* Responds to mouse presses */ + +static void txt_table_mouse_press(TXT_UNCAST_ARG(table), int x, int y, int b) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *widget; + int i; + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + + /* NULL widgets are spacers */ + + if (is_actual_widget(widget)) + { + if (x >= widget->x && x < (signed)(widget->x + widget->w) && + y >= widget->y && y < (signed)(widget->y + widget->h)) + { + /* This is the widget that was clicked! + * + * Select the cell if the widget is selectable + */ + + if (txt_selectable_widget(widget)) + { + change_selection(table, i % table->columns, + i / table->columns); + } + + /* Propagate click */ + + txt_widget_mouse_press(widget, x, y, b); + + break; + } + } + } +} + +/* Determine whether the table is selectable. */ + +static int txt_table_selectable(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + /* Is the currently-selected cell selectable? */ + + if (selectable_cell(table, table->selected_x, table->selected_y)) + { + return 1; + } + + /* Find the first selectable cell and set selected_x, selected_y. */ + + for (i = 0; i < table->num_widgets; ++i) + { + if (is_actual_widget(table->widgets[i]) && + txt_selectable_widget(table->widgets[i])) + { + change_selection(table, i % table->columns, i / table->columns); + return 1; + } + } + + /* No selectable widgets exist within the table. */ + + return 0; +} + +/* Need to pass through focus changes to the selected child widget. */ + +static void txt_table_focused(TXT_UNCAST_ARG(table), int focused) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + i = table->selected_y * table->columns + table->selected_x; + + if (i < table->num_widgets) + { + if (is_actual_widget(table->widgets[i])) + { + txt_set_widget_focus(table->widgets[i], focused); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Remove all entries from a table */ + +void txt_clear_table(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + int i; + + /* Free all widgets + * Skip over the first (num_columns) widgets in the array, as these + * are the column struts used to control column width + */ + + for (i = table->columns; i < table->num_widgets; ++i) + { + if (is_actual_widget(table->widgets[i])) + { + txt_destroy_widget(table->widgets[i]); + } + } + + /* Shrink the table to just the column strut widgets */ + + table->num_widgets = table->columns; +} + +void txt_add_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_table_t, table); + TXT_CAST_ARG(txt_widget_t, widget); + int is_separator; + int i; + + /* Convenience alias for NULL: */ + + if (widget == &txt_table_empty) + { + widget = NULL; + } + else if (widget == &txt_table_eol) + { + fill_row_to_end(table); + return; + } + + /* We have special handling for the separator widget: */ + + is_separator = is_actual_widget(widget) && + widget->widget_class == &txt_separator_class; + + /* If we add two separators consecutively, the new separator replaces the + * first. This allows us to override the "implicit" separator that is + * added at the top of a window when it is created. + */ + + if (is_separator) + { + for (i = table->num_widgets - 1; i >= 0; --i) + { + txt_widget_t *last_widget; + last_widget = table->widgets[i]; + + if (is_actual_widget(last_widget) && + widget->widget_class == &txt_separator_class && + last_widget->widget_class == &txt_separator_class) + { + table->widgets[i] = widget; + txt_destroy_widget(last_widget); + return; + } + else if (last_widget != &txt_table_overflow_right) + { + break; + } + } + } + + /* Separators begin on a new line. */ + + if (is_separator) + { + fill_row_to_end(table); + } + + table->widgets = realloc( + table->widgets, + sizeof(txt_widget_t *) * (table->num_widgets + 1)); + table->widgets[table->num_widgets] = widget; + ++table->num_widgets; + + /* Maintain parent pointer. */ + + if (is_actual_widget(widget)) + { + widget->parent = &table->widget; + } + + /* Separators always take up the entire line. */ + + if (is_separator) + { + fill_row_to_end(table); + } +} + +/* Add multiple widgets to a table. */ + +void txt_add_widgets(TXT_UNCAST_ARG(table), ...) +{ + TXT_CAST_ARG(txt_table_t, table); + va_list args; + txt_widget_t *widget; + + va_start(args, TXT_UNCAST_ARG_NAME(table)); + + /* Keep adding widgets until a NULL is reached. */ + + for (; ; ) + { + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + break; + } + + txt_add_widget(table, widget); + } + + va_end(args); +} + +void txt_init_table(txt_table_t *table, int columns) +{ + int i; + + txt_init_widget(table, &txt_table_class); + table->columns = columns; + table->widgets = NULL; + table->num_widgets = 0; + table->selected_x = 0; + table->selected_y = 0; + + /* Add a strut for each column at the start of the table. + * These are used by the txt_set_column_widths function below: + * the struts are created with widths of 0 each, but this + * function changes them. + */ + + for (i = 0; i < columns; ++i) + { + txt_add_widget(table, txt_new_strut(0, 0)); + } +} + +txt_table_t *txt_new_table(int columns) +{ + txt_table_t *table; + + table = malloc(sizeof(txt_table_t)); + + txt_init_table(table, columns); + + return table; +} + +/* Alternative to txt_new_table() that allows a list of widgets to be + * provided in its arguments. + */ + +txt_table_t *txt_make_table(int columns, ...) +{ + txt_table_t *table; + va_list args; + + table = txt_new_table(columns); + va_start(args, columns); + + for (; ; ) + { + txt_widget_t *widget; + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + break; + } + + txt_add_widget(table, widget); + } + + va_end(args); + + return table; +} + +/* Create a horizontal table from a list of widgets. */ + +txt_table_t *txt_new_horiz_box(TXT_UNCAST_ARG(first_widget), ...) +{ + TXT_CAST_ARG(txt_widget_t, first_widget); + txt_table_t *result; + va_list args; + int num_args; + + /* First, find the number of arguments to determine the width of + * the box. + */ + + va_start(args, TXT_UNCAST_ARG_NAME(first_widget)); + + num_args = 1; + + for (; ; ) + { + txt_widget_t *widget; + + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + /* End of list */ + + break; + } + else + { + ++num_args; + } + } + + va_end(args); + + /* Create the table. */ + + result = txt_new_table(num_args); + txt_add_widget(result, first_widget); + + /* Go through the list again and add each widget. */ + + va_start(args, TXT_UNCAST_ARG_NAME(first_widget)); + + for (; ; ) + { + txt_widget_t *widget; + + widget = va_arg(args, txt_widget_t *); + + if (widget == NULL) + { + /* End of list */ + + break; + } + else + { + txt_add_widget(result, widget); + } + } + + va_end(args); + + return result; +} + +/* Get the currently-selected widget in a table, recursively searching + * through sub-tables if necessary. + */ + +txt_widget_t *txt_get_selected_widget(TXT_UNCAST_ARG(table)) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t *result; + int index; + + index = table->selected_y * table->columns + table->selected_x; + + result = NULL; + + if (index >= 0 && index < table->num_widgets) + { + result = table->widgets[index]; + + if (!is_actual_widget(result)) + { + result = NULL; + } + } + + if (result != NULL && result->widget_class == &txt_table_class) + { + result = txt_get_selected_widget(result); + } + + return result; +} + +/* Selects a given widget in a table, recursively searching any tables + * within this table. Returns 1 if successful, 0 if unsuccessful. + */ + +int txt_select_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_table_t, table); + TXT_CAST_ARG(txt_widget_t, widget); + int i; + + for (i = 0; i < table->num_widgets; ++i) + { + if (!is_actual_widget(table->widgets[i])) + { + continue; + } + + if (table->widgets[i] == widget) + { + /* Found the item! Select it and return. */ + + change_selection(table, i % table->columns, i / table->columns); + + return 1; + } + + if (table->widgets[i]->widget_class == &txt_table_class) + { + /* This item is a subtable. Recursively search this table. */ + + if (txt_select_widget(table->widgets[i], widget)) + { + /* Found it in the subtable. Select this subtable and return. + */ + + change_selection(table, i % table->columns, + i / table->columns); + + return 1; + } + } + } + + /* Not found. */ + + return 0; +} + +void txt_set_table_columns(TXT_UNCAST_ARG(table), int new_columns) +{ + TXT_CAST_ARG(txt_table_t, table); + txt_widget_t **new_widgets; + txt_widget_t *widget; + int new_num_widgets; + int i; + int j; + int x; + + /* We need as many full rows as are in the current list, plus the + * remainder from the last row. + */ + + new_num_widgets = (table->num_widgets / table->columns) * new_columns + + (table->num_widgets % table->columns); + new_widgets = calloc(new_num_widgets, sizeof(txt_widget_t *)); + + /* Reset and add one by one from the old table. */ + + new_num_widgets = 0; + + for (i = 0; i < table->num_widgets; ++i) + { + widget = table->widgets[i]; + x = i % table->columns; + + if (x < new_columns) + { + new_widgets[new_num_widgets] = widget; + ++new_num_widgets; + } + else if (is_actual_widget(widget)) + { + txt_destroy_widget(widget); + } + + /* When we reach the last column of a row, we must pad it out with + * extra widgets to reach the next row. + */ + + if (x == table->columns - 1) + { + for (j = table->columns; j < new_columns; ++j) + { + /* First row? We need to add struts that are used to apply + * the column widths. + */ + + if (i < table->columns) + { + widget = &txt_new_strut(0, 0)->widget; + } + else + { + widget = &txt_table_overflow_right; + } + + new_widgets[new_num_widgets] = widget; + ++new_num_widgets; + } + } + } + + free(table->widgets); + table->widgets = new_widgets; + table->num_widgets = new_num_widgets; + table->columns = new_columns; +} + +/* Sets the widths of columns in a table. */ + +void txt_set_column_widths(TXT_UNCAST_ARG(table), ...) +{ + TXT_CAST_ARG(txt_table_t, table); + va_list args; + txt_strut_t *strut; + int i; + int width; + + va_start(args, TXT_UNCAST_ARG_NAME(table)); + + for (i = 0; i < table->columns; ++i) + { + width = va_arg(args, int); + + strut = (txt_strut_t *)table->widgets[i]; + strut->width = width; + } + + va_end(args); +} + +/* Moves the select by at least the given number of characters. + * Currently quietly ignores pagex, as we don't use it. + */ + +int txt_page_table(TXT_UNCAST_ARG(table), int pagex, int pagey) +{ + TXT_CAST_ARG(txt_table_t, table); + unsigned int *column_widths; + unsigned int *row_heights; + int rows; + int changed = 0; + + rows = table_rows(table); + + row_heights = malloc(sizeof(int) * rows); + column_widths = malloc(sizeof(int) * table->columns); + + calc_row_col_sizes(table, row_heights, column_widths); + + if (pagex) + { + /* @todo Jump selection to the left or right as needed */ + } + + if (pagey) + { + int new_x; + int new_y; + int distance = 0; + int dir; + + /* What direction are we moving? */ + + if (pagey > 0) + { + dir = 1; + } + else + { + dir = -1; + } + + /* Move the cursor until the desired distance is reached. */ + + new_y = table->selected_y; + + while (new_y >= 0 && new_y < rows) + { + /* We are about to travel a distance equal to the height of the row + * we are about to leave. + */ + + distance += row_heights[new_y]; + + /* *Now* increment the loop. */ + + new_y += dir; + + new_x = find_selectable_column(table, new_y, table->selected_x); + + if (new_x >= 0) + { + /* Found a selectable widget in this column! + * Select it anyway in case we don't find something better. + */ + + change_selection(table, new_x, new_y); + changed = 1; + + /* ...but is it far enough away? */ + + if (distance >= abs(pagey)) + { + break; + } + } + } + } + + free(row_heights); + free(column_widths); + + return changed; +} diff --git a/games/NXDoom/textscreen/txt_table.h b/games/NXDoom/textscreen/txt_table.h new file mode 100644 index 000000000..d1dffbeff --- /dev/null +++ b/games/NXDoom/textscreen/txt_table.h @@ -0,0 +1,265 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_table.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_TABLE_H +#define TXT_TABLE_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/** + * Magic value that if used in a table, will indicate that the cell is + * empty and the widget in the cell to the left can overflow into it. + */ + +#define TXT_TABLE_OVERFLOW_RIGHT (&txt_table_overflow_right) + +/** + * Magic value that if used in a table, will indicate that the cell is + * empty and the widget in the cell above it can overflow down into it. + */ + +#define TXT_TABLE_OVERFLOW_DOWN (&txt_table_overflow_down) + +/** + * Magic value that if given to @ref txt_add_widget(), will pad out all + * columns until the end of line. + */ + +#define TXT_TABLE_EOL (&txt_table_eol) + +/** + * Indicates an empty space to @ref txt_add_widgets(). Equivalent to + * txt_add_widget(table, NULL), except that NULL is used by txt_add_widgets() + * to indicate the end of input. + */ + +#define TXT_TABLE_EMPTY (&txt_table_empty) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Table widget. + * + * A table is a widget that contains other widgets. It may have + * multiple columns, in which case the child widgets are laid out + * in a grid. Columns automatically grow as necessary, although + * minimum column widths can be set using @ref txt_set_column_widths. + * + * To create a new table, use @ref txt_new_table. It is also + * possible to use @ref txt_new_horiz_box to create a table, specifying + * widgets to place inside a horizontal list. A vertical list is + * possible simply by creating a table containing a single column. + */ + +struct txt_table_s +{ + txt_widget_t widget; + + /* Widgets in this table + * The widget at (x,y) in the table is widgets[columns * y + x] + */ + + txt_widget_t **widgets; + int num_widgets; + + /* Number of columns */ + + int columns; + + /* Currently selected: */ + + int selected_x; + int selected_y; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern txt_widget_class_t txt_table_class; +extern txt_widget_t txt_table_overflow_right; +extern txt_widget_t txt_table_overflow_down; +extern txt_widget_t txt_table_eol; +extern txt_widget_t txt_table_empty; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_init_table(txt_table_t *table, int columns); + +/** + * Create a new table. + * + * @param columns The number of columns in the new table. + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_new_table(int columns); + +/** + * Create a new table and populate it with provided widgets. + * + * The arguments to this function are variable. Each argument must be a + * pointer to a widget, and the list is terminated with a NULL. + * + * @param columns The number of columns in the new table. + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_make_table(int columns, ...); + +/** + * Create a table containing the specified widgets packed horizontally, + * from left to right. + * + * The arguments to this function are variable. Each argument must + * be a pointer to a widget, and the list is terminated with a + * NULL. + * + * @return Pointer to the new table structure. + */ + +txt_table_t *txt_new_horiz_box(TXT_UNCAST_ARG(first_widget), ...); + +/** + * Get the currently selected widget within a table. + * + * This function will recurse through subtables if necessary. + * + * @param table The table. + * @return Pointer to the widget that is currently selected. + */ + +txt_widget_t *txt_get_selected_widget(TXT_UNCAST_ARG(table)); + +/** + * Add a widget to a table. + * + * Widgets are added to tables horizontally, from left to right. + * For example, for a table with three columns, the first call + * to this function will add a widget to the first column, the second + * call to the second column, the third call to the third column, + * and the fourth will return to the first column, starting a new + * row. + * + * For adding many widgets, it may be easier to use + * @ref txt_add_widgets. + * + * @param table The table. + * @param widget The widget to add. + */ + +void txt_add_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)); + +/** + * Add multiple widgets to a table. + * + * Widgets are added as described in the documentation for the + * @ref txt_add_widget function. This function adds multiple + * widgets. The number of arguments is variable, and the argument + * list must be terminated by a NULL pointer. + * + * @param table The table. + */ + +void txt_add_widgets(TXT_UNCAST_ARG(table), ...); + +/** + * Select the given widget that is contained within the specified + * table. + * + * This function will recursively search through subtables if + * necessary. + * + * @param table The table. + * @param widget The widget to select. + * @return Non-zero (true) if it has been selected, + * or zero (false) if it was not found within + * this table. + */ + +int txt_select_widget(TXT_UNCAST_ARG(table), TXT_UNCAST_ARG(widget)); + +/** + * Change the number of columns in the table. + * + * Existing widgets in the table will be preserved, unless the change + * reduces the number of columns, in which case the widgets from the + * 'deleted' columns will be freed. + * + * This function can be useful for changing the number of columns in + * a window, which by default are tables containing a single column. + * + * @param table The table. + * @param new_columns The new number of columns. + */ + +void txt_set_table_columns(TXT_UNCAST_ARG(table), int new_columns); + +/** + * Set the widths of the columns of the table. + * + * The arguments to this function are variable, and correspond + * to the number of columns in the table. For example, if a table + * has five columns, the width of each of the five columns must be + * specified. + * + * The width values are in number of characters. + * + * Note that this function only sets the minimum widths for columns; + * if the columns contain widgets that are wider than the widths + * specified, they will be larger. + * + * @param table The table. + */ + +void txt_set_column_widths(TXT_UNCAST_ARG(table), ...); + +/** + * Remove all widgets from a table. + * + * @param table The table. + */ + +void txt_clear_table(TXT_UNCAST_ARG(table)); + +/** + * Hack to move the selection in a table by a 'page', triggered by the + * scrollpane. This acts as per the keyboard events for the arrows, but moves + * the selection by at least the specified number of characters. + * + * @param table The table. + * @param pagex Minimum distance to move the selection horizontally. + * @param pagey Minimum distance to move the selection vertically. + * @return Non-zero if the selection has been changed. + */ + +int txt_page_table(TXT_UNCAST_ARG(table), int pagex, int pagey); + +#endif /* #ifndef TXT_TABLE_T */ diff --git a/games/NXDoom/textscreen/txt_utf8.c b/games/NXDoom/textscreen/txt_utf8.c new file mode 100644 index 000000000..086025994 --- /dev/null +++ b/games/NXDoom/textscreen/txt_utf8.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_utf8.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "txt_utf8.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Encode a Unicode character as UTF-8, storing it in the buffer 'p' + * and returning the new, incremented position. + */ + +char *txt_encode_utf8(char *p, unsigned int c) +{ + if (c < 0x80) /* 1 character (ASCII): */ + { + p[0] = c; + return p + 1; + } + else if (c < 0x800) /* 2 character: */ + { + p[0] = 0xc0 | (c >> 6); + p[1] = 0x80 | (c & 0x3f); + return p + 2; + } + else if (c < 0x10000) /* 3 chacater: */ + { + p[0] = 0xe0 | (c >> 12); + p[1] = 0x80 | ((c >> 6) & 0x3f); + p[2] = 0x80 | (c & 0x3f); + return p + 3; + } + else if (c < 0x200000) /* 4 character: */ + { + p[0] = 0xf0 | (c >> 18); + p[1] = 0x80 | ((c >> 12) & 0x3f); + p[2] = 0x80 | ((c >> 6) & 0x3f); + p[3] = 0x80 | (c & 0x3f); + return p + 4; + } + else + { + return p; /* Too big! */ + } +} + +/* Decode UTF-8 character, incrementing *ptr over the decoded bytes. */ + +unsigned int txt_decode_utf8(const char **ptr) +{ + const char *p = *ptr; + unsigned int c; + + /* UTF-8 decode. */ + + if ((*p & 0x80) == 0) /* 1 character (ASCII): */ + { + c = *p; + *ptr += 1; + } + else if ((p[0] & 0xe0) == 0xc0 /* 2 character: */ + && (p[1] & 0xc0) == 0x80) + { + c = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f); + *ptr += 2; + } + else if ((p[0] & 0xf0) == 0xe0 /* 3 character: */ + && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80) + { + c = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + *ptr += 3; + } + else if ((p[0] & 0xf8) == 0xf0 /* 4 character: */ + && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80 && + (p[3] & 0xc0) == 0x80) + { + c = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | + ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + *ptr += 4; + } + else + { + /* Decode failure. + * Don't bother with 5/6 byte sequences. + */ + + c = 0; + } + + return c; +} + +/* Count the number of characters in a UTF-8 string. */ + +unsigned int txt_utf8_strlen(const char *s) +{ + const char *p; + unsigned int result = 0; + unsigned int c; + + for (p = s; *p != '\0'; ) + { + c = txt_decode_utf8(&p); + + if (c == 0) + { + break; + } + + ++result; + } + + return result; +} + +/* Skip past the first n characters in a UTF-8 string. */ + +char *txt_utf8_skip_chars(const char *s, unsigned int n) +{ + unsigned int i; + const char *p; + + p = s; + + for (i = 0; i < n; ++i) + { + if (txt_decode_utf8(&p) == 0) + { + break; + } + } + + return (char *)p; +} diff --git a/games/NXDoom/textscreen/txt_utf8.h b/games/NXDoom/textscreen/txt_utf8.h new file mode 100644 index 000000000..0d2f98ef3 --- /dev/null +++ b/games/NXDoom/textscreen/txt_utf8.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_utf8.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_UTF8_H +#define TXT_UTF8_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +char *txt_encode_utf8(char *p, unsigned int c); +unsigned int txt_decode_utf8(const char **ptr); +unsigned int txt_utf8_strlen(const char *s); +char *txt_utf8_skip_chars(const char *s, unsigned int n); + +#endif /* TXT_UTF8_H */ diff --git a/games/NXDoom/textscreen/txt_widget.c b/games/NXDoom/textscreen/txt_widget.c new file mode 100644 index 000000000..01c44643c --- /dev/null +++ b/games/NXDoom/textscreen/txt_widget.c @@ -0,0 +1,350 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_widget.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_widget.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef struct +{ + char *signal_name; + txt_widget_signal_f func; + void *user_data; +} txt_callback_t; + +struct txt_callback_table_s +{ + int refcount; + txt_callback_t *callbacks; + int num_callbacks; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int txt_contains_widget(TXT_UNCAST_ARG(haystack), + TXT_UNCAST_ARG(needle)) +{ + TXT_CAST_ARG(txt_widget_t, haystack); + TXT_CAST_ARG(txt_widget_t, needle); + + while (needle != NULL) + { + if (needle == haystack) + { + return 1; + } + + needle = needle->parent; + } + + return 0; +} + +static txt_callback_table_t *txt_new_callback_table(void) +{ + txt_callback_table_t *table; + + table = malloc(sizeof(txt_callback_table_t)); + table->callbacks = NULL; + table->num_callbacks = 0; + table->refcount = 1; + + return table; +} + +static void txt_ref_callback_table(txt_callback_table_t *table) +{ + ++table->refcount; +} + +static void txt_unref_callback_table(txt_callback_table_t *table) +{ + int i; + + --table->refcount; + + if (table->refcount == 0) + { + /* No more references to this table */ + + for (i = 0; i < table->num_callbacks; ++i) + { + free(table->callbacks[i].signal_name); + } + + free(table->callbacks); + free(table); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_init_widget(TXT_UNCAST_ARG(widget), + txt_widget_class_t *widget_class) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class = widget_class; + widget->callback_table = txt_new_callback_table(); + widget->parent = NULL; + + /* Not focused until we hear otherwise. */ + + widget->focused = 0; + + /* Visible by default. */ + + widget->visible = 1; + + /* Align left by default */ + + widget->align = TXT_HORIZ_LEFT; +} + +void txt_signal_connect(TXT_UNCAST_ARG(widget), const char *signal_name, + txt_widget_signal_f func, void *user_data) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_callback_table_t *table; + txt_callback_t *callback; + + table = widget->callback_table; + + /* Add a new callback to the table */ + + table->callbacks = realloc( + table->callbacks, sizeof(txt_callback_t) * (table->num_callbacks + 1)); + callback = &table->callbacks[table->num_callbacks]; + ++table->num_callbacks; + + callback->signal_name = strdup(signal_name); + callback->func = func; + callback->user_data = user_data; +} + +void txt_emit_signal(TXT_UNCAST_ARG(widget), const char *signal_name) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_callback_table_t *table; + int i; + + table = widget->callback_table; + + /* Don't destroy the table while we're searching through it + * (one of the callbacks may destroy this window) + */ + + txt_ref_callback_table(table); + + /* Search the table for all callbacks with this name and invoke + * the functions. + */ + + for (i = 0; i < table->num_callbacks; ++i) + { + if (!strcmp(table->callbacks[i].signal_name, signal_name)) + { + table->callbacks[i].func(widget, table->callbacks[i].user_data); + } + } + + /* Finished using the table */ + + txt_unref_callback_table(table); +} + +void txt_calc_widget_size(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class->size_calc(widget); +} + +void txt_draw_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_saved_colors_t colors; + + /* The drawing function might change the fg/bg colors, + * so make sure we restore them after it's done. + */ + + txt_save_colours(&colors); + + /* For convenience... */ + + txt_goto_xy(widget->x, widget->y); + + /* Call drawer method */ + + widget->widget_class->drawer(widget); + + txt_restore_colours(&colors); +} + +void txt_destroy_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->widget_class->destructor(widget); + txt_unref_callback_table(widget->callback_table); + free(widget); +} + +int txt_widget_key_press(TXT_UNCAST_ARG(widget), int key) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->key_press != NULL) + { + return widget->widget_class->key_press(widget, key); + } + + return 0; +} + +void txt_set_widget_focus(TXT_UNCAST_ARG(widget), int focused) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget == NULL) + { + return; + } + + if (widget->focused != focused) + { + widget->focused = focused; + + if (widget->widget_class->focus_change != NULL) + { + widget->widget_class->focus_change(widget, focused); + } + } +} + +void txt_set_widget_align(TXT_UNCAST_ARG(widget), + txt_horiz_align_t horiz_align) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + widget->align = horiz_align; +} + +void txt_widget_mouse_press(TXT_UNCAST_ARG(widget), int x, int y, int b) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->mouse_press != NULL) + { + widget->widget_class->mouse_press(widget, x, y, b); + } +} + +void txt_layout_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->layout != NULL) + { + widget->widget_class->layout(widget); + } +} + +int txt_always_selectable(TXT_UNCAST_ARG(widget)) +{ + return 1; +} + +int txt_never_selectable(TXT_UNCAST_ARG(widget)) +{ + return 0; +} + +int txt_selectable_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->widget_class->selectable != NULL) + { + return widget->widget_class->selectable(widget); + } + else + { + return 0; + } +} + +int txt_hovering_over_widget(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + txt_window_t *active_window; + int x; + int y; + + /* We can only be hovering over widgets in the active window. */ + + active_window = txt_get_active_window(); + + if (active_window == NULL || !txt_contains_widget(active_window, widget)) + { + return 0; + } + + /* Is the mouse cursor within the bounds of the widget? */ + + txt_get_mouse_position(&x, &y); + + return (x >= widget->x && x < widget->x + widget->w && y >= widget->y && + y < widget->y + widget->h); +} + +void txt_set_widget_bg(TXT_UNCAST_ARG(widget)) +{ + TXT_CAST_ARG(txt_widget_t, widget); + + if (widget->focused) + { + txt_bgcolour(TXT_COLOR_GREY, 0); + } + else if (txt_hovering_over_widget(widget)) + { + txt_bgcolour(TXT_HOVER_BACKGROUND, 0); + } + else + { + /* Use normal window background. */ + } +} diff --git a/games/NXDoom/textscreen/txt_widget.h b/games/NXDoom/textscreen/txt_widget.h new file mode 100644 index 000000000..109be5e9c --- /dev/null +++ b/games/NXDoom/textscreen/txt_widget.h @@ -0,0 +1,190 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_widget.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_WIDGET_H +#define TXT_WIDGET_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TXT_UNCAST_ARG_NAME(name) uncast_##name +#define TXT_UNCAST_ARG(name) void *TXT_UNCAST_ARG_NAME(name) +#define TXT_CAST_ARG(type, name) type *name = (type *)uncast_##name + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Forward definitions */ + +typedef struct txt_table_s txt_table_t; +typedef struct txt_strut_s txt_strut_t; +typedef struct txt_spincontrol_s txt_spincontrol_t; +typedef struct txt_separator_s txt_separator_t; +typedef struct txt_scrollpane_s txt_scrollpane_t; +typedef struct txt_radiobutton_s txt_radiobutton_t; +typedef struct txt_inputbox_s txt_inputbox_t; +typedef struct txt_dropdown_list_s txt_dropdown_list_t; +typedef struct txt_checkbox_s txt_checkbox_t; +typedef struct txt_button_s txt_button_t; + +typedef enum +{ + TXT_VERT_TOP, + TXT_VERT_CENTER, + TXT_VERT_BOTTOM, +} txt_vert_align_t; + +typedef enum +{ + TXT_HORIZ_LEFT, + TXT_HORIZ_CENTER, + TXT_HORIZ_RIGHT, +} txt_horiz_align_t; + +/* A GUI widget. + * + * A widget is an individual component of a GUI. Various different widget + * types exist. + * + * Widgets may emit signals. The types of signal emitted by a widget + * depend on the type of the widget. It is possible to be notified + * when a signal occurs using the @ref txt_signal_connect function. + */ + +typedef struct txt_widget_s txt_widget_t; + +typedef struct txt_widget_class_s txt_widget_class_t; +typedef struct txt_callback_table_s txt_callback_table_t; + +typedef void (*txt_widget_size_calc_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_drawer_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_destroy_f)(TXT_UNCAST_ARG(widget)); +typedef int (*txt_widget_keypress_f)(TXT_UNCAST_ARG(widget), int key); +typedef void (*txt_widget_signal_f)(TXT_UNCAST_ARG(widget), void *user_data); +typedef void (*txt_mouse_press_f)(TXT_UNCAST_ARG(widget), int x, int y, + int b); +typedef void (*txt_widget_layout_f)(TXT_UNCAST_ARG(widget)); +typedef int (*txt_widget_selectable_f)(TXT_UNCAST_ARG(widget)); +typedef void (*txt_widget_focus_f)(TXT_UNCAST_ARG(widget), int focused); + +struct txt_widget_class_s +{ + txt_widget_selectable_f selectable; + txt_widget_size_calc_f size_calc; + txt_widget_drawer_f drawer; + txt_widget_keypress_f key_press; + txt_widget_destroy_f destructor; + txt_mouse_press_f mouse_press; + txt_widget_layout_f layout; + txt_widget_focus_f focus_change; +}; + +struct txt_widget_s +{ + txt_widget_class_t *widget_class; + txt_callback_table_t *callback_table; + int visible; + txt_horiz_align_t align; + int focused; + + /* These are set automatically when the window is drawn and should + * not be set manually. + */ + + int x; + int y; + unsigned int w; + unsigned int h; + + /* Pointer up to parent widget that contains this widget. */ + + txt_widget_t *parent; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void txt_init_widget(TXT_UNCAST_ARG(widget), + txt_widget_class_t *widget_class); +void txt_calc_widget_size(TXT_UNCAST_ARG(widget)); +void txt_draw_widget(TXT_UNCAST_ARG(widget)); +void txt_emit_signal(TXT_UNCAST_ARG(widget), const char *signal_name); +int txt_widget_key_press(TXT_UNCAST_ARG(widget), int key); +void txt_widget_mouse_press(TXT_UNCAST_ARG(widget), int x, int y, int b); +void txt_destroy_widget(TXT_UNCAST_ARG(widget)); +void txt_layout_widget(TXT_UNCAST_ARG(widget)); +int txt_always_selectable(TXT_UNCAST_ARG(widget)); +int txt_never_selectable(TXT_UNCAST_ARG(widget)); +void txt_set_widget_focus(TXT_UNCAST_ARG(widget), int focused); + +/** + * Set a callback function to be invoked when a signal occurs. + * + * @param widget The widget to watch. + * @param signal_name The signal to watch. + * @param func The callback function to invoke. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_signal_connect(TXT_UNCAST_ARG(widget), const char *signal_name, + txt_widget_signal_f func, void *user_data); + +/** + * Set the policy for how a widget should be aligned within a table. + * By default, widgets are aligned to the left of the column. + * + * @param widget The widget. + * @param horiz_align The alignment to use. + */ + +void txt_set_widget_align(TXT_UNCAST_ARG(widget), + txt_horiz_align_t horiz_align); + +/** + * Query whether a widget is selectable with the cursor. + * + * @param widget The widget. + * @return Non-zero if the widget is selectable. + */ + +int txt_selectable_widget(TXT_UNCAST_ARG(widget)); + +/** + * Query whether the mouse is hovering over the specified widget. + * + * @param widget The widget. + * @return Non-zero if the mouse cursor is over the widget. + */ + +int txt_hovering_over_widget(TXT_UNCAST_ARG(widget)); + +/** + * Set the background to draw the specified widget, depending on + * whether it is selected and the mouse is hovering over it. + * + * @param widget The widget. + */ + +void txt_set_widget_bg(TXT_UNCAST_ARG(widget)); + +#endif /* TXT_WIDGET_H */ diff --git a/games/NXDoom/textscreen/txt_window.c b/games/NXDoom/textscreen/txt_window.c new file mode 100644 index 000000000..1f48eac5a --- /dev/null +++ b/games/NXDoom/textscreen/txt_window.c @@ -0,0 +1,609 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_desktop.h" +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_label.h" +#include "txt_main.h" +#include "txt_separator.h" +#include "txt_window.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void calc_window_position(txt_window_t *window) +{ + switch (window->horiz_align) + { + case TXT_HORIZ_LEFT: + window->window_x = window->x; + break; + case TXT_HORIZ_CENTER: + window->window_x = window->x - (window->window_w / 2); + break; + case TXT_HORIZ_RIGHT: + window->window_x = window->x - (window->window_w - 1); + break; + } + + switch (window->vert_align) + { + case TXT_VERT_TOP: + window->window_y = window->y; + break; + case TXT_VERT_CENTER: + window->window_y = window->y - (window->window_h / 2); + break; + case TXT_VERT_BOTTOM: + window->window_y = window->y - (window->window_h - 1); + break; + } +} + +static void layout_action_area(txt_window_t *window) +{ + txt_widget_t *widget; + int space_available; + int space_left_offset; + + /* We need to calculate the available horizontal space for the center + * action widget, so that we can center it within it. + * To start with, we have the entire action area available. + */ + + space_available = window->window_w; + space_left_offset = 0; + + /* Left action */ + + if (window->actions[TXT_HORIZ_LEFT] != NULL) + { + widget = window->actions[TXT_HORIZ_LEFT]; + + txt_calc_widget_size(widget); + + widget->x = window->window_x + 1; + widget->y = window->window_y + window->window_h - widget->h - 1; + + /* Adjust available space: */ + + space_available -= widget->w; + space_left_offset += widget->w; + + txt_layout_widget(widget); + } + + /* Draw the right action */ + + if (window->actions[TXT_HORIZ_RIGHT] != NULL) + { + widget = window->actions[TXT_HORIZ_RIGHT]; + + txt_calc_widget_size(widget); + + widget->x = window->window_x + window->window_w - 1 - widget->w; + widget->y = window->window_y + window->window_h - widget->h - 1; + + /* Adjust available space: */ + + space_available -= widget->w; + + txt_layout_widget(widget); + } + + /* Draw the center action */ + + if (window->actions[TXT_HORIZ_CENTER] != NULL) + { + widget = window->actions[TXT_HORIZ_CENTER]; + + txt_calc_widget_size(widget); + + /* The left and right widgets have left a space sandwiched between + * them. Center this widget within that space. + */ + + widget->x = window->window_x + space_left_offset + + (space_available - widget->w) / 2; + widget->y = window->window_y + window->window_h - widget->h - 1; + + txt_layout_widget(widget); + } +} + +static void draw_action_area(txt_window_t *window) +{ + int i; + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL) + { + txt_draw_widget(window->actions[i]); + } + } +} + +static void calc_action_area_size(txt_window_t *window, unsigned int *w, + unsigned int *h) +{ + txt_widget_t *widget; + int i; + + *w = 0; + *h = 0; + + /* Calculate the width of all the action widgets and use this + * to create an overall min. width of the action area + */ + + for (i = 0; i < 3; ++i) + { + widget = (txt_widget_t *)window->actions[i]; + + if (widget != NULL) + { + txt_calc_widget_size(widget); + *w += widget->w; + + if (widget->h > *h) + { + *h = widget->h; + } + } + } +} + +/* Sets size and position of all widgets in a window */ + +static void txt_layout_window(txt_window_t *window) +{ + txt_widget_t *widgets = (txt_widget_t *)window; + unsigned int widgets_w; + unsigned int actionarea_w; + unsigned int actionarea_h; + + /* Calculate size of table */ + + txt_calc_widget_size(window); + + /* Widgets area: add one character of padding on each side */ + + widgets_w = widgets->w + 2; + + /* Calculate the size of the action area + * Make window wide enough to action area + */ + + calc_action_area_size(window, &actionarea_w, &actionarea_h); + + if (actionarea_w > widgets_w) widgets_w = actionarea_w; + + /* Set the window size based on widgets_w */ + + window->window_w = widgets_w + 2; + window->window_h = widgets->h + 1; + + /* If the window has a title, add an extra two lines */ + + if (window->title != NULL) + { + window->window_h += 2; + } + + /* If the window has an action area, add extra lines */ + + if (actionarea_h > 0) + { + window->window_h += actionarea_h + 1; + } + + /* Use the x,y position as the centerpoint and find the location to + * draw the window. + */ + + calc_window_position(window); + + /* Set the table size and position */ + + widgets->w = widgets_w - 2; + + /* widgets->h (already set) */ + + widgets->x = window->window_x + 2; + widgets->y = window->window_y; + + if (window->title != NULL) + { + widgets->y += 2; + } + + /* Layout the table and action area */ + + layout_action_area(window); + txt_layout_widget(widgets); +} + +static void txt_open_url(const char *url) +{ +#if 0 + char *cmd; + size_t cmd_len; + int retval; + + cmd_len = strlen(url) + 30; + cmd = malloc(cmd_len); + + /* The Unix situation sucks as usual, but the closest thing to a + * standard that exists is the xdg-utils package. + */ + + if (system("xdg-open --version 2>/dev/null") != 0) + { + fprintf(stderr, + "xdg-utils is not installed. Can't open this URL:\n%s\n", url); + free(cmd); + return; + } + + txt_snprintf(cmd, cmd_len, "xdg-open \"%s\"", url); + + retval = system(cmd); + if (retval != 0) + { + fprintf(stderr, "txt_open_url: error executing '%s'; return code %d\n", + cmd, retval); + } + + free(cmd); +#endif +} + +static int mouse_button_press(txt_window_t *window, int b) +{ + int x; + int y; + int i; + txt_widget_t *widgets; + txt_widget_t *widget; + + /* Lay out the window, set positions and sizes of all widgets */ + + txt_layout_window(window); + + /* Get the current mouse position */ + + txt_get_mouse_position(&x, &y); + + /* Try the mouse button listener + * This happens whether it is in the window range or not + */ + + if (window->mouse_listener != NULL) + { + /* Mouse listener can eat button presses */ + + if (window->mouse_listener(window, x, y, b, + window->mouse_listener_data)) + { + return 1; + } + } + + /* Is it within the table range? */ + + widgets = (txt_widget_t *)window; + + if (x >= widgets->x && x < (signed)(widgets->x + widgets->w) && + y >= widgets->y && y < (signed)(widgets->y + widgets->h)) + { + txt_widget_mouse_press(window, x, y, b); + return 1; + } + + /* Was one of the action area buttons pressed? */ + + for (i = 0; i < 3; ++i) + { + widget = window->actions[i]; + + if (widget != NULL && x >= widget->x && + x < (signed)(widget->x + widget->w) && y >= widget->y && + y < (signed)(widget->y + widget->h)) + { + int was_focused; + + /* Main table temporarily loses focus when action area button + * is clicked. This way, any active input boxes that depend + * on having focus will save their values before the + * action is performed. + */ + + was_focused = window->table.widget.focused; + txt_set_widget_focus(window, 0); + txt_set_widget_focus(window, was_focused); + + /* Pass through mouse press. */ + + txt_widget_mouse_press(widget, x, y, b); + return 1; + } + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void txt_set_window_action(txt_window_t *window, txt_horiz_align_t position, + TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_widget_t, action); + + if (window->actions[position] != NULL) + { + txt_destroy_widget(window->actions[position]); + } + + window->actions[position] = action; + + /* Maintain parent pointer. */ + + if (action != NULL) + { + action->parent = &window->table.widget; + } +} + +txt_window_t *txt_new_window(const char *title) +{ + int i; + + txt_window_t *win; + + win = malloc(sizeof(txt_window_t)); + + txt_init_table(&win->table, 1); + + if (title == NULL) + { + win->title = NULL; + } + else + { + win->title = strdup(title); + } + + win->x = TXT_SCREEN_W / 2; + win->y = TXT_SCREEN_H / 2; + win->horiz_align = TXT_HORIZ_CENTER; + win->vert_align = TXT_VERT_CENTER; + win->key_listener = NULL; + win->mouse_listener = NULL; + win->help_url = NULL; + + txt_add_widget(win, txt_new_separator(NULL)); + + for (i = 0; i < 3; ++i) + { + win->actions[i] = NULL; + } + + txt_add_desktop_window(win); + + /* Default actions */ + + txt_set_window_action(win, TXT_HORIZ_LEFT, + txt_new_window_escape_action(win)); + txt_set_window_action(win, TXT_HORIZ_RIGHT, + txt_new_window_select_action(win)); + + return win; +} + +void txt_close_window(txt_window_t *window) +{ + int i; + + txt_emit_signal(window, "closed"); + txt_remove_desktop_window(window); + + free(window->title); + + /* Destroy all actions */ + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL) + { + txt_destroy_widget(window->actions[i]); + } + } + + /* Destroy table and window */ + + txt_destroy_widget(window); +} + +void txt_draw_window(txt_window_t *window) +{ + txt_widget_t *widgets; + + txt_layout_window(window); + + if (window->table.widget.focused) + { + txt_bgcolour(TXT_ACTIVE_WINDOW_BACKGROUND, 0); + } + else + { + txt_bgcolour(TXT_INACTIVE_WINDOW_BACKGROUND, 0); + } + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + + /* Draw the window */ + + txt_draw_window_frame(window->title, window->window_x, window->window_y, + window->window_w, window->window_h); + + /* Draw all widgets */ + + txt_draw_widget(window); + + /* Draw an action area, if we have one */ + + widgets = (txt_widget_t *)window; + + if (widgets->y + widgets->h < window->window_y + window->window_h - 1) + { + /* Separator for action area */ + + txt_draw_separator(window->window_x, widgets->y + widgets->h, + window->window_w); + + /* Action area at the window bottom */ + + draw_action_area(window); + } +} + +void txt_set_window_position(txt_window_t *window, + txt_horiz_align_t horiz_align, + txt_vert_align_t vert_align, int x, int y) +{ + window->vert_align = vert_align; + window->horiz_align = horiz_align; + window->x = x; + window->y = y; +} + +int txt_window_keypress(txt_window_t *window, int c) +{ + int i; + + /* Is this a mouse button ? */ + + if (c >= TXT_MOUSE_BASE && c < TXT_MOUSE_BASE + TXT_MAX_MOUSE_BUTTONS) + { + return mouse_button_press(window, c); + } + + /* Try the window key spy */ + + if (window->key_listener != NULL) + { + /* key listener can eat keys */ + + if (window->key_listener(window, c, window->key_listener_data)) + { + return 1; + } + } + + /* Send to the currently selected widget: */ + + if (txt_widget_key_press(window, c)) + { + return 1; + } + + /* Try all of the action buttons */ + + for (i = 0; i < 3; ++i) + { + if (window->actions[i] != NULL && + txt_widget_key_press(window->actions[i], c)) + { + return 1; + } + } + + return 0; +} + +void txt_set_key_listener(txt_window_t *window, + txt_window_keypress_t key_listener, + void *user_data) +{ + window->key_listener = key_listener; + window->key_listener_data = user_data; +} + +void txt_set_mouse_listener(txt_window_t *window, + txt_window_mouse_press_t mouse_listener, + void *user_data) +{ + window->mouse_listener = mouse_listener; + window->mouse_listener_data = user_data; +} + +void txt_set_window_focus(txt_window_t *window, int focused) +{ + txt_set_widget_focus(window, focused); +} + +void txt_set_window_help_url(txt_window_t *window, const char *help_url) +{ + window->help_url = help_url; +} + +void txt_open_window_help_url(txt_window_t *window) +{ + if (window->help_url != NULL) + { + txt_open_url(window->help_url); + } +} + +txt_window_t *txt_message_box(const char *title, const char *message, ...) +{ + txt_window_t *window; + char buf[256]; + va_list args; + + va_start(args, message); + txt_vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + + window = txt_new_window(title); + txt_add_widget(window, txt_new_label(buf)); + + txt_set_window_action(window, TXT_HORIZ_LEFT, NULL); + txt_set_window_action(window, TXT_HORIZ_CENTER, + txt_new_window_escape_action(window)); + txt_set_window_action(window, TXT_HORIZ_RIGHT, NULL); + + return window; +} diff --git a/games/NXDoom/textscreen/txt_window.h b/games/NXDoom/textscreen/txt_window.h new file mode 100644 index 000000000..b2803ce56 --- /dev/null +++ b/games/NXDoom/textscreen/txt_window.h @@ -0,0 +1,215 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_WINDOW_H +#define TXT_WINDOW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_table.h" +#include "txt_widget.h" + +#include "txt_window_action.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Callback function for window key presses */ + +typedef int (*txt_window_keypress_t)(txt_window_t *window, int key, + void *user_data); +typedef int (*txt_window_mouse_press_t)(txt_window_t *window, int x, int y, + int b, void *user_data); + +struct txt_window_s +{ + /* Base class: all windows are tables with one column. */ + + txt_table_t table; + + /* Window title */ + + char *title; + + /* Screen coordinates of the window */ + + txt_vert_align_t vert_align; + txt_horiz_align_t horiz_align; + int x; + int y; + + /* Actions that appear in the box at the bottom of the window */ + + txt_widget_t *actions[3]; + + /* Callback functions to invoke when keys/mouse buttons are pressed */ + + txt_window_keypress_t key_listener; + void *key_listener_data; + txt_window_mouse_press_t mouse_listener; + void *mouse_listener_data; + + /* These are set automatically when the window is drawn */ + + int window_x; + int window_y; + unsigned int window_w; + unsigned int window_h; + + /* URL of a webpage with help about this window. If set, a help key + * indicator is shown while this window is active. + */ + + const char *help_url; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Open a new window. + * + * @param title Title to display in the titlebar of the new window + * (UTF-8 format). + * @return Pointer to a new @ref txt_window_t structure + * representing the new window. + */ + +txt_window_t *txt_new_window(const char *title); + +/** + * Close a window. + * + * @param window Tine window to close. + */ + +void txt_close_window(txt_window_t *window); + +/** + * Set the position of a window on the screen. + * + * The position is specified as a pair of x, y, coordinates on the + * screen. These specify the position of a particular point on the + * window. The following are some examples: + * + * + * // Centered on the screen: + * + * txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_CENTER, + * TXT_SCREEN_W / 2, TXT_SCREEN_H / 2); + * + * // Horizontally centered, with top of the window on line 6: + * + * txt_set_window_position(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, + * TXT_SCREEN_W / 2, 6); + * + * // Top-left of window at 20, 6: + * + * txt_set_window_position(window, TXT_HORIZ_LEFT, TXT_VERT_TOP, 20, 6); + * + * + * @param window The window. + * @param horiz_align Horizontal location on the window for the X + * position. + * @param vert_align Vertical location on the window for the Y position. + * @param x X coordinate (horizontal axis) for window position. + * @param y Y coordinate (vertical axis) for window position. + */ + +void txt_set_window_position(txt_window_t *window, + txt_horiz_align_t horiz_align, + txt_vert_align_t vert_align, int x, int y); + +/** + * Set a window action for a given window. + * + * Each window can have up to three window actions, which provide + * keyboard shortcuts that can be used within a given window. + * + * @param window The window. + * @param position The window action slot to set (left, center or right). + * @param action The window action widget. If this is NULL, any + * current window action in the given slot is removed. + */ + +void txt_set_window_action(txt_window_t *window, txt_horiz_align_t position, + TXT_UNCAST_ARG(action)); + +/** + * Set a callback function to be invoked whenever a key is pressed within + * a window. + * + * @param window The window. + * @param key_listener Callback function. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_set_key_listener(txt_window_t *window, + txt_window_keypress_t key_listener, + void *user_data); + +/** + * Set a callback function to be invoked whenever a mouse button is pressed + * within a window. + * + * @param window The window. + * @param mouse_listener Callback function. + * @param user_data User-specified pointer to pass to the callback + * function. + */ + +void txt_set_mouse_listener(txt_window_t *window, + txt_window_mouse_press_t mouse_listener, + void *user_data); + +/** + * Open a window displaying a message. + * + * @param title Title of the window (UTF-8 format). + * @param message The message to display in the window + * (UTF-8 format). + * @return The new window. + */ + +txt_window_t *txt_message_box(const char *title, const char *message, ...); + +/** + * Set the help URL for the given window. + * + * @param window The window. + * @param help_url String containing URL of the help page for this + * window, or NULL to set no help for this window. + */ + +void txt_set_window_help_url(txt_window_t *window, const char *help_url); + +/** + * Open the help URL for the given window, if one is set. + * + * @param window The window. + */ + +void txt_open_window_help_url(txt_window_t *window); + +#endif /* TXT_WINDOW_H */ diff --git a/games/NXDoom/textscreen/txt_window_action.c b/games/NXDoom/textscreen/txt_window_action.c new file mode 100644 index 000000000..d14f41d66 --- /dev/null +++ b/games/NXDoom/textscreen/txt_window_action.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window_action.c + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "doomkeys.h" + +#include "txt_gui.h" +#include "txt_io.h" +#include "txt_main.h" +#include "txt_utf8.h" +#include "txt_window.h" +#include "txt_window_action.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void txt_window_action_size_calc(TXT_UNCAST_ARG(action)); +static void txt_window_action_drawer(TXT_UNCAST_ARG(action)); +static void txt_window_action_destructor(TXT_UNCAST_ARG(action)); +static int txt_window_action_key_press(TXT_UNCAST_ARG(action), int key); +static void txt_window_action_mouse_press(TXT_UNCAST_ARG(action), int x, + int y, int b); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +txt_widget_class_t txt_window_action_class = +{ + txt_always_selectable, + txt_window_action_size_calc, + txt_window_action_drawer, + txt_window_action_key_press, + txt_window_action_destructor, + txt_window_action_mouse_press, + NULL, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void txt_window_action_size_calc(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + char buf[10]; + + txt_get_key_description(action->key, buf, sizeof(buf)); + + /* Width is label length, plus key description length, plus '=' + * and two surrounding spaces. + */ + + action->widget.w = + txt_utf8_strlen(action->label) + txt_utf8_strlen(buf) + 3; + action->widget.h = 1; +} + +static void txt_window_action_drawer(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + int hovering; + char buf[10]; + + txt_get_key_description(action->key, buf, sizeof(buf)); + + hovering = txt_hovering_over_widget(action); + txt_set_widget_bg(action); + + txt_draw_string(" "); + txt_fgcolour(hovering ? TXT_COLOR_BRIGHT_WHITE : TXT_COLOR_BRIGHT_GREEN); + txt_draw_string(buf); + txt_fgcolour(TXT_COLOR_BRIGHT_CYAN); + txt_draw_string("="); + + txt_fgcolour(TXT_COLOR_BRIGHT_WHITE); + txt_draw_string(action->label); + txt_draw_string(" "); +} + +static void txt_window_action_destructor(TXT_UNCAST_ARG(action)) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + free(action->label); +} + +static int txt_window_action_key_press(TXT_UNCAST_ARG(action), int key) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + if (tolower(key) == tolower(action->key)) + { + txt_emit_signal(action, "pressed"); + return 1; + } + + return 0; +} + +static void txt_window_action_mouse_press(TXT_UNCAST_ARG(action), int x, + int y, int b) +{ + TXT_CAST_ARG(txt_window_action_t, action); + + /* Simulate a press of the key */ + + if (b == TXT_MOUSE_LEFT) + { + txt_window_action_key_press(action, action->key); + } +} + +static void window_close_callback(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(window)) +{ + TXT_CAST_ARG(txt_window_t, window); + + txt_close_window(window); +} + +static void window_select_callback(TXT_UNCAST_ARG(widget), + TXT_UNCAST_ARG(window)) +{ + TXT_CAST_ARG(txt_window_t, window); + + txt_widget_key_press(window, KEY_ENTER); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +txt_window_action_t *txt_new_window_action(int key, const char *label) +{ + txt_window_action_t *action; + + action = malloc(sizeof(txt_window_action_t)); + + txt_init_widget(action, &txt_window_action_class); + action->key = key; + action->label = strdup(label); + + return action; +} + +/* An action with the name "close" the closes the window */ + +txt_window_action_t *txt_new_window_escape_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ESCAPE, "Close"); + txt_signal_connect(action, "pressed", window_close_callback, window); + + return action; +} + +/* Exactly the same as the above, but the button is named "abort" */ + +txt_window_action_t *txt_new_window_abort_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ESCAPE, "Abort"); + txt_signal_connect(action, "pressed", window_close_callback, window); + + return action; +} + +txt_window_action_t *txt_new_window_select_action(txt_window_t *window) +{ + txt_window_action_t *action; + + action = txt_new_window_action(KEY_ENTER, "Select"); + txt_signal_connect(action, "pressed", window_select_callback, window); + + return action; +} diff --git a/games/NXDoom/textscreen/txt_window_action.h b/games/NXDoom/textscreen/txt_window_action.h new file mode 100644 index 000000000..c76c6ecae --- /dev/null +++ b/games/NXDoom/textscreen/txt_window_action.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * apps/games/NXDoom/textscreen/txt_window_action.h + * + * SPDX-License-Identifer: GPLv2 + * + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ****************************************************************************/ + +#ifndef TXT_WINDOW_ACTION_H +#define TXT_WINDOW_ACTION_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "txt_widget.h" +#include "txt_window.h" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/** + * @file txt_window_action.h + * + * Window action widget. + */ + +/** + * Window action widget. + * + * A window action is attached to a window and corresponds to a + * keyboard shortcut that is active within that window. When the + * key is pressed, the action is triggered. + * + * When a window action is triggered, the "pressed" signal is emitted. + */ + +typedef struct txt_window_action_s txt_window_action_t; + +struct txt_window_action_s +{ + txt_widget_t widget; + char *label; + int key; +}; + +/* A window. + * + * A window contains widgets, and may also be treated as a table + * (@ref txt_table_t) containing a single column. + * + * Windows can be created using @ref txt_new_window and closed using + * @ref txt_close_window. When a window is closed, it emits the + * "closed" signal. + * + * In addition to the widgets within a window, windows also have + * a "tray" area at their bottom containing window action widgets. + * These widgets allow keyboard shortcuts to trigger common actions. + * Each window has three slots for keyboard shortcuts. By default, + * the left slot contains an action to close the window when the + * escape button is pressed, while the right slot contains an + * action to activate the currently-selected widget. + */ + +typedef struct txt_window_s txt_window_t; /* Forward definition */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/** + * Create a new window action. + * + * @param key The keyboard key that triggers this action. + * @param label Label to display for this action in the tray + * at the bottom of the window (UTF-8 format). + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_action(int key, const char *label); + +/** + * Create a new window action that closes the window when the + * escape key is pressed. The label "Close" is used. + * + * @param window The window to close. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_escape_action(txt_window_t *window); + +/** + * Create a new window action that closes the window when the + * escape key is pressed. The label "Abort" is used. + * + * @param window The window to close. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_abort_action(txt_window_t *window); + +/** + * Create a new "select" window action. This does not really do + * anything, but reminds the user that "enter" can be pressed to + * activate the currently-selected widget. + * + * @param window The window. + * @return Pointer to the new window action widget. + */ + +txt_window_action_t *txt_new_window_select_action(txt_window_t *window); + +#endif /* TXT_WINDOW_ACTION_H */ diff --git a/graphics/ft80x/ft80x.h b/graphics/ft80x/ft80x.h index 6462f1ce1..64254691c 100644 --- a/graphics/ft80x/ft80x.h +++ b/graphics/ft80x/ft80x.h @@ -32,7 +32,7 @@ #include #include -#include +#include #ifdef CONFIG_GRAPHICS_FT80X diff --git a/graphics/input/CMakeLists.txt b/graphics/input/CMakeLists.txt index 2ec8eb86f..8213d364a 100644 --- a/graphics/input/CMakeLists.txt +++ b/graphics/input/CMakeLists.txt @@ -62,3 +62,17 @@ if(CONFIG_GRAPHICS_INPUT_TOOL) SRCS generator/input.c) endif() + +if(CONFIG_GRAPHICS_INPUT_GETEVENT) + nuttx_add_application( + MODULE + ${CONFIG_GRAPHICS_INPUT_GETEVENT} + NAME + getevent + STACKSIZE + ${CONFIG_GRAPHICS_INPUT_GETEVENT_STACKSIZE} + PRIORITY + ${CONFIG_GRAPHICS_INPUT_GETEVENT_PRIORITY} + SRCS + getevent/getevent.c) +endif() diff --git a/graphics/input/Kconfig b/graphics/input/Kconfig index 08430e8d8..7cda0c370 100644 --- a/graphics/input/Kconfig +++ b/graphics/input/Kconfig @@ -59,3 +59,39 @@ config GRAPHICS_INPUT_TOOL_PRIORITY The priority of the input task. endif # GRAPHICS_INPUT_TOOL + +menuconfig GRAPHICS_INPUT_GETEVENT + tristate "Enable getevent tool" + default n + ---help--- + The getevent is a command-line input event monitor. It + captures and displays real-time events from mouse, + touchscreen, and keyboard devices via the input subsystem. + + Use getevent to debug and verify input drivers, inspect + raw event data during device bring-up, or diagnose + input issues on a running system. It supports automatic + device detection in /dev as well as manual device path + specification. + +if GRAPHICS_INPUT_GETEVENT + +config GRAPHICS_INPUT_GETEVENT_STACKSIZE + int "getevent stack size" + default DEFAULT_TASK_STACKSIZE + ---help--- + The size of stack allocated for the getevent task. + +config GRAPHICS_INPUT_GETEVENT_PRIORITY + int "getevent priority" + default 100 + ---help--- + The priority of the getevent task. + +config GRAPHICS_INPUT_GETEVENT_DETAIL_INFO + bool "getevent detail info" + default n + ---help--- + Enable detailed information in getevent output. + +endif # GRAPHICS_INPUT_GETEVENT diff --git a/graphics/input/Make.defs b/graphics/input/Make.defs index 8bf4cb9c1..5931c7950 100644 --- a/graphics/input/Make.defs +++ b/graphics/input/Make.defs @@ -20,6 +20,6 @@ # ############################################################################ -ifneq ($(CONFIG_GRAPHICS_INPUT_GENERATOR),) +ifneq ($(CONFIG_GRAPHICS_INPUT_GENERATOR)$(CONFIG_GRAPHICS_INPUT_GETEVENT),) CONFIGURED_APPS += $(APPDIR)/graphics/input endif diff --git a/graphics/input/Makefile b/graphics/input/Makefile index b88166a09..e0a974b51 100644 --- a/graphics/input/Makefile +++ b/graphics/input/Makefile @@ -50,4 +50,16 @@ MODULE += $(CONFIG_GRAPHICS_INPUT_TOOL) MAINSRC += generator/input.c endif +# getevent tool + +ifneq ($(CONFIG_GRAPHICS_INPUT_GETEVENT),) +PROGNAME += getevent +PRIORITY += $(CONFIG_GRAPHICS_INPUT_GETEVENT_PRIORITY) +STACKSIZE += $(CONFIG_GRAPHICS_INPUT_GETEVENT_STACKSIZE) +MODULE += $(CONFIG_GRAPHICS_INPUT_GETEVENT) + +MAINSRC += getevent/getevent.c +CFLAGS += -Wno-discarded-qualifiers +endif + include $(APPDIR)/Application.mk diff --git a/graphics/input/generator/input_gen_ctx.c b/graphics/input/generator/input_gen_ctx.c index 6449979fd..ea415e020 100644 --- a/graphics/input/generator/input_gen_ctx.c +++ b/graphics/input/generator/input_gen_ctx.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include #include diff --git a/graphics/input/generator/input_gen_dev.c b/graphics/input/generator/input_gen_dev.c index 117946d9c..9c921107c 100644 --- a/graphics/input/generator/input_gen_dev.c +++ b/graphics/input/generator/input_gen_dev.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include diff --git a/graphics/input/generator/input_gen_event.c b/graphics/input/generator/input_gen_event.c index 64637bb85..51219d51b 100644 --- a/graphics/input/generator/input_gen_event.c +++ b/graphics/input/generator/input_gen_event.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include diff --git a/graphics/input/getevent/getevent.c b/graphics/input/getevent/getevent.c new file mode 100644 index 000000000..c70f90d47 --- /dev/null +++ b/graphics/input/getevent/getevent.c @@ -0,0 +1,562 @@ +/**************************************************************************** + * apps/graphics/input/getevent/getevent.c + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GETEVENT_INFO(format, ...) \ + printf("[getevent]: " format, ##__VA_ARGS__) + +#define GETEVENT_ERR(format, ...) \ + fprintf(stderr, "[getevent]: " format, ##__VA_ARGS__) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct input_device_s; + +typedef CODE int (*input_init_cb)(FAR struct input_device_s *); +typedef CODE void (*input_read_cb)(FAR struct input_device_s *); + +struct touch_priv_s +{ + uint8_t maxpoint; + struct touch_sample_s sample[]; +}; + +struct input_device_s +{ + int fd; + input_init_cb init_cb; + input_read_cb read_cb; + FAR void *priv; + FAR struct input_device_s *next; + char path[]; +}; + +struct input_devices_ctx_s +{ + FAR struct input_device_s *head; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: open_device + ****************************************************************************/ + +static int open_device(FAR struct input_device_s *dev) +{ + GETEVENT_INFO("opening %s\n", dev->path); + dev->fd = open(dev->path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + + if (dev->fd < 0) + { + GETEVENT_ERR("open %s failed, errno: %d\n", dev->path, errno); + return -errno; + } + + if (dev->init_cb) + { + return dev->init_cb(dev); + } + + return 0; +} + +/**************************************************************************** + * Name: touch_init + ****************************************************************************/ + +static int touch_init(FAR struct input_device_s *dev) +{ + FAR struct touch_priv_s *priv; + uint8_t maxpoint; + int ret; + + ret = ioctl(dev->fd, TSIOC_GETMAXPOINTS, &maxpoint); + if (ret < 0) + { + GETEVENT_ERR("ioctl GETMAXPOINTS failed: %d, " + "errno: %d\n", ret, errno); + return -errno; + } + + priv = calloc(1, sizeof(struct touch_priv_s) + + SIZEOF_TOUCH_SAMPLE_S(maxpoint)); + if (!priv) + { + GETEVENT_ERR("Memory allocation failed for touch_priv_s\n"); + return -ENOMEM; + } + + priv->maxpoint = maxpoint; + dev->priv = priv; + GETEVENT_INFO("ioctl TSIOC_GETMAXPOINTS: %d\n", maxpoint); + return ret; +} + +/**************************************************************************** + * Name: read_mouse_event + ****************************************************************************/ + +static void read_mouse_event(FAR struct input_device_s *dev) +{ + struct mouse_report_s sample; + ssize_t nbytes; + + /* Read one sample */ + + nbytes = read(dev->fd, &sample, sizeof(struct mouse_report_s)); + + /* Print the sample data on successful return */ + + if (nbytes == sizeof(struct mouse_report_s)) + { + GETEVENT_INFO("mouse event: %s\n", dev->path); + GETEVENT_INFO(" buttons : %02" PRIx8 "\n", sample.buttons); + GETEVENT_INFO(" x : %d\n", sample.x); + GETEVENT_INFO(" y : %d\n", sample.y); +#ifdef CONFIG_INPUT_MOUSE_WHEEL + GETEVENT_INFO(" wheel : %d\n", sample.wheel); +#endif + } +} + +/**************************************************************************** + * Name: read_touch_event + ****************************************************************************/ + +static void read_touch_event(FAR struct input_device_s *dev) +{ + FAR struct touch_priv_s *priv = (FAR struct touch_priv_s *)dev->priv; + ssize_t nbytes; + size_t i; + + /* Read one sample */ + + nbytes = read(dev->fd, priv->sample, + SIZEOF_TOUCH_SAMPLE_S(priv->maxpoint)); + + /* Print the sample data on successful return */ + + if (nbytes == SIZEOF_TOUCH_SAMPLE_S(priv->maxpoint)) + { + GETEVENT_INFO("touch event: %s\n", dev->path); + GETEVENT_INFO(" npoints : %" PRId32 "\n", priv->sample->npoints); + for (i = 0; i < priv->maxpoint; i++) + { + if (priv->sample->point[i].flags == 0) + { + break; + } + + GETEVENT_INFO("Point : %d\n", priv->sample->point[i].id); + GETEVENT_INFO(" flags : %02x\n", priv->sample->point[i].flags); + GETEVENT_INFO(" x : %d\n", priv->sample->point[i].x); + GETEVENT_INFO(" y : %d\n", priv->sample->point[i].y); +#ifdef CONFIG_GRAPHICS_INPUT_GETEVENT_DETAIL_INFO + GETEVENT_INFO(" h : %d\n", priv->sample->point[i].h); + GETEVENT_INFO(" w : %d\n", priv->sample->point[i].w); + GETEVENT_INFO(" pressure : %d\n", + priv->sample->point[i].pressure); +#endif + GETEVENT_INFO(" timestamp : %" PRIu64"\n", + priv->sample->point[i].timestamp); + } + } +} + +/**************************************************************************** + * Name: read_keyboard_event + ****************************************************************************/ + +static void read_keyboard_event(FAR struct input_device_s *dev) +{ + struct keyboard_event_s sample; + ssize_t nbytes; + + /* Read one sample */ + + nbytes = read(dev->fd, &sample, sizeof(struct keyboard_event_s)); + + /* Print the sample data on successful return */ + + if (nbytes == sizeof(struct keyboard_event_s)) + { + GETEVENT_INFO("keyboard event: %s\n", dev->path); + GETEVENT_INFO(" type : %" PRIu32 "\n", sample.type); + GETEVENT_INFO(" code : %" PRIu32 "\n", sample.code); + } +} + +/**************************************************************************** + * Name: cleanup_input_devices + ****************************************************************************/ + +static void cleanup_input_devices(FAR struct input_devices_ctx_s *ctx) +{ + FAR struct input_device_s *dev = ctx->head; + FAR struct input_device_s *next; + + while (dev) + { + next = dev->next; + if (dev->fd >= 0) + { + close(dev->fd); + } + + free(dev->priv); + free(dev); + dev = next; + } + + ctx->head = NULL; +} + +/**************************************************************************** + * Name: signal_handler + ****************************************************************************/ + +static void +signal_handler(int signum, FAR siginfo_t *info, FAR void *context) +{ + FAR volatile sig_atomic_t *running = info->si_user; + *running = 0; +} + +/**************************************************************************** + * Name: run_event_loop + ****************************************************************************/ + +static void run_event_loop(FAR struct input_devices_ctx_s *ctx) +{ + FAR struct input_device_s *dev; + FAR struct pollfd *fds = NULL; + volatile sig_atomic_t running = 1; + struct sigaction sa; + int fd_count = 0; + int timeout = 500; + int i = 0; + int ret; + + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_user = (FAR void *)&running; + sigaction(SIGINT, &sa, NULL); + + for (dev = ctx->head; dev; dev = dev->next) + { + if (dev->fd >= 0) + { + fd_count++; + } + } + + fds = calloc(fd_count, sizeof(struct pollfd)); + + if (!fds) + { + GETEVENT_ERR("Memory allocation failed for fds\n"); + return; + } + + for (dev = ctx->head; dev; dev = dev->next) + { + if (dev->fd >= 0) + { + fds[i].fd = dev->fd; + fds[i].events = POLLIN; + fds[i].revents = 0; + i++; + } + } + + while (running) + { + ret = poll(fds, fd_count, timeout); + if (ret < 0 && errno != EINTR) + { + GETEVENT_ERR("poll error: %d\n", errno); + break; + } + + for (i = 0; i < fd_count; i++) + { + if (!(fds[i].revents & POLLIN)) + { + continue; + } + + for (dev = ctx->head; dev; dev = dev->next) + { + if (dev->fd == fds[i].fd) + { + dev->read_cb(dev); + break; + } + } + } + } + + free(fds); +} + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(void) +{ + printf("Usage: getevent [OPTIONS]\n"); + printf("Options:\n"); + printf(" -h Show this help message\n"); + printf(" -m /dev/mouse -- mouse device path\n"); + printf(" -t /dev/input -- touch device path\n"); + printf(" -k /dev/keyboard -- keyboard device path\n"); + printf("No options: Auto detect devices in /dev\n"); +} + +/**************************************************************************** + * Name: alloc_input_device + ****************************************************************************/ + +static FAR struct input_device_s * +alloc_input_device(FAR struct input_devices_ctx_s *ctx, + FAR const char *path) +{ + FAR struct input_device_s *dev; + FAR struct input_device_s *cur; + size_t path_len = strlen(path) + 1; + + dev = calloc(1, sizeof(struct input_device_s) + path_len); + if (!dev) + { + GETEVENT_ERR("Memory allocation failed for device\n"); + return NULL; + } + + memcpy(dev->path, path, path_len); + + if (!ctx->head) + { + ctx->head = dev; + } + else + { + cur = ctx->head; + while (cur->next) + { + cur = cur->next; + } + + cur->next = dev; + } + + return dev; +} + +/**************************************************************************** + * Name: add_input_device + ****************************************************************************/ + +static int add_input_device(FAR struct input_devices_ctx_s *ctx, + input_init_cb init_cb, + input_read_cb read_cb, + FAR const char *path) +{ + FAR struct input_device_s *dev = alloc_input_device(ctx, path); + + if (!dev) + { + return -ENOMEM; + } + + dev->fd = -1; + dev->init_cb = init_cb; + dev->read_cb = read_cb; + + return open_device(dev); +} + +/**************************************************************************** + * Name: detect_devices + ****************************************************************************/ + +static int detect_devices(FAR struct input_devices_ctx_s *ctx) +{ + FAR struct dirent *entry; + char path[PATH_MAX]; + FAR DIR *dir; + int ret = 0; + + dir = opendir("/dev"); + if (!dir) + { + GETEVENT_ERR("Failed to open /dev directory, errno: %d\n", errno); + return -errno; + } + + while ((entry = readdir(dir))) + { + if (entry->d_type == DT_CHR) + { + if (snprintf(path, sizeof(path), "/dev/%s", entry->d_name) < 0) + { + GETEVENT_ERR("Memory allocation failed for path\n"); + continue; + } + + if (strncmp(entry->d_name, "mouse", 5) == 0) + { + ret = add_input_device(ctx, NULL, read_mouse_event, path); + } + else if (strncmp(entry->d_name, "input", 5) == 0) + { + ret = add_input_device(ctx, touch_init, read_touch_event, + path); + } + else if (strncmp(entry->d_name, "kbd", 3) == 0) + { + ret = add_input_device(ctx, NULL, read_keyboard_event, path); + } + + if (ret < 0) + { + break; + } + } + } + + closedir(dir); + return ret; +} + +/**************************************************************************** + * Name: parse_commandline + ****************************************************************************/ + +static int parse_commandline(FAR struct input_devices_ctx_s *ctx, + int argc, FAR char **argv) +{ + int ret = -EINVAL; + int ch; + + if (argc == 1) + { + return detect_devices(ctx); + } + + while ((ch = getopt(argc, argv, "hn:m:t:k:")) != -1) + { + switch (ch) + { + case 'm': + ret = add_input_device(ctx, NULL, read_mouse_event, optarg); + break; + case 't': + ret = add_input_device(ctx, touch_init, read_touch_event, optarg); + break; + case 'k': + ret = add_input_device(ctx, NULL, read_keyboard_event, optarg); + break; + case '?': + case 'h': + default: + show_usage(); + break; + } + + if (ret < 0) + { + break; + } + } + + return ret; +} + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct input_devices_ctx_s ctx; + int ret; + + memset(&ctx, 0, sizeof(ctx)); + + /* Parse the command line arguments */ + + ret = parse_commandline(&ctx, argc, argv); + if (ret < 0) + { + goto cleanup; + } + + /* Start event loop for input processing */ + + run_event_loop(&ctx); + + /* Clean up resources */ + +cleanup: + cleanup_input_devices(&ctx); + + GETEVENT_INFO("Terminating!\n"); + + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/graphics/libyuv/CMakeLists.txt b/graphics/libyuv/CMakeLists.txt index 6060e4f34..c792f090f 100644 --- a/graphics/libyuv/CMakeLists.txt +++ b/graphics/libyuv/CMakeLists.txt @@ -41,7 +41,7 @@ if(CONFIG_LIBYUV) FetchContent_GetProperties(libyuv_fetch) if(NOT libyuv_fetch_POPULATED) - FetchContent_Populate(llibyuv_fetch) + FetchContent_Populate(libyuv_fetch) endif() endif() diff --git a/graphics/libyuv/Makefile b/graphics/libyuv/Makefile index d15033fd1..e4421ee1d 100644 --- a/graphics/libyuv/Makefile +++ b/graphics/libyuv/Makefile @@ -29,7 +29,7 @@ CXXFLAGS += -Wno-shadow -Wno-undef CXXSRCS = $(wildcard $(SRCDIR)/source/*.cc) libyuv.tar.gz: - $(Q) curl -L https://chromium.googlesource.com/libyuv/libyuv/+archive/refs/heads/$(CONFIG_LIBYUV_BRANCH).tar.gz -o libyuv.tar.gz + $(Q) curl -L https://github.com/NuttX/nuttx/releases/download/libyuv/libyuv-refs_heads_$(CONFIG_LIBYUV_BRANCH).tar.gz -o libyuv.tar.gz $(Q) mkdir -p $(SRCDIR) $(Q) tar zxf libyuv.tar.gz --directory $(SRCDIR) ifeq ($(CONFIG_LIBYUV_BRANCH),"stable") diff --git a/graphics/lvgl/CMakeLists.txt b/graphics/lvgl/CMakeLists.txt index 352b5da59..ba5af28e2 100644 --- a/graphics/lvgl/CMakeLists.txt +++ b/graphics/lvgl/CMakeLists.txt @@ -27,12 +27,21 @@ if(CONFIG_GRAPHICS_LVGL) # ############################################################################ set(LVGL_DIR ${CMAKE_CURRENT_LIST_DIR}/lvgl) + set(LVGL_VERSION + "${CONFIG_LVGL_VERSION_MAJOR}.${CONFIG_LVGL_VERSION_MINOR}.${CONFIG_LVGL_VERSION_PATCH}" + ) + if(NOT EXISTS ${LVGL_DIR}) + set(LVGL_URL + "https://github.com/lvgl/lvgl/archive/refs/tags/v${LVGL_VERSION}.zip") FetchContent_Declare( lvgl_fetch - DOWNLOAD_DIR ${CMAKE_CURRENT_LIST_DIR} - URL "https://github.com/lvgl/lvgl/archive/refs/tags/v9.2.1.zip" SOURCE_DIR - ${CMAKE_CURRENT_LIST_DIR}/lvgl BINARY_DIR + URL ${LVGL_URL} + DOWNLOAD_DIR + ${CMAKE_CURRENT_LIST_DIR} + SOURCE_DIR + ${CMAKE_CURRENT_LIST_DIR}/lvgl + BINARY_DIR ${CMAKE_BINARY_DIR}/apps/graphics/lvgl/lvgl DOWNLOAD_NO_PROGRESS true TIMEOUT 120) diff --git a/graphics/lvgl/Kconfig b/graphics/lvgl/Kconfig index 4daf467f3..2d9ad2f8e 100644 --- a/graphics/lvgl/Kconfig +++ b/graphics/lvgl/Kconfig @@ -11,7 +11,7 @@ menuconfig GRAPHICS_LVGL if GRAPHICS_LVGL -# Kconfig file for LVGL v9.2.1 +# Kconfig file for LVGL v9.2.2 menu "LVGL configuration" @@ -1495,7 +1495,7 @@ menu "LVGL configuration" default 2 # LVGL_VERSION_MINOR config LVGL_VERSION_PATCH int - default 1 # LVGL_VERSION_PATCH + default 2 # LVGL_VERSION_PATCH endmenu menu "Devices" diff --git a/graphics/lvgl/Makefile b/graphics/lvgl/Makefile index a81fac1eb..ce9ad6b7f 100644 --- a/graphics/lvgl/Makefile +++ b/graphics/lvgl/Makefile @@ -58,8 +58,11 @@ WD := ${shell echo $(CURDIR) | sed -e 's/ /\\ /g'} CONFIG_GRAPH_LVGL_URL ?= "https://github.com/lvgl/lvgl/archive/refs/tags" -LVGL_VERSION = 9.2.1 -LVGL_TARBALL = v$(LVGL_VERSION).zip +_MAJ := $(CONFIG_LVGL_VERSION_MAJOR) +_MIN := $(CONFIG_LVGL_VERSION_MINOR) +_PAT := $(CONFIG_LVGL_VERSION_PATCH) +LVGL_VERSION := $(_MAJ).$(_MIN).$(_PAT) +LVGL_TARBALL := v$(LVGL_VERSION).zip LVGL_UNPACKNAME = lvgl UNPACK ?= unzip -o $(if $(V),,-q) diff --git a/graphics/microwindows/.gitignore b/graphics/microwindows/.gitignore new file mode 100644 index 000000000..0cb9c8404 --- /dev/null +++ b/graphics/microwindows/.gitignore @@ -0,0 +1,2 @@ +/microwindows +/*.zip diff --git a/graphics/microwindows/Kconfig b/graphics/microwindows/Kconfig new file mode 100644 index 000000000..9d5f0cb8c --- /dev/null +++ b/graphics/microwindows/Kconfig @@ -0,0 +1,131 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig GRAPHICS_MICROWINDOWS + bool "Microwindows or the Nano-X Window System" + default n + depends on VIDEO_FB + ---help--- + Enable Microwindows or the Nano-X Window System + +if GRAPHICS_MICROWINDOWS + +config MICROWINDOWS_FB_PATH + string "Framebuffer device path" + default "/dev/fb0" + ---help--- + Path to the NuttX framebuffer device. + +choice + prompt "Keyboard driver" + default MICROWINDOWS_KBD_EVENT + ---help--- + Select the keyboard input driver for Microwindows. + +config MICROWINDOWS_KBD_EVENT + bool "Event-mode keyboard driver" + ---help--- + Reads keyboard_event_s events from a NuttX keyboard device + (e.g., /dev/kbd). Supports key press/release tracking and + modifier keys (Shift, Ctrl, Alt). + +config MICROWINDOWS_KBD_RAW + bool "Raw-mode keyboard driver" + depends on INPUT_KEYBOARD_BYTESTREAM + ---help--- + Reads raw byte stream from a character device (e.g., /dev/kbda) + and decodes escape sequences via the kbd_codec library. Suitable + for USB HID keyboards on QEMU. + +config MICROWINDOWS_KBD_NONE + bool "No keyboard driver" + ---help--- + No keyboard driver is compiled. Microwindows will use a null + keyboard driver that never produces input. Suitable for + display-only applications. + +config MICROWINDOWS_KBD_CUSTOM + bool "Custom keyboard driver" + ---help--- + No built-in keyboard driver is compiled. The application or BSP + must provide its own kbddev symbol and KBDDEVICE implementation, + and add the source file to the build (e.g., via CSRCS in its + Makefile). See the Microwindows documentation for details. + +endchoice + +config MICROWINDOWS_KBD_EVENT_PATH + string "Event keyboard device path" + default "/dev/kbd" + depends on MICROWINDOWS_KBD_EVENT + ---help--- + Device path for the event-mode keyboard driver. + +config MICROWINDOWS_KBD_RAW_PATH + string "Raw keyboard device path" + default "/dev/kbda" + depends on MICROWINDOWS_KBD_RAW + ---help--- + Device path for the raw-mode keyboard driver. + +choice + prompt "Mouse/touchscreen driver" + default MICROWINDOWS_MOUSE_RELATIVE + ---help--- + Select the pointing device driver for Microwindows. + +config MICROWINDOWS_MOUSE_RELATIVE + bool "Relative mouse driver" + ---help--- + Reads mouse_report_s events from a NuttX mouse device + (e.g., /dev/mouse0). Supports three buttons and scroll wheel. + +config MICROWINDOWS_MOUSE_TS + bool "Touchscreen driver" + ---help--- + Reads touch_sample_s events from a NuttX touchscreen device + (e.g., /dev/input0). Supports absolute positioning. + +config MICROWINDOWS_MOUSE_NONE + bool "No mouse/touchscreen driver" + ---help--- + No pointing device driver is compiled. Microwindows will use a + null mouse driver that never produces input and hides the cursor. + Suitable for display-only applications. + +config MICROWINDOWS_MOUSE_CUSTOM + bool "Custom mouse/touchscreen driver" + ---help--- + No built-in pointing device driver is compiled. The application + or BSP must provide its own mousedev symbol and MOUSEDEVICE + implementation, and add the source file to the build (e.g., via + CSRCS in its Makefile). See the Microwindows documentation for + details. + +endchoice + +config MICROWINDOWS_MOUSE_PATH + string "Mouse device path" + default "/dev/mouse0" + depends on MICROWINDOWS_MOUSE_RELATIVE + ---help--- + Device path for the relative mouse driver. + +config MICROWINDOWS_TS_PATH + string "Touchscreen device path" + default "/dev/input0" + depends on MICROWINDOWS_MOUSE_TS + ---help--- + Device path for the touchscreen driver. + +config MICROWINDOWS_MWIN + bool "Win32 API (mwin) support" + default n + ---help--- + Build the Microwindows Win32 (mwin) API library. This provides + a high-level windowing API similar to the Microsoft Windows API + with window management, controls, and standard dialogs. + +endif # GRAPHICS_MICROWINDOWS diff --git a/graphics/microwindows/Make.defs b/graphics/microwindows/Make.defs new file mode 100644 index 000000000..935abf66b --- /dev/null +++ b/graphics/microwindows/Make.defs @@ -0,0 +1,33 @@ +############################################################################ +# apps/graphics/microwindows/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_GRAPHICS_MICROWINDOWS),) +CONFIGURED_APPS += $(APPDIR)/graphics/microwindows + +# It allows `microwindows/src/include` import. + +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/include + +CFLAGS += -DMWCONFIG_FILE='"mwconfig.nuttx"' + +endif diff --git a/graphics/microwindows/Makefile b/graphics/microwindows/Makefile new file mode 100644 index 000000000..2ea7e4c1a --- /dev/null +++ b/graphics/microwindows/Makefile @@ -0,0 +1,156 @@ +############################################################################ +# apps/graphics/microwindows/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# Microwindows graphic library + +MICROWINDOWS_DIR = . +MICROWINDOWS_DIR_NAME = microwindows + +# Set up build configuration and environment + +WD := ${shell echo $(CURDIR) | sed -e 's/ /\\ /g'} + +MICROWINDOWS_COMMIT_HASH := 1f269127b991d01dea5784eacee321a23e34d3d5 + +CONFIG_GRAPH_MICROWINDOWS_URL ?= https://codeload.github.com/ghaerr/microwindows/zip/$(MICROWINDOWS_COMMIT_HASH) + +MICROWINDOWS_TARBALL := microwindows-$(MICROWINDOWS_COMMIT_HASH).zip + +MICROWINDOWS_UNPACKNAME = microwindows +UNPACK ?= unzip -o $(if $(V),,-q) +CURL ?= curl -L $(if $(V),,-Ss) + +MICROWINDOWS_UNPACKDIR = $(WD)/$(MICROWINDOWS_UNPACKNAME) + +$(MICROWINDOWS_TARBALL): + $(ECHO_BEGIN)"Downloading: $(MICROWINDOWS_TARBALL)" + $(Q) $(CURL) -o $(MICROWINDOWS_TARBALL) $(CONFIG_GRAPH_MICROWINDOWS_URL) + $(ECHO_END) + +$(MICROWINDOWS_UNPACKNAME): $(MICROWINDOWS_TARBALL) + $(ECHO_BEGIN)"Unpacking: $(MICROWINDOWS_TARBALL) -> $(MICROWINDOWS_UNPACKNAME)" + $(Q) $(UNPACK) $(MICROWINDOWS_TARBALL) + $(Q) mv microwindows-$(MICROWINDOWS_COMMIT_HASH) $(MICROWINDOWS_UNPACKNAME) + $(Q) touch $(MICROWINDOWS_UNPACKNAME) + $(ECHO_END) + +# Download and unpack tarball if no git repo found +ifeq ($(wildcard $(MICROWINDOWS_UNPACKNAME)/.git),) +context:: $(MICROWINDOWS_UNPACKNAME) + +distclean:: + $(call DELDIR, $(MICROWINDOWS_UNPACKNAME)) + $(call DELFILE, $(MICROWINDOWS_TARBALL)) +endif + +CFLAGS += -Wno-undef +CFLAGS += -Wno-shadow + +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/graphics/microwindows/microwindows/src/drivers + +CFLAGS += -DNUTTX_FB_PATH=\"$(CONFIG_MICROWINDOWS_FB_PATH)\" +ifdef CONFIG_MICROWINDOWS_KBD_EVENT_PATH +CFLAGS += -DNUTTX_KBD_EVENT_PATH=\"$(CONFIG_MICROWINDOWS_KBD_EVENT_PATH)\" +endif +ifdef CONFIG_MICROWINDOWS_KBD_RAW_PATH +CFLAGS += -DNUTTX_KBD_RAW_PATH=\"$(CONFIG_MICROWINDOWS_KBD_RAW_PATH)\" +endif +ifdef CONFIG_MICROWINDOWS_MOUSE_PATH +CFLAGS += -DNUTTX_MOUSE_PATH=\"$(CONFIG_MICROWINDOWS_MOUSE_PATH)\" +endif +ifdef CONFIG_MICROWINDOWS_TS_PATH +CFLAGS += -DNUTTX_TOUCHSCREEN_PATH=\"$(CONFIG_MICROWINDOWS_TS_PATH)\" +endif + +MW_DIR_OBJ = $(WD)/$(MICROWINDOWS_DIR_NAME)/src + +# Select NuttX drivers via Microwindows Objects.rules +ARCH = NUTTX + +ifeq ($(CONFIG_MICROWINDOWS_KBD_EVENT),y) +KEYBOARD = NUTTXKBD +else ifeq ($(CONFIG_MICROWINDOWS_KBD_RAW),y) +KEYBOARD = NUTTXRAWKBD +else ifeq ($(CONFIG_MICROWINDOWS_KBD_NONE),y) +KEYBOARD = NOKBD +else ifeq ($(CONFIG_MICROWINDOWS_KBD_CUSTOM),y) +KEYBOARD = CUSTOM +endif + +ifeq ($(CONFIG_MICROWINDOWS_MOUSE_RELATIVE),y) +MOUSE = NUTTXMOUSE +else ifeq ($(CONFIG_MICROWINDOWS_MOUSE_TS),y) +MOUSE = NUTTXTOUCH +else ifeq ($(CONFIG_MICROWINDOWS_MOUSE_NONE),y) +MOUSE = NOMOUSE +else ifeq ($(CONFIG_MICROWINDOWS_MOUSE_CUSTOM),y) +MOUSE = CUSTOM +endif + +HAVE_FNT_SUPPORT = Y + +-include microwindows/src/engine/Objects.rules +-include microwindows/src/drivers/Objects.rules +-include microwindows/src/fonts/Objects.rules + +CSRCS += $(MW_CORE_OBJS:%.o=%.c) + +ifneq ($(CONFIG_MICROWINDOWS_MWIN),) + +# Win32 API +MWIN_CORE_SRCS = microwindows/src/mwin/winmain.c +MWIN_CORE_SRCS += microwindows/src/mwin/winuser.c +MWIN_CORE_SRCS += microwindows/src/mwin/wingdi.c +MWIN_CORE_SRCS += microwindows/src/mwin/winexpos.c +MWIN_CORE_SRCS += microwindows/src/mwin/winclip.c +MWIN_CORE_SRCS += microwindows/src/mwin/winevent.c +MWIN_CORE_SRCS += microwindows/src/mwin/windefw.c +MWIN_CORE_SRCS += microwindows/src/mwin/winrgn.c +MWIN_CORE_SRCS += microwindows/src/mwin/winfont.c +MWIN_CORE_SRCS += microwindows/src/mwin/winsbar.c +MWIN_CORE_SRCS += microwindows/src/mwin/winres.c + +# mwin winlib +WINLIB_SRCS = microwindows/src/mwin/winlib/draw3d.c +WINLIB_SRCS += microwindows/src/mwin/winlib/fastfill.c +WINLIB_SRCS += microwindows/src/mwin/winlib/windlg.c +WINLIB_SRCS += microwindows/src/mwin/winlib/graph3d.c +WINLIB_SRCS += microwindows/src/mwin/winlib/caret.c +WINLIB_SRCS += microwindows/src/mwin/winlib/static.c +WINLIB_SRCS += microwindows/src/mwin/winlib/button.c +WINLIB_SRCS += microwindows/src/mwin/winlib/newedit.c +WINLIB_SRCS += microwindows/src/mwin/winlib/newlistbox.c +WINLIB_SRCS += microwindows/src/mwin/winlib/combobox.c +WINLIB_SRCS += microwindows/src/mwin/winlib/progbar.c +WINLIB_SRCS += microwindows/src/mwin/winlib/scrlbar.c +WINLIB_SRCS += microwindows/src/mwin/winlib/medit.c +WINLIB_SRCS += microwindows/src/mwin/winlib/ptinsid.c +WINLIB_SRCS += microwindows/src/mwin/winlib/insetr.c + +CSRCS += $(MWIN_CORE_SRCS) $(WINLIB_SRCS) +CFLAGS += -DNOGDI + +endif + +include $(APPDIR)/Application.mk diff --git a/graphics/nxwidgets/UnitTests/CButton/cbutton_main.cxx b/graphics/nxwidgets/UnitTests/CButton/cbutton_main.cxx index 472348ba5..663048593 100644 --- a/graphics/nxwidgets/UnitTests/CButton/cbutton_main.cxx +++ b/graphics/nxwidgets/UnitTests/CButton/cbutton_main.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CButton/cbuttontest.cxx b/graphics/nxwidgets/UnitTests/CButton/cbuttontest.cxx index 773f0435e..c7628b41a 100644 --- a/graphics/nxwidgets/UnitTests/CButton/cbuttontest.cxx +++ b/graphics/nxwidgets/UnitTests/CButton/cbuttontest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CButton/cbuttontest.hxx b/graphics/nxwidgets/UnitTests/CButton/cbuttontest.hxx index 8b1749d88..608e77929 100644 --- a/graphics/nxwidgets/UnitTests/CButton/cbuttontest.hxx +++ b/graphics/nxwidgets/UnitTests/CButton/cbuttontest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarray_main.cxx b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarray_main.cxx index 8af3de7d1..51a09a16c 100644 --- a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarray_main.cxx +++ b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarray_main.cxx @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.cxx b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.cxx index a19c6f880..e84caf02f 100644 --- a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.cxx +++ b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.hxx b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.hxx index a13783bd5..0d69a2838 100644 --- a/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.hxx +++ b/graphics/nxwidgets/UnitTests/CButtonArray/cbuttonarraytest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckbox_main.cxx b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckbox_main.cxx index 8719611ba..087e31499 100644 --- a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckbox_main.cxx +++ b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckbox_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.cxx b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.cxx index 1ff5a0a53..8f8398324 100644 --- a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.cxx +++ b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.hxx b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.hxx index 1bd72dba5..205884f47 100644 --- a/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.hxx +++ b/graphics/nxwidgets/UnitTests/CCheckBox/ccheckboxtest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbutton_main.cxx b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbutton_main.cxx index ec673c5f2..01081b38e 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbutton_main.cxx +++ b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbutton_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.cxx b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.cxx index f46468626..acee748fa 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.cxx +++ b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.hxx b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.hxx index d7593734f..89884872f 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.hxx +++ b/graphics/nxwidgets/UnitTests/CGlyphButton/cglyphbuttontest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontal_main.cxx b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontal_main.cxx index e40c78be7..59546fa73 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontal_main.cxx +++ b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontal_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.cxx b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.cxx index 2809bfb26..a9e38f00a 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.cxx +++ b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.hxx b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.hxx index c3fd43ecc..0d563eecb 100644 --- a/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.hxx +++ b/graphics/nxwidgets/UnitTests/CGlyphSliderHorizontal/cglyphsliderhorizontaltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CImage/cimage_main.cxx b/graphics/nxwidgets/UnitTests/CImage/cimage_main.cxx index 3423d6e74..339627a74 100644 --- a/graphics/nxwidgets/UnitTests/CImage/cimage_main.cxx +++ b/graphics/nxwidgets/UnitTests/CImage/cimage_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CImage/cimagetest.cxx b/graphics/nxwidgets/UnitTests/CImage/cimagetest.cxx index 2472f19f2..9eedd8aa4 100644 --- a/graphics/nxwidgets/UnitTests/CImage/cimagetest.cxx +++ b/graphics/nxwidgets/UnitTests/CImage/cimagetest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CImage/cimagetest.hxx b/graphics/nxwidgets/UnitTests/CImage/cimagetest.hxx index 34568a5f4..99970b6f1 100644 --- a/graphics/nxwidgets/UnitTests/CImage/cimagetest.hxx +++ b/graphics/nxwidgets/UnitTests/CImage/cimagetest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CKeypad/ckeypad_main.cxx b/graphics/nxwidgets/UnitTests/CKeypad/ckeypad_main.cxx index bfd5dfd2c..9e9d4b269 100644 --- a/graphics/nxwidgets/UnitTests/CKeypad/ckeypad_main.cxx +++ b/graphics/nxwidgets/UnitTests/CKeypad/ckeypad_main.cxx @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.cxx b/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.cxx index 64cf7c366..a51555e44 100644 --- a/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.cxx +++ b/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.hxx b/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.hxx index f566d8bcd..783fb0b52 100644 --- a/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.hxx +++ b/graphics/nxwidgets/UnitTests/CKeypad/ckeypadtest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLabel/clabel_main.cxx b/graphics/nxwidgets/UnitTests/CLabel/clabel_main.cxx index bed815541..c822abf1a 100644 --- a/graphics/nxwidgets/UnitTests/CLabel/clabel_main.cxx +++ b/graphics/nxwidgets/UnitTests/CLabel/clabel_main.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLabel/clabeltest.cxx b/graphics/nxwidgets/UnitTests/CLabel/clabeltest.cxx index 1851674ad..1b439125f 100644 --- a/graphics/nxwidgets/UnitTests/CLabel/clabeltest.cxx +++ b/graphics/nxwidgets/UnitTests/CLabel/clabeltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CLabel/clabeltest.hxx b/graphics/nxwidgets/UnitTests/CLabel/clabeltest.hxx index 57ccc1f0d..317a08bec 100644 --- a/graphics/nxwidgets/UnitTests/CLabel/clabeltest.hxx +++ b/graphics/nxwidgets/UnitTests/CLabel/clabeltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbutton_main.cxx b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbutton_main.cxx index ca0310ae2..0663f74c3 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbutton_main.cxx +++ b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbutton_main.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.cxx b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.cxx index 58fe9b6a1..a8dbe2603 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.cxx +++ b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.hxx b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.hxx index 71946d325..641ccb9ec 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.hxx +++ b/graphics/nxwidgets/UnitTests/CLatchButton/clatchbuttontest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarray_main.cxx b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarray_main.cxx index fd6dcbeb0..a1e713660 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarray_main.cxx +++ b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarray_main.cxx @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.cxx b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.cxx index 3da02db99..146f9172a 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.cxx +++ b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.hxx b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.hxx index 212d512a7..9ebfed210 100644 --- a/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.hxx +++ b/graphics/nxwidgets/UnitTests/CLatchButtonArray/clatchbuttonarraytest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CListBox/clistbox_main.cxx b/graphics/nxwidgets/UnitTests/CListBox/clistbox_main.cxx index 45864f1e5..c9bb459ed 100644 --- a/graphics/nxwidgets/UnitTests/CListBox/clistbox_main.cxx +++ b/graphics/nxwidgets/UnitTests/CListBox/clistbox_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.cxx b/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.cxx index 7bd3836de..b81fff628 100644 --- a/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.cxx +++ b/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.hxx b/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.hxx index 452240dc9..7171c26cd 100644 --- a/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.hxx +++ b/graphics/nxwidgets/UnitTests/CListBox/clistboxtest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbar_main.cxx b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbar_main.cxx index d5c5d4b9f..3df02715f 100644 --- a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbar_main.cxx +++ b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbar_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.cxx b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.cxx index 11ad6bb07..537774ea8 100644 --- a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.cxx +++ b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.hxx b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.hxx index dd8dcf103..657da3d8e 100644 --- a/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.hxx +++ b/graphics/nxwidgets/UnitTests/CProgressBar/cprogressbartest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobutton_main.cxx b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobutton_main.cxx index fd0c13b40..b093382a1 100644 --- a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobutton_main.cxx +++ b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobutton_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.cxx b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.cxx index 683dc02dc..5d0be7bb5 100644 --- a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.cxx +++ b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.hxx b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.hxx index fc8d7ee29..8a5c1f0df 100644 --- a/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.hxx +++ b/graphics/nxwidgets/UnitTests/CRadioButton/cradiobuttontest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontal_main.cxx b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontal_main.cxx index 51ea0e6e2..2429a75d1 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontal_main.cxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontal_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.cxx b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.cxx index e59f0fe3b..21501c125 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.cxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.hxx b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.hxx index c8b31b392..b53d318d5 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.hxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarHorizontal/cscrollbarhorizontaltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarvertical_main.cxx b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarvertical_main.cxx index 04664b08b..9497febe5 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarvertical_main.cxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarvertical_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.cxx b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.cxx index bbf21f2c2..8770f5df4 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.cxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.hxx b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.hxx index 159fb3bf2..fc062fed2 100644 --- a/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.hxx +++ b/graphics/nxwidgets/UnitTests/CScrollbarVertical/cscrollbarverticaltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontal_main.cxx b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontal_main.cxx index 9b8f21b54..6c122a20b 100644 --- a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontal_main.cxx +++ b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontal_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.cxx b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.cxx index 8a9e19496..5dd3880f7 100644 --- a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.cxx +++ b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.hxx b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.hxx index 538af2daf..9f9297399 100644 --- a/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.hxx +++ b/graphics/nxwidgets/UnitTests/CSliderHorizonal/csliderhorizontaltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderVertical/cslidervertical_main.cxx b/graphics/nxwidgets/UnitTests/CSliderVertical/cslidervertical_main.cxx index 40872745c..2d109b35a 100644 --- a/graphics/nxwidgets/UnitTests/CSliderVertical/cslidervertical_main.cxx +++ b/graphics/nxwidgets/UnitTests/CSliderVertical/cslidervertical_main.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.cxx b/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.cxx index efad3275e..aa2241764 100644 --- a/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.cxx +++ b/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.hxx b/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.hxx index 7bad5e81d..0bac9b3d6 100644 --- a/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.hxx +++ b/graphics/nxwidgets/UnitTests/CSliderVertical/csliderverticaltest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CTextBox/ctextbox_main.cxx b/graphics/nxwidgets/UnitTests/CTextBox/ctextbox_main.cxx index 92de3dd85..ef9a5e362 100644 --- a/graphics/nxwidgets/UnitTests/CTextBox/ctextbox_main.cxx +++ b/graphics/nxwidgets/UnitTests/CTextBox/ctextbox_main.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.cxx b/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.cxx index 4d0fad790..d34d937b9 100644 --- a/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.cxx +++ b/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.hxx b/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.hxx index 427824422..21e994d74 100644 --- a/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.hxx +++ b/graphics/nxwidgets/UnitTests/CTextBox/ctextboxtest.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/src/ccallback.cxx b/graphics/nxwidgets/src/ccallback.cxx index 510f55161..f07deaf93 100644 --- a/graphics/nxwidgets/src/ccallback.cxx +++ b/graphics/nxwidgets/src/ccallback.cxx @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -308,7 +308,7 @@ void CCallback::newKeyboardEvent(NXHANDLE hwnd, uint8_t nCh, * NXEVENT_SYCNCHED - Synchronization handshake * * This completes the handshake started by nx_synch(). nx_synch() - * sends a syncrhonization messages to the NX server which responds + * sends a synchronization message to the NX server which responds * with this event. The sleeping client is awakened and continues * graphics processing, completing the handshake. * diff --git a/graphics/nxwidgets/src/cgraphicsport.cxx b/graphics/nxwidgets/src/cgraphicsport.cxx index 2e299162d..ffe56033e 100644 --- a/graphics/nxwidgets/src/cgraphicsport.cxx +++ b/graphics/nxwidgets/src/cgraphicsport.cxx @@ -64,7 +64,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/src/cimage.cxx b/graphics/nxwidgets/src/cimage.cxx index a500c3b4e..337b7fea4 100644 --- a/graphics/nxwidgets/src/cimage.cxx +++ b/graphics/nxwidgets/src/cimage.cxx @@ -62,7 +62,7 @@ #include #include -#include +#include #include diff --git a/graphics/nxwidgets/src/clabelgrid.cxx b/graphics/nxwidgets/src/clabelgrid.cxx index 6302f4c6a..175535bba 100644 --- a/graphics/nxwidgets/src/clabelgrid.cxx +++ b/graphics/nxwidgets/src/clabelgrid.cxx @@ -59,7 +59,7 @@ ****************************************************************************/ #include -#include +#include #include "graphics/nxwidgets/clabelgrid.hxx" #include "graphics/nxwidgets/clabel.hxx" diff --git a/graphics/nxwidgets/src/cnumericedit.cxx b/graphics/nxwidgets/src/cnumericedit.cxx index 770f727c7..22556c7d8 100644 --- a/graphics/nxwidgets/src/cnumericedit.cxx +++ b/graphics/nxwidgets/src/cnumericedit.cxx @@ -66,7 +66,7 @@ #include #include -#include +#include #include "graphics/nxwidgets/cnumericedit.hxx" #include "graphics/nxwidgets/cbutton.hxx" diff --git a/graphics/nxwidgets/src/cnxserver.cxx b/graphics/nxwidgets/src/cnxserver.cxx index 67ad93332..60f841833 100644 --- a/graphics/nxwidgets/src/cnxserver.cxx +++ b/graphics/nxwidgets/src/cnxserver.cxx @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include diff --git a/graphics/nxwidgets/src/cnxtimer.cxx b/graphics/nxwidgets/src/cnxtimer.cxx index 81ba0da64..dd6be5101 100644 --- a/graphics/nxwidgets/src/cnxtimer.cxx +++ b/graphics/nxwidgets/src/cnxtimer.cxx @@ -64,7 +64,7 @@ #include #include #include -#include +#include #include #include diff --git a/graphics/nxwidgets/src/cscaledbitmap.cxx b/graphics/nxwidgets/src/cscaledbitmap.cxx index ec46ca5c4..65f1a372f 100644 --- a/graphics/nxwidgets/src/cscaledbitmap.cxx +++ b/graphics/nxwidgets/src/cscaledbitmap.cxx @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include @@ -64,7 +64,7 @@ CScaledBitmap::CScaledBitmap(IBitmap *bitmap, struct nxgl_size_s &newSize) m_xScale = itob16((uint32_t)m_bitmap->getWidth()) / newSize.w; // Similarly, yScale will be used to convert a request Y position to a Y - // positionin the contained bitmap: + // position in the contained bitmap: // // yImage = yRequested * oldHeight / newHeight // = yRequested * yScale diff --git a/graphics/nxwidgets/src/cscrollingpanel.cxx b/graphics/nxwidgets/src/cscrollingpanel.cxx index d369d42fc..80f51c87c 100644 --- a/graphics/nxwidgets/src/cscrollingpanel.cxx +++ b/graphics/nxwidgets/src/cscrollingpanel.cxx @@ -62,7 +62,7 @@ #include #include -#include +#include #include "graphics/nxwidgets/cwidgetcontrol.hxx" #include "graphics/nxwidgets/cscrollingpanel.hxx" diff --git a/graphics/nxwidgets/src/cwidgetcontrol.cxx b/graphics/nxwidgets/src/cwidgetcontrol.cxx index 921b05412..3107d7729 100644 --- a/graphics/nxwidgets/src/cwidgetcontrol.cxx +++ b/graphics/nxwidgets/src/cwidgetcontrol.cxx @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include "graphics/nxwidgets/nxconfig.hxx" diff --git a/graphics/nxwidgets/src/singletons.cxx b/graphics/nxwidgets/src/singletons.cxx index e6c4da73e..a6e44797c 100644 --- a/graphics/nxwidgets/src/singletons.cxx +++ b/graphics/nxwidgets/src/singletons.cxx @@ -64,7 +64,7 @@ #include #include #include -#include +#include #include "graphics/nxwidgets/nxconfig.hxx" #include "graphics/nxwidgets/cnxserver.hxx" diff --git a/graphics/nxwm/src/ccalibration.cxx b/graphics/nxwm/src/ccalibration.cxx index 21adb2183..262a9d567 100644 --- a/graphics/nxwm/src/ccalibration.cxx +++ b/graphics/nxwm/src/ccalibration.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #ifdef CONFIG_NXWM_TOUCHSCREEN_CONFIGDATA diff --git a/graphics/nxwm/src/chexcalculator.cxx b/graphics/nxwm/src/chexcalculator.cxx index e5ab0d9e8..3ba68eac3 100644 --- a/graphics/nxwm/src/chexcalculator.cxx +++ b/graphics/nxwm/src/chexcalculator.cxx @@ -27,7 +27,7 @@ #include #include -#include +#include #include "graphics/nxwidgets/cwidgetcontrol.hxx" @@ -734,7 +734,7 @@ void CHexCalculator::handleActionEvent(const NXWidgets::CWidgetEventArgs &e) if (m_result) { - m_accum = (uint64_t)g_keyDesc[index].value; + m_accum = g_keyDesc[index].value; } // Otherwise, add the new value to the accumulator. The way @@ -743,12 +743,12 @@ void CHexCalculator::handleActionEvent(const NXWidgets::CWidgetEventArgs &e) else if (m_hexMode) { m_accum <<= 4; - m_accum |= (uint64_t)g_keyDesc[index].value; + m_accum |= g_keyDesc[index].value; } else { m_accum *= 10; - m_accum += (uint64_t)g_keyDesc[index].value; + m_accum += g_keyDesc[index].value; } updateText(); } diff --git a/graphics/nxwm/src/ckeyboard.cxx b/graphics/nxwm/src/ckeyboard.cxx index 20dcc44cc..7afb0b0ae 100644 --- a/graphics/nxwm/src/ckeyboard.cxx +++ b/graphics/nxwm/src/ckeyboard.cxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "graphics/nxwm/nxwmconfig.hxx" @@ -188,7 +188,7 @@ int CKeyboard::open(void) if (errcode == ENOENT || errcode == ENODEV) { - // REVIST: Can we inject a constant string here to let the + // REVISIT: Can we inject a constant string here to let the // user know that we are waiting for a USB keyboard to be // connected? diff --git a/graphics/nxwm/src/cmediaplayer.cxx b/graphics/nxwm/src/cmediaplayer.cxx index bcfb95718..8561eb469 100644 --- a/graphics/nxwm/src/cmediaplayer.cxx +++ b/graphics/nxwm/src/cmediaplayer.cxx @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "system/nxplayer.h" #include diff --git a/graphics/nxwm/src/cnxterm.cxx b/graphics/nxwm/src/cnxterm.cxx index 0c4d20455..40bc5997b 100644 --- a/graphics/nxwm/src/cnxterm.cxx +++ b/graphics/nxwm/src/cnxterm.cxx @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include "nshlib/nshlib.h" diff --git a/graphics/nxwm/src/ctaskbar.cxx b/graphics/nxwm/src/ctaskbar.cxx index d66d36850..7160b93d9 100644 --- a/graphics/nxwm/src/ctaskbar.cxx +++ b/graphics/nxwm/src/ctaskbar.cxx @@ -26,7 +26,7 @@ #include -#include +#include #include @@ -617,7 +617,7 @@ bool CTaskbar::maximizeApplication(IApplication *app) /** * Minimize an application by moving its window to the bottom of the and - * redrawing the next visible appliation. + * redrawing the next visible application. * * @param app. The new application to add to the task bar * @return true on success diff --git a/graphics/nxwm/src/ctouchscreen.cxx b/graphics/nxwm/src/ctouchscreen.cxx index bbebc0f7a..b7807c710 100644 --- a/graphics/nxwm/src/ctouchscreen.cxx +++ b/graphics/nxwm/src/ctouchscreen.cxx @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include @@ -181,7 +181,7 @@ void CTouchscreen::setCalibrationData(const struct SCalibrationData &caldata) m_calibData = caldata; // Note that we have calibration data. Data will now be scaled and forwarded - // to NX (unless we are still in cpature mode) + // to NX (unless we are still in capture mode) m_calibrated = true; @@ -205,7 +205,7 @@ bool CTouchscreen::waitRawTouchData(struct touch_sample_s *touch) { _info("Capturing touch input\n"); - // Setup to cpature raw data into the user provided buffer + // Setup to capture raw data into the user provided buffer sched_lock(); m_touch = touch; diff --git a/graphics/nxwm/src/cwindowmessenger.cxx b/graphics/nxwm/src/cwindowmessenger.cxx index 8761e4799..92a2ba91a 100644 --- a/graphics/nxwm/src/cwindowmessenger.cxx +++ b/graphics/nxwm/src/cwindowmessenger.cxx @@ -28,7 +28,7 @@ #include -#include +#include #include #include "graphics/nxwm/nxwmconfig.hxx" diff --git a/graphics/nxwm/src/nxwm_main.cxx b/graphics/nxwm/src/nxwm_main.cxx index 98feabbf6..6b6a3ca1d 100644 --- a/graphics/nxwm/src/nxwm_main.cxx +++ b/graphics/nxwm/src/nxwm_main.cxx @@ -485,16 +485,6 @@ static bool createMediaPlayer(void) int main(int argc, char *argv[]) { -#if defined(CONFIG_BOARDCTL) && !defined(CONFIG_BOARD_LATE_INITIALIZE) - // Should we perform board-specific initialization? There are two ways - // that board initialization can occur: 1) automatically via - // board_late_initialize() during bootup if CONFIG_BOARD_LATE_INITIALIZE, or - // 2) here via a call to boardctl() if the interface is enabled - // (CONFIG_BOARDCTL=y). - - boardctl(BOARDIOC_INIT, 0); -#endif - #ifdef CONFIG_NXWM_NXTERM // Initialize the NSH library diff --git a/graphics/slcd/cslcd.cxx b/graphics/slcd/cslcd.cxx index 8f7320e1e..f732b8e42 100644 --- a/graphics/slcd/cslcd.cxx +++ b/graphics/slcd/cslcd.cxx @@ -25,7 +25,7 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include +#include #include diff --git a/graphics/tiff/tiff_addstrip.c b/graphics/tiff/tiff_addstrip.c index ff4a9d598..965152eac 100644 --- a/graphics/tiff/tiff_addstrip.c +++ b/graphics/tiff/tiff_addstrip.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include "graphics/tiff.h" @@ -65,13 +65,15 @@ * tiff_initialize(). * * Input Parameters: - * info - A pointer to the caller allocated parameter passing/TIFF state instance. + * info - A pointer to the caller allocated parameter passing/TIFF + * state instance. * buffer - A buffer containing a single row of data. * * Returned Value: * Zero (OK) on success. A negated errno value on failure. * ****************************************************************************/ + int tiff_convstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) { #ifdef CONFIG_DEBUG_GRAPHICS @@ -100,9 +102,9 @@ int tiff_convstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) /* Convert RGB565 to RGB888 */ rgb565 = *src++; - *dest++ = (rgb565 >> (11-3)) & 0xf8; /* Move bits 11-15 to 3-7 */ - *dest++ = (rgb565 >> ( 5-2)) & 0xfc; /* Move bits 5-10 to 2-7 */ - *dest++ = (rgb565 << ( 3)) & 0xf8; /* Move bits 0- 4 to 3-7 */ + *dest++ = (rgb565 >> (11 - 3)) & 0xf8; /* Move bits 11-15 to 3-7 */ + *dest++ = (rgb565 >> (5 - 2)) & 0xfc; /* Move bits 5-10 to 2-7 */ + *dest++ = (rgb565 << 3) & 0xf8; /* Move bits 0- 4 to 3-7 */ /* Update the byte count */ @@ -113,7 +115,7 @@ int tiff_convstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) /* Flush the conversion buffer to tmpfile2 when it becomes full */ - if (nbytes > (info->iosize-3)) + if (nbytes > (info->iosize - 3)) { ret = tiff_write(info->tmp2fd, info->iobuffer, nbytes); if (ret < 0) @@ -134,6 +136,7 @@ int tiff_convstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) #ifdef CONFIG_DEBUG_GRAPHICS DEBUGASSERT(ntotal == info->bps); #endif + return ret; } @@ -150,7 +153,8 @@ int tiff_convstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) * tiff_initialize(). * * Input Parameters: - * info - A pointer to the caller allocated parameter passing/TIFF state instance. + * info - A pointer to the caller allocated parameter passing/TIFF + * state instance. * buffer - A buffer containing a single row of data. * * Returned Value: @@ -172,7 +176,9 @@ int tiff_addstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) ret = tiff_convstrip(info, strip); } - /* For other formats, it is a simple write using the number of bytes per strip */ + /* For other formats, it is a simple write using the number of bytes per + * strip. + */ else { @@ -191,6 +197,7 @@ int tiff_addstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) { goto errout; } + info->outsize += 4; ret = tiff_putint32(info->tmp1fd, info->tmp2size); @@ -198,6 +205,7 @@ int tiff_addstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) { goto errout; } + info->tmp1size += 4; /* Increment the size of tmp2file. */ @@ -212,14 +220,17 @@ int tiff_addstrip(FAR struct tiff_info_s *info, FAR const uint8_t *strip) ret = (int)newsize; goto errout; } + info->tmp2size = (size_t)newsize; /* Increment the number of strips in the TIFF file */ info->nstrips++; + return OK; errout: + tiff_abort(info); return ret; } diff --git a/graphics/tiff/tiff_finalize.c b/graphics/tiff/tiff_finalize.c index 2ad01a06e..da49343c9 100644 --- a/graphics/tiff/tiff_finalize.c +++ b/graphics/tiff/tiff_finalize.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include "graphics/tiff.h" diff --git a/graphics/tiff/tiff_initialize.c b/graphics/tiff/tiff_initialize.c index b6de8408d..5ce27cac4 100644 --- a/graphics/tiff/tiff_initialize.c +++ b/graphics/tiff/tiff_initialize.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include "graphics/tiff.h" @@ -41,6 +41,7 @@ /**************************************************************************** * Pre-Processor Definitions ****************************************************************************/ + /* Bi-level Images * * Offset Description Contents/Notes @@ -50,13 +51,18 @@ * 8 [2 bytes padding] * IFD: 10 Number of Directory Entries 13 * 12 NewSubfileType - * 24 ImageWidth Number of columns is a user parameter - * 36 ImageLength Number of rows is a user parameter - * 48 Compression Hard-coded no compression (for now) + * 24 ImageWidth Number of columns is a user + * parameter + * 36 ImageLength Number of rows is a user + * parameter + * 48 Compression Hard-coded no compression + * (for now) * 60 PhotometricInterpretation Value is a user parameter - * 72 StripOffsets Offset and count determined as strips added + * 72 StripOffsets Offset and count determined + * as strips added * 84 RowsPerStrip Value is a user parameter - * 96 StripByteCounts Offset and count determined as strips added + * 96 StripByteCounts Offset and count determined + * as strips added * 108 XResolution Value is a user parameter * 120 YResolution Value is a user parameter * 132 Resolution Unit Hard-coded to "inches" @@ -67,8 +73,10 @@ * Values: * 172 XResolution Hard-coded to 300/1 * 180 YResolution Hard-coded to 300/1 - * 188 "NuttX" Length = 6 (including NUL terminator) - * 194 "YYYY:MM:DD HH:MM:SS" Length = 20 (ncluding NUL terminator) + * 188 "NuttX" Length = 6 (including NUL + * terminator) + * 194 "YYYY:MM:DD HH:MM:SS" Length = 20 (including NUL + * terminator) * 214 [2 bytes padding] * 216 StripByteCounts Beginning of strip byte counts * xxx StripOffsets Beginning of strip offsets @@ -76,7 +84,7 @@ * xxx Data for strips Beginning of strip data */ -#define TIFF_IFD_OFFSET (SIZEOF_TIFF_HEADER+2) +#define TIFF_IFD_OFFSET (SIZEOF_TIFF_HEADER + 2) #define TIFF_BILEV_NIFDENTRIES 13 #define TIFF_BILEV_STRIPIFDOFFS 72 @@ -102,14 +110,19 @@ * 8 [2 bytes padding] * IFD: 10 Number of Directory Entries 14 * 12 NewSubfileType - * 24 ImageWidth Number of columns is a user parameter - * 36 ImageLength Number of rows is a user parameter + * 24 ImageWidth Number of columns is a user + * parameter + * 36 ImageLength Number of rows is a user + * parameter * 48 BitsPerSample - * 60 Compression Hard-coded no compression (for now) + * 60 Compression Hard-coded no compression + * (for now) * 72 PhotometricInterpretation Value is a user parameter - * 84 StripOffsets Offset and count determined as strips added + * 84 StripOffsets Offset and count determined + * as strips added * 96 RowsPerStrip Value is a user parameter - * 108 StripByteCounts Offset and count determined as strips added + * 108 StripByteCounts Offset and count determined + * as strips added * 120 XResolution Value is a user parameter * 132 YResolution Value is a user parameter * 144 Resolution Unit Hard-coded to "inches" @@ -120,8 +133,10 @@ * Values: * 184 XResolution Hard-coded to 300/1 * 192 YResolution Hard-coded to 300/1 - * 200 "NuttX" Length = 6 (including NUL terminator) - * 206 "YYYY:MM:DD HH:MM:SS" Length = 20 (ncluding NUL terminator) + * 200 "NuttX" Length = 6 (including NUL + * terminator) + * 206 "YYYY:MM:DD HH:MM:SS" Length = 20 (including NUL + * terminator) * 226 [2 bytes padding] * 228 StripByteCounts Beginning of strip byte counts * xxx StripOffsets Beginning of strip offsets @@ -148,15 +163,20 @@ * 8 [2 bytes padding] * IFD: 10 Number of Directory Entries 15 * 12 NewSubfileType - * 24 ImageWidth Number of columns is a user parameter - * 36 ImageLength Number of rows is a user parameter + * 24 ImageWidth Number of columns is a user + * parameter + * 36 ImageLength Number of rows is a user + * parameter * 48 BitsPerSample 8, 8, 8 - * 60 Compression Hard-coded no compression (for now) + * 60 Compression Hard-coded no compression + * (for now) * 72 PhotometricInterpretation Value is a user parameter - * 84 StripOffsets Offset and count determined as strips added + * 84 StripOffsets Offset and count determined + * as strips added * 96 SamplesPerPixel Hard-coded to 3 * 108 RowsPerStrip Value is a user parameter - * 120 StripByteCounts Offset and count determined as strips added + * 120 StripByteCounts Offset and count determined + * as strips added * 132 XResolution Value is a user parameter * 144 YResolution Value is a user parameter * 156 Resolution Unit Hard-coded to "inches" @@ -169,8 +189,10 @@ * 204 YResolution Hard-coded to 300/1 * 212 BitsPerSample 8, 8, 8 * 218 [2 bytes padding] - * 220 "NuttX" Length = 6 (including NUL terminator) - * 226 "YYYY:MM:DD HH:MM:SS" Length = 20 (ncluding NUL terminator) + * 220 "NuttX" Length = 6 (including NUL + * terminator) + * 226 "YYYY:MM:DD HH:MM:SS" Length = 20 (including NUL + * terminator) * 246 [2 bytes padding] * 248 StripByteCounts Beginning of strip byte counts * xxx StripOffsets Beginning of strip offsets @@ -189,8 +211,12 @@ #define TIFF_RGB_DATEOFFSET 226 #define TIFF_RGB_STRIPBCOFFSET 248 -/* Debug *******************************************************************/ -/* CONFIG_DEBUG_TIFFOFFSETS may be defined (along with CONFIG_DEBUG_FEATURES and +/**************************************************************************** + * Debug + ****************************************************************************/ + +/* CONFIG_DEBUG_TIFFOFFSETS may be defined (along with CONFIG_DEBUG_FEATURES + * and * CONFIG_DEBUG_GRAPHICS) in order to verify the pre-determined TIFF file * offsets. */ @@ -200,11 +226,11 @@ #endif #ifdef CONFIG_DEBUG_TIFFOFFSETS -# define tiff_offset(o,l) (o) += (l) -# define tiff_checkoffs(o,x) DEBUGASSERT((o) == (x)) +# define tiff_offset(o, l) (o) += (l) +# define tiff_checkoffs(o, x) DEBUGASSERT((o) == (x)) #else -# define tiff_offset(o,l) -# define tiff_checkoffs(o,x) +# define tiff_offset(o, l) +# define tiff_checkoffs(o, x) #endif /**************************************************************************** @@ -408,9 +434,9 @@ static int tiff_datetime(FAR char *timbuf, unsigned int buflen) /* Break the current time up into the format needed by strftime */ - gmtime_r((FAR const time_t*)&ts.tv_sec, &tm); + gmtime_r(&ts.tv_sec, &tm); - /* Comvert the current time in the TIFF format */ + /* Convert the current time to TIFF format */ strftime(timbuf, buflen, TIFF_DATETIME_FORMAT, &tm); return OK; @@ -427,7 +453,8 @@ static int tiff_datetime(FAR char *timbuf, unsigned int buflen) * Setup to create a new TIFF file. * * Input Parameters: - * info - A pointer to the caller allocated parameter passing/TIFF state instance. + * info - A pointer to the caller allocated parameter passing/TIFF state + * instance. * * Returned Value: * Zero (OK) on success. A negated errno value on failure. @@ -447,7 +474,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) /* Open all output files */ - info->outfd = open(info->outfile, O_RDWR|O_CREAT|O_TRUNC, 0666); + info->outfd = open(info->outfile, O_RDWR | O_CREAT | O_TRUNC, 0666); if (info->outfd < 0) { gerr("ERROR: Failed to open %s for reading/writing: %d\n", @@ -455,7 +482,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) goto errout; } - info->tmp1fd = open(info->tmpfile1, O_RDWR|O_CREAT|O_TRUNC, 0666); + info->tmp1fd = open(info->tmpfile1, O_RDWR | O_CREAT | O_TRUNC, 0666); if (info->tmp1fd < 0) { gerr("ERROR: Failed to open %s for reading/writing: %d\n", @@ -463,7 +490,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) goto errout; } - info->tmp2fd = open(info->tmpfile2, O_RDWR|O_CREAT|O_TRUNC, 0666); + info->tmp2fd = open(info->tmpfile2, O_RDWR | O_CREAT | O_TRUNC, 0666); if (info->tmp2fd < 0) { gerr("ERROR: Failed to open %s for reading/writing: %d\n", @@ -505,7 +532,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) case FB_FMT_RGB24: /* BPP=24 R=8, G=8, B=8 */ info->filefmt = &g_rgbinfo; /* RGB file image file info */ info->imgflags = IMGFLAGS_FMT_RGB24; /* Bit encoded image characteristics */ - info->bps = 3 *info->pps; /* Bytes per strip */ + info->bps = 3 * info->pps; /* Bytes per strip */ break; default: @@ -526,6 +553,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, TIFF_IFD_OFFSET); /* Write the Number of directory entries @@ -538,6 +566,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, 2); /* Write the NewSubfileType IFD entry @@ -545,11 +574,13 @@ int tiff_initialize(FAR struct tiff_info_s *info) * All formats: Offset 12 NewSubfileType */ - ret = tiff_putifdentry16(info, IFD_TAG_NEWSUBFILETYPE, IFD_FIELD_LONG, 1, 0); + ret = tiff_putifdentry16(info, IFD_TAG_NEWSUBFILETYPE, + IFD_FIELD_LONG, 1, 0); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write ImageWidth and ImageLength @@ -558,17 +589,20 @@ int tiff_initialize(FAR struct tiff_info_s *info) * 36 ImageLength Number of rows is a user parameter */ - ret = tiff_putifdentry16(info, IFD_TAG_IMAGEWIDTH, IFD_FIELD_SHORT, 1, info->imgwidth); + ret = tiff_putifdentry16(info, IFD_TAG_IMAGEWIDTH, + IFD_FIELD_SHORT, 1, info->imgwidth); if (ret == OK) { - ret= tiff_putifdentry16(info, IFD_TAG_IMAGELENGTH, IFD_FIELD_SHORT, 1, info->imgheight); + ret = tiff_putifdentry16(info, IFD_TAG_IMAGELENGTH, + IFD_FIELD_SHORT, 1, info->imgheight); } if (ret < 0) { goto errout; } - tiff_offset(offset, 2*SIZEOF_IFD_ENTRY); + + tiff_offset(offset, 2 * SIZEOF_IFD_ENTRY); /* Write BitsPerSample * @@ -589,20 +623,24 @@ int tiff_initialize(FAR struct tiff_info_s *info) val16 = 4; } - ret = tiff_putifdentry16(info, IFD_TAG_BITSPERSAMPLE, IFD_FIELD_SHORT, 1, val16); + ret = tiff_putifdentry16(info, IFD_TAG_BITSPERSAMPLE, + IFD_FIELD_SHORT, 1, val16); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); } else if (IMGFLAGS_ISRGB(info->imgflags)) { - ret = tiff_putifdentry(info, IFD_TAG_BITSPERSAMPLE, IFD_FIELD_SHORT, 3, TIFF_RGB_BPSOFFSET); + ret = tiff_putifdentry(info, IFD_TAG_BITSPERSAMPLE, + IFD_FIELD_SHORT, 3, TIFF_RGB_BPSOFFSET); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); } @@ -613,11 +651,13 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 60 " " " " "" " " " " " " */ - ret = tiff_putifdentry16(info, IFD_TAG_COMPRESSION, IFD_FIELD_SHORT, 1, TAG_COMP_NONE); + ret = tiff_putifdentry16(info, IFD_TAG_COMPRESSION, + IFD_FIELD_SHORT, 1, TAG_COMP_NONE); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write PhotometricInterpretation: @@ -641,6 +681,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write StripOffsets: @@ -656,6 +697,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write SamplesPerPixel @@ -667,11 +709,13 @@ int tiff_initialize(FAR struct tiff_info_s *info) if (IMGFLAGS_ISRGB(info->imgflags)) { - ret = tiff_putifdentry16(info, IFD_TAG_SAMPLESPERPIXEL, IFD_FIELD_SHORT, 1, 3); + ret = tiff_putifdentry16(info, IFD_TAG_SAMPLESPERPIXEL, + IFD_FIELD_SHORT, 1, 3); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); } @@ -682,26 +726,33 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 108 Value is a user parameter */ - ret = tiff_putifdentry16(info, IFD_TAG_ROWSPERSTRIP, IFD_FIELD_SHORT, 1, info->rps); + ret = tiff_putifdentry16(info, IFD_TAG_ROWSPERSTRIP, + IFD_FIELD_SHORT, 1, info->rps); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write StripByteCounts: * - * Bi-level Images: Offset 96 Count determined as strips added, Value offset = 216 - * Greyscale: Offset 108 Count determined as strips added, Value offset = 228 - * RGB: Offset 120 Count determined as strips added, Value offset = 248 + * Bi-level Images: Offset 96 Count determined as strips added, Value + * offset = 216 + * Greyscale: Offset 108 Count determined as strips added, Value + * offset = 228 + * RGB: Offset 120 Count determined as strips added, Value + * offset = 248 */ tiff_checkoffs(offset, info->filefmt->sbcifdoffset); - ret = tiff_putifdentry(info, IFD_TAG_STRIPCOUNTS, IFD_FIELD_LONG, 0, info->filefmt->sbcoffset); + ret = tiff_putifdentry(info, IFD_TAG_STRIPCOUNTS, + IFD_FIELD_LONG, 0, info->filefmt->sbcoffset); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write XResolution and YResolution: @@ -711,17 +762,22 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 132 and 144, Values are a user parameters */ - ret = tiff_putifdentry(info, IFD_TAG_XRESOLUTION, IFD_FIELD_RATIONAL, 1, info->filefmt->xresoffset); + ret = tiff_putifdentry(info, IFD_TAG_XRESOLUTION, + IFD_FIELD_RATIONAL, 1, + info->filefmt->xresoffset); if (ret == OK) { - ret = tiff_putifdentry(info, IFD_TAG_YRESOLUTION, IFD_FIELD_RATIONAL, 1, info->filefmt->yresoffset); + ret = tiff_putifdentry(info, IFD_TAG_YRESOLUTION, + IFD_FIELD_RATIONAL, 1, + info->filefmt->yresoffset); } if (ret < 0) { goto errout; } - tiff_offset(offset, 2*SIZEOF_IFD_ENTRY); + + tiff_offset(offset, 2 * SIZEOF_IFD_ENTRY); /* Write ResolutionUnit: * @@ -730,11 +786,13 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 156, Hard-coded to "inches" */ - ret = tiff_putifdentry16(info, IFD_TAG_RESUNIT, IFD_FIELD_SHORT, 1, TAG_RESUNIT_INCH); + ret = tiff_putifdentry16(info, IFD_TAG_RESUNIT, + IFD_FIELD_SHORT, 1, TAG_RESUNIT_INCH); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write Software: @@ -744,11 +802,14 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 168 Count, Hard-coded "NuttX" */ - ret = tiff_putifdentry(info, IFD_TAG_SOFTWARE, IFD_FIELD_ASCII, TIFF_SOFTWARE_STRLEN, info->filefmt->swoffset); + ret = tiff_putifdentry(info, IFD_TAG_SOFTWARE, + IFD_FIELD_ASCII, TIFF_SOFTWARE_STRLEN, + info->filefmt->swoffset); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write DateTime: @@ -758,11 +819,14 @@ int tiff_initialize(FAR struct tiff_info_s *info) * RGB: Offset 180 Count, Format "YYYY:MM:DD HH:MM:SS" */ - ret = tiff_putifdentry(info, IFD_TAG_DATETIME, IFD_FIELD_ASCII, TIFF_DATETIME_STRLEN, info->filefmt->dateoffset); + ret = tiff_putifdentry(info, IFD_TAG_DATETIME, + IFD_FIELD_ASCII, TIFF_DATETIME_STRLEN, + info->filefmt->dateoffset); if (ret < 0) { goto errout; } + tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write Next IFD Offset and 2 bytes of padding: @@ -780,6 +844,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, 4); /* Now we begin the value section of the file */ @@ -807,6 +872,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, 8); tiff_checkoffs(offset, info->filefmt->yresoffset); @@ -820,6 +886,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, 8); /* Write RGB BitsPerSample Data: @@ -837,6 +904,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) tiff_putint16(info->outfd, 8); tiff_putint16(info->outfd, 8); tiff_putint16(info->outfd, 0); + tiff_offset(offset, 8); } @@ -849,11 +917,13 @@ int tiff_initialize(FAR struct tiff_info_s *info) */ tiff_checkoffs(offset, info->filefmt->swoffset); - ret = tiff_putstring(info->outfd, TIFF_SOFTWARE_STRING, TIFF_SOFTWARE_STRLEN); + ret = tiff_putstring(info->outfd, TIFF_SOFTWARE_STRING, + TIFF_SOFTWARE_STRLEN); if (ret < 0) { goto errout; } + tiff_offset(offset, TIFF_SOFTWARE_STRLEN); /* Write the DateTime string: @@ -876,6 +946,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, TIFF_DATETIME_STRLEN); /* Add two bytes of padding */ @@ -885,6 +956,7 @@ int tiff_initialize(FAR struct tiff_info_s *info) { goto errout; } + tiff_offset(offset, 2); /* And that should do it! */ diff --git a/graphics/tiff/tiff_utils.c b/graphics/tiff/tiff_utils.c index c16f7b3e3..12aa4bb8e 100644 --- a/graphics/tiff/tiff_utils.c +++ b/graphics/tiff/tiff_utils.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "graphics/tiff.h" @@ -93,10 +93,10 @@ void tiff_put32(FAR uint8_t *dest, uint32_t value) { #ifdef CONFIG_ENDIAN_BIG tiff_put16(dest, (uint16_t)(value >> 16)); - tiff_put16(dest+2, (uint16_t)(value & 0xffff)); + tiff_put16(dest + 2, (uint16_t)(value & 0xffff)); #else tiff_put16(dest, (uint16_t)(value & 0xffff)); - tiff_put16(dest+2, (uint16_t)(value >> 16)); + tiff_put16(dest + 2, (uint16_t)(value >> 16)); #endif } @@ -112,11 +112,10 @@ uint16_t tiff_get16(FAR uint8_t *src) uint32_t tiff_get32(FAR uint8_t *src) { #ifdef CONFIG_ENDIAN_BIG - return (uint32_t)tiff_get16(src) << 16 | (uint32_t)tiff_get16(src+2); + return (uint32_t)tiff_get16(src) << 16 | (uint32_t)tiff_get16(src + 2); #else - return (uint32_t)tiff_get16(src+2) << 16 | (uint32_t)tiff_get16(src); + return (uint32_t)tiff_get16(src + 2) << 16 | (uint32_t)tiff_get16(src); #endif - } /**************************************************************************** @@ -153,7 +152,7 @@ ssize_t tiff_read(int fd, FAR void *buffer, size_t count) * (ntotal). */ - nbytes = read(fd, buffer, count-ntotal); + nbytes = read(fd, buffer, count - ntotal); /* Check for an error */ @@ -166,22 +165,26 @@ ssize_t tiff_read(int fd, FAR void *buffer, size_t count) errval = errno; if (errval != EINTR) { - /* Other errors are bad news and we will break out with an error */ + /* Other errors are bad news and we will break out with an + * error. + */ return -errval; } } - /* Zero is a special case and means that the end of file was encountered. */ + /* Zero is a special case and means that the end of file was + * encountered. + */ else if (nbytes == 0) { break; } - /* What if read returns some number of bytes other than the requested number? - * This probably means that the end-of-file will be encountered the next time - * that we call read(). + /* What if read returns some number of bytes other than the requested + * number? This probably means that the end-of-file will be + * encountered the next time that we call read(). */ else @@ -236,13 +239,17 @@ int tiff_write(int fd, FAR const void *buffer, size_t count) errval = errno; if (errval != EINTR) { - /* Other errors are bad news and we will break out with an error */ + /* Other errors are bad news and we will break out with an + * error. + */ return -errval; } } - /* What if write returns some number of bytes other than the requested number? */ + /* What if write returns some number of bytes other than the requested + * number? + */ else { @@ -326,7 +333,7 @@ int tiff_putstring(int fd, FAR const char *string, int len) #ifdef CONFIG_DEBUG_GRAPHICS int actual = strlen(string); - DEBUGASSERT(len = actual+1); + DEBUGASSERT(len == actual + 1); #endif return tiff_write(fd, string, len); } @@ -335,7 +342,7 @@ int tiff_putstring(int fd, FAR const char *string, int len) * Name: tiff_wordalign * * Description: - * Pad a file with zeros as necessary to achieve word alignament. + * Pad a file with zeros as necessary to achieve word alignment. * * Input Parameters: * fd - File descriptor to be used. @@ -362,7 +369,9 @@ ssize_t tiff_wordalign(int fd, size_t size) { return (ssize_t)ret; } + size += nbytes; } + return size; } diff --git a/graphics/twm4nx/Kconfig b/graphics/twm4nx/Kconfig index 3f9600f68..3b114df88 100644 --- a/graphics/twm4nx/Kconfig +++ b/graphics/twm4nx/Kconfig @@ -42,17 +42,6 @@ config TWM4NX_REVMINOR string "Twm4Nx minor version number" default "0" -config TWM4NX_ARCHINIT - bool "Have architecture-specific initialization" - default n - select BOARDCTL - depends on !NSH_ARCHINIT - ---help--- - Set if your board provides architecture specific initialization - via the board-interface function boardctl(). The boardctl() - function will be called early in Twm4Nx initialization to allow - board logic to do such things as configure MMC/SD slots. - config TWM4NX_NETINIT bool "Network initialization" default y diff --git a/graphics/twm4nx/apps/ccalibration.cxx b/graphics/twm4nx/apps/ccalibration.cxx index 7eab8067a..40317cdb6 100644 --- a/graphics/twm4nx/apps/ccalibration.cxx +++ b/graphics/twm4nx/apps/ccalibration.cxx @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include diff --git a/graphics/twm4nx/apps/cclock.cxx b/graphics/twm4nx/apps/cclock.cxx index 55c6c3e34..e9d3b55bd 100644 --- a/graphics/twm4nx/apps/cclock.cxx +++ b/graphics/twm4nx/apps/cclock.cxx @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include diff --git a/graphics/twm4nx/apps/cnxterm.cxx b/graphics/twm4nx/apps/cnxterm.cxx index 1918775a8..a36edc4db 100644 --- a/graphics/twm4nx/apps/cnxterm.cxx +++ b/graphics/twm4nx/apps/cnxterm.cxx @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include "nshlib/nshlib.h" diff --git a/graphics/twm4nx/src/cmainmenu.cxx b/graphics/twm4nx/src/cmainmenu.cxx index 2c2034f3b..e2b9c1992 100644 --- a/graphics/twm4nx/src/cmainmenu.cxx +++ b/graphics/twm4nx/src/cmainmenu.cxx @@ -27,7 +27,7 @@ #include #include -#include +#include #include diff --git a/graphics/twm4nx/src/twm4nx_main.cxx b/graphics/twm4nx/src/twm4nx_main.cxx index 30185ad07..3cfb3a6b5 100644 --- a/graphics/twm4nx/src/twm4nx_main.cxx +++ b/graphics/twm4nx/src/twm4nx_main.cxx @@ -98,23 +98,6 @@ int main(int argc, FAR char *argv[]) int ret; -#if defined(CONFIG_TWM4NX_ARCHINIT) && defined(CONFIG_BOARDCTL) && \ - !defined(CONFIG_BOARD_LATE_INITIALIZE) - // Should we perform board-specific initialization? There are two ways - // that board initialization can occur: 1) automatically via - // board_late_initialize() during bootup if CONFIG_BOARD_LATE_INITIALIZE, or - // 2) here via a call to boardctl() if the interface is enabled - // (CONFIG_BOARDCTL=y). board_early_initialize() is also possibility, - // although less likely. - - ret = boardctl(BOARDIOC_INIT, 0); - if (ret < 0) - { - twmerr("ERROR: boardctl(BOARDIOC_INIT) failed: %d\n", errno); - return EXIT_FAILURE; - } -#endif - #ifdef CONFIG_TWM4NX_NETINIT /* Bring up the network */ diff --git a/import/Make.defs b/import/Make.defs index 8943963ed..2b8236d25 100644 --- a/import/Make.defs +++ b/import/Make.defs @@ -97,7 +97,7 @@ endif ifeq ($(CONFIG_BINFMT_ELF_RELOCATABLE),y) LDELFFLAGS += -r endif -LDELFFLAGS += -e __start -Bstatic +LDELFFLAGS += -e _start -Bstatic # Remove other ld scripts, just use import ld scripts # diff --git a/include/.gitignore b/include/.gitignore index 9d6149edc..b237d4f3c 100644 --- a/include/.gitignore +++ b/include/.gitignore @@ -1,2 +1,4 @@ /netutils/cJSON.h /netutils/cJSON_Utils.h +/audioutils/rtttl.h +/audioutils/morsey.h diff --git a/include/canutils/obd.h b/include/canutils/obd.h index bc6667285..c49b2a152 100644 --- a/include/canutils/obd.h +++ b/include/canutils/obd.h @@ -96,13 +96,13 @@ int obd_send_request(FAR struct obd_dev_s *dev, uint8_t opmode, uint8_t pid); * Wait for a message from ECUs with requested PID that was sent using * obd_send_request(). * - * It will return an error case it doesn't receive the msg after the elapsed - * "timeout" time. + * It will return an error case it doesn't receive the msg after the + * elapsed "timeout" time. * ****************************************************************************/ -int obd_wait_response(FAR struct obd_dev_s *dev, uint8_t opmode, uint8_t pid, - int timeout); +int obd_wait_response(FAR struct obd_dev_s *dev, uint8_t opmode, + uint8_t pid, int timeout); /**************************************************************************** * Name: obd_decode_pid @@ -116,4 +116,4 @@ int obd_wait_response(FAR struct obd_dev_s *dev, uint8_t opmode, uint8_t pid, FAR char *obd_decode_pid(FAR struct obd_dev_s *dev, uint8_t pid); -#endif /*__APPS_INCLUDE_CANUTILS_OBD_H */ +#endif /* __APPS_INCLUDE_CANUTILS_OBD_H */ diff --git a/include/fsutils/passwd.h b/include/fsutils/passwd.h index 996bcf509..93fed255d 100644 --- a/include/fsutils/passwd.h +++ b/include/fsutils/passwd.h @@ -36,9 +36,9 @@ /* passwd_verify() return value tests */ -#define PASSWORD_VERIFY_MATCH(ret) (ret == 1) -#define PASSWORD_VERIFY_NOMATCH(ret) (ret == 0) -#define PASSWORD_VERIFY_ERROR(ret) (ret < 0) +#define PASSWORD_VERIFY_MATCH(ret) ((ret) == 0) +#define PASSWORD_VERIFY_NOMATCH(ret) ((ret) == -1) +#define PASSWORD_VERIFY_ERROR(ret) ((ret) < -1) /**************************************************************************** * Public Function Prototypes @@ -115,9 +115,8 @@ int passwd_update(FAR const char *username, FAR const char *password); * password - The password to be verified * * Returned Value: - * One (1) is returned on success match, Zero (OK) is returned on an - * unsuccessful match; a negated errno value is returned on any other - * failure. + * Zero (0) is returned on a successful match, -1 on mismatch or invalid + * hash format; a negated errno value is returned on other failures. * ****************************************************************************/ diff --git a/include/graphics/nxwidgets/cscaledbitmap.hxx b/include/graphics/nxwidgets/cscaledbitmap.hxx index b2cc5f577..f175dd6ee 100644 --- a/include/graphics/nxwidgets/cscaledbitmap.hxx +++ b/include/graphics/nxwidgets/cscaledbitmap.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/graphics/nxwm/cstartwindow.hxx b/include/graphics/nxwm/cstartwindow.hxx index fb4fb2acb..2c1d0178a 100644 --- a/include/graphics/nxwm/cstartwindow.hxx +++ b/include/graphics/nxwm/cstartwindow.hxx @@ -29,7 +29,7 @@ #include -#include +#include #include "graphics/nxwidgets/tnxarray.hxx" diff --git a/include/graphics/twm4nx/apps/calib_config.hxx b/include/graphics/twm4nx/apps/calib_config.hxx index 85abc746a..ffcdfc6a7 100644 --- a/include/graphics/twm4nx/apps/calib_config.hxx +++ b/include/graphics/twm4nx/apps/calib_config.hxx @@ -28,7 +28,7 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include +#include #include "graphics/nxwidgets/nxconfig.hxx" #include "graphics/twm4nx/twm4nx_config.hxx" diff --git a/include/graphics/twm4nx/apps/clock_config.hxx b/include/graphics/twm4nx/apps/clock_config.hxx index b6c059060..2e2db213c 100644 --- a/include/graphics/twm4nx/apps/clock_config.hxx +++ b/include/graphics/twm4nx/apps/clock_config.hxx @@ -28,7 +28,7 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include +#include #include "graphics/nxglyphs.hxx" #include "graphics/nxwidgets/crlepalettebitmap.hxx" diff --git a/include/graphics/twm4nx/apps/nxterm_config.hxx b/include/graphics/twm4nx/apps/nxterm_config.hxx index e90be9b90..2d0661e9e 100644 --- a/include/graphics/twm4nx/apps/nxterm_config.hxx +++ b/include/graphics/twm4nx/apps/nxterm_config.hxx @@ -28,7 +28,7 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include +#include #include "graphics/nxglyphs.hxx" #include "graphics/nxwidgets/crlepalettebitmap.hxx" diff --git a/include/graphics/twm4nx/ciconwidget.hxx b/include/graphics/twm4nx/ciconwidget.hxx index e96b69ca8..e9152fc19 100644 --- a/include/graphics/twm4nx/ciconwidget.hxx +++ b/include/graphics/twm4nx/ciconwidget.hxx @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/graphics/twm4nx/twm4nx_config.hxx b/include/graphics/twm4nx/twm4nx_config.hxx index 679cc887e..60b34d09d 100644 --- a/include/graphics/twm4nx/twm4nx_config.hxx +++ b/include/graphics/twm4nx/twm4nx_config.hxx @@ -28,7 +28,7 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include +#include #include "graphics/nxglyphs.hxx" #include "graphics/nxwidgets/nxconfig.hxx" diff --git a/include/industry/abnt_codi.h b/include/industry/abnt_codi.h index 049a7b29f..e1dbeb494 100644 --- a/include/industry/abnt_codi.h +++ b/include/industry/abnt_codi.h @@ -104,7 +104,8 @@ uint8_t abnt_codi_checksum(FAR const uint8_t *data); /* Parse a specific ABNT CODI sequence. */ -bool abnt_codi_parse(FAR const uint8_t *data, FAR struct abnt_codi_proto_s *proto); +bool abnt_codi_parse(FAR const uint8_t *data, + FAR struct abnt_codi_proto_s *proto); #ifdef __cplusplus } diff --git a/include/logging/nxscope/nxscope_intf.h b/include/logging/nxscope/nxscope_intf.h index b422e0d77..937619f3a 100644 --- a/include/logging/nxscope/nxscope_intf.h +++ b/include/logging/nxscope/nxscope_intf.h @@ -99,6 +99,16 @@ struct nxscope_ser_cfg_s }; #endif +#ifdef CONFIG_LOGGING_NXSCOPE_INTF_UDP +/* Nxscope UDP interface configuration */ + +struct nxscope_udp_cfg_s +{ + uint16_t port; /* Local UDP port */ + bool nonblock; /* Nonblocking operation */ +}; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -133,6 +143,21 @@ int nxscope_ser_init(FAR struct nxscope_intf_s *intf, void nxscope_ser_deinit(FAR struct nxscope_intf_s *intf); #endif +#ifdef CONFIG_LOGGING_NXSCOPE_INTF_UDP +/**************************************************************************** + * Name: nxscope_udp_init + ****************************************************************************/ + +int nxscope_udp_init(FAR struct nxscope_intf_s *intf, + FAR struct nxscope_udp_cfg_s *cfg); + +/**************************************************************************** + * Name: nxscope_udp_deinit + ****************************************************************************/ + +void nxscope_udp_deinit(FAR struct nxscope_intf_s *intf); +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/include/netutils/base64.h b/include/netutils/base64.h index e5869d8ac..5f146d7e8 100644 --- a/include/netutils/base64.h +++ b/include/netutils/base64.h @@ -30,17 +30,17 @@ * may be used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ @@ -73,6 +73,10 @@ FAR void *base64w_encode(FAR const void *src, size_t len, FAR void *dst, FAR size_t *out_len); FAR void *base64w_decode(FAR const void *src, size_t len, FAR void *dst, FAR size_t *out_len); +int base64url_encode(FAR const void *src, size_t len, FAR char *dst, + size_t dstlen); +int base64url_decode(FAR const char *src, FAR void *dst, size_t dstmax, + FAR size_t *out_len); #endif /* CONFIG_CODECS_BASE64 */ #ifdef __cplusplus diff --git a/include/netutils/cmux.h b/include/netutils/cmux.h index 42f0d5ca0..8bbf2e1f0 100644 --- a/include/netutils/cmux.h +++ b/include/netutils/cmux.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include /**************************************************************************** diff --git a/include/netutils/dhcpc.h b/include/netutils/dhcpc.h index d55fca170..b17d6f368 100644 --- a/include/netutils/dhcpc.h +++ b/include/netutils/dhcpc.h @@ -39,6 +39,7 @@ * Included Files ****************************************************************************/ +#include #include #include @@ -46,6 +47,18 @@ * Pre-processor Definitions ****************************************************************************/ +/* Provide a default value for CONFIG_NETDB_DNSSERVER_NAMESERVERS if + * CONFIG_NETDB_DNSCLIENT is not enabled. + */ + +#if !defined(CONFIG_NETDB_DNSSERVER_NAMESERVERS) +# define CONFIG_NETDB_DNSSERVER_NAMESERVERS 1 +#endif + +#if !defined(CONFIG_NETUTILS_DHCPC_NTP_SERVERS) +# define CONFIG_NETUTILS_DHCPC_NTP_SERVERS 1 +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -57,6 +70,8 @@ struct dhcpc_state struct in_addr netmask; struct in_addr dnsaddr[CONFIG_NETDB_DNSSERVER_NAMESERVERS]; uint8_t num_dnsaddr; /* Number of DNS addresses received */ + struct in_addr ntpaddr[CONFIG_NETUTILS_DHCPC_NTP_SERVERS]; + uint8_t num_ntpaddr; /* Number of NTP addresses received */ struct in_addr default_router; uint32_t lease_time; /* Lease expires in this number of seconds */ uint32_t renewal_time; /* Seconds to transition to RENEW state(T1) */ diff --git a/include/netutils/icmp_ping.h b/include/netutils/icmp_ping.h index eec500de9..d9164afc1 100644 --- a/include/netutils/icmp_ping.h +++ b/include/netutils/icmp_ping.h @@ -50,6 +50,8 @@ #define ICMP_E_POLL -11 /* extra: error code */ #define ICMP_E_RECVFROM -13 /* extra: error code */ #define ICMP_E_RECVSMALL -15 /* extra: recv bytes */ +#define ICMP_E_BINDDEV -17 /* extra: error bind */ +#define ICMP_E_FILTER -19 /* extra: error filter */ /* Negative even number represent warning(recoverable) */ @@ -70,6 +72,9 @@ struct ping_result_s; struct ping_info_s { FAR const char *hostname; /* Host name to ping */ +#ifdef CONFIG_NET_BINDTODEVICE + FAR const char *devname; /* Device name to bind */ +#endif uint16_t count; /* Number of pings requested */ uint16_t datalen; /* Number of bytes to be sent */ uint16_t delay; /* Deciseconds to delay between pings */ diff --git a/include/netutils/icmpv6_ping.h b/include/netutils/icmpv6_ping.h index 3f793f741..33db0fc6f 100644 --- a/include/netutils/icmpv6_ping.h +++ b/include/netutils/icmpv6_ping.h @@ -50,6 +50,7 @@ #define ICMPv6_E_POLL -11 /* extra: error code */ #define ICMPv6_E_RECVFROM -13 /* extra: error code */ #define ICMPv6_E_RECVSMALL -15 /* extra: recv bytes */ +#define ICMPV6_E_BINDDEV -17 /* extra: error bind */ /* Negative even number represent warning(recoverable) */ @@ -70,6 +71,9 @@ struct ping6_result_s; struct ping6_info_s { FAR const char *hostname; /* Host name to ping */ +#ifdef CONFIG_NET_BINDTODEVICE + FAR const char *devname; /* Device name to bind */ +#endif uint16_t count; /* Number of pings requested */ uint16_t datalen; /* Number of bytes to be sent */ uint16_t delay; /* Deciseconds to delay between pings */ diff --git a/include/netutils/netinit.h b/include/netutils/netinit.h index 3c3c85d50..320fa1498 100644 --- a/include/netutils/netinit.h +++ b/include/netutils/netinit.h @@ -57,10 +57,6 @@ # define CONFIG_NETINIT_DNSIPADDR CONFIG_NETINIT_DRIPADDR #endif -#ifndef CONFIG_NETINIT_MACADDR -# define CONFIG_NETINIT_MACADDR 0x00e0deadbeef -#endif - #if !defined(CONFIG_NETINIT_THREAD) || !defined(CONFIG_ARCH_PHY_INTERRUPT) || \ !defined(CONFIG_NETDEV_PHY_IOCTL) || !defined(CONFIG_NET_UDP) # undef CONFIG_NETINIT_MONITOR diff --git a/include/netutils/netlib.h b/include/netutils/netlib.h index 469bd0461..d76c93e96 100644 --- a/include/netutils/netlib.h +++ b/include/netutils/netlib.h @@ -52,9 +52,15 @@ #include #include +#include #include #include +#ifdef CONFIG_NET_IPTABLES +# include +# include +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -197,6 +203,11 @@ struct url_s }; #endif +#ifdef CONFIG_NETUTILS_DHCPC +typedef CODE void (*netlib_dhcp_ntp_callback_t) + (FAR const char *ntp_server_list, FAR void *arg); +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -241,6 +252,44 @@ ssize_t netlib_get_devices(FAR struct netlib_device_s *devlist, bool netlib_ipv4addrconv(FAR const char *addrstr, FAR uint8_t *addr); bool netlib_ethaddrconv(FAR const char *hwstr, FAR uint8_t *hw); +#ifdef CONFIG_NETUTILS_DHCPC +/**************************************************************************** + * Name: netlib_set_ntp_servers_from_dhcp + * + * Description: + * Update the currently active DHCP option 42 NTP server list. The list + * contains semicolon-separated hostnames or addresses. Passing NULL or + * an empty string clears the DHCP-provided list. + * + ****************************************************************************/ + +int netlib_set_ntp_servers_from_dhcp(FAR const char *ntp_server_list); + +/**************************************************************************** + * Name: netlib_register_dhcp_ntp_callback + * + * Description: + * Register a callback to receive DHCP option 42 NTP server list updates. + * The current list, if any, is replayed immediately after registration. + * Only one callback may be registered at a time. + * + ****************************************************************************/ + +int netlib_register_dhcp_ntp_callback(netlib_dhcp_ntp_callback_t callback, + FAR void *arg); + +/**************************************************************************** + * Name: netlib_unregister_dhcp_ntp_callback + * + * Description: + * Unregister a previously registered DHCP option 42 NTP update callback. + * + ****************************************************************************/ + +int netlib_unregister_dhcp_ntp_callback(netlib_dhcp_ntp_callback_t callback, + FAR void *arg); +#endif + #ifdef CONFIG_NET_ETHERNET /* Get and set IP/MAC addresses (Ethernet L2 only) */ @@ -328,6 +377,11 @@ int netlib_getessid(FAR const char *ifname, FAR char *essid, size_t idlen); int netlib_setessid(FAR const char *ifname, FAR const char *essid); #endif +#ifdef CONFIG_NET_VLAN +int netlib_add_vlan(FAR const char *ifname, int vlanid, int prio); +int netlib_del_vlan(FAR const char *vlanif); +#endif + #ifdef CONFIG_NET_ARP /* ARP Table Support */ @@ -477,6 +531,8 @@ void netlib_server(uint16_t portno, pthread_startroutine_t handler, int netlib_getifstatus(FAR const char *ifname, FAR uint8_t *flags); int netlib_ifup(FAR const char *ifname); int netlib_ifdown(FAR const char *ifname); +int netlib_ifarp(const char *ifname); +int netlib_ifnoarp(const char *ifname); /* DNS server addressing */ @@ -499,6 +555,66 @@ int netlib_getifstatistics(FAR const char *ifname, FAR struct netdev_statistics_s *stat); #endif +/* Network check support */ + +#ifdef CONFIG_NET_ARP_ACD +int netlib_check_ifconflict(FAR const char *ifname); +#endif + +/**************************************************************************** + * Name: netlib_check_ipconnectivity + * + * Description: + * Check network connectivity by pinging a remote IP address. + * If ip is NULL, ping the gateway of each network interface, + * and optionally the routers from the routing table. If ping + * is disabled, just check the status of the IP network card. + * + * Parameters: + * ip The ipv4 address to check, or NULL to ping gateways + * timeout The max timeout of each ping + * retry The retry times of ping + * + * Return: + * nums of remote reply of ping; a negative or ZERO on failure + * + ****************************************************************************/ + +int netlib_check_ipconnectivity(FAR const char *ip, int timeout, int retry); + +#ifdef CONFIG_NETUTILS_PING + +/**************************************************************************** + * Name: netlib_check_ifconnectivity + * + * Description: + * Check network connectivity by pinging the default gateway + * of the specified network interface. + * + * Parameters: + * ifname The name of the interface to use + * timeout The timeout of ping + * retry The retry times of ping + * + * Return: + * nums of gateway reply of ping; a negative on failure. + * + ****************************************************************************/ + +int netlib_check_ifconnectivity(FAR const char *ifname, + int timeout, int retry); +#else +#define netlib_check_ifconnectivity(i, t, r) 1 +#endif + +#ifdef CONFIG_MM_IOB +int netlib_get_iobinfo(FAR struct iob_stats_s *iob); +#endif + +int netlib_check_httpconnectivity(FAR const char *host, + FAR const char *getmsg, + int port, int expect_code); + #undef EXTERN #ifdef __cplusplus } diff --git a/include/netutils/ptpd.h b/include/netutils/ptpd.h index 8e5a3c508..1d455c6be 100644 --- a/include/netutils/ptpd.h +++ b/include/netutils/ptpd.h @@ -27,6 +27,8 @@ * Included Files ****************************************************************************/ +#include + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -35,6 +37,17 @@ * Public Types ****************************************************************************/ +struct ptpd_config_s +{ + FAR const char *interface; + FAR const char *clock; + bool client_only; + bool hardware_ts; + bool delay_e2e; + bool bmca; + sa_family_t af; +}; + /* PTPD status information structure */ struct ptpd_status_s @@ -115,10 +128,10 @@ extern "C" * Name: ptpd_start * * Description: - * Start the PTP daemon and bind it to specified interface. + * Start the PTP daemon and bind it to specified config. * * Input Parameters: - * interface - Name of the network interface to bind to, e.g. "eth0" + * config - The configs of PTP daemon, includes interface, af and clock... * * Returned Value: * On success, the non-negative task ID of the PTP daemon is returned; @@ -126,7 +139,7 @@ extern "C" * ****************************************************************************/ -int ptpd_start(FAR const char *interface); +int ptpd_start(FAR const struct ptpd_config_s *config); /**************************************************************************** * Name: ptpd_status diff --git a/include/nshlib/nshlib.h b/include/nshlib/nshlib.h index f9565894a..f2153fed0 100644 --- a/include/nshlib/nshlib.h +++ b/include/nshlib/nshlib.h @@ -69,21 +69,24 @@ struct nsh_param_s { - /* Redirect input/output through `fd` OR `path_name` + /* Redirect input/output/error through `fd` OR `path_name` * * Select one: - * 1. Using fd_in/fd_out as oldfd for dup2() if greater than -1. - * 2. Using file_in/file_out as full path to the file if it is - * not NULL, and oflags_in/oflags_out as flags for open(). + * 1. Using fd_in/fd_out/fd_err as oldfd for dup2() if greater than -1. + * 2. Using file_in/file_out/file_err as full path to the file if it is + * not NULL, and oflags_in/oflags_out/oflags_err as flags for open(). */ int fd_in; int fd_out; + int fd_err; int oflags_in; int oflags_out; + int oflags_err; FAR const char *file_in; FAR const char *file_out; + FAR const char *file_err; }; /**************************************************************************** diff --git a/include/nxmodbus/nxmb_client.h b/include/nxmodbus/nxmb_client.h new file mode 100644 index 000000000..49c10e542 --- /dev/null +++ b/include/nxmodbus/nxmb_client.h @@ -0,0 +1,261 @@ +/**************************************************************************** + * apps/include/nxmodbus/nxmb_client.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NXMODBUS_NXMB_CLIENT_H +#define __APPS_INCLUDE_NXMODBUS_NXMB_CLIENT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Name: nxmb_read_coils + * + * Description: + * Read coil values from a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first coil address to read. + * count - The number of coils to read. + * buf - The destination buffer for the returned coil values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_read_coils(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint8_t *buf); + +/**************************************************************************** + * Name: nxmb_read_discrete + * + * Description: + * Read discrete input values from a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first discrete input address to read. + * count - The number of discrete inputs to read. + * buf - The destination buffer for the returned values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_read_discrete(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint8_t *buf); + +/**************************************************************************** + * Name: nxmb_read_input + * + * Description: + * Read input registers from a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first input register address to read. + * count - The number of registers to read. + * buf - The destination buffer for the returned register values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_read_input(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint16_t *buf); + +/**************************************************************************** + * Name: nxmb_read_holding + * + * Description: + * Read holding registers from a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first holding register address to read. + * count - The number of registers to read. + * buf - The destination buffer for the returned register values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_read_holding(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint16_t *buf); + +/**************************************************************************** + * Name: nxmb_write_coil + * + * Description: + * Write a single coil on a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The coil address to update. + * value - The coil state to write. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_write_coil(nxmb_handle_t h, uint8_t uid, uint16_t addr, bool value); + +/**************************************************************************** + * Name: nxmb_write_holding + * + * Description: + * Write a single holding register on a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The holding register address to update. + * value - The register value to write. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_write_holding(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t value); + +/**************************************************************************** + * Name: nxmb_write_coils + * + * Description: + * Write multiple coils on a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first coil address to update. + * count - The number of coils to write. + * buf - The source buffer containing coil values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_write_coils(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR const uint8_t *buf); + +/**************************************************************************** + * Name: nxmb_write_holdings + * + * Description: + * Write multiple holding registers on a remote unit. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * addr - The first holding register address to update. + * count - The number of registers to write. + * buf - The source buffer containing register values. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_write_holdings(nxmb_handle_t h, uint8_t uid, uint16_t addr, + uint16_t count, FAR const uint16_t *buf); + +/**************************************************************************** + * Name: nxmb_readwrite_holdings + * + * Description: + * Perform a combined read/write of holding registers on a remote unit + * using FC23 (0x17). The write is performed before the read on the + * server side. + * + * Input Parameters: + * h - The NxModbus client instance. + * uid - The remote unit identifier. + * rd_addr - The first holding register address to read. + * rd_count - The number of registers to read (1-125). + * rd_buf - The destination buffer for the returned register values. + * wr_addr - The first holding register address to write. + * wr_count - The number of registers to write (1-121). + * wr_buf - The source buffer containing register values to write. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_readwrite_holdings(nxmb_handle_t h, uint8_t uid, uint16_t rd_addr, + uint16_t rd_count, FAR uint16_t *rd_buf, + uint16_t wr_addr, uint16_t wr_count, + FAR const uint16_t *wr_buf); + +/**************************************************************************** + * Name: nxmb_set_timeout + * + * Description: + * Set the client-side response timeout. + * + * Input Parameters: + * h - The NxModbus client instance. + * timeout_ms - The timeout in milliseconds. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_set_timeout(nxmb_handle_t h, uint32_t timeout_ms); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NXMODBUS_NXMB_CLIENT_H */ diff --git a/include/nxmodbus/nxmb_raw.h b/include/nxmodbus/nxmb_raw.h new file mode 100644 index 000000000..e01f42598 --- /dev/null +++ b/include/nxmodbus/nxmb_raw.h @@ -0,0 +1,108 @@ +/**************************************************************************** + * apps/include/nxmodbus/nxmb_raw.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NXMODBUS_NXMB_RAW_H +#define __APPS_INCLUDE_NXMODBUS_NXMB_RAW_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_RAW_HDR_SIZE 8 + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Name: nxmb_raw_submit_rx + * + * Description: + * Submit a received raw ADU into the NxModbus protocol core. + * + * Input Parameters: + * h - The NxModbus instance receiving the frame. + * adu - The received raw ADU container. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_raw_submit_rx(nxmb_handle_t h, FAR const struct nxmb_adu_s *adu); + +/**************************************************************************** + * Name: nxmb_raw_put_header + * + * Description: + * Serialize the 8-byte MBAP+FC header for an ADU. + * + * Input Parameters: + * adu - The ADU providing the header fields. + * hdr - The destination buffer for the serialized header. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void nxmb_raw_put_header(FAR const struct nxmb_adu_s *adu, FAR uint8_t *hdr); + +/**************************************************************************** + * Name: nxmb_raw_get_header + * + * Description: + * Parse the 8-byte MBAP+FC header into an ADU structure. + * + * Input Parameters: + * adu - The destination ADU structure. + * hdr - The source buffer containing the serialized header. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void nxmb_raw_get_header(FAR struct nxmb_adu_s *adu, FAR const uint8_t *hdr); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NXMODBUS_NXMB_RAW_H */ diff --git a/include/nxmodbus/nxmodbus.h b/include/nxmodbus/nxmodbus.h new file mode 100644 index 000000000..f5307989b --- /dev/null +++ b/include/nxmodbus/nxmodbus.h @@ -0,0 +1,378 @@ +/**************************************************************************** + * apps/include/nxmodbus/nxmodbus.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NXMODBUS_NXMODBUS_H +#define __APPS_INCLUDE_NXMODBUS_NXMODBUS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_TCP_PORT_DEFAULT 502 + +/* Maximum number of PDU data bytes (everything after the FC byte). + * Configured via Kconfig (CONFIG_NXMODBUS_ADU_DATA_MAX). + */ + +#define NXMB_ADU_DATA_MAX CONFIG_NXMODBUS_ADU_DATA_MAX + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Opaque handle to an NxModbus instance. */ + +typedef FAR struct nxmb_context_s *nxmb_handle_t; + +/* Custom frame handler */ + +typedef CODE int (*nxmb_custom_fc_handler_t)(nxmb_handle_t ctx); + +/* Modbus Application Data Unit container. + * + * Single canonical ADU representation shared by the protocol core and all + * transports. Transports parse incoming wire frames into this struct and + * serialize this struct back out on transmit. + * + * trans_id, proto_id, length - MBAP header fields (TCP-only). + * unit_id - Modbus address byte. + * fc - Modbus function code byte. + * data[] - PDU payload after the function code. + * crc - RTU/ASCII checksum (serial-only). + * + * The MBAP `length` field counts the bytes following it: unit_id + fc + + * data_count. Transports that don't carry MBAP still populate this field + * with the same value so that consumers can derive data_count uniformly: + * data_count = adu.length - 2 + */ + +struct nxmb_adu_s +{ + uint16_t trans_id; + uint16_t proto_id; + uint16_t length; + uint8_t unit_id; + uint8_t fc; + uint8_t data[NXMB_ADU_DATA_MAX]; + uint16_t crc; +}; + +/* Callback used by raw ADU mode to emit a fully-formed frame. */ + +typedef int (*nxmb_raw_tx_cb_t)(FAR const struct nxmb_adu_s *adu, + FAR void *user_data); + +/* Supported transport modes. */ + +enum nxmb_mode_e +{ + NXMB_MODE_INVAL = 0, + NXMB_MODE_RTU, /* Modbus RTU */ + NXMB_MODE_ASCII, /* Modbus ASCII */ + NXMB_MODE_TCP, /* Modbus TCP */ + NXMB_MODE_RAW /* Custom ADU transport */ +}; + +/* Serial parity configuration. */ + +enum nxmb_parity_e +{ + NXMB_PAR_NONE = 0, + NXMB_PAR_EVEN, + NXMB_PAR_ODD +}; + +/* Register access direction passed to callbacks. */ + +enum nxmb_regmode_e +{ + NXMB_REG_READ = 0, + NXMB_REG_WRITE +}; + +/* Standard Modbus exception codes. */ + +enum nxmb_exception_e +{ + NXMB_EX_NONE = 0x00, + NXMB_EX_ILLEGAL_FUNCTION = 0x01, + NXMB_EX_ILLEGAL_DATA_ADDRESS = 0x02, + NXMB_EX_ILLEGAL_DATA_VALUE = 0x03, + NXMB_EX_DEVICE_FAILURE = 0x04, + NXMB_EX_ACKNOWLEDGE = 0x05, + NXMB_EX_DEVICE_BUSY = 0x06, + NXMB_EX_MEMORY_PARITY_ERROR = 0x08, + NXMB_EX_GATEWAY_PATH_FAILED = 0x0a, + NXMB_EX_GATEWAY_TGT_FAILED = 0x0b +}; + +/* Serial transport configuration. */ + +struct nxmb_serial_config_s +{ + FAR const char *devpath; + enum nxmb_parity_e parity; + uint32_t baudrate; +}; + +/* TCP transport configuration. + * + * For slave mode, bindaddr selects the local address and port selects the + * listening port. For client mode, host selects the remote endpoint and + * port selects the remote port. + */ + +struct nxmb_tcp_config_s +{ + FAR const char *host; + FAR const char *bindaddr; + uint16_t port; +}; + +/* Raw ADU transport configuration. */ + +struct nxmb_raw_config_s +{ + FAR void *user_data; + nxmb_raw_tx_cb_t tx_cb; +}; + +union nxmb_transport_config_u +{ + struct nxmb_serial_config_s serial; + struct nxmb_tcp_config_s tcp; + struct nxmb_raw_config_s raw; +}; + +/* Generic NxModbus instance configuration. */ + +struct nxmb_config_s +{ + union nxmb_transport_config_u transport; + enum nxmb_mode_e mode; + uint8_t unit_id; + bool is_client; +}; + +/* Application callback table for Modbus data model access. + * + * The protocol stack passes buffers covering a contiguous address range and + * the application fills or consumes them depending on the register mode. + */ + +struct nxmb_callbacks_s +{ + CODE int (*coil_cb)(FAR uint8_t *buf, uint16_t addr, uint16_t ncoils, + enum nxmb_regmode_e mode, FAR void *priv); + CODE int (*discrete_cb)(FAR uint8_t *buf, uint16_t addr, + uint16_t ndiscrete, FAR void *priv); + CODE int (*input_cb)(FAR uint8_t *buf, uint16_t addr, uint16_t nregs, + FAR void *priv); + CODE int (*holding_cb)(FAR uint8_t *buf, uint16_t addr, uint16_t nregs, + enum nxmb_regmode_e mode, FAR void *priv); + FAR void *priv; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Name: nxmb_create + * + * Description: + * Create and initialize an NxModbus instance. This function allocates one + * entry from the static NxModbus instance pool and initializes it for the + * selected transport mode. + * + * Input Parameters: + * handle - A location to receive the opaque NxModbus handle. + * config - The transport and role configuration for the new instance. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_create(FAR nxmb_handle_t *handle, + FAR const struct nxmb_config_s *config); + +/**************************************************************************** + * Name: nxmb_destroy + * + * Description: + * Destroy a previously created NxModbus instance. This function releases + * a handle previously obtained from nxmb_create(). + * + * Input Parameters: + * handle - The NxModbus instance to destroy. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_destroy(nxmb_handle_t handle); + +/**************************************************************************** + * Name: nxmb_set_callbacks + * + * Description: + * Register the application callback table for an instance. The registered + * callback table defines how the application exposes coils, discrete + * inputs, input registers, and holding registers to the protocol stack. + * + * Input Parameters: + * handle - The NxModbus instance to update. + * cbs - The callback table to associate with the instance. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_set_callbacks(nxmb_handle_t handle, + FAR const struct nxmb_callbacks_s *cbs); + +/**************************************************************************** + * Name: nxmb_enable + * + * Description: + * Enable the configured transport and protocol handling. This function + * prepares the configured transport backend so the instance can start + * sending or receiving Modbus ADUs. + * + * Input Parameters: + * handle - The NxModbus instance to enable. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_enable(nxmb_handle_t handle); + +/**************************************************************************** + * Name: nxmb_disable + * + * Description: + * Disable an active NxModbus instance. This function stops frame + * processing and releases any transport state held by an enabled instance. + * + * Input Parameters: + * handle - The NxModbus instance to disable. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_disable(nxmb_handle_t handle); + +/**************************************************************************** + * Name: nxmb_poll + * + * Description: + * Execute one server-side polling iteration. This function performs a + * single poll of the transport backend and processes one server-side + * Modbus transaction when data is available. + * + * Input Parameters: + * handle - The NxModbus instance to poll. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_poll(nxmb_handle_t handle); + +/**************************************************************************** + * Name: nxmb_set_server_id + * + * Description: + * Configure the data returned by FC17 (Report Server ID). The response + * contains the server ID byte, a run indicator, and optional additional + * data supplied by the application. + * + * Input Parameters: + * handle - The NxModbus instance to update. + * id - The server ID byte. + * is_running - True if the device is in a running state. + * additional - Optional additional data bytes (may be NULL). + * addlen - Length of additional data. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_set_server_id(nxmb_handle_t handle, uint8_t id, bool is_running, + FAR const uint8_t *additional, uint16_t addlen); + +#ifdef CONFIG_NXMODBUS_CUSTOM_FC +/**************************************************************************** + * Name: nxmb_register_custom_fc + * + * Description: + * Register a custom Modbus function code handler for an instance. + * Custom handlers are checked after standard handlers, so standard + * function codes cannot be overridden. + * + * Input Parameters: + * h - The NxModbus instance handle. + * fc - The custom function code to register. + * handler - The handler function. Receives the instance handle and + * operates on ctx->adu in place: the request fields + * (adu.fc, adu.data[], adu.length) are overwritten with the + * response. Returns zero on success or a negated errno value + * on failure. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int nxmb_register_custom_fc(nxmb_handle_t h, uint8_t fc, + nxmb_custom_fc_handler_t handler); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NXMODBUS_NXMODBUS_H */ diff --git a/include/system/microros_transport.h b/include/system/microros_transport.h new file mode 100644 index 000000000..fc89bde3c --- /dev/null +++ b/include/system/microros_transport.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/include/system/microros_transport.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_SYSTEM_MICROROS_TRANSPORT_H +#define __APPS_INCLUDE_SYSTEM_MICROROS_TRANSPORT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* microros_transport_init + * + * Register the NuttX-native transport (UDP or Serial, selected by Kconfig) + * with the rmw_microxrcedds layer. Must be called once before + * rclc_support_init(). + * + * Returns 0 on success, negated errno on failure. + */ + +int microros_transport_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_SYSTEM_MICROROS_TRANSPORT_H */ diff --git a/include/system/readline.h b/include/system/readline.h index 5ab963879..8f40c92c1 100644 --- a/include/system/readline.h +++ b/include/system/readline.h @@ -88,8 +88,9 @@ extern "C" * * If a prompt string is used by the application, then the application * must provide the prompt string to readline() by calling this function. - * This is needed only for tab completion in cases where is it necessary - * to reprint the prompt string. + * This is needed for tab completion, and for line editing's full-line + * redraws (Home, End, history recall, ...), in cases where it is + * necessary to reprint the prompt string. * * Input Parameters: * prompt - The prompt string. This function may then be @@ -108,7 +109,7 @@ extern "C" * ****************************************************************************/ -#ifdef CONFIG_READLINE_TABCOMPLETION +#if defined(CONFIG_READLINE_TABCOMPLETION) || defined(CONFIG_READLINE_EDIT) FAR const char *readline_prompt(FAR const char *prompt); #else # define readline_prompt(p) diff --git a/include/system/smf.h b/include/system/smf.h new file mode 100644 index 000000000..edb1bf81b --- /dev/null +++ b/include/system/smf.h @@ -0,0 +1,267 @@ +/**************************************************************************** + * apps/include/system/smf.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_SYSTEM_SMF_H +#define __APPS_INCLUDE_SYSTEM_SMF_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT +# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION +# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ + { \ + .entry = (_entry), \ + .run = (_run), \ + .exit = (_exit), \ + .parent = (_parent), \ + .initial = (_initial), \ + } +# else +# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ + { \ + .entry = (_entry), \ + .run = (_run), \ + .exit = (_exit), \ + .parent = (_parent), \ + } +# endif +#else +# define SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial) \ + { \ + .entry = (_entry), \ + .run = (_run), \ + .exit = (_exit), \ + } +#endif + +#define SMF_CTX(o) ((struct smf_ctx *)(o)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum smf_state_result +{ + SMF_EVENT_HANDLED, + SMF_EVENT_PROPAGATE +}; + +typedef void (*state_method)(void *obj); + +typedef enum smf_state_result (*state_execution)(void *obj); + +struct smf_state +{ + /* Optional method that will be run when this state is entered. */ + + state_method entry; + + /* Optional method that will be run repeatedly during the state machine + * loop. + */ + + state_execution run; + + /* Optional method that will be run when this state exits. */ + + state_method exit; + +#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT + /* Optional parent state that contains common entry/run/exit + * implementation among various child states. + * entry: Parent function executes BEFORE child function. + * run: Parent function executes AFTER child function. + * exit: Parent function executes AFTER child function. + * Note: When transitioning between two child states with a shared + * parent, that parent's exit and entry functions do not execute. + */ + + const struct smf_state *parent; + +# ifdef CONFIG_SYSTEM_SMF_INITIAL_TRANSITION + /* Optional initial transition state. NULL for leaf states. */ + + const struct smf_state *initial; +# endif /* CONFIG_SYSTEM_SMF_INITIAL_TRANSITION */ +#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */ +}; + +struct smf_ctx +{ + /* Current state the state machine is executing. */ + + const struct smf_state *current; + + /* Previous state the state machine executed. */ + + const struct smf_state *previous; + +#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT + /* Currently executing state (which may be a parent). */ + + const struct smf_state *executing; +#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */ + + /* This value is set by the set_terminate function and should + * terminate the state machine when its set to a value other than + * zero when it's returned by the run_state function. + */ + + int32_t terminate_val; + + /* The state machine casts this to a "struct internal_ctx" and it's + * used to track state machine context. + */ + + uint32_t internal; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: smf_set_initial + * + * Description: + * Initialize the state machine and set its initial state. + * + * Input Parameters: + * ctx - State machine context + * init_state - Initial state the state machine starts in + * + ****************************************************************************/ + +void smf_set_initial(struct smf_ctx *ctx, + const struct smf_state *init_state); + +/**************************************************************************** + * Name: smf_set_state + * + * Description: + * Change the state machine state. This handles exiting the previous state + * and entering the target state. For HSMs the entry and exit actions of + * the Least Common Ancestor are not run. + * + * Input Parameters: + * ctx - State machine context + * new_state - State to transition to + * + ****************************************************************************/ + +void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state); + +/**************************************************************************** + * Name: smf_set_terminate + * + * Description: + * Terminate a state machine. + * + * Input Parameters: + * ctx - State machine context + * val - Non-zero termination value returned by smf_run_state + * + ****************************************************************************/ + +void smf_set_terminate(struct smf_ctx *ctx, int32_t val); + +/**************************************************************************** + * Name: smf_run_state + * + * Description: + * Run one iteration of a state machine (including any parent states). + * + * Input Parameters: + * ctx - State machine context + * + * Returned Value: + * A non-zero value terminates the state machine. This non-zero value may + * represent a terminal state being reached or detection of an error. + * + ****************************************************************************/ + +int32_t smf_run_state(struct smf_ctx *ctx); + +/**************************************************************************** + * Name: smf_get_current_leaf_state + * + * Description: + * Get the current leaf state. This may be a parent state if the HSM is + * malformed (initial transitions are not set up correctly). + * + ****************************************************************************/ + +static inline const struct smf_state * +smf_get_current_leaf_state(const struct smf_ctx *ctx) +{ + return ctx->current; +} + +/**************************************************************************** + * Name: smf_get_current_executing_state + * + * Description: + * Get the state that is currently executing. This may be a parent state. + * + ****************************************************************************/ + +static inline const struct smf_state * +smf_get_current_executing_state(const struct smf_ctx *ctx) +{ +#ifdef CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT + return ctx->executing; +#else + return ctx->current; +#endif /* CONFIG_SYSTEM_SMF_ANCESTOR_SUPPORT */ +} + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __APPS_INCLUDE_SYSTEM_SMF_H */ diff --git a/industry/foc/fixed16/foc_cordic.c b/industry/foc/fixed16/foc_cordic.c index fdd4cc059..22af88a46 100644 --- a/industry/foc/fixed16/foc_cordic.c +++ b/industry/foc/fixed16/foc_cordic.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/industry/foc/float/foc_cordic.c b/industry/foc/float/foc_cordic.c index f53c05674..d4fe510ff 100644 --- a/industry/foc/float/foc_cordic.c +++ b/industry/foc/float/foc_cordic.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/industry/modbus/.gitignore b/industry/modbus/.gitignore new file mode 100644 index 000000000..9e1d2593e --- /dev/null +++ b/industry/modbus/.gitignore @@ -0,0 +1 @@ +/Kconfig diff --git a/industry/modbus/CMakeLists.txt b/industry/modbus/CMakeLists.txt new file mode 100644 index 000000000..89038a9a7 --- /dev/null +++ b/industry/modbus/CMakeLists.txt @@ -0,0 +1,25 @@ +# ############################################################################## +# apps/industry/modbus/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +nuttx_add_subdirectory() + +nuttx_generate_kconfig(MENUDESC "Modbus") diff --git a/industry/modbus/Make.defs b/industry/modbus/Make.defs new file mode 100644 index 000000000..b358e9be4 --- /dev/null +++ b/industry/modbus/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/industry/modbus/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(wildcard $(APPDIR)/industry/modbus/*/Make.defs) diff --git a/industry/modbus/Makefile b/industry/modbus/Makefile new file mode 100644 index 000000000..320c97faf --- /dev/null +++ b/industry/modbus/Makefile @@ -0,0 +1,25 @@ +############################################################################ +# apps/industry/modbus/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +MENUDESC = "Modbus" + +include $(APPDIR)/Directory.mk diff --git a/modbus/CMakeLists.txt b/industry/modbus/freemodbus/CMakeLists.txt similarity index 94% rename from modbus/CMakeLists.txt rename to industry/modbus/freemodbus/CMakeLists.txt index edba762df..d49e6c069 100644 --- a/modbus/CMakeLists.txt +++ b/industry/modbus/freemodbus/CMakeLists.txt @@ -1,5 +1,5 @@ # ############################################################################## -# apps/modbus/CMakeLists.txt +# apps/industry/modbus/freemodbus/CMakeLists.txt # # SPDX-License-Identifier: Apache-2.0 # @@ -20,11 +20,11 @@ # # ############################################################################## -if(CONFIG_MODBUS) +if(CONFIG_INDUSTRY_MODBUS) set(CSRCS) - if(CONFIG_MODBUS_SLAVE) + if(CONFIG_INDUSTRY_MODBUS_SLAVE) list(APPEND CSRCS mb.c) endif() @@ -63,7 +63,7 @@ if(CONFIG_MODBUS) list(APPEND CSRCS nuttx/portother.c) - if(CONFIG_MODBUS_SLAVE) + if(CONFIG_INDUSTRY_MODBUS_SLAVE) list(APPEND CSRCS nuttx/portevent.c nuttx/portserial.c nuttx/porttimer.c) endif() diff --git a/modbus/Kconfig b/industry/modbus/freemodbus/Kconfig similarity index 95% rename from modbus/Kconfig rename to industry/modbus/freemodbus/Kconfig index d891d181c..7c9e834f3 100644 --- a/modbus/Kconfig +++ b/industry/modbus/freemodbus/Kconfig @@ -4,11 +4,11 @@ # menu "FreeModBus" -config MODBUS +config INDUSTRY_MODBUS bool "Modbus support using FreeModbus" default n -if MODBUS +if INDUSTRY_MODBUS config MB_FUNC_HANDLERS_MAX int "Maximum number of Modbus functions" @@ -20,11 +20,11 @@ config MB_FUNC_HANDLERS_MAX the sum of all enabled functions in this file and custom function handlers. If set to small adding more functions will fail. -config MODBUS_SLAVE +config INDUSTRY_MODBUS_SLAVE bool "Modbus slave support via FreeModBus" default n -if MODBUS_SLAVE +if INDUSTRY_MODBUS_SLAVE config MB_ASCII_ENABLED bool "Modbus ASCII support" default y @@ -144,26 +144,26 @@ config MB_FUNC_READWRITE_HOLDING_ENABLED ---help--- If the Read/Write Multiple Registers function should be enabled. -endif # MODBUS_SLAVE +endif # INDUSTRY_MODBUS_SLAVE -config MODBUS_MASTER +config INDUSTRY_MODBUS_MASTER bool "Modbus Master support via FreeModBus" default n config MB_ASCII_MASTER bool "Modbus ASCII master" default n - depends on MODBUS_MASTER + depends on INDUSTRY_MODBUS_MASTER config MB_RTU_MASTER bool "Modbus RTU master" default n - depends on MODBUS_MASTER + depends on INDUSTRY_MODBUS_MASTER config MB_PORT_HAS_CLOSE bool "Platform close callbacks" default n - depends on MODBUS_MASTER + depends on INDUSTRY_MODBUS_MASTER ---help--- A port which wants to get an callback must select CONFIG_MB_HAS_CLOSE and provide vMBMasterPortClose() as @@ -250,9 +250,9 @@ config MB_MASTER_FUNC_READWRITE_HOLDING_ENABLED If the Read/Write Multiple Registers function should be enabled. endif # MB_ASCII_MASTER || MB_RTU_MASTER -endif # MODBUS +endif # INDUSTRY_MODBUS -config MODBUS_DISABLE_LOG +config INDUSTRY_MODBUS_DISABLE_LOG bool "Disable logging for FreeModBus" default !FILE_STREAM ---help--- diff --git a/industry/modbus/freemodbus/Make.defs b/industry/modbus/freemodbus/Make.defs new file mode 100644 index 000000000..7d867a0aa --- /dev/null +++ b/industry/modbus/freemodbus/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/industry/modbus/freemodbus/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_INDUSTRY_MODBUS),) +CONFIGURED_APPS += $(APPDIR)/industry/modbus/freemodbus +endif diff --git a/modbus/Makefile b/industry/modbus/freemodbus/Makefile similarity index 91% rename from modbus/Makefile rename to industry/modbus/freemodbus/Makefile index ce7eac1d5..dddb6559a 100644 --- a/modbus/Makefile +++ b/industry/modbus/freemodbus/Makefile @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/Makefile +# apps/industry/modbus/freemodbus/Makefile # # SPDX-License-Identifier: Apache-2.0 # @@ -24,9 +24,9 @@ include $(APPDIR)/Make.defs # FreeModBus Library -ifeq ($(CONFIG_MODBUS),y) +ifeq ($(CONFIG_INDUSTRY_MODBUS),y) - ifeq ($(CONFIG_MODBUS_SLAVE),y) + ifeq ($(CONFIG_INDUSTRY_MODBUS_SLAVE),y) CSRCS += mb.c endif diff --git a/modbus/ascii/Make.defs b/industry/modbus/freemodbus/ascii/Make.defs similarity index 90% rename from modbus/ascii/Make.defs rename to industry/modbus/freemodbus/ascii/Make.defs index 972cd903f..c2fcf2218 100644 --- a/modbus/ascii/Make.defs +++ b/industry/modbus/freemodbus/ascii/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/ascii/Make.defs +# apps/industry/modbus/freemodbus/ascii/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -26,6 +26,6 @@ CSRCS += mbascii.c DEPPATH += --dep-path ascii VPATH += :ascii -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/modbus/ascii +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/industry/modbus/freemodbus/ascii endif diff --git a/modbus/ascii/mbascii.c b/industry/modbus/freemodbus/ascii/mbascii.c similarity index 99% rename from modbus/ascii/mbascii.c rename to industry/modbus/freemodbus/ascii/mbascii.c index 3540628ad..1ba55f99c 100644 --- a/modbus/ascii/mbascii.c +++ b/industry/modbus/freemodbus/ascii/mbascii.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/ascii/mbascii.c + * apps/industry/modbus/freemodbus/ascii/mbascii.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/ascii/mbascii.h b/industry/modbus/freemodbus/ascii/mbascii.h similarity index 98% rename from modbus/ascii/mbascii.h rename to industry/modbus/freemodbus/ascii/mbascii.h index 355484103..5351de010 100644 --- a/modbus/ascii/mbascii.h +++ b/industry/modbus/freemodbus/ascii/mbascii.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/ascii/mbascii.h + * apps/industry/modbus/ascii/mbascii.h * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/functions/Make.defs b/industry/modbus/freemodbus/functions/Make.defs similarity index 91% rename from modbus/functions/Make.defs rename to industry/modbus/freemodbus/functions/Make.defs index a4e6ea90c..7c53d5bfc 100644 --- a/modbus/functions/Make.defs +++ b/industry/modbus/freemodbus/functions/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/functions/Make.defs +# apps/industry/modbus/freemodbus/functions/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -33,4 +33,4 @@ endif DEPPATH += --dep-path functions VPATH += :functions -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/modbus/functions +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/industry/modbus/freemodbus/functions diff --git a/modbus/functions/mbfunccoils.c b/industry/modbus/freemodbus/functions/mbfunccoils.c similarity index 100% rename from modbus/functions/mbfunccoils.c rename to industry/modbus/freemodbus/functions/mbfunccoils.c diff --git a/modbus/functions/mbfunccoils_m.c b/industry/modbus/freemodbus/functions/mbfunccoils_m.c similarity index 99% rename from modbus/functions/mbfunccoils_m.c rename to industry/modbus/freemodbus/functions/mbfunccoils_m.c index c258d757d..ca59e4caf 100644 --- a/modbus/functions/mbfunccoils_m.c +++ b/industry/modbus/freemodbus/functions/mbfunccoils_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/functions/mbfunccoils_m.c + * apps/industry/modbus/functions/mbfunccoils_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 Armink diff --git a/modbus/functions/mbfuncdiag.c b/industry/modbus/freemodbus/functions/mbfuncdiag.c similarity index 97% rename from modbus/functions/mbfuncdiag.c rename to industry/modbus/freemodbus/functions/mbfuncdiag.c index b34581e6e..c9add6fd4 100644 --- a/modbus/functions/mbfuncdiag.c +++ b/industry/modbus/freemodbus/functions/mbfuncdiag.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/functions/mbfuncdiag.c + * apps/industry/modbus/functions/mbfuncdiag.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/functions/mbfuncdisc.c b/industry/modbus/freemodbus/functions/mbfuncdisc.c similarity index 100% rename from modbus/functions/mbfuncdisc.c rename to industry/modbus/freemodbus/functions/mbfuncdisc.c diff --git a/modbus/functions/mbfuncdisc_m.c b/industry/modbus/freemodbus/functions/mbfuncdisc_m.c similarity index 99% rename from modbus/functions/mbfuncdisc_m.c rename to industry/modbus/freemodbus/functions/mbfuncdisc_m.c index 29386996d..770ab899d 100644 --- a/modbus/functions/mbfuncdisc_m.c +++ b/industry/modbus/freemodbus/functions/mbfuncdisc_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/functions/mbfuncdisc_m.c + * apps/industry/modbus/functions/mbfuncdisc_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 Armink diff --git a/modbus/functions/mbfuncholding.c b/industry/modbus/freemodbus/functions/mbfuncholding.c similarity index 100% rename from modbus/functions/mbfuncholding.c rename to industry/modbus/freemodbus/functions/mbfuncholding.c diff --git a/modbus/functions/mbfuncholding_m.c b/industry/modbus/freemodbus/functions/mbfuncholding_m.c similarity index 99% rename from modbus/functions/mbfuncholding_m.c rename to industry/modbus/freemodbus/functions/mbfuncholding_m.c index b017be715..59be35b26 100644 --- a/modbus/functions/mbfuncholding_m.c +++ b/industry/modbus/freemodbus/functions/mbfuncholding_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/functions/mbfuncholding_m.c + * apps/industry/modbus/functions/mbfuncholding_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 Armink diff --git a/modbus/functions/mbfuncinput.c b/industry/modbus/freemodbus/functions/mbfuncinput.c similarity index 100% rename from modbus/functions/mbfuncinput.c rename to industry/modbus/freemodbus/functions/mbfuncinput.c diff --git a/modbus/functions/mbfuncinput_m.c b/industry/modbus/freemodbus/functions/mbfuncinput_m.c similarity index 99% rename from modbus/functions/mbfuncinput_m.c rename to industry/modbus/freemodbus/functions/mbfuncinput_m.c index 4ca7e8113..2d4799037 100644 --- a/modbus/functions/mbfuncinput_m.c +++ b/industry/modbus/freemodbus/functions/mbfuncinput_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/functions/mbfuncinput_m.c + * apps/industry/modbus/functions/mbfuncinput_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 Armink diff --git a/modbus/functions/mbfuncother.c b/industry/modbus/freemodbus/functions/mbfuncother.c similarity index 100% rename from modbus/functions/mbfuncother.c rename to industry/modbus/freemodbus/functions/mbfuncother.c diff --git a/modbus/functions/mbutils.c b/industry/modbus/freemodbus/functions/mbutils.c similarity index 100% rename from modbus/functions/mbutils.c rename to industry/modbus/freemodbus/functions/mbutils.c diff --git a/modbus/mb.c b/industry/modbus/freemodbus/mb.c similarity index 99% rename from modbus/mb.c rename to industry/modbus/freemodbus/mb.c index 16f5567d8..3d4b9bf5a 100644 --- a/modbus/mb.c +++ b/industry/modbus/freemodbus/mb.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/mb.c + * apps/industry/modbus/mb.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/mb_m.c b/industry/modbus/freemodbus/mb_m.c similarity index 100% rename from modbus/mb_m.c rename to industry/modbus/freemodbus/mb_m.c diff --git a/modbus/nuttx/Make.defs b/industry/modbus/freemodbus/nuttx/Make.defs similarity index 88% rename from modbus/nuttx/Make.defs rename to industry/modbus/freemodbus/nuttx/Make.defs index 7960ecee4..1c3413ce3 100644 --- a/modbus/nuttx/Make.defs +++ b/industry/modbus/freemodbus/nuttx/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/nuttx/Make.defs +# apps/industry/modbus/freemodbus/nuttx/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -22,7 +22,7 @@ CSRCS += portother.c -ifeq ($(CONFIG_MODBUS_SLAVE),y) +ifeq ($(CONFIG_INDUSTRY_MODBUS_SLAVE),y) CSRCS += portevent.c portserial.c porttimer.c endif @@ -32,4 +32,4 @@ endif DEPPATH += --dep-path nuttx VPATH += :nuttx -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/modbus/nuttx +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/industry/modbus/freemodbus/nuttx diff --git a/modbus/nuttx/port.h b/industry/modbus/freemodbus/nuttx/port.h similarity index 94% rename from modbus/nuttx/port.h rename to industry/modbus/freemodbus/nuttx/port.h index c79dcdd98..149cefad2 100644 --- a/modbus/nuttx/port.h +++ b/industry/modbus/freemodbus/nuttx/port.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/port.h + * apps/industry/modbus/nuttx/port.h * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter @@ -28,8 +28,8 @@ * ****************************************************************************/ -#ifndef __APPS_MODBUS_NUTTX_PORT_H -#define __APPS_MODBUS_NUTTX_PORT_H +#ifndef __APPS_INDUSTRY_MODBUS_NUTTX_PORT_H +#define __APPS_INDUSTRY_MODBUS_NUTTX_PORT_H /**************************************************************************** * Included Files @@ -79,7 +79,7 @@ typedef enum void vMBPortEnterCritical(void); void vMBPortExitCritical(void); -#ifndef CONFIG_MODBUS_DISABLE_LOG +#ifndef CONFIG_INDUSTRY_MODBUS_DISABLE_LOG void vMBPortLog(eMBPortLogLevel eLevel, const char *szModule, const char *szFmt, ...) printf_like(3, 4); #else @@ -103,4 +103,4 @@ bool xMBPortSerialSetTimeout(uint32_t dwTimeoutMs); } #endif -#endif /* __APPS_MODBUS_NUTTX_PORT_H */ +#endif /* __APPS_INDUSTRY_MODBUS_NUTTX_PORT_H */ diff --git a/modbus/nuttx/portevent.c b/industry/modbus/freemodbus/nuttx/portevent.c similarity index 98% rename from modbus/nuttx/portevent.c rename to industry/modbus/freemodbus/nuttx/portevent.c index bce6e9c54..d9d31072d 100644 --- a/modbus/nuttx/portevent.c +++ b/industry/modbus/freemodbus/nuttx/portevent.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portevent.c + * apps/industry/modbus/nuttx/portevent.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/portevent_m.c b/industry/modbus/freemodbus/nuttx/portevent_m.c similarity index 99% rename from modbus/nuttx/portevent_m.c rename to industry/modbus/freemodbus/nuttx/portevent_m.c index fb40f4dee..90d0c0e6e 100644 --- a/modbus/nuttx/portevent_m.c +++ b/industry/modbus/freemodbus/nuttx/portevent_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portevent_m.c + * apps/industry/modbus/nuttx/portevent_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/portother.c b/industry/modbus/freemodbus/nuttx/portother.c similarity index 97% rename from modbus/nuttx/portother.c rename to industry/modbus/freemodbus/nuttx/portother.c index 3de2a38b5..b6c4731d9 100644 --- a/modbus/nuttx/portother.c +++ b/industry/modbus/freemodbus/nuttx/portother.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portother.c + * apps/industry/modbus/nuttx/portother.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter @@ -73,7 +73,7 @@ void vMBPortLogFile(FILE * fNewLogFile) fLogFile = fNewLogFile; } -#ifndef CONFIG_MODBUS_DISABLE_LOG +#ifndef CONFIG_INDUSTRY_MODBUS_DISABLE_LOG void vMBPortLog(eMBPortLogLevel eLevel, const char * szModule, const char * szFmt, ...) { diff --git a/modbus/nuttx/portother_m.c b/industry/modbus/freemodbus/nuttx/portother_m.c similarity index 98% rename from modbus/nuttx/portother_m.c rename to industry/modbus/freemodbus/nuttx/portother_m.c index 1d703c192..9ee337f2a 100644 --- a/modbus/nuttx/portother_m.c +++ b/industry/modbus/freemodbus/nuttx/portother_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portother_m.c + * apps/industry/modbus/nuttx/portother_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/portserial.c b/industry/modbus/freemodbus/nuttx/portserial.c similarity index 99% rename from modbus/nuttx/portserial.c rename to industry/modbus/freemodbus/nuttx/portserial.c index 35155a732..4cb502aa5 100644 --- a/modbus/nuttx/portserial.c +++ b/industry/modbus/freemodbus/nuttx/portserial.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portserial.c + * apps/industry/modbus/nuttx/portserial.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/portserial_m.c b/industry/modbus/freemodbus/nuttx/portserial_m.c similarity index 99% rename from modbus/nuttx/portserial_m.c rename to industry/modbus/freemodbus/nuttx/portserial_m.c index 98c11eb28..d7fc25526 100644 --- a/modbus/nuttx/portserial_m.c +++ b/industry/modbus/freemodbus/nuttx/portserial_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/portserial_m.c + * apps/industry/modbus/nuttx/portserial_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/porttimer.c b/industry/modbus/freemodbus/nuttx/porttimer.c similarity index 98% rename from modbus/nuttx/porttimer.c rename to industry/modbus/freemodbus/nuttx/porttimer.c index 1685df6ee..d09561f4b 100644 --- a/modbus/nuttx/porttimer.c +++ b/industry/modbus/freemodbus/nuttx/porttimer.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/porttimer.c + * apps/industry/modbus/nuttx/porttimer.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/nuttx/porttimer_m.c b/industry/modbus/freemodbus/nuttx/porttimer_m.c similarity index 99% rename from modbus/nuttx/porttimer_m.c rename to industry/modbus/freemodbus/nuttx/porttimer_m.c index 1e878aa00..8d9f3f3af 100644 --- a/modbus/nuttx/porttimer_m.c +++ b/industry/modbus/freemodbus/nuttx/porttimer_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/nuttx/porttimer_m.c + * apps/industry/modbus/nuttx/porttimer_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/rtu/Make.defs b/industry/modbus/freemodbus/rtu/Make.defs similarity index 91% rename from modbus/rtu/Make.defs rename to industry/modbus/freemodbus/rtu/Make.defs index ac4ab7e2a..46656017d 100644 --- a/modbus/rtu/Make.defs +++ b/industry/modbus/freemodbus/rtu/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/rtu/Make.defs +# apps/industry/modbus/freemodbus/rtu/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -40,6 +40,6 @@ endif DEPPATH += --dep-path rtu VPATH += :rtu -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/modbus/rtu +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/industry/modbus/freemodbus/rtu endif diff --git a/modbus/rtu/mbcrc.c b/industry/modbus/freemodbus/rtu/mbcrc.c similarity index 99% rename from modbus/rtu/mbcrc.c rename to industry/modbus/freemodbus/rtu/mbcrc.c index dcaddec71..df16f13e0 100644 --- a/modbus/rtu/mbcrc.c +++ b/industry/modbus/freemodbus/rtu/mbcrc.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/rtu/mbcrc.c + * apps/industry/modbus/rtu/mbcrc.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/rtu/mbcrc.h b/industry/modbus/freemodbus/rtu/mbcrc.h similarity index 100% rename from modbus/rtu/mbcrc.h rename to industry/modbus/freemodbus/rtu/mbcrc.h diff --git a/modbus/rtu/mbrtu.c b/industry/modbus/freemodbus/rtu/mbrtu.c similarity index 99% rename from modbus/rtu/mbrtu.c rename to industry/modbus/freemodbus/rtu/mbrtu.c index 0f8262d4f..ead36d164 100644 --- a/modbus/rtu/mbrtu.c +++ b/industry/modbus/freemodbus/rtu/mbrtu.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/rtu/mbrtu.c + * apps/industry/modbus/rtu/mbrtu.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/rtu/mbrtu.h b/industry/modbus/freemodbus/rtu/mbrtu.h similarity index 98% rename from modbus/rtu/mbrtu.h rename to industry/modbus/freemodbus/rtu/mbrtu.h index 418311bff..ff31a1844 100644 --- a/modbus/rtu/mbrtu.h +++ b/industry/modbus/freemodbus/rtu/mbrtu.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/rtu/mbrtu.h + * apps/industry/modbus/rtu/mbrtu.h * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/rtu/mbrtu_m.c b/industry/modbus/freemodbus/rtu/mbrtu_m.c similarity index 99% rename from modbus/rtu/mbrtu_m.c rename to industry/modbus/freemodbus/rtu/mbrtu_m.c index ed8e62488..4a67a0508 100644 --- a/modbus/rtu/mbrtu_m.c +++ b/industry/modbus/freemodbus/rtu/mbrtu_m.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/rtu/mbrtu_m.c + * apps/industry/modbus/rtu/mbrtu_m.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 China Beijing Armink diff --git a/modbus/rtu/mbrtu_m.h b/industry/modbus/freemodbus/rtu/mbrtu_m.h similarity index 98% rename from modbus/rtu/mbrtu_m.h rename to industry/modbus/freemodbus/rtu/mbrtu_m.h index 5fb2eee43..b59652b59 100644 --- a/modbus/rtu/mbrtu_m.h +++ b/industry/modbus/freemodbus/rtu/mbrtu_m.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/rtu/mbrtu_m.h + * apps/industry/modbus/rtu/mbrtu_m.h * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2013 China Beijing Armink diff --git a/modbus/tcp/Make.defs b/industry/modbus/freemodbus/tcp/Make.defs similarity index 90% rename from modbus/tcp/Make.defs rename to industry/modbus/freemodbus/tcp/Make.defs index dfd220f41..281a9a5be 100644 --- a/modbus/tcp/Make.defs +++ b/industry/modbus/freemodbus/tcp/Make.defs @@ -1,5 +1,5 @@ ############################################################################ -# apps/modbus/tcp/Make.defs +# apps/industry/modbus/freemodbus/tcp/Make.defs # # SPDX-License-Identifier: Apache-2.0 # @@ -26,6 +26,6 @@ CSRCS += mbtcp.c DEPPATH += --dep-path tcp VPATH += :tcp -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/modbus/tcp +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/industry/modbus/freemodbus/tcp endif diff --git a/modbus/tcp/mbtcp.c b/industry/modbus/freemodbus/tcp/mbtcp.c similarity index 99% rename from modbus/tcp/mbtcp.c rename to industry/modbus/freemodbus/tcp/mbtcp.c index ca9c41c43..6957effb9 100644 --- a/modbus/tcp/mbtcp.c +++ b/industry/modbus/freemodbus/tcp/mbtcp.c @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/tcp/mbtcp.c + * apps/industry/modbus/tcp/mbtcp.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/modbus/tcp/mbtcp.h b/industry/modbus/freemodbus/tcp/mbtcp.h similarity index 98% rename from modbus/tcp/mbtcp.h rename to industry/modbus/freemodbus/tcp/mbtcp.h index 39fa9c8ce..26e380cb9 100644 --- a/modbus/tcp/mbtcp.h +++ b/industry/modbus/freemodbus/tcp/mbtcp.h @@ -1,5 +1,5 @@ /**************************************************************************** - * apps/modbus/tcp/mbtcp.h + * apps/industry/modbus/tcp/mbtcp.h * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2006 Christian Walter diff --git a/industry/nxmodbus/CMakeLists.txt b/industry/nxmodbus/CMakeLists.txt new file mode 100644 index 000000000..35be513e4 --- /dev/null +++ b/industry/nxmodbus/CMakeLists.txt @@ -0,0 +1,68 @@ +# ############################################################################## +# apps/industry/nxmodbus/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_INDUSTRY_NXMODBUS) + + set(CSRCS) + + # Core sources + list(APPEND CSRCS core/nxmb_context.c core/nxmb_utils.c) + + if(CONFIG_NXMODBUS_SERVER) + list(APPEND CSRCS core/nxmb_server.c) + list(APPEND CSRCS core/nxmb_func.c) + list(APPEND CSRCS core/nxmb_func_coils.c core/nxmb_func_discrete.c) + list(APPEND CSRCS core/nxmb_func_holding.c core/nxmb_func_input.c) + list(APPEND CSRCS core/nxmb_func_diag.c core/nxmb_func_other.c) + endif() + + if(CONFIG_NXMODBUS_CLIENT) + list(APPEND CSRCS core/nxmb_client.c) + endif() + + # Transport layer sources + if(CONFIG_NXMODBUS_RTU OR CONFIG_NXMODBUS_ASCII) + list(APPEND CSRCS transport/nxmb_serial_common.c) + endif() + + if(CONFIG_NXMODBUS_RTU) + list(APPEND CSRCS transport/nxmb_crc.c transport/nxmb_serial.c) + endif() + + if(CONFIG_NXMODBUS_ASCII) + list(APPEND CSRCS transport/nxmb_lrc.c transport/nxmb_ascii.c) + endif() + + if(CONFIG_NXMODBUS_RAW_ADU) + list(APPEND CSRCS transport/nxmb_raw.c) + endif() + + if(CONFIG_NXMODBUS_TCP) + list(APPEND CSRCS transport/nxmb_tcp.c) + endif() + + if(CSRCS) + target_include_directories(apps PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_sources(apps PRIVATE ${CSRCS}) + endif() + +endif() diff --git a/industry/nxmodbus/Kconfig b/industry/nxmodbus/Kconfig new file mode 100644 index 000000000..31f555fe7 --- /dev/null +++ b/industry/nxmodbus/Kconfig @@ -0,0 +1,245 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig INDUSTRY_NXMODBUS + bool "NxModbus - New Modbus Stack" + default n + ---help--- + NxModbus is a modern Modbus stack for NuttX with multi-instance + support, per-instance register callbacks, and pluggable transport + backends (RTU, ASCII, TCP, Raw ADU). + + This implementation follows NuttX coding standards and is + designed for upstream contribution to nuttx-apps. + +if INDUSTRY_NXMODBUS + +config NXMODBUS_SERVER + bool "Server (slave) support" + default y + ---help--- + Enable Modbus server (slave) support. The server processes + incoming Modbus requests and calls registered register + callbacks. + +config NXMODBUS_CLIENT + bool "Client (master) support" + default n + ---help--- + Enable Modbus client (master) support. The client sends + Modbus requests to a server and receives responses. + +config NXMODBUS_RTU + bool "Modbus RTU mode" + default y + ---help--- + Enable Modbus RTU (Remote Terminal Unit) serial transmission + mode. Uses binary encoding with CRC16 error checking. + +config NXMODBUS_ASCII + bool "Modbus ASCII mode" + default n + ---help--- + Enable Modbus ASCII serial transmission mode. Uses + hex-encoded text with LRC error checking. Slower but + more human-readable than RTU. + +config NXMODBUS_TCP + bool "Modbus TCP mode" + default n + depends on NET_TCP + ---help--- + Enable Modbus TCP support. Modbus frames are encapsulated + in TCP/IP using the MBAP header format. Requires network + stack with TCP support. + +config NXMODBUS_RAW_ADU + bool "Raw ADU transport support" + default n + ---help--- + Enable raw ADU (Application Data Unit) transport mode. + The application provides callbacks for transmitting and + receiving raw Modbus frames. This enables custom transports + such as TLS, CAN, BLE, or MQTT. + +config NXMODBUS_MAX_INSTANCES + int "Maximum number of NxModbus instances" + default 1 + range 1 16 + ---help--- + Maximum number of simultaneous NxModbus instances. Each + instance can use a different transport (RTU, TCP, etc.) + and configuration. Instances are allocated from a static + pool at compile time. + +config NXMODBUS_ADU_DATA_MAX + int "ADU data buffer size in bytes" + default 252 + range 4 252 + ---help--- + Size of the data[] payload in struct nxmb_adu_s, in bytes. + This is the PDU body after the function code. The Modbus + specification allows up to 252 data bytes (1 fc + 252 data = + 253-byte PDU max). Reduce this value to save RAM on + resource-constrained systems. + +config NXMODBUS_REP_SERVER_ID_BUF + int "Report Server ID buffer size" + default 32 + range 4 253 + ---help--- + Size of the buffer used by FC17 (Report Server ID). + This buffer holds the server ID byte, run indicator, and + optional additional data configured via nxmb_set_server_id(). + +config NXMODBUS_ASCII_TIMEOUT_SEC + int "ASCII character timeout (seconds)" + default 1 + range 1 60 + depends on NXMODBUS_ASCII + ---help--- + Character timeout for Modbus ASCII mode in seconds. If no + character is received within this period, the current frame + is discarded. Default is 1 second per the Modbus specification. + +config NXMODBUS_CLIENT_TIMEOUT_MS + int "Client default response timeout (ms)" + default 1000 + range 100 60000 + depends on NXMODBUS_CLIENT + ---help--- + Default response timeout for client (master) mode in + milliseconds. If no response is received from the server + within this period, the request fails with ETIMEDOUT. + Can be overridden at runtime via nxmb_set_timeout(). + +config NXMODBUS_TCP_MAX_CLIENTS + int "Maximum simultaneous TCP client connections" + default 1 + range 1 8 + depends on NXMODBUS_TCP + ---help--- + Maximum number of simultaneous TCP client connections + accepted by a Modbus TCP server instance. Each connection + uses one file descriptor. Set to 1 for minimal resource + usage. + +config NXMODBUS_TCP_TIMEOUT_SEC + int "TCP idle connection timeout (seconds)" + default 60 + range 1 3600 + depends on NXMODBUS_TCP + ---help--- + Idle timeout for TCP connections in seconds. If no data is + received on a client connection within this period, the + connection is closed. Set to a higher value for slow networks. + +config NXMODBUS_RTU_IDLE_TIMEOUT_MS + int "RTU inter-frame idle timeout (ms)" + default 50 + range 1 1000 + depends on NXMODBUS_RTU + ---help--- + Fallback idle timeout in milliseconds used by the RTU + transport when waiting for a new frame via select(). This + is the maximum time to block waiting for data when no + frame is in progress. The T3.5 character timing is still + used for frame delimiting within an active reception. + +menu "Function Code Selection" + +config NXMODBUS_FUNC_READ_COILS + bool "FC01 Read Coils" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC01 Read Coils function code handler. + +config NXMODBUS_FUNC_READ_DISCRETE + bool "FC02 Read Discrete Inputs" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC02 Read Discrete Inputs function code handler. + +config NXMODBUS_FUNC_READ_HOLDING + bool "FC03 Read Holding Registers" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC03 Read Holding Registers function code handler. + +config NXMODBUS_FUNC_READ_INPUT + bool "FC04 Read Input Registers" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC04 Read Input Registers function code handler. + +config NXMODBUS_FUNC_WRITE_COIL + bool "FC05 Write Single Coil" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC05 Write Single Coil function code handler. + +config NXMODBUS_FUNC_WRITE_HOLDING + bool "FC06 Write Single Holding Register" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC06 Write Single Holding Register function + code handler. + +config NXMODBUS_FUNC_DIAGNOSTICS + bool "FC08 Diagnostics" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC08 Diagnostics function code handler. + Only sub-function 0x0000 (Return Query Data) is supported. + +config NXMODBUS_FUNC_WRITE_COILS + bool "FC15 Write Multiple Coils" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC15 Write Multiple Coils function code handler. + +config NXMODBUS_FUNC_WRITE_HOLDINGS + bool "FC16 Write Multiple Holding Registers" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC16 Write Multiple Holding Registers function + code handler. + +config NXMODBUS_FUNC_REPORT_SERVER_ID + bool "FC17 Report Server ID" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC17 Report Server ID function code handler. + +config NXMODBUS_FUNC_READWRITE_HOLDINGS + bool "FC23 Read/Write Multiple Holding Registers" + default y + depends on NXMODBUS_SERVER + ---help--- + Enable FC23 Read/Write Multiple Holding Registers + function code handler. + +endmenu # Function Code Selection + +config NXMODBUS_CUSTOM_FC + bool "Custom function code extension support" + default y + depends on NXMODBUS + ---help--- + Enable support for registering custom Modbus function + code handlers. Applications can extend the Modbus + function code set via nxmb_register_custom_fc(). + +endif # INDUSTRY_NXMODBUS diff --git a/industry/nxmodbus/Make.defs b/industry/nxmodbus/Make.defs new file mode 100644 index 000000000..539b48d64 --- /dev/null +++ b/industry/nxmodbus/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/modbus/nxmodbus/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_INDUSTRY_NXMODBUS),) +CONFIGURED_APPS += $(APPDIR)/industry/nxmodbus +endif diff --git a/industry/nxmodbus/Makefile b/industry/nxmodbus/Makefile new file mode 100644 index 000000000..64caacdba --- /dev/null +++ b/industry/nxmodbus/Makefile @@ -0,0 +1,76 @@ +############################################################################ +# apps/industry/nxmodbus/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# NxModbus Library + +ifeq ($(CONFIG_INDUSTRY_NXMODBUS),y) + +# Core sources +CSRCS := core/nxmb_context.c +CSRCS += core/nxmb_utils.c + +ifeq ($(CONFIG_NXMODBUS_SERVER),y) +CSRCS += core/nxmb_server.c +CSRCS += core/nxmb_func.c +CSRCS += core/nxmb_func_coils.c +CSRCS += core/nxmb_func_discrete.c +CSRCS += core/nxmb_func_holding.c +CSRCS += core/nxmb_func_input.c +CSRCS += core/nxmb_func_diag.c +CSRCS += core/nxmb_func_other.c +endif + +ifeq ($(CONFIG_NXMODBUS_CLIENT),y) +CSRCS += core/nxmb_client.c +endif + +# Transport layer sources +ifneq (,$(filter y,$(CONFIG_NXMODBUS_RTU) $(CONFIG_NXMODBUS_ASCII))) +CSRCS += transport/nxmb_serial_common.c +endif + +ifeq ($(CONFIG_NXMODBUS_RTU),y) +CSRCS += transport/nxmb_crc.c +CSRCS += transport/nxmb_serial.c +endif + +ifeq ($(CONFIG_NXMODBUS_ASCII),y) +CSRCS += transport/nxmb_lrc.c +CSRCS += transport/nxmb_ascii.c +endif + +ifeq ($(CONFIG_NXMODBUS_RAW_ADU),y) +CSRCS += transport/nxmb_raw.c +endif + +ifeq ($(CONFIG_NXMODBUS_TCP),y) +CSRCS += transport/nxmb_tcp.c +endif + +# Include private headers +CFLAGS += ${INCDIR_PREFIX}$(CURDIR) + +endif + +include $(APPDIR)/Application.mk diff --git a/industry/nxmodbus/core/nxmb_client.c b/industry/nxmodbus/core/nxmb_client.c new file mode 100644 index 000000000..24b264f92 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_client.c @@ -0,0 +1,739 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_client.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nxmb_client_state_s +{ + uint32_t timeout_ms; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int nxmb_client_tx_wait_rx(nxmb_handle_t ctx, + uint8_t expected_uid, uint8_t expected_fc); +static int nxmb_client_validate_response(nxmb_handle_t ctx, + uint8_t expected_uid, + uint8_t expected_fc); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_exception_to_errno + ****************************************************************************/ + +static int nxmb_exception_to_errno(uint8_t exception) +{ + switch (exception) + { + case NXMB_EX_ILLEGAL_FUNCTION: + return -ENXIO; + + case NXMB_EX_ILLEGAL_DATA_ADDRESS: + return -EFAULT; + + case NXMB_EX_ILLEGAL_DATA_VALUE: + return -EINVAL; + + case NXMB_EX_DEVICE_FAILURE: + return -EIO; + + case NXMB_EX_ACKNOWLEDGE: + return -EBUSY; + + case NXMB_EX_DEVICE_BUSY: + return -EBUSY; + + default: + return -EPROTO; + } +} + +/**************************************************************************** + * Name: nxmb_client_validate_response + ****************************************************************************/ + +static int nxmb_client_validate_response(nxmb_handle_t ctx, + uint8_t expected_uid, + uint8_t expected_fc) +{ + uint8_t rx_uid; + uint8_t rx_fc; + + if (ctx->adu.length < 2) + { + return -EPROTO; + } + + rx_uid = ctx->adu.unit_id; + rx_fc = ctx->adu.fc; + + if (rx_uid != expected_uid) + { + return -EPROTO; + } + + if (rx_fc == (expected_fc | 0x80)) + { + if (ctx->adu.length < 3) + { + return -EPROTO; + } + + return nxmb_exception_to_errno(ctx->adu.data[0]); + } + + if (rx_fc != expected_fc) + { + return -EPROTO; + } + + return OK; +} + +/**************************************************************************** + * Name: nxmb_client_tx_wait_rx + ****************************************************************************/ + +static int nxmb_client_tx_wait_rx(nxmb_handle_t ctx, uint8_t expected_uid, + uint8_t expected_fc) +{ + FAR struct nxmb_client_state_s *state; + uint64_t deadline; + int ret; + + if (ctx == NULL || ctx->client_state == NULL) + { + return -EINVAL; + } + + state = (FAR struct nxmb_client_state_s *)ctx->client_state; + + ret = ctx->transport_ops->send(ctx); + if (ret < 0) + { + return ret; + } + + /* Broadcast frames do not receive a response */ + + if (expected_uid == NXMB_ADDRESS_BROADCAST) + { + return OK; + } + + deadline = nxmb_util_clock_ms() + state->timeout_ms; + + for (; ; ) + { + if (nxmb_util_clock_ms() >= deadline) + { + return -ETIMEDOUT; + } + + ret = ctx->transport_ops->receive(ctx); + if (ret > 0) + { + return nxmb_client_validate_response(ctx, expected_uid, + expected_fc); + } + else if (ret < 0 && ret != -EAGAIN) + { + return ret; + } + } +} + +/**************************************************************************** + * Name: nxmb_client_read_bits + * + * Description: + * Common helper for FC01 (Read Coils) and FC02 (Read Discrete Inputs). + * + ****************************************************************************/ + +static int nxmb_client_read_bits(nxmb_handle_t ctx, uint8_t uid, uint8_t fc, + uint16_t addr, uint16_t count, + FAR uint8_t *buf) +{ + uint16_t nbytes; + int ret; + + nbytes = (count + 7) / 8; + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = fc; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], count); + ctx->adu.length = 6; + + ret = nxmb_client_tx_wait_rx(ctx, uid, fc); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (ctx->adu.length < (3 + nbytes) || ctx->adu.data[0] != nbytes) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + memcpy(buf, &ctx->adu.data[1], nbytes); + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_client_read_regs + * + * Description: + * Common helper for FC03 (Read Holding) and FC04 (Read Input) registers. + * + ****************************************************************************/ + +static int nxmb_client_read_regs(nxmb_handle_t ctx, uint8_t uid, uint8_t fc, + uint16_t addr, uint16_t count, + FAR uint16_t *buf) +{ + uint16_t nbytes; + int ret; + int i; + + nbytes = count * 2; + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = fc; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], count); + ctx->adu.length = 6; + + ret = nxmb_client_tx_wait_rx(ctx, uid, fc); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (ctx->adu.length < (3 + nbytes) || ctx->adu.data[0] != nbytes) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + for (i = 0; i < count; i++) + { + buf[i] = nxmb_util_get_u16_be(&ctx->adu.data[1 + i * 2]); + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_read_coils + ****************************************************************************/ + +int nxmb_read_coils(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint8_t *buf) +{ + DEBUGASSERT(ctx && buf); + + if (uid == NXMB_ADDRESS_BROADCAST || count == 0 || count > 2000) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + return nxmb_client_read_bits(ctx, uid, NXMB_FC_READ_COILS, addr, + count, buf); +} + +/**************************************************************************** + * Name: nxmb_read_discrete + ****************************************************************************/ + +int nxmb_read_discrete(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint8_t *buf) +{ + DEBUGASSERT(ctx && buf); + + if (uid == NXMB_ADDRESS_BROADCAST || count == 0 || count > 2000) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + return nxmb_client_read_bits(ctx, uid, NXMB_FC_READ_DISCRETE, addr, + count, buf); +} + +/**************************************************************************** + * Name: nxmb_read_input + ****************************************************************************/ + +int nxmb_read_input(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint16_t *buf) +{ + DEBUGASSERT(ctx && buf); + + if (uid == NXMB_ADDRESS_BROADCAST || count == 0 || count > 125) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + return nxmb_client_read_regs(ctx, uid, NXMB_FC_READ_INPUT, addr, + count, buf); +} + +/**************************************************************************** + * Name: nxmb_read_holding + ****************************************************************************/ + +int nxmb_read_holding(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR uint16_t *buf) +{ + DEBUGASSERT(ctx && buf); + + if (uid == NXMB_ADDRESS_BROADCAST || count == 0 || count > 125) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + return nxmb_client_read_regs(ctx, uid, NXMB_FC_READ_HOLDING, addr, + count, buf); +} + +/**************************************************************************** + * Name: nxmb_write_coil + ****************************************************************************/ + +int nxmb_write_coil(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + bool value) +{ + uint16_t coil_value; + int ret; + + DEBUGASSERT(ctx); + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + coil_value = value ? 0xff00 : 0x0000; + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = NXMB_FC_WRITE_COIL; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], coil_value); + ctx->adu.length = 6; + + ret = nxmb_client_tx_wait_rx(ctx, uid, NXMB_FC_WRITE_COIL); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (uid != NXMB_ADDRESS_BROADCAST && ctx->adu.length < 6) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_write_holding + ****************************************************************************/ + +int nxmb_write_holding(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t value) +{ + int ret; + + DEBUGASSERT(ctx); + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = NXMB_FC_WRITE_HOLDING; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], value); + ctx->adu.length = 6; + + ret = nxmb_client_tx_wait_rx(ctx, uid, NXMB_FC_WRITE_HOLDING); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (uid != NXMB_ADDRESS_BROADCAST && ctx->adu.length < 6) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_write_coils + ****************************************************************************/ + +int nxmb_write_coils(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR const uint8_t *buf) +{ + uint16_t nbytes; + int ret; + + DEBUGASSERT(ctx && buf); + + if (count == 0 || count > 1968) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + nbytes = (count + 7) / 8; + + /* Request layout in adu.data[]: addr(2) + count(2) + bcnt(1) + nbytes */ + + if (5 + nbytes > NXMB_ADU_DATA_MAX) + { + return -EMSGSIZE; + } + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = NXMB_FC_WRITE_COILS; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], count); + ctx->adu.data[4] = nbytes; + memcpy(&ctx->adu.data[5], buf, nbytes); + ctx->adu.length = 7 + nbytes; + + ret = nxmb_client_tx_wait_rx(ctx, uid, NXMB_FC_WRITE_COILS); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (uid != NXMB_ADDRESS_BROADCAST && ctx->adu.length < 6) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_write_holdings + ****************************************************************************/ + +int nxmb_write_holdings(nxmb_handle_t ctx, uint8_t uid, uint16_t addr, + uint16_t count, FAR const uint16_t *buf) +{ + uint16_t nbytes; + int ret; + int i; + + DEBUGASSERT(ctx && buf); + + if (count == 0 || count > 123) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + nbytes = count * 2; + + /* Request layout in adu.data[]: addr(2) + count(2) + bcnt(1) + nbytes */ + + if (5 + nbytes > NXMB_ADU_DATA_MAX) + { + return -EMSGSIZE; + } + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = NXMB_FC_WRITE_HOLDINGS; + nxmb_util_put_u16_be(&ctx->adu.data[0], addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], count); + ctx->adu.data[4] = nbytes; + + for (i = 0; i < count; i++) + { + nxmb_util_put_u16_be(&ctx->adu.data[5 + i * 2], buf[i]); + } + + ctx->adu.length = 7 + nbytes; + + ret = nxmb_client_tx_wait_rx(ctx, uid, NXMB_FC_WRITE_HOLDINGS); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (uid != NXMB_ADDRESS_BROADCAST && ctx->adu.length < 6) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_readwrite_holdings + ****************************************************************************/ + +int nxmb_readwrite_holdings(nxmb_handle_t ctx, uint8_t uid, uint16_t rd_addr, + uint16_t rd_count, FAR uint16_t *rd_buf, + uint16_t wr_addr, uint16_t wr_count, + FAR const uint16_t *wr_buf) +{ + uint16_t rd_nbytes; + uint16_t wr_nbytes; + int ret; + int i; + + DEBUGASSERT(ctx && rd_buf && wr_buf); + + if (uid == NXMB_ADDRESS_BROADCAST || rd_count == 0 || rd_count > 125 || + wr_count == 0 || wr_count > 121) + { + return -EINVAL; + } + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + rd_nbytes = rd_count * 2; + wr_nbytes = wr_count * 2; + + /* Request layout: rd_addr(2) + rd_qty(2) + wr_addr(2) + wr_qty(2) + + * wr_bcnt(1) + wr_nbytes + */ + + if (9 + wr_nbytes > NXMB_ADU_DATA_MAX) + { + return -EMSGSIZE; + } + + pthread_mutex_lock(&ctx->lock); + + ctx->adu.unit_id = uid; + ctx->adu.fc = NXMB_FC_READWRITE_HOLDINGS; + nxmb_util_put_u16_be(&ctx->adu.data[0], rd_addr); + nxmb_util_put_u16_be(&ctx->adu.data[2], rd_count); + nxmb_util_put_u16_be(&ctx->adu.data[4], wr_addr); + nxmb_util_put_u16_be(&ctx->adu.data[6], wr_count); + ctx->adu.data[8] = (uint8_t)wr_nbytes; + + for (i = 0; i < wr_count; i++) + { + nxmb_util_put_u16_be(&ctx->adu.data[9 + i * 2], wr_buf[i]); + } + + ctx->adu.length = 11 + wr_nbytes; + + ret = nxmb_client_tx_wait_rx(ctx, uid, NXMB_FC_READWRITE_HOLDINGS); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (ctx->adu.length < (3 + rd_nbytes)) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + if (ctx->adu.data[0] != rd_nbytes) + { + pthread_mutex_unlock(&ctx->lock); + return -EPROTO; + } + + for (i = 0; i < rd_count; i++) + { + rd_buf[i] = nxmb_util_get_u16_be(&ctx->adu.data[1 + i * 2]); + } + + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_set_timeout + ****************************************************************************/ + +int nxmb_set_timeout(nxmb_handle_t ctx, uint32_t timeout_ms) +{ + FAR struct nxmb_client_state_s *state; + + DEBUGASSERT(ctx && ctx->client_state); + + if (!ctx->is_client) + { + return -ENOTSUP; + } + + state = (FAR struct nxmb_client_state_s *)ctx->client_state; + + pthread_mutex_lock(&ctx->lock); + state->timeout_ms = timeout_ms; + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_client_init + ****************************************************************************/ + +int nxmb_client_init(nxmb_handle_t ctx) +{ + FAR struct nxmb_client_state_s *state; + + DEBUGASSERT(ctx && ctx->is_client); + + state = calloc(1, sizeof(struct nxmb_client_state_s)); + if (state == NULL) + { + return -ENOMEM; + } + + state->timeout_ms = CONFIG_NXMODBUS_CLIENT_TIMEOUT_MS; + + ctx->client_state = state; + + return OK; +} + +/**************************************************************************** + * Name: nxmb_client_deinit + ****************************************************************************/ + +int nxmb_client_deinit(nxmb_handle_t ctx) +{ + FAR struct nxmb_client_state_s *state; + + DEBUGASSERT(ctx && ctx->client_state); + + state = (FAR struct nxmb_client_state_s *)ctx->client_state; + + free(state); + + ctx->client_state = NULL; + + return OK; +} diff --git a/industry/nxmodbus/core/nxmb_context.c b/industry/nxmodbus/core/nxmb_context.c new file mode 100644 index 000000000..e6a76c581 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_context.c @@ -0,0 +1,392 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_context.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct nxmb_context_s g_nxmb_pool[CONFIG_NXMODBUS_MAX_INSTANCES]; +static pthread_mutex_t g_pool_lock = PTHREAD_MUTEX_INITIALIZER; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_create + * + * Description: + * Create a new NxModbus instance. + * + * Input Parameters: + * handle - Pointer to receive the instance handle + * config - Instance configuration + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_create(FAR nxmb_handle_t *handle, + FAR const struct nxmb_config_s *config) +{ + nxmb_handle_t ctx = NULL; + int ret = -ENOMEM; + int i; + + DEBUGASSERT(handle && config); + +#ifndef CONFIG_NXMODBUS_CLIENT + if (config->is_client) + { + /* Master not supported */ + + return -EINVAL; + } +#endif + + pthread_mutex_lock(&g_pool_lock); + + for (i = 0; i < CONFIG_NXMODBUS_MAX_INSTANCES; i++) + { + if (g_nxmb_pool[i].mode == NXMB_MODE_INVAL) + { + ctx = &g_nxmb_pool[i]; + break; + } + } + + if (ctx != NULL) + { + memset(ctx, 0, sizeof(struct nxmb_context_s)); + + ret = pthread_mutex_init(&ctx->lock, NULL); + if (ret == 0) + { + ctx->mode = config->mode; + ctx->unit_id = config->unit_id; + ctx->is_client = config->is_client; + + switch (config->mode) + { +#ifdef CONFIG_NXMODBUS_RTU + case NXMB_MODE_RTU: + ctx->transport_cfg.serial = config->transport.serial; + ctx->transport_ops = &g_nxmb_serial_ops; + break; +#endif + +#ifdef CONFIG_NXMODBUS_ASCII + case NXMB_MODE_ASCII: + ctx->transport_cfg.serial = config->transport.serial; + ctx->transport_ops = &g_nxmb_ascii_ops; + break; +#endif + +#ifdef CONFIG_NXMODBUS_TCP + case NXMB_MODE_TCP: + ctx->transport_cfg.tcp = config->transport.tcp; + ctx->transport_ops = &g_nxmb_tcp_ops; + break; +#endif + +#ifdef CONFIG_NXMODBUS_RAW_ADU + case NXMB_MODE_RAW: + ctx->transport_cfg.raw = config->transport.raw; + ctx->transport_ops = &g_nxmb_raw_ops; + break; +#endif + + default: + pthread_mutex_destroy(&ctx->lock); + memset(ctx, 0, sizeof(struct nxmb_context_s)); + ret = -EINVAL; + goto out; + } + + *handle = ctx; + ret = 0; + } + else + { + memset(ctx, 0, sizeof(struct nxmb_context_s)); + ret = -ret; + } + } + +out: + pthread_mutex_unlock(&g_pool_lock); + return ret; +} + +/**************************************************************************** + * Name: nxmb_destroy + * + * Description: + * Destroy an NxModbus instance. + * + * Input Parameters: + * handle - Instance handle + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_destroy(nxmb_handle_t ctx) +{ +#ifdef CONFIG_NXMODBUS_CUSTOM_FC + FAR struct nxmb_custom_fc_s *entry; + FAR struct nxmb_custom_fc_s *next; +#endif + + DEBUGASSERT(ctx); + + pthread_mutex_lock(&g_pool_lock); + pthread_mutex_lock(&ctx->lock); + + if (ctx->enabled) + { + pthread_mutex_unlock(&ctx->lock); + pthread_mutex_unlock(&g_pool_lock); + return -EBUSY; + } + + pthread_mutex_unlock(&ctx->lock); + pthread_mutex_destroy(&ctx->lock); + +#ifdef CONFIG_NXMODBUS_CUSTOM_FC + entry = ctx->custom_fc_list; + while (entry != NULL) + { + next = entry->next; + free(entry); + entry = next; + } +#endif + + memset(ctx, 0, sizeof(struct nxmb_context_s)); + pthread_mutex_unlock(&g_pool_lock); + + return 0; +} + +/**************************************************************************** + * Name: nxmb_set_callbacks + * + * Description: + * Set application callbacks for register access. + * + * Input Parameters: + * handle - Instance handle + * callbacks - Pointer to callback structure + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_set_callbacks(nxmb_handle_t ctx, + FAR const struct nxmb_callbacks_s *callbacks) +{ + DEBUGASSERT(ctx); + + pthread_mutex_lock(&ctx->lock); + ctx->callbacks = callbacks; + pthread_mutex_unlock(&ctx->lock); + + return 0; +} + +/**************************************************************************** + * Name: nxmb_enable + * + * Description: + * Enable an NxModbus instance and initialize transport. + * + * Input Parameters: + * handle - Instance handle + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_enable(nxmb_handle_t ctx) +{ + int ret; + + DEBUGASSERT(ctx && ctx->transport_ops && ctx->transport_ops->init); + + pthread_mutex_lock(&ctx->lock); + + if (ctx->enabled) + { + pthread_mutex_unlock(&ctx->lock); + return -EBUSY; + } + +#ifdef CONFIG_NXMODBUS_CLIENT + if (ctx->is_client) + { + ret = nxmb_client_init(ctx); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + } +#endif + + /* Initialize transport */ + + ret = ctx->transport_ops->init(ctx); + if (ret < 0) + { +#ifdef CONFIG_NXMODBUS_CLIENT + if (ctx->is_client) + { + nxmb_client_deinit(ctx); + } +#endif + + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + ctx->enabled = true; + pthread_mutex_unlock(&ctx->lock); + + return 0; +} + +/**************************************************************************** + * Name: nxmb_set_server_id + * + * Description: + * Configure the data returned by FC17 (Report Server ID). + * + * Input Parameters: + * handle - Instance handle + * id - Server ID byte + * is_running - True if device is running + * additional - Optional additional data (may be NULL) + * addlen - Length of additional data + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_set_server_id(nxmb_handle_t ctx, uint8_t id, bool is_running, + FAR const uint8_t *additional, uint16_t addlen) +{ + DEBUGASSERT(ctx); + + /* The first two bytes are server ID and run indicator. + * The rest is optional additional data. + */ + + if (addlen + 2 > CONFIG_NXMODBUS_REP_SERVER_ID_BUF) + { + return -ENOMEM; + } + + pthread_mutex_lock(&ctx->lock); + + ctx->server_id_len = 0; + ctx->server_id_buf[ctx->server_id_len++] = id; + ctx->server_id_buf[ctx->server_id_len++] = + (uint8_t)(is_running ? 0xff : 0x00); + + if (addlen > 0 && additional != NULL) + { + memcpy(&ctx->server_id_buf[ctx->server_id_len], additional, + (size_t)addlen); + ctx->server_id_len += addlen; + } + + pthread_mutex_unlock(&ctx->lock); + + return 0; +} + +/**************************************************************************** + * Name: nxmb_disable + * + * Description: + * Disable an NxModbus instance and deinitialize transport. + * + * Input Parameters: + * handle - Instance handle + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_disable(nxmb_handle_t ctx) +{ + DEBUGASSERT(ctx && ctx->transport_ops && ctx->transport_ops->deinit); + + pthread_mutex_lock(&ctx->lock); + + if (!ctx->enabled) + { + pthread_mutex_unlock(&ctx->lock); + return 0; + } + + ctx->transport_ops->deinit(ctx); + +#ifdef CONFIG_NXMODBUS_CLIENT + if (ctx->is_client) + { + nxmb_client_deinit(ctx); + } +#endif + + ctx->enabled = false; + pthread_mutex_unlock(&ctx->lock); + + return 0; +} diff --git a/industry/nxmodbus/core/nxmb_func.c b/industry/nxmodbus/core/nxmb_func.c new file mode 100644 index 000000000..13b595b4f --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func.c @@ -0,0 +1,354 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_EXCEPTION_FLAG 0x80 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Function code handler signature */ + +typedef enum nxmb_exception_e (*nxmb_fc_handler_t)(nxmb_handle_t ctx); + +/* Dispatch table entry */ + +struct nxmb_fc_entry_s +{ + uint8_t fc; + nxmb_fc_handler_t handler; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Standard function code dispatch table */ + +static const struct nxmb_fc_entry_s g_nxmb_fc_table[] = +{ +#ifdef CONFIG_NXMODBUS_FUNC_READ_COILS + { NXMB_FC_READ_COILS, nxmb_fc01_read_coils }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_READ_DISCRETE + { NXMB_FC_READ_DISCRETE, nxmb_fc02_read_discrete }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_READ_HOLDING + { NXMB_FC_READ_HOLDING, nxmb_fc03_read_holding }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_READ_INPUT + { NXMB_FC_READ_INPUT, nxmb_fc04_read_input }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_WRITE_COIL + { NXMB_FC_WRITE_COIL, nxmb_fc05_write_coil }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_WRITE_HOLDING + { NXMB_FC_WRITE_HOLDING, nxmb_fc06_write_holding }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_DIAGNOSTICS + { NXMB_FC_DIAGNOSTICS, nxmb_fc08_diagnostics }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_WRITE_COILS + { NXMB_FC_WRITE_COILS, nxmb_fc15_write_coils }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_WRITE_HOLDINGS + { NXMB_FC_WRITE_HOLDINGS, nxmb_fc16_write_holdings }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_REPORT_SERVER_ID + { NXMB_FC_REPORT_SERVER_ID, nxmb_fc17_report_server_id }, +#endif +#ifdef CONFIG_NXMODBUS_FUNC_READWRITE_HOLDINGS + { NXMB_FC_READWRITE_HOLDINGS, nxmb_fc23_readwrite_holding } +#endif +}; + +#define NXMB_FC_TABLE_SIZE \ + (sizeof(g_nxmb_fc_table) / sizeof(g_nxmb_fc_table[0])) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_build_exception + * + * Description: + * Build a Modbus exception response in ctx->adu in place. + * + * Input Parameters: + * ctx - Instance context + * fc - Function code + * exception - NxModbus exception code + * + ****************************************************************************/ + +static void nxmb_build_exception(nxmb_handle_t ctx, uint8_t fc, + enum nxmb_exception_e exception) +{ + ctx->adu.fc = fc | NXMB_EXCEPTION_FLAG; + ctx->adu.data[0] = (uint8_t)exception; + + /* unit_id + fc + 1 exception byte */ + + ctx->adu.length = 3; +} + +/**************************************************************************** + * Name: nxmb_lookup_standard_fc + * + * Description: + * Look up a standard function code handler in the dispatch table. + * + * Input Parameters: + * fc - Function code to look up + * + * Returned Value: + * Handler function pointer, or NULL if not found + * + ****************************************************************************/ + +static nxmb_fc_handler_t nxmb_lookup_standard_fc(uint8_t fc) +{ + int i; + + for (i = 0; i < NXMB_FC_TABLE_SIZE; i++) + { + if (g_nxmb_fc_table[i].fc == fc) + { + return g_nxmb_fc_table[i].handler; + } + } + + return NULL; +} + +#ifdef CONFIG_NXMODBUS_CUSTOM_FC + +/**************************************************************************** + * Name: nxmb_lookup_custom_fc + * + * Description: + * Look up a custom function code handler in the instance list. + * + * Input Parameters: + * ctx - Instance context + * fc - Function code to look up + * + * Returned Value: + * Handler function pointer, or NULL if not found + * + ****************************************************************************/ + +static nxmb_custom_fc_handler_t nxmb_lookup_custom_fc( + nxmb_handle_t ctx, uint8_t fc) +{ + FAR struct nxmb_custom_fc_s *entry; + + for (entry = ctx->custom_fc_list; entry != NULL; entry = entry->next) + { + if (entry->fc == fc) + { + return entry->handler; + } + } + + return NULL; +} + +#endif /* CONFIG_NXMODBUS_CUSTOM_FC */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_dispatch_function + * + * Description: + * Dispatch a Modbus function code to the appropriate handler. Operates in + * place on ctx->adu. Checks standard handlers first, then custom handlers + * if enabled. Builds an exception response if no handler is found or the + * handler fails. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_dispatch_function(nxmb_handle_t ctx) +{ + nxmb_fc_handler_t handler; + enum nxmb_exception_e exception; + uint8_t fc; +#ifdef CONFIG_NXMODBUS_CUSTOM_FC + nxmb_custom_fc_handler_t custom_handler; + int ret; +#endif + + DEBUGASSERT(ctx); + + if (ctx->adu.length < 2) + { + return -EINVAL; + } + + fc = ctx->adu.fc; + + /* Look up standard handler first */ + + handler = nxmb_lookup_standard_fc(fc); + +#ifdef CONFIG_NXMODBUS_CUSTOM_FC + + /* If not found, check custom handlers */ + + if (handler == NULL) + { + custom_handler = nxmb_lookup_custom_fc(ctx, fc); + if (custom_handler != NULL) + { + ret = custom_handler(ctx); + if (ret < 0) + { + nxmb_build_exception(ctx, fc, NXMB_EX_DEVICE_FAILURE); + } + + return 0; + } + } +#endif + + /* If no handler found, return illegal function exception */ + + if (handler == NULL) + { + nxmb_build_exception(ctx, fc, NXMB_EX_ILLEGAL_FUNCTION); + return 0; + } + + /* Call the handler */ + + exception = handler(ctx); + + /* If handler returned exception, build exception response */ + + if (exception != NXMB_EX_NONE) + { + nxmb_build_exception(ctx, fc, exception); + } + + return 0; +} + +#ifdef CONFIG_NXMODBUS_CUSTOM_FC +/**************************************************************************** + * Name: nxmb_register_custom_fc + * + * Description: + * Register a custom Modbus function code handler for an instance. + * + * Custom handlers are checked after standard handlers, so standard + * function codes (FC01-FC16) cannot be overridden. + * + * Input Parameters: + * h - The NxModbus instance handle + * fc - The custom function code to register + * handler - The handler function + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_register_custom_fc(nxmb_handle_t ctx, uint8_t fc, + nxmb_custom_fc_handler_t handler) +{ + FAR struct nxmb_custom_fc_s *entry; + FAR struct nxmb_custom_fc_s *new_entry; + + DEBUGASSERT(ctx && handler); + + /* Check if FC is already a standard handler */ + + if (nxmb_lookup_standard_fc(fc) != NULL) + { + return -EEXIST; + } + + pthread_mutex_lock(&ctx->lock); + + /* Check if FC is already registered as custom */ + + for (entry = ctx->custom_fc_list; entry != NULL; entry = entry->next) + { + if (entry->fc == fc) + { + pthread_mutex_unlock(&ctx->lock); + return -EEXIST; + } + } + + /* Allocate new entry */ + + new_entry = malloc(sizeof(struct nxmb_custom_fc_s)); + if (new_entry == NULL) + { + pthread_mutex_unlock(&ctx->lock); + return -ENOMEM; + } + + /* Initialize and prepend to list */ + + new_entry->fc = fc; + new_entry->handler = handler; + new_entry->next = ctx->custom_fc_list; + ctx->custom_fc_list = new_entry; + + pthread_mutex_unlock(&ctx->lock); + return 0; +} + +#endif /* CONFIG_NXMODBUS_CUSTOM_FC */ diff --git a/industry/nxmodbus/core/nxmb_func_coils.c b/industry/nxmodbus/core/nxmb_func_coils.c new file mode 100644 index 000000000..3235fcc9e --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_coils.c @@ -0,0 +1,259 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_coils.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Offsets are in adu.data[]; lengths refer to data only. */ + +/* FC01 Read Coils */ + +#define NXMB_FC01_ADDR_OFF 0 +#define NXMB_FC01_QTY_OFF 2 +#define NXMB_FC01_REQ_DATA_LEN 4 + +/* FC05 Write Single Coil */ + +#define NXMB_FC05_ADDR_OFF 0 +#define NXMB_FC05_VALUE_OFF 2 +#define NXMB_FC05_REQ_DATA_LEN 4 + +/* FC15 Write Multiple Coils */ + +#define NXMB_FC15_ADDR_OFF 0 +#define NXMB_FC15_QTY_OFF 2 +#define NXMB_FC15_BCNT_OFF 4 +#define NXMB_FC15_DATA_OFF 5 +#define NXMB_FC15_RESP_DATA_LEN 4 + +/* Maximum coil quantity per Modbus specification. + * FC01/FC02 read max is 2000 (0x07D0). + * FC15 write max is 1968 (0x07B0). + */ + +#define NXMB_COIL_READ_QTY_MAX 2000 +#define NXMB_COIL_WRITE_QTY_MAX 1968 + +/* Coil ON/OFF wire values for FC05 */ + +#define NXMB_COIL_ON 0xff00 +#define NXMB_COIL_OFF 0x0000 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc01_read_coils + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc01_read_coils(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t nbytes; + int ret; + + if (data_len != NXMB_FC01_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->coil_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC01_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC01_QTY_OFF]); + + if (qty < 1 || qty > NXMB_COIL_READ_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + nbytes = (uint8_t)((qty + 7) / 8); + + /* Response is byte_count + coil data */ + + if (1 + nbytes > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Build response: byte_count + coil data */ + + ctx->adu.fc = NXMB_FC_READ_COILS; + ctx->adu.data[0] = nbytes; + + /* Zero data area so unused trailing bits are clear */ + + memset(&ctx->adu.data[1], 0, nbytes); + + ret = ctx->callbacks->coil_cb(&ctx->adu.data[1], addr, qty, NXMB_REG_READ, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + ctx->adu.length = (uint16_t)(2 + 1 + nbytes); + return NXMB_EX_NONE; +} + +/**************************************************************************** + * Name: nxmb_fc05_write_coil + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc05_write_coil(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t value; + uint8_t coil_buf[2]; + int ret; + + if (data_len != NXMB_FC05_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->coil_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC05_ADDR_OFF]); + value = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC05_VALUE_OFF]); + + if (addr == 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + if (value != NXMB_COIL_ON && value != NXMB_COIL_OFF) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Convert wire value to single-bit buffer for callback */ + + coil_buf[0] = (value == NXMB_COIL_ON) ? 1 : 0; + coil_buf[1] = 0; + + ret = ctx->callbacks->coil_cb(coil_buf, addr, 1, NXMB_REG_WRITE, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + /* Response echoes the request — adu.length is already correct */ + + return NXMB_EX_NONE; +} + +/**************************************************************************** + * Name: nxmb_fc15_write_coils + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc15_write_coils(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t byte_count; + uint8_t byte_count_exp; + int ret; + + if (data_len < NXMB_FC15_DATA_OFF) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->coil_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC15_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC15_QTY_OFF]); + byte_count = ctx->adu.data[NXMB_FC15_BCNT_OFF]; + + if (qty < 1 || qty > NXMB_COIL_WRITE_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + byte_count_exp = (uint8_t)((qty + 7) / 8); + + if (byte_count != byte_count_exp) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Verify total data length matches header + payload */ + + if (data_len != (uint16_t)(NXMB_FC15_DATA_OFF + byte_count)) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + ret = ctx->callbacks->coil_cb(&ctx->adu.data[NXMB_FC15_DATA_OFF], addr, + qty, NXMB_REG_WRITE, ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + /* Response: addr(2) + qty(2) — already in data[0..3] from request */ + + ctx->adu.length = (uint16_t)(2 + NXMB_FC15_RESP_DATA_LEN); + return NXMB_EX_NONE; +} diff --git a/industry/nxmodbus/core/nxmb_func_diag.c b/industry/nxmodbus/core/nxmb_func_diag.c new file mode 100644 index 000000000..8575c610d --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_diag.c @@ -0,0 +1,83 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_diag.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_DIAG_SUBFUNC_OFF 0 +#define NXMB_DIAG_DATA_OFF 2 +#define NXMB_DIAG_REQ_MIN_DATA_LEN 4 + +/* Sub-function codes */ + +#define NXMB_DIAG_RETURN_QUERY 0x0000 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc08_diagnostics + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc08_diagnostics(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t subfunc; + + if (data_len < NXMB_DIAG_REQ_MIN_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + subfunc = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_DIAG_SUBFUNC_OFF]); + + switch (subfunc) + { + case NXMB_DIAG_RETURN_QUERY: + { + /* Response echoes the request — adu is already correct */ + + return NXMB_EX_NONE; + } + + default: + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + } +} diff --git a/industry/nxmodbus/core/nxmb_func_discrete.c b/industry/nxmodbus/core/nxmb_func_discrete.c new file mode 100644 index 000000000..b30a0a720 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_discrete.c @@ -0,0 +1,110 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_discrete.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_FC02_ADDR_OFF 0 +#define NXMB_FC02_QTY_OFF 2 +#define NXMB_FC02_REQ_DATA_LEN 4 + +#define NXMB_DISCRETE_QTY_MAX 2000 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc02_read_discrete + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc02_read_discrete(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t nbytes; + int ret; + + if (data_len != NXMB_FC02_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->discrete_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC02_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC02_QTY_OFF]); + + if (qty < 1 || qty > NXMB_DISCRETE_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + nbytes = (uint8_t)((qty + 7) / 8); + + /* Response is byte_count + discrete data */ + + if (1 + nbytes > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + ctx->adu.fc = NXMB_FC_READ_DISCRETE; + ctx->adu.data[0] = nbytes; + + memset(&ctx->adu.data[1], 0, nbytes); + + ret = ctx->callbacks->discrete_cb(&ctx->adu.data[1], addr, qty, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + ctx->adu.length = (uint16_t)(2 + 1 + nbytes); + return NXMB_EX_NONE; +} diff --git a/industry/nxmodbus/core/nxmb_func_holding.c b/industry/nxmodbus/core/nxmb_func_holding.c new file mode 100644 index 000000000..a4fe74cc5 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_holding.c @@ -0,0 +1,331 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_holding.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* PDU data offsets are relative to ctx->adu.data[] (after unit_id + fc). + * Lengths refer to the data payload only — adu.length = 2 + data_len. + */ + +/* FC03 Read Holding Registers */ + +#define NXMB_FC03_ADDR_OFF 0 +#define NXMB_FC03_QTY_OFF 2 +#define NXMB_FC03_REQ_DATA_LEN 4 + +/* FC06 Write Single Holding Register */ + +#define NXMB_FC06_ADDR_OFF 0 +#define NXMB_FC06_VALUE_OFF 2 +#define NXMB_FC06_REQ_DATA_LEN 4 + +/* FC16 Write Multiple Holding Registers */ + +#define NXMB_FC16_ADDR_OFF 0 +#define NXMB_FC16_QTY_OFF 2 +#define NXMB_FC16_BCNT_OFF 4 +#define NXMB_FC16_DATA_OFF 5 +#define NXMB_FC16_RESP_DATA_LEN 4 + +/* FC23 Read/Write Multiple Holding Registers */ + +#define NXMB_FC23_RD_ADDR_OFF 0 +#define NXMB_FC23_RD_QTY_OFF 2 +#define NXMB_FC23_WR_ADDR_OFF 4 +#define NXMB_FC23_WR_QTY_OFF 6 +#define NXMB_FC23_WR_BCNT_OFF 8 +#define NXMB_FC23_WR_DATA_OFF 9 +#define NXMB_FC23_REQ_MIN_DATA_LEN 9 + +/* Quantity limits per Modbus Application Protocol Specification */ + +#define NXMB_REG_READ_QTY_MAX 125 /* 0x007D */ +#define NXMB_REG_WRITE_MUL_QTY_MAX 123 /* 0x007B */ +#define NXMB_REG_READWRITE_RD_QTY_MAX 125 /* 0x007D */ +#define NXMB_REG_READWRITE_WR_QTY_MAX 121 /* 0x0079 */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc03_read_holding + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc03_read_holding(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t nbytes; + int ret; + + if (data_len != NXMB_FC03_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->holding_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC03_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC03_QTY_OFF]); + + if (qty < 1 || qty > NXMB_REG_READ_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + nbytes = (uint8_t)(qty * 2); + + /* Response is byte_count + register data */ + + if (1 + nbytes > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Build response: byte_count + register data (big-endian) */ + + ctx->adu.fc = NXMB_FC_READ_HOLDING; + ctx->adu.data[0] = nbytes; + + ret = ctx->callbacks->holding_cb(&ctx->adu.data[1], addr, qty, + NXMB_REG_READ, ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + ctx->adu.length = (uint16_t)(2 + 1 + nbytes); + return NXMB_EX_NONE; +} + +/**************************************************************************** + * Name: nxmb_fc06_write_holding + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc06_write_holding(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + int ret; + + if (data_len != NXMB_FC06_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->holding_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC06_ADDR_OFF]); + + if (addr == 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + ret = ctx->callbacks->holding_cb(&ctx->adu.data[NXMB_FC06_VALUE_OFF], addr, + 1, NXMB_REG_WRITE, ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + /* Response echoes the request — adu.length is already correct */ + + return NXMB_EX_NONE; +} + +/**************************************************************************** + * Name: nxmb_fc16_write_holdings + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc16_write_holdings(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t byte_count; + int ret; + + if (data_len < NXMB_FC16_DATA_OFF) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->holding_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC16_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC16_QTY_OFF]); + byte_count = ctx->adu.data[NXMB_FC16_BCNT_OFF]; + + if (qty < 1 || qty > NXMB_REG_WRITE_MUL_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + if (byte_count != (uint8_t)(qty * 2)) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Verify total data length matches header + payload */ + + if (data_len != (uint16_t)(NXMB_FC16_DATA_OFF + byte_count)) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + ret = ctx->callbacks->holding_cb(&ctx->adu.data[NXMB_FC16_DATA_OFF], addr, + qty, NXMB_REG_WRITE, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + /* Response: addr(2) + qty(2) — already in data[0..3] from request */ + + ctx->adu.length = (uint16_t)(2 + NXMB_FC16_RESP_DATA_LEN); + return NXMB_EX_NONE; +} + +/**************************************************************************** + * Name: nxmb_fc23_readwrite_holding + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc23_readwrite_holding(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t rd_addr; + uint16_t rd_qty; + uint16_t wr_addr; + uint16_t wr_qty; + uint8_t wr_bcnt; + uint16_t rd_bcnt; + int ret; + + if (data_len < NXMB_FC23_REQ_MIN_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->holding_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + rd_addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC23_RD_ADDR_OFF]); + rd_qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC23_RD_QTY_OFF]); + wr_addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC23_WR_ADDR_OFF]); + wr_qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC23_WR_QTY_OFF]); + wr_bcnt = ctx->adu.data[NXMB_FC23_WR_BCNT_OFF]; + + if (rd_qty < 1 || rd_qty > NXMB_REG_READWRITE_RD_QTY_MAX || wr_qty < 1 || + wr_qty > NXMB_REG_READWRITE_WR_QTY_MAX || wr_bcnt != (wr_qty * 2)) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)rd_addr + rd_qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + if ((uint32_t)wr_addr + wr_qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + /* Verify total request data length matches header + write payload */ + + if (data_len != (uint16_t)(NXMB_FC23_WR_DATA_OFF + wr_bcnt)) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Response is byte_count + read register data — must fit in adu.data[] */ + + if (1 + rd_qty * 2 > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + ret = ctx->callbacks->holding_cb(&ctx->adu.data[NXMB_FC23_WR_DATA_OFF], + wr_addr, wr_qty, NXMB_REG_WRITE, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + /* Build response: byte_count + read register data, overwriting request */ + + ctx->adu.fc = NXMB_FC_READWRITE_HOLDINGS; + rd_bcnt = rd_qty * 2; + ctx->adu.data[0] = (uint8_t)rd_bcnt; + + ret = ctx->callbacks->holding_cb(&ctx->adu.data[1], rd_addr, rd_qty, + NXMB_REG_READ, ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + ctx->adu.length = (uint16_t)(2 + 1 + rd_bcnt); + return NXMB_EX_NONE; +} diff --git a/industry/nxmodbus/core/nxmb_func_input.c b/industry/nxmodbus/core/nxmb_func_input.c new file mode 100644 index 000000000..151d2510b --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_input.c @@ -0,0 +1,115 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_input.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* FC04 Read Input Registers — offsets in adu.data[], lengths are data + * only + */ + +#define NXMB_FC04_ADDR_OFF 0 +#define NXMB_FC04_QTY_OFF 2 +#define NXMB_FC04_REQ_DATA_LEN 4 + +/* Maximum register quantity per Modbus specification */ + +#define NXMB_REG_READ_QTY_MAX 125 /* 0x007D */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc04_read_input + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc04_read_input(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + uint16_t addr; + uint16_t qty; + uint8_t nbytes; + int ret; + + if (data_len != NXMB_FC04_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if (ctx->callbacks == NULL || ctx->callbacks->input_cb == NULL) + { + return NXMB_EX_ILLEGAL_FUNCTION; + } + + addr = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC04_ADDR_OFF]); + qty = nxmb_util_get_u16_be(&ctx->adu.data[NXMB_FC04_QTY_OFF]); + + if (qty < 1 || qty > NXMB_REG_READ_QTY_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + if ((uint32_t)addr + qty > 0xffffu) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + nbytes = (uint8_t)(qty * 2); + + /* Response is byte_count + register data */ + + if (1 + nbytes > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* Build response: byte_count + register data (big-endian) */ + + ctx->adu.fc = NXMB_FC_READ_INPUT; + ctx->adu.data[0] = nbytes; + + ret = ctx->callbacks->input_cb(&ctx->adu.data[1], addr, qty, + ctx->callbacks->priv); + if (ret != 0) + { + return nxmb_cb_ret_to_exception(ret); + } + + ctx->adu.length = (uint16_t)(2 + 1 + nbytes); + return NXMB_EX_NONE; +} diff --git a/industry/nxmodbus/core/nxmb_func_other.c b/industry/nxmodbus/core/nxmb_func_other.c new file mode 100644 index 000000000..5827026df --- /dev/null +++ b/industry/nxmodbus/core/nxmb_func_other.c @@ -0,0 +1,99 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_func_other.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_REPORT_ID_REQ_DATA_LEN 0 + +/* Run indicator status */ + +#define NXMB_RUN_STATUS_ON 0xff +#define NXMB_RUN_STATUS_OFF 0x00 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_fc17_report_server_id + * + * Description: + * Handle FC17 (0x11) Report Server ID request. Returns the configured + * unit ID and a run indicator byte (0xFF = ON). + * + ****************************************************************************/ + +enum nxmb_exception_e nxmb_fc17_report_server_id(nxmb_handle_t ctx) +{ + uint16_t data_len = ctx->adu.length - 2; + + if (data_len != NXMB_REPORT_ID_REQ_DATA_LEN) + { + return NXMB_EX_ILLEGAL_DATA_VALUE; + } + + /* If the application configured server ID data via nxmb_set_server_id(), + * use that; otherwise fall back to unit_id + run status ON. + */ + + ctx->adu.fc = NXMB_FC_REPORT_SERVER_ID; + + if (ctx->server_id_len > 0) + { + /* Response is byte_count + server_id_buf */ + + if (1 + ctx->server_id_len > NXMB_ADU_DATA_MAX) + { + return NXMB_EX_DEVICE_FAILURE; + } + + ctx->adu.data[0] = (uint8_t)ctx->server_id_len; + memcpy(&ctx->adu.data[1], ctx->server_id_buf, ctx->server_id_len); + ctx->adu.length = (uint16_t)(2 + 1 + ctx->server_id_len); + } + else + { + ctx->adu.data[0] = 2; /* byte count */ + ctx->adu.data[1] = ctx->unit_id; /* server ID */ + ctx->adu.data[2] = NXMB_RUN_STATUS_ON; /* run indicator */ + ctx->adu.length = (uint16_t)(2 + 3); + } + + return NXMB_EX_NONE; +} diff --git a/industry/nxmodbus/core/nxmb_server.c b/industry/nxmodbus/core/nxmb_server.c new file mode 100644 index 000000000..4168b53f2 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_server.c @@ -0,0 +1,121 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_server.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_FUNC_ERROR 0x80 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_poll + * + * Description: + * Poll for Modbus events and process received frames. + * + * Input Parameters: + * handle - Instance handle + * + * Returned Value: + * 0 on success, negative errno on failure + * + ****************************************************************************/ + +int nxmb_poll(nxmb_handle_t ctx) +{ + uint8_t rcv_address; + int ret; + + DEBUGASSERT(ctx && ctx->transport_ops && ctx->transport_ops->receive); + + pthread_mutex_lock(&ctx->lock); + + if (!ctx->enabled) + { + pthread_mutex_unlock(&ctx->lock); + return -EAGAIN; + } + + ret = ctx->transport_ops->receive(ctx); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + + if (ret == 0 || ctx->adu.length == 0) + { + pthread_mutex_unlock(&ctx->lock); + return 0; + } + + rcv_address = ctx->adu.unit_id; + + if (rcv_address != NXMB_ADDRESS_BROADCAST && rcv_address != ctx->unit_id) + { + pthread_mutex_unlock(&ctx->lock); + return 0; + } + + /* Dispatch to function code handler. Handlers mutate ctx->adu in place, + * turning the request into the response. + */ + + ret = nxmb_dispatch_function(ctx); + + if (rcv_address != NXMB_ADDRESS_BROADCAST) + { + if (ctx->transport_ops->send != NULL) + { + ret = ctx->transport_ops->send(ctx); + if (ret < 0) + { + pthread_mutex_unlock(&ctx->lock); + return ret; + } + } + } + + pthread_mutex_unlock(&ctx->lock); + return 0; +} diff --git a/industry/nxmodbus/core/nxmb_utils.c b/industry/nxmodbus/core/nxmb_utils.c new file mode 100644 index 000000000..016d370c5 --- /dev/null +++ b/industry/nxmodbus/core/nxmb_utils.c @@ -0,0 +1,127 @@ +/**************************************************************************** + * apps/industry/nxmodbus/core/nxmb_utils.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_util_set_bits + * + * Description: + * Set bits in a byte buffer. + * + * Input Parameters: + * buf - Byte buffer + * bit_offset - Bit offset from start of buffer + * nbits - Number of bits to set (max 8) + * value - Value to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +void nxmb_util_set_bits(FAR uint8_t *buf, uint16_t bit_offset, uint8_t nbits, + uint8_t value) +{ + uint16_t word_buf; + uint16_t mask; + uint16_t byte_offset; + uint16_t pre_bits; + uint16_t val = value; + + byte_offset = bit_offset / 8; + pre_bits = bit_offset - (byte_offset * 8); + + val <<= pre_bits; + mask = (uint16_t)((1 << nbits) - 1); + mask <<= pre_bits; + + word_buf = buf[byte_offset]; + if (pre_bits + nbits > 8) + { + word_buf |= buf[byte_offset + 1] << 8; + } + + word_buf = (word_buf & (~mask)) | val; + + buf[byte_offset] = (uint8_t)(word_buf & 0xff); + if (pre_bits + nbits > 8) + { + buf[byte_offset + 1] = (uint8_t)(word_buf >> 8); + } +} + +/**************************************************************************** + * Name: nxmb_util_get_bits + * + * Description: + * Get bits from a byte buffer. + * + * Input Parameters: + * buf - Byte buffer + * bit_offset - Bit offset from start of buffer + * nbits - Number of bits to get (max 8) + * + * Returned Value: + * Bit value + * + ****************************************************************************/ + +uint8_t nxmb_util_get_bits(FAR const uint8_t *buf, uint16_t bit_offset, + uint8_t nbits) +{ + uint16_t word_buf; + uint16_t mask; + uint16_t byte_offset; + uint16_t pre_bits; + + byte_offset = bit_offset / 8; + pre_bits = bit_offset - (byte_offset * 8); + + mask = (uint16_t)((1 << nbits) - 1); + + word_buf = buf[byte_offset]; + if (pre_bits + nbits > 8) + { + word_buf |= buf[byte_offset + 1] << 8; + } + + word_buf >>= pre_bits; + word_buf &= mask; + + return (uint8_t)word_buf; +} + diff --git a/industry/nxmodbus/nxmb_internal.h b/industry/nxmodbus/nxmb_internal.h new file mode 100644 index 000000000..6e781bd04 --- /dev/null +++ b/industry/nxmodbus/nxmb_internal.h @@ -0,0 +1,379 @@ +/**************************************************************************** + * apps/industry/nxmodbus/nxmb_internal.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INDUSTRY_NXMODBUS_NXMB_INTERNAL_H +#define __APPS_INDUSTRY_NXMODBUS_NXMB_INTERNAL_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Pre-processor Definitions */ + +#define NXMB_ADDRESS_BROADCAST 0 + +/* Modbus function codes */ + +#define NXMB_FC_READ_COILS 0x01 +#define NXMB_FC_READ_DISCRETE 0x02 +#define NXMB_FC_READ_HOLDING 0x03 +#define NXMB_FC_READ_INPUT 0x04 +#define NXMB_FC_WRITE_COIL 0x05 +#define NXMB_FC_WRITE_HOLDING 0x06 +#define NXMB_FC_DIAGNOSTICS 0x08 +#define NXMB_FC_WRITE_COILS 0x0f +#define NXMB_FC_WRITE_HOLDINGS 0x10 +#define NXMB_FC_REPORT_SERVER_ID 0x11 +#define NXMB_FC_READWRITE_HOLDINGS 0x17 + +/* Transport operations interface. + * + * receive() must not block indefinitely. Implementations should use an + * internal timeout (e.g. select()) and return 0 or -EAGAIN when no + * complete frame is available yet. The client polling loop relies on + * this to enforce its own response timeout. + */ + +struct nxmb_transport_ops_s +{ + CODE int (*init)(nxmb_handle_t ctx); + CODE int (*deinit)(nxmb_handle_t ctx); + CODE int (*send)(nxmb_handle_t ctx); + CODE int (*receive)(nxmb_handle_t ctx); +}; + +/* Custom function code handler */ + +struct nxmb_custom_fc_s +{ + FAR struct nxmb_custom_fc_s *next; + nxmb_custom_fc_handler_t handler; + uint8_t fc; +}; + +/* NxModbus instance context */ + +struct nxmb_context_s +{ + FAR const struct nxmb_callbacks_s *callbacks; + FAR const struct nxmb_transport_ops_s *transport_ops; + enum nxmb_mode_e mode; + pthread_mutex_t lock; + uint8_t unit_id; + bool is_client; + bool enabled; + + /* Transport configuration */ + + union nxmb_transport_config_u transport_cfg; + + /* Single ADU container reused for both RX and TX. Modbus is half-duplex, + * so request and response never coexist: handlers mutate the request + * in place to form the response. + */ + + struct nxmb_adu_s adu; + + /* Report Server ID (FC17) data */ + + uint8_t server_id_buf[CONFIG_NXMODBUS_REP_SERVER_ID_BUF]; + uint16_t server_id_len; + + /* Custom handlers */ + + FAR struct nxmb_custom_fc_s *custom_fc_list; + FAR void *transport_state; + FAR void *client_state; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef CONFIG_NXMODBUS_RTU +extern const struct nxmb_transport_ops_s g_nxmb_serial_ops; +#endif + +#ifdef CONFIG_NXMODBUS_ASCII +extern const struct nxmb_transport_ops_s g_nxmb_ascii_ops; +#endif + +#ifdef CONFIG_NXMODBUS_RAW_ADU +extern const struct nxmb_transport_ops_s g_nxmb_raw_ops; +#endif + +#ifdef CONFIG_NXMODBUS_TCP +extern const struct nxmb_transport_ops_s g_nxmb_tcp_ops; +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_cb_ret_to_exception + * + * Description: + * Map a callback return value to a Modbus exception code. + * + * -ENOENT -> address not supported by the application + * anything else -> generic device failure + * + ****************************************************************************/ + +static inline enum nxmb_exception_e nxmb_cb_ret_to_exception(int ret) +{ + if (ret == -ENOENT) + { + return NXMB_EX_ILLEGAL_DATA_ADDRESS; + } + + return NXMB_EX_DEVICE_FAILURE; +} + +/**************************************************************************** + * Name: nxmb_util_get_u16_be + * + * Description: + * Extract a big-endian 16-bit value from a buffer. + * + ****************************************************************************/ + +static inline uint16_t nxmb_util_get_u16_be(FAR const uint8_t *buf) +{ + return (uint16_t)((buf[0] << 8) | buf[1]); +} + +/**************************************************************************** + * Name: nxmb_util_put_u16_be + * + * Description: + * Store a 16-bit value as big-endian in a buffer. + * + ****************************************************************************/ + +static inline void nxmb_util_put_u16_be(FAR uint8_t *buf, uint16_t value) +{ + buf[0] = (uint8_t)(value >> 8); + buf[1] = (uint8_t)(value & 0xff); +} + +/**************************************************************************** + * Name: nxmb_util_clock_ms + * + * Description: + * Return the current monotonic clock value in milliseconds. + * + ****************************************************************************/ + +static inline uint64_t nxmb_util_clock_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_SERIAL_TERMIOS +/**************************************************************************** + * Name: nxmb_serial_configure + * + * Description: + * Configure serial port with termios settings for Modbus communication. + * Shared by RTU and ASCII transports. + * + * Input Parameters: + * fd - File descriptor + * baudrate - Baud rate + * parity - Parity setting + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_serial_configure(int fd, uint32_t baudrate, + enum nxmb_parity_e parity); +#endif + +/**************************************************************************** + * Name: nxmb_util_set_bits + * + * Description: + * Set up to 8 bits in a byte buffer at the given bit offset. + * + * Input Parameters: + * buf - Byte buffer + * bit_offset - Bit offset from start of buffer + * nbits - Number of bits to set (max 8) + * value - Bit values to write + * + ****************************************************************************/ + +void nxmb_util_set_bits(FAR uint8_t *buf, uint16_t bit_offset, uint8_t nbits, + uint8_t value); + +/**************************************************************************** + * Name: nxmb_util_get_bits + * + * Description: + * Extract up to 8 bits from a byte buffer at the given bit offset. + * + * Input Parameters: + * buf - Byte buffer + * bit_offset - Bit offset from start of buffer + * nbits - Number of bits to extract (max 8) + * + * Returned Value: + * Extracted bit values + * + ****************************************************************************/ + +uint8_t nxmb_util_get_bits(FAR const uint8_t *buf, uint16_t bit_offset, + uint8_t nbits); + +#ifdef CONFIG_NXMODBUS_RTU +/**************************************************************************** + * Name: nxmb_crc16 + * + * Description: + * Calculate Modbus RTU CRC16 checksum. + * + * Input Parameters: + * buf - Pointer to data buffer + * len - Length of data in bytes + * + * Returned Value: + * CRC16 value + * + ****************************************************************************/ + +uint16_t nxmb_crc16(FAR const uint8_t *buf, uint16_t len); +#endif + +#ifdef CONFIG_NXMODBUS_ASCII +/**************************************************************************** + * Name: nxmb_lrc + * + * Description: + * Calculate Modbus ASCII LRC checksum. + * + * Input Parameters: + * buf - Pointer to data buffer + * len - Length of data in bytes + * + * Returned Value: + * LRC value (8-bit) + * + ****************************************************************************/ + +uint8_t nxmb_lrc(FAR const uint8_t *buf, uint16_t len); +#endif + +#ifdef CONFIG_NXMODBUS_CLIENT +/**************************************************************************** + * Name: nxmb_client_init + * + * Description: + * Initialize client state for master mode. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_client_init(nxmb_handle_t ctx); + +/**************************************************************************** + * Name: nxmb_client_deinit + * + * Description: + * Deinitialize client state. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_client_deinit(nxmb_handle_t ctx); +#endif + +/**************************************************************************** + * Name: nxmb_dispatch_function + * + * Description: + * Dispatch a Modbus function code to the appropriate handler. Operates + * in place on ctx->adu. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_dispatch_function(nxmb_handle_t ctx); + +/* Function-code handlers operate on ctx->adu in place: they read the + * request from adu.fc/adu.data[]/adu.length and overwrite those fields + * with the response. + */ + +enum nxmb_exception_e nxmb_fc01_read_coils(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc02_read_discrete(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc05_write_coil(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc15_write_coils(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc03_read_holding(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc04_read_input(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc06_write_holding(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc16_write_holdings(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc23_readwrite_holding(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc08_diagnostics(nxmb_handle_t ctx); +enum nxmb_exception_e nxmb_fc17_report_server_id(nxmb_handle_t ctx); + +#endif /* __APPS_INDUSTRY_NXMODBUS_NXMB_INTERNAL_H */ diff --git a/industry/nxmodbus/transport/nxmb_ascii.c b/industry/nxmodbus/transport/nxmb_ascii.c new file mode 100644 index 000000000..668a89821 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_ascii.c @@ -0,0 +1,550 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_ascii.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SERIAL_TERMIOS +# include +#endif + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_ASCII_START_CHAR ':' +#define NXMB_ASCII_CR '\r' +#define NXMB_ASCII_LF '\n' + +/* Serialized binary frame: unit_id(1) + fc(1) + data + LRC(1) */ + +#define NXMB_ASCII_BIN_MAX (NXMB_ADU_DATA_MAX + 3) + +/* Hex-encoded ASCII frame: ':' + 2*bin + CR + LF */ + +#define NXMB_ASCII_BUFLEN (NXMB_ASCII_BIN_MAX * 2 + 4) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nxmb_ascii_state_s +{ + int fd; + uint8_t rx_buf[NXMB_ASCII_BUFLEN]; + uint16_t rx_pos; + bool in_frame; +#ifdef CONFIG_SERIAL_TERMIOS + struct termios old_tio; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int nxmb_ascii_char_to_hex(char c); +static char nxmb_ascii_hex_to_char(uint8_t nibble); +static uint16_t nxmb_ascii_encode(FAR const uint8_t *bin, uint16_t bin_len, + FAR char *ascii); +static int nxmb_ascii_decode(FAR const char *ascii, uint16_t ascii_len, + FAR uint8_t *bin); +static int nxmb_ascii_init(nxmb_handle_t ctx); +static int nxmb_ascii_deinit(nxmb_handle_t ctx); +static int nxmb_ascii_send(nxmb_handle_t ctx); +static int nxmb_ascii_receive(nxmb_handle_t ctx); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct nxmb_transport_ops_s g_nxmb_ascii_ops = +{ + .init = nxmb_ascii_init, + .deinit = nxmb_ascii_deinit, + .send = nxmb_ascii_send, + .receive = nxmb_ascii_receive +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_ascii_char_to_hex + * + * Description: + * Convert ASCII hex character to binary nibble. + * + * Input Parameters: + * c - ASCII hex character + * + * Returned Value: + * Binary value 0-15, or -1 on error + * + ****************************************************************************/ + +static int nxmb_ascii_char_to_hex(char c) +{ + if (c >= '0' && c <= '9') + { + return c - '0'; + } + else if (c >= 'A' && c <= 'F') + { + return c - 'A' + 10; + } + else if (c >= 'a' && c <= 'f') + { + return c - 'a' + 10; + } + + return -1; +} + +/**************************************************************************** + * Name: nxmb_ascii_hex_to_char + * + * Description: + * Convert binary nibble to ASCII hex character. + * + * Input Parameters: + * nibble - Binary value 0-15 + * + * Returned Value: + * ASCII hex character + * + ****************************************************************************/ + +static char nxmb_ascii_hex_to_char(uint8_t nibble) +{ + if (nibble < 10) + { + return '0' + nibble; + } + else + { + return 'A' + (nibble - 10); + } +} + +/**************************************************************************** + * Name: nxmb_ascii_encode + * + * Description: + * Encode binary data to ASCII hex string. + * + * Input Parameters: + * bin - Binary data + * bin_len - Binary data length + * ascii - Output ASCII buffer + * + * Returned Value: + * ASCII string length + * + ****************************************************************************/ + +static uint16_t nxmb_ascii_encode(FAR const uint8_t *bin, uint16_t bin_len, + FAR char *ascii) +{ + uint16_t pos = 0; + uint16_t i; + + for (i = 0; i < bin_len; i++) + { + ascii[pos++] = nxmb_ascii_hex_to_char(bin[i] >> 4); + ascii[pos++] = nxmb_ascii_hex_to_char(bin[i] & 0x0f); + } + + return pos; +} + +/**************************************************************************** + * Name: nxmb_ascii_decode + * + * Description: + * Decode ASCII hex string to binary data. + * + * Input Parameters: + * ascii - ASCII hex string + * ascii_len - ASCII string length (must be even) + * bin - Output binary buffer + * + * Returned Value: + * Binary data length, or negative errno on error + * + ****************************************************************************/ + +static int nxmb_ascii_decode(FAR const char *ascii, uint16_t ascii_len, + FAR uint8_t *bin) +{ + uint16_t i; + int high; + int low; + + if (ascii_len % 2 != 0) + { + return -EINVAL; + } + + for (i = 0; i < ascii_len; i += 2) + { + high = nxmb_ascii_char_to_hex(ascii[i]); + low = nxmb_ascii_char_to_hex(ascii[i + 1]); + + if (high < 0 || low < 0) + { + return -EINVAL; + } + + bin[i / 2] = (high << 4) | low; + } + + return ascii_len / 2; +} + +/**************************************************************************** + * Name: nxmb_ascii_init + * + * Description: + * Initialize serial ASCII transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_ascii_init(nxmb_handle_t ctx) +{ + FAR struct nxmb_ascii_state_s *state; + FAR const struct nxmb_serial_config_s *cfg; + int ret; + + DEBUGASSERT(ctx); + + cfg = &ctx->transport_cfg.serial; + + if (cfg->devpath == NULL) + { + return -EINVAL; + } + + state = calloc(1, sizeof(struct nxmb_ascii_state_s)); + if (state == NULL) + { + return -ENOMEM; + } + + state->fd = open(cfg->devpath, O_RDWR | O_NOCTTY); + if (state->fd < 0) + { + ret = -errno; + free(state); + return ret; + } + +#ifdef CONFIG_SERIAL_TERMIOS + if (tcgetattr(state->fd, &state->old_tio) < 0) + { + ret = -errno; + close(state->fd); + free(state); + return ret; + } + + ret = nxmb_serial_configure(state->fd, cfg->baudrate, cfg->parity); + if (ret < 0) + { + close(state->fd); + free(state); + return ret; + } +#endif + + state->rx_pos = 0; + state->in_frame = false; + + ctx->transport_state = state; + + return 0; +} + +/**************************************************************************** + * Name: nxmb_ascii_deinit + * + * Description: + * Deinitialize serial ASCII transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_ascii_deinit(nxmb_handle_t ctx) +{ + FAR struct nxmb_ascii_state_s *state; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + +#ifdef CONFIG_SERIAL_TERMIOS + tcsetattr(state->fd, TCSANOW, &state->old_tio); +#endif + close(state->fd); + free(state); + + ctx->transport_state = NULL; + + return 0; +} + +/**************************************************************************** + * Name: nxmb_ascii_send + * + * Description: + * Send ASCII frame over serial port. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_ascii_send(nxmb_handle_t ctx) +{ + FAR struct nxmb_ascii_state_s *state; + uint8_t bin[NXMB_ASCII_BIN_MAX]; + char tx_buf[NXMB_ASCII_BUFLEN]; + uint16_t data_len; + uint16_t pos = 0; + uint8_t lrc; + ssize_t written; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + if (ctx->adu.length < 2) + { + return -EINVAL; + } + + data_len = ctx->adu.length - 2; + + /* Linearize the ADU into the wire byte order: unit_id, fc, data... */ + + bin[0] = ctx->adu.unit_id; + bin[1] = ctx->adu.fc; + memcpy(&bin[2], ctx->adu.data, data_len); + + lrc = nxmb_lrc(bin, ctx->adu.length); + ctx->adu.crc = lrc; + + tx_buf[pos++] = NXMB_ASCII_START_CHAR; + pos += nxmb_ascii_encode(bin, ctx->adu.length, &tx_buf[pos]); + tx_buf[pos++] = nxmb_ascii_hex_to_char(lrc >> 4); + tx_buf[pos++] = nxmb_ascii_hex_to_char(lrc & 0x0f); + tx_buf[pos++] = NXMB_ASCII_CR; + tx_buf[pos++] = NXMB_ASCII_LF; + + written = write(state->fd, tx_buf, pos); + + if (written < 0) + { + return -errno; + } + + if (written != pos) + { + return -EIO; + } + + return 0; +} + +/**************************************************************************** + * Name: nxmb_ascii_receive + * + * Description: + * Receive ASCII frame from serial port. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_ascii_receive(nxmb_handle_t ctx) +{ + FAR struct nxmb_ascii_state_s *state; + uint8_t bin[NXMB_ASCII_BIN_MAX]; + struct timeval timeout; + fd_set readfds; + ssize_t nread; + uint8_t c; + uint8_t lrc_calc; + uint8_t lrc_recv; + int ret; + int bin_len; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + FD_ZERO(&readfds); + FD_SET(state->fd, &readfds); + + timeout.tv_sec = CONFIG_NXMODBUS_ASCII_TIMEOUT_SEC; + timeout.tv_usec = 0; + + ret = select(state->fd + 1, &readfds, NULL, NULL, &timeout); + + if (ret < 0) + { + return -errno; + } + + if (ret == 0) + { + return 0; + } + + if (!FD_ISSET(state->fd, &readfds)) + { + return 0; + } + + nread = read(state->fd, &c, 1); + if (nread < 0) + { + return -errno; + } + + if (nread == 0) + { + return 0; + } + + if (c == NXMB_ASCII_START_CHAR) + { + state->rx_pos = 0; + state->in_frame = true; + return 0; + } + + if (!state->in_frame) + { + return 0; + } + + if (c == NXMB_ASCII_LF) + { + state->in_frame = false; + + /* Minimum valid frame: addr(2) + func(2) + lrc(2) + CR = 7 chars. + * Maximum valid frame: NXMB_ASCII_BIN_MAX hex pairs + CR. Reject + * anything larger so the decoded length fits bin[]. + */ + + if (state->rx_pos < 7 || + state->rx_pos > NXMB_ASCII_BIN_MAX * 2 + 1 || + state->rx_buf[state->rx_pos - 1] != NXMB_ASCII_CR) + { + state->rx_pos = 0; + return 0; + } + + /* Decode all hex chars (exclude CR) into a temp binary buffer. + * Layout: unit_id + fc + data... + LRC. + */ + + bin_len = nxmb_ascii_decode((FAR const char *)state->rx_buf, + state->rx_pos - 1, bin); + + if (bin_len < 3) + { + state->rx_pos = 0; + return 0; + } + + lrc_calc = nxmb_lrc(bin, bin_len - 1); + lrc_recv = bin[bin_len - 1]; + + if (lrc_calc != lrc_recv) + { + state->rx_pos = 0; + return 0; + } + + /* Unpack into ctx->adu. Length excludes LRC. */ + + ctx->adu.unit_id = bin[0]; + ctx->adu.fc = bin[1]; + memcpy(ctx->adu.data, &bin[2], (bin_len - 3)); + ctx->adu.crc = lrc_recv; + ctx->adu.length = (bin_len - 1); + + state->rx_pos = 0; + + return 1; + } + + if (state->rx_pos < sizeof(state->rx_buf)) + { + state->rx_buf[state->rx_pos++] = c; + } + else + { + state->in_frame = false; + state->rx_pos = 0; + } + + return 0; +} diff --git a/industry/nxmodbus/transport/nxmb_crc.c b/industry/nxmodbus/transport/nxmb_crc.c new file mode 100644 index 000000000..abfffffb4 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_crc.c @@ -0,0 +1,108 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_crc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint16_t g_crc16_table[256] = +{ + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, + 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, + 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, + 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, + 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, + 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, + 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, + 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, + 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, + 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, + 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, + 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, + 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, + 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, + 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, + 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, + 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, + 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, + 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, + 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, + 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, + 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, + 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, + 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, + 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, + 0x4100, 0x81c1, 0x8081, 0x4040 +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_crc16 + * + * Description: + * Calculate Modbus RTU CRC16 checksum. + * + * Input Parameters: + * buf - Pointer to data buffer + * len - Length of data in bytes + * + * Returned Value: + * CRC16 value + * + ****************************************************************************/ + +uint16_t nxmb_crc16(FAR const uint8_t *buf, uint16_t len) +{ + uint16_t crc = 0xffff; + uint8_t index; + + if (buf == NULL || len == 0) + { + return crc; + } + + while (len--) + { + index = (crc ^ *buf++); + crc = (crc >> 8) ^ g_crc16_table[index]; + } + + return crc; +} diff --git a/industry/nxmodbus/transport/nxmb_lrc.c b/industry/nxmodbus/transport/nxmb_lrc.c new file mode 100644 index 000000000..7768c9cb5 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_lrc.c @@ -0,0 +1,70 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_lrc.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_lrc + * + * Description: + * Calculate Modbus ASCII LRC checksum. + * + * Input Parameters: + * buf - Pointer to data buffer + * len - Length of data in bytes + * + * Returned Value: + * LRC value (8-bit) + * + ****************************************************************************/ + +uint8_t nxmb_lrc(FAR const uint8_t *buf, uint16_t len) +{ + uint8_t lrc = 0; + uint16_t i; + + if (buf == NULL || len == 0) + { + return lrc; + } + + for (i = 0; i < len; i++) + { + lrc += buf[i]; + } + + return (uint8_t)(-((int8_t)lrc)); +} diff --git a/industry/nxmodbus/transport/nxmb_raw.c b/industry/nxmodbus/transport/nxmb_raw.c new file mode 100644 index 000000000..8352581fe --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_raw.c @@ -0,0 +1,303 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_raw.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MBAP_PROTO_ID 0x0000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Raw ADU transport state */ + +struct nxmb_raw_state_s +{ + FAR void *user_data; + nxmb_raw_tx_cb_t tx_callback; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int nxmb_raw_init(nxmb_handle_t ctx); +static int nxmb_raw_deinit(nxmb_handle_t ctx); +static int nxmb_raw_send(nxmb_handle_t ctx); +static int nxmb_raw_receive(nxmb_handle_t ctx); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct nxmb_transport_ops_s g_nxmb_raw_ops = +{ + .init = nxmb_raw_init, + .deinit = nxmb_raw_deinit, + .send = nxmb_raw_send, + .receive = nxmb_raw_receive, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_raw_init + * + * Description: + * Initialize raw ADU transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_raw_init(nxmb_handle_t ctx) +{ + FAR const struct nxmb_raw_config_s *cfg; + FAR struct nxmb_raw_state_s *state; + + DEBUGASSERT(ctx); + + cfg = &ctx->transport_cfg.raw; + + if (cfg->tx_cb == NULL) + { + return -EINVAL; + } + + state = malloc(sizeof(struct nxmb_raw_state_s)); + if (state == NULL) + { + return -ENOMEM; + } + + state->tx_callback = cfg->tx_cb; + state->user_data = cfg->user_data; + + ctx->transport_state = state; + + return OK; +} + +/**************************************************************************** + * Name: nxmb_raw_deinit + * + * Description: + * Deinitialize raw ADU transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_raw_deinit(nxmb_handle_t ctx) +{ + DEBUGASSERT(ctx && ctx->transport_state); + + free(ctx->transport_state); + ctx->transport_state = NULL; + + return OK; +} + +/**************************************************************************** + * Name: nxmb_raw_send + * + * Description: + * Send raw ADU via user callback. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_raw_send(nxmb_handle_t ctx) +{ + FAR struct nxmb_raw_state_s *state; + int ret; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + if (state->tx_callback == NULL) + { + return -EINVAL; + } + + ctx->adu.proto_id = MBAP_PROTO_ID; + + ret = state->tx_callback(&ctx->adu, state->user_data); + if (ret < 0) + { + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: nxmb_raw_receive + * + * Description: + * Receive raw ADU (not used in raw mode, frames are submitted via + * nxmb_raw_submit_rx). + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * -ENOTSUP (raw mode uses submit pattern, not polling) + * + ****************************************************************************/ + +static int nxmb_raw_receive(nxmb_handle_t ctx) +{ + return -ENOTSUP; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_raw_submit_rx + * + * Description: + * Submit a received raw ADU into the NxModbus protocol core. + * + * Input Parameters: + * h - The NxModbus instance receiving the frame + * adu - The received raw ADU container + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_raw_submit_rx(nxmb_handle_t ctx, FAR const struct nxmb_adu_s *adu) +{ + DEBUGASSERT(ctx && adu); + + if (adu->proto_id != MBAP_PROTO_ID) + { + return -EPROTO; + } + + if (adu->length < 2) + { + return -EINVAL; + } + + if ((adu->length - 2) > NXMB_ADU_DATA_MAX) + { + return -EMSGSIZE; + } + + pthread_mutex_lock(&ctx->lock); + ctx->adu = *adu; + pthread_mutex_unlock(&ctx->lock); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_raw_put_header + * + * Description: + * Serialize the 8-byte MBAP+FC header for an ADU. + * + * Input Parameters: + * adu - The ADU providing the header fields + * hdr - The destination buffer for the serialized header + * + * Returned Value: + * None + * + ****************************************************************************/ + +void nxmb_raw_put_header(FAR const struct nxmb_adu_s *adu, FAR uint8_t *hdr) +{ + DEBUGASSERT(adu && hdr); + + nxmb_util_put_u16_be(&hdr[0], adu->trans_id); + nxmb_util_put_u16_be(&hdr[2], adu->proto_id); + nxmb_util_put_u16_be(&hdr[4], adu->length); + hdr[6] = adu->unit_id; + hdr[7] = adu->fc; +} + +/**************************************************************************** + * Name: nxmb_raw_get_header + * + * Description: + * Parse the 8-byte MBAP+FC header into an ADU structure. + * + * Input Parameters: + * adu - The destination ADU structure + * hdr - The source buffer containing the serialized header + * + * Returned Value: + * None + * + ****************************************************************************/ + +void nxmb_raw_get_header(FAR struct nxmb_adu_s *adu, FAR const uint8_t *hdr) +{ + DEBUGASSERT(adu && hdr); + + adu->trans_id = nxmb_util_get_u16_be(&hdr[0]); + adu->proto_id = nxmb_util_get_u16_be(&hdr[2]); + adu->length = nxmb_util_get_u16_be(&hdr[4]); + adu->unit_id = hdr[6]; + adu->fc = hdr[7]; +} diff --git a/industry/nxmodbus/transport/nxmb_serial.c b/industry/nxmodbus/transport/nxmb_serial.c new file mode 100644 index 000000000..679e0d597 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_serial.c @@ -0,0 +1,423 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_serial.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SERIAL_TERMIOS +# include +#endif + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define NXMB_RTU_MIN_FRAME_SIZE 5 + +/* Wire frame: unit_id + fc + data + 2 CRC bytes */ + +#define NXMB_RTU_FRAME_MAX (NXMB_ADU_DATA_MAX + 4) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nxmb_serial_state_s +{ + int fd; + uint32_t t35_usec; + bool rx_frame_now; + uint16_t rx_pos; + uint8_t rx_buf[NXMB_RTU_FRAME_MAX]; +#ifdef CONFIG_SERIAL_TERMIOS + struct termios old_tio; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static uint32_t nxmb_serial_calculate_t35(uint32_t baudrate); +static int nxmb_serial_init(nxmb_handle_t ctx); +static int nxmb_serial_deinit(nxmb_handle_t ctx); +static int nxmb_serial_send(nxmb_handle_t ctx); +static int nxmb_serial_receive(nxmb_handle_t ctx); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct nxmb_transport_ops_s g_nxmb_serial_ops = +{ + .init = nxmb_serial_init, + .deinit = nxmb_serial_deinit, + .send = nxmb_serial_send, + .receive = nxmb_serial_receive +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_serial_calculate_t35 + * + * Description: + * Calculate T3.5 character timeout in microseconds based on baud rate. + * + * Input Parameters: + * baudrate - Serial baud rate + * + * Returned Value: + * T3.5 timeout in microseconds + * + ****************************************************************************/ + +static uint32_t nxmb_serial_calculate_t35(uint32_t baudrate) +{ + if (baudrate <= 19200) + { + /* T3.5 = 3.5 * 11 bits/char * 1e6 us/s / baudrate + * = 38500000 / baudrate (in microseconds). + * Use integer arithmetic to avoid floating-point, which may + * require FPU context save/restore on embedded targets. + */ + + return 38500000u / baudrate; + } + else + { + /* Per Modbus spec, fixed 1750 us for baud rates above 19200 */ + + return 1750; + } +} + +/**************************************************************************** + * Name: nxmb_serial_init + * + * Description: + * Initialize serial RTU transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_serial_init(nxmb_handle_t ctx) +{ + FAR struct nxmb_serial_state_s *state; + FAR const struct nxmb_serial_config_s *cfg; + int ret; + + DEBUGASSERT(ctx); + + cfg = &ctx->transport_cfg.serial; + + if (cfg->devpath == NULL) + { + return -EINVAL; + } + + state = calloc(1, sizeof(struct nxmb_serial_state_s)); + if (state == NULL) + { + return -ENOMEM; + } + + state->fd = open(cfg->devpath, O_RDWR | O_NOCTTY); + if (state->fd < 0) + { + ret = -errno; + free(state); + return ret; + } + +#ifdef CONFIG_SERIAL_TERMIOS + if (tcgetattr(state->fd, &state->old_tio) < 0) + { + ret = -errno; + close(state->fd); + free(state); + return ret; + } + + ret = nxmb_serial_configure(state->fd, cfg->baudrate, cfg->parity); + + if (ret < 0) + { + close(state->fd); + free(state); + return ret; + } + + tcflush(state->fd, TCIOFLUSH); +#endif + + state->t35_usec = nxmb_serial_calculate_t35(cfg->baudrate); + state->rx_frame_now = false; + + ctx->transport_state = state; + + return 0; +} + +/**************************************************************************** + * Name: nxmb_serial_deinit + * + * Description: + * Deinitialize serial RTU transport. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_serial_deinit(nxmb_handle_t ctx) +{ + FAR struct nxmb_serial_state_s *state; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + +#ifdef CONFIG_SERIAL_TERMIOS + tcsetattr(state->fd, TCSANOW, &state->old_tio); +#endif + close(state->fd); + free(state); + + ctx->transport_state = NULL; + + return 0; +} + +/**************************************************************************** + * Name: nxmb_serial_send + * + * Description: + * Send RTU frame over serial port. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_serial_send(nxmb_handle_t ctx) +{ + FAR struct nxmb_serial_state_s *state; + uint8_t frame[NXMB_RTU_FRAME_MAX]; + uint16_t data_len; + uint16_t total; + uint16_t crc; + ssize_t written; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + if (ctx->adu.length < 2) + { + return -EINVAL; + } + + data_len = ctx->adu.length - 2; + + /* Linearize the ADU and append CRC: unit_id, fc, data..., crc_lo, crc_hi */ + + frame[0] = ctx->adu.unit_id; + frame[1] = ctx->adu.fc; + memcpy(&frame[2], ctx->adu.data, data_len); + + crc = nxmb_crc16(frame, ctx->adu.length); + + frame[ctx->adu.length] = (crc & 0xff); + frame[ctx->adu.length + 1] = (crc >> 8); + + ctx->adu.crc = crc; + + total = ctx->adu.length + 2; + written = write(state->fd, frame, total); + + if (written < 0) + { + return -errno; + } + + if (written != total) + { + return -EIO; + } + + return 0; +} + +/**************************************************************************** + * Name: nxmb_serial_receive + * + * Description: + * Receive RTU frame from serial port with dynamic timeout. + * + * Input Parameters: + * ctx - Instance context + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int nxmb_serial_receive(nxmb_handle_t ctx) +{ + FAR struct nxmb_serial_state_s *state; + struct timeval timeout; + fd_set readfds; + ssize_t nread; + uint16_t crc_calc; + uint16_t crc_recv; + uint16_t data_len; + int ret; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + FD_ZERO(&readfds); + FD_SET(state->fd, &readfds); + + if (state->rx_frame_now) + { + timeout.tv_sec = 0; + timeout.tv_usec = state->t35_usec; + } + else + { + timeout.tv_sec = 0; + timeout.tv_usec = CONFIG_NXMODBUS_RTU_IDLE_TIMEOUT_MS * 1000; + } + + ret = select(state->fd + 1, &readfds, NULL, NULL, &timeout); + + if (ret < 0) + { + return -errno; + } + + if (ret == 0) + { + if (state->rx_frame_now) + { + state->rx_frame_now = false; + + if (state->rx_pos < NXMB_RTU_MIN_FRAME_SIZE) + { + state->rx_pos = 0; + return 0; + } + + crc_calc = nxmb_crc16(state->rx_buf, state->rx_pos - 2); + crc_recv = state->rx_buf[state->rx_pos - 2] | + (state->rx_buf[state->rx_pos - 1] << 8); + + if (crc_calc != crc_recv) + { + state->rx_pos = 0; + return 0; + } + + /* Unpack frame into ctx->adu (excluding the trailing CRC). */ + + data_len = (state->rx_pos - 4); + ctx->adu.unit_id = state->rx_buf[0]; + ctx->adu.fc = state->rx_buf[1]; + memcpy(ctx->adu.data, &state->rx_buf[2], data_len); + ctx->adu.crc = crc_recv; + ctx->adu.length = (state->rx_pos - 2); + + state->rx_pos = 0; + return 1; + } + + return 0; + } + + if (!FD_ISSET(state->fd, &readfds)) + { + return 0; + } + + if (!state->rx_frame_now) + { + state->rx_pos = 0; + state->rx_frame_now = true; + } + + nread = read(state->fd, + &state->rx_buf[state->rx_pos], + sizeof(state->rx_buf) - state->rx_pos); + if (nread < 0) + { + return -errno; + } + + if (nread == 0) + { + return 0; + } + + state->rx_pos += nread; + + if (state->rx_pos >= sizeof(state->rx_buf)) + { + state->rx_pos = 0; + state->rx_frame_now = false; + return 0; + } + + return 0; +} diff --git a/industry/nxmodbus/transport/nxmb_serial_common.c b/industry/nxmodbus/transport/nxmb_serial_common.c new file mode 100644 index 000000000..72b2d1a87 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_serial_common.c @@ -0,0 +1,138 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_serial_common.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#ifdef CONFIG_SERIAL_TERMIOS +# include +#endif + +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +#ifdef CONFIG_SERIAL_TERMIOS + +/**************************************************************************** + * Name: nxmb_serial_configure + * + * Description: + * Configure serial port with termios settings for Modbus communication. + * + * Input Parameters: + * fd - File descriptor + * baudrate - Baud rate + * parity - Parity setting + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +int nxmb_serial_configure(int fd, uint32_t baudrate, + enum nxmb_parity_e parity) +{ + struct termios tio; + speed_t speed; + + if (tcgetattr(fd, &tio) < 0) + { + return -errno; + } + + switch (baudrate) + { + case 9600: + speed = B9600; + break; + case 19200: + speed = B19200; + break; + case 38400: + speed = B38400; + break; + case 57600: + speed = B57600; + break; + case 115200: + speed = B115200; + break; + default: + return -EINVAL; + } + + cfsetispeed(&tio, speed); + cfsetospeed(&tio, speed); + + tio.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); + tio.c_cflag |= CS8 | CLOCAL | CREAD; + + switch (parity) + { + case NXMB_PAR_NONE: + + /* Modbus spec: no parity requires 2 stop bits so the + * character frame remains 11 bits. + */ + + tio.c_cflag |= CSTOPB; + break; + case NXMB_PAR_EVEN: + tio.c_cflag |= PARENB; + break; + case NXMB_PAR_ODD: + tio.c_cflag |= PARENB | PARODD; + break; + default: + return -EINVAL; + } + + tio.c_iflag &= + ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + tio.c_oflag &= ~OPOST; + tio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 0; + + if (tcsetattr(fd, TCSANOW, &tio) < 0) + { + return -errno; + } + + return 0; +} + +#endif /* CONFIG_SERIAL_TERMIOS */ diff --git a/industry/nxmodbus/transport/nxmb_tcp.c b/industry/nxmodbus/transport/nxmb_tcp.c new file mode 100644 index 000000000..f4b2dc940 --- /dev/null +++ b/industry/nxmodbus/transport/nxmb_tcp.c @@ -0,0 +1,684 @@ +/**************************************************************************** + * apps/industry/nxmodbus/transport/nxmb_tcp.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "nxmb_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MBAP_HEADER_SIZE 7 +#define MBAP_FC_SIZE 8 +#define NXMB_TCP_RECV_MS 1000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nxmb_tcp_client_s +{ + int fd; + uint16_t trans_id; + time_t last_activity; +}; + +struct nxmb_tcp_state_s +{ + struct nxmb_tcp_client_s clients[CONFIG_NXMODBUS_TCP_MAX_CLIENTS]; + int listen_fd; + int active_idx; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int nxmb_tcp_init(nxmb_handle_t ctx); +static int nxmb_tcp_deinit(nxmb_handle_t ctx); +static int nxmb_tcp_send(nxmb_handle_t ctx); +static int nxmb_tcp_receive(nxmb_handle_t ctx); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct nxmb_transport_ops_s g_nxmb_tcp_ops = +{ + .init = nxmb_tcp_init, + .deinit = nxmb_tcp_deinit, + .send = nxmb_tcp_send, + .receive = nxmb_tcp_receive, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxmb_tcp_put_header + ****************************************************************************/ + +static void nxmb_tcp_put_header(FAR const struct nxmb_adu_s *adu, + FAR uint8_t *hdr) +{ + nxmb_util_put_u16_be(&hdr[0], adu->trans_id); + nxmb_util_put_u16_be(&hdr[2], adu->proto_id); + nxmb_util_put_u16_be(&hdr[4], adu->length); + hdr[6] = adu->unit_id; + hdr[7] = adu->fc; +} + +/**************************************************************************** + * Name: nxmb_tcp_get_header + ****************************************************************************/ + +static void nxmb_tcp_get_header(FAR struct nxmb_adu_s *adu, + FAR const uint8_t *hdr) +{ + adu->trans_id = nxmb_util_get_u16_be(&hdr[0]); + adu->proto_id = nxmb_util_get_u16_be(&hdr[2]); + adu->length = nxmb_util_get_u16_be(&hdr[4]); + adu->unit_id = hdr[6]; + adu->fc = hdr[7]; +} + +/**************************************************************************** + * Name: nxmb_tcp_create_server + ****************************************************************************/ + +static int nxmb_tcp_create_server(FAR const struct nxmb_tcp_config_s *cfg) +{ + struct sockaddr_in addr; + int listen_fd; + int ret; +#ifdef CONFIG_NET_SOCKOPTS + int opt = 1; +#endif + + listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) + { + return -errno; + } + +#ifdef CONFIG_NET_SOCKOPTS + setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#endif + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cfg->port); + + if (cfg->bindaddr != NULL) + { + ret = inet_pton(AF_INET, cfg->bindaddr, &addr.sin_addr); + if (ret != 1) + { + close(listen_fd); + return -EINVAL; + } + } + else + { + addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + + ret = bind(listen_fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + { + close(listen_fd); + return -errno; + } + + ret = listen(listen_fd, CONFIG_NXMODBUS_TCP_MAX_CLIENTS); + if (ret < 0) + { + close(listen_fd); + return -errno; + } + + return listen_fd; +} + +/**************************************************************************** + * Name: nxmb_tcp_connect_client + ****************************************************************************/ + +static int nxmb_tcp_connect_client(FAR const struct nxmb_tcp_config_s *cfg) +{ + struct sockaddr_in addr; + int client_fd; + int ret; + + if (cfg->host == NULL) + { + return -EINVAL; + } + + client_fd = socket(AF_INET, SOCK_STREAM, 0); + if (client_fd < 0) + { + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(cfg->port); + + ret = inet_pton(AF_INET, cfg->host, &addr.sin_addr); + if (ret != 1) + { + close(client_fd); + return -EINVAL; + } + + ret = connect(client_fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + { + close(client_fd); + return -errno; + } + + return client_fd; +} + +/**************************************************************************** + * Name: nxmb_tcp_recv_full + ****************************************************************************/ + +static int nxmb_tcp_recv_full(int fd, FAR uint8_t *buf, size_t len, + int timeout_ms) +{ + struct timeval tv; + fd_set readfds; + size_t received; + int ret; + + received = 0; + + while (received < len) + { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + ret = select(fd + 1, &readfds, NULL, NULL, &tv); + if (ret < 0) + { + return -errno; + } + else if (ret == 0) + { + return -ETIMEDOUT; + } + + ret = recv(fd, &buf[received], len - received, 0); + if (ret < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + { + continue; + } + + return -errno; + } + else if (ret == 0) + { + return -ECONNRESET; + } + + received += ret; + } + + return received; +} + +/**************************************************************************** + * Name: nxmb_tcp_init + ****************************************************************************/ + +static int nxmb_tcp_init(nxmb_handle_t ctx) +{ + FAR const struct nxmb_tcp_config_s *cfg; + FAR struct nxmb_tcp_state_s *state; + int fd; + int i; + + DEBUGASSERT(ctx); + + cfg = &ctx->transport_cfg.tcp; + + state = calloc(1, sizeof(struct nxmb_tcp_state_s)); + if (state == NULL) + { + return -ENOMEM; + } + + state->listen_fd = -1; + state->active_idx = -1; + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + state->clients[i].fd = -1; + } + + if (ctx->is_client) + { + fd = nxmb_tcp_connect_client(cfg); + if (fd < 0) + { + free(state); + return fd; + } + + state->clients[0].fd = fd; + state->clients[0].last_activity = time(NULL); + } + else + { + fd = nxmb_tcp_create_server(cfg); + if (fd < 0) + { + free(state); + return fd; + } + + state->listen_fd = fd; + } + + ctx->transport_state = state; + + return OK; +} + +/**************************************************************************** + * Name: nxmb_tcp_deinit + ****************************************************************************/ + +static int nxmb_tcp_deinit(nxmb_handle_t ctx) +{ + FAR struct nxmb_tcp_state_s *state; + int i; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + if (state->clients[i].fd >= 0) + { + close(state->clients[i].fd); + } + } + + if (state->listen_fd >= 0) + { + close(state->listen_fd); + } + + free(state); + ctx->transport_state = NULL; + + return OK; +} + +/**************************************************************************** + * Name: nxmb_tcp_send + ****************************************************************************/ + +static int nxmb_tcp_send(nxmb_handle_t ctx) +{ + FAR struct nxmb_tcp_state_s *state; + FAR struct nxmb_tcp_client_s *client; + uint8_t header[MBAP_FC_SIZE]; + struct iovec iov[2]; + uint16_t data_len; + uint16_t total; + ssize_t sent; + int fd; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + + /* For server mode, respond to the client that sent the request. + * For client mode, use the single connection (index 0). + */ + + if (ctx->is_client) + { + client = &state->clients[0]; + } + else + { + if (state->active_idx < 0) + { + return -ENOTCONN; + } + + client = &state->clients[state->active_idx]; + } + + fd = client->fd; + if (fd < 0) + { + return -ENOTCONN; + } + + if (ctx->adu.length < 2) + { + return -EINVAL; + } + + /* Populate the MBAP fields before serializing the header */ + + ctx->adu.trans_id = client->trans_id; + ctx->adu.proto_id = 0x0000; + + data_len = ctx->adu.length - 2; + + /* Emit MBAP header + PDU data via writev() to avoid a stack-side + * copy. The kernel gathers both iovecs into a single TCP segment. + */ + + nxmb_tcp_put_header(&ctx->adu, header); + + iov[0].iov_base = header; + iov[0].iov_len = MBAP_FC_SIZE; + iov[1].iov_base = ctx->adu.data; + iov[1].iov_len = data_len; + + total = MBAP_FC_SIZE + data_len; + sent = writev(fd, iov, (data_len > 0) ? 2 : 1); + if (sent != total) + { + return (sent < 0) ? -errno : -EIO; + } + + client->last_activity = time(NULL); + + return OK; +} + +/**************************************************************************** + * Name: nxmb_tcp_evict_idle + * + * Description: + * Close idle client connections that exceed the configured timeout. + * + ****************************************************************************/ + +static void nxmb_tcp_evict_idle(FAR struct nxmb_tcp_state_s *state, + time_t now) +{ + int i; + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + if (state->clients[i].fd >= 0 && + (now - state->clients[i].last_activity) > + CONFIG_NXMODBUS_TCP_TIMEOUT_SEC) + { + close(state->clients[i].fd); + state->clients[i].fd = -1; + } + } +} + +/**************************************************************************** + * Name: nxmb_tcp_accept_new + * + * Description: + * Accept a pending connection into the first free client slot. + * + ****************************************************************************/ + +static void nxmb_tcp_accept_new(FAR struct nxmb_tcp_state_s *state) +{ + int fd; + int i; + + fd = accept(state->listen_fd, NULL, NULL); + if (fd < 0) + { + return; + } + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + if (state->clients[i].fd < 0) + { + state->clients[i].fd = fd; + state->clients[i].last_activity = time(NULL); + return; + } + } + + /* No free slot -- reject the connection */ + + close(fd); +} + +/**************************************************************************** + * Name: nxmb_tcp_read_frame + * + * Description: + * Read a complete Modbus TCP frame from a client connection. + * On error (except timeout), the connection is closed. + * + ****************************************************************************/ + +static int nxmb_tcp_read_frame(nxmb_handle_t ctx, + FAR struct nxmb_tcp_client_s *client) +{ + uint8_t header[MBAP_FC_SIZE]; + uint16_t data_len; + int ret; + + ret = nxmb_tcp_recv_full(client->fd, header, MBAP_FC_SIZE, + NXMB_TCP_RECV_MS); + if (ret < 0) + { + if (ret == -ETIMEDOUT || ret == -EAGAIN) + { + return -EAGAIN; + } + + close(client->fd); + client->fd = -1; + return ret; + } + + nxmb_tcp_get_header(&ctx->adu, header); + + if (ctx->adu.proto_id != 0x0000) + { + close(client->fd); + client->fd = -1; + return -EPROTO; + } + + if (ctx->adu.length < 2) + { + close(client->fd); + client->fd = -1; + return -EINVAL; + } + + data_len = ctx->adu.length - 2; + + if (data_len > NXMB_ADU_DATA_MAX) + { + close(client->fd); + client->fd = -1; + return -EMSGSIZE; + } + + if (data_len > 0) + { + ret = nxmb_tcp_recv_full(client->fd, ctx->adu.data, data_len, + NXMB_TCP_RECV_MS); + if (ret < 0) + { + close(client->fd); + client->fd = -1; + return ret; + } + } + + client->trans_id = ctx->adu.trans_id; + client->last_activity = time(NULL); + + return ctx->adu.length; +} + +static int nxmb_tcp_receive(nxmb_handle_t ctx) +{ + FAR struct nxmb_tcp_state_s *state; + struct timeval tv; + fd_set readfds; + time_t now; + int maxfd; + int ret; + int i; + + DEBUGASSERT(ctx && ctx->transport_state); + + state = ctx->transport_state; + state->active_idx = -1; + + /* Client mode: single connection, read directly */ + + if (ctx->is_client) + { + if (state->clients[0].fd < 0) + { + return -ENOTCONN; + } + + state->active_idx = 0; + return nxmb_tcp_read_frame(ctx, &state->clients[0]); + } + + /* Server mode: multiplex listen socket + all client connections */ + + now = time(NULL); + nxmb_tcp_evict_idle(state, now); + + FD_ZERO(&readfds); + maxfd = -1; + + if (state->listen_fd >= 0) + { + FD_SET(state->listen_fd, &readfds); + maxfd = state->listen_fd; + } + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + if (state->clients[i].fd >= 0) + { + FD_SET(state->clients[i].fd, &readfds); + if (state->clients[i].fd > maxfd) + { + maxfd = state->clients[i].fd; + } + } + } + + if (maxfd < 0) + { + return -EAGAIN; + } + + tv.tv_sec = 0; + tv.tv_usec = 50000; + + ret = select(maxfd + 1, &readfds, NULL, NULL, &tv); + if (ret < 0) + { + return -errno; + } + + if (ret == 0) + { + return -EAGAIN; + } + + /* Accept new connections if listen socket is ready */ + + if (state->listen_fd >= 0 && FD_ISSET(state->listen_fd, &readfds)) + { + nxmb_tcp_accept_new(state); + } + + /* Check each client for incoming data (first ready wins). + * In server mode, a per-client read failure (disconnect, protocol + * error, etc.) must not be fatal - nxmb_tcp_read_frame has already + * closed the offending fd, and the listen socket remains open for new + * connections. Translate any such error to -EAGAIN so the caller + * keeps polling. + */ + + for (i = 0; i < CONFIG_NXMODBUS_TCP_MAX_CLIENTS; i++) + { + if (state->clients[i].fd >= 0 && + FD_ISSET(state->clients[i].fd, &readfds)) + { + state->active_idx = i; + ret = nxmb_tcp_read_frame(ctx, &state->clients[i]); + if (ret < 0 && ret != -EAGAIN) + { + state->active_idx = -1; + return -EAGAIN; + } + + return ret; + } + } + + return -EAGAIN; +} diff --git a/industry/scpi/Kconfig b/industry/scpi/Kconfig index d8911ecc7..6797051e6 100644 --- a/industry/scpi/Kconfig +++ b/industry/scpi/Kconfig @@ -7,10 +7,71 @@ config SCPI_PARSER tristate "SCPI instrument side parser" default n +if SCPI_PARSER + +menu "SCPI parser unit support" + +config SCPI_PARSER_UNITS_IMPERIAL + bool "Imperial units" + default n + +config SCPI_PARSER_UNITS_ANGLE + bool "Angle units" + default n + +config SCPI_PARSER_UNITS_PARTICLES + bool "Particle units" + default n + +config SCPI_PARSER_UNITS_DISTANCE + bool "Distance units" + default n + +config SCPI_PARSER_UNITS_MAGNETIC + bool "Magnetic units" + default n + +config SCPI_PARSER_UNITS_LIGHT + bool "Light units" + default n + +config SCPI_PARSER_UNITS_ENERGY_FORCE_MASS + bool "Energy, force, and mass units" + default n + +config SCPI_PARSER_UNITS_TIME + bool "Time units" + default n + +config SCPI_PARSER_UNITS_TEMPERATURE + bool "Temperature units" + default n + +config SCPI_PARSER_UNITS_RATIO + bool "Ratio units" + default n + +config SCPI_PARSER_UNITS_POWER + bool "Power units" + default y + +config SCPI_PARSER_UNITS_FREQUENCY + bool "Frequency units" + default y + +config SCPI_PARSER_UNITS_ELECTRIC + bool "Electric units" + default y + +config SCPI_PARSER_UNITS_ELECTRIC_CHARGE_CONDUCTANCE + bool "Electric charge and conductance units" + default n + +endmenu + config SCPI_PARSER_DEMO bool "Enable the SCPI parser library demo" default n - depends on SCPI_PARSER if SCPI_PARSER_DEMO @@ -23,3 +84,5 @@ config SCPI_PARSER_DEMO_STACKSIZE default DEFAULT_TASK_STACKSIZE endif + +endif diff --git a/industry/scpi/Makefile b/industry/scpi/Makefile index c86267297..515011129 100644 --- a/industry/scpi/Makefile +++ b/industry/scpi/Makefile @@ -39,6 +39,24 @@ CFLAGS += -DHAVE_SNPRINTF CFLAGS += -DHAVE_STRNDUP CFLAGS += -DHAVE_STRNCASECMP +SCPI_UNIT = $(if $(filter y,$(CONFIG_SCPI_PARSER_UNITS_$(1))),1,0) +SCPI_UECC = $(call SCPI_UNIT,ELECTRIC_CHARGE_CONDUCTANCE) + +CFLAGS += -DUSE_UNITS_IMPERIAL=$(call SCPI_UNIT,IMPERIAL) +CFLAGS += -DUSE_UNITS_ANGLE=$(call SCPI_UNIT,ANGLE) +CFLAGS += -DUSE_UNITS_PARTICLES=$(call SCPI_UNIT,PARTICLES) +CFLAGS += -DUSE_UNITS_DISTANCE=$(call SCPI_UNIT,DISTANCE) +CFLAGS += -DUSE_UNITS_MAGNETIC=$(call SCPI_UNIT,MAGNETIC) +CFLAGS += -DUSE_UNITS_LIGHT=$(call SCPI_UNIT,LIGHT) +CFLAGS += -DUSE_UNITS_ENERGY_FORCE_MASS=$(call SCPI_UNIT,ENERGY_FORCE_MASS) +CFLAGS += -DUSE_UNITS_TIME=$(call SCPI_UNIT,TIME) +CFLAGS += -DUSE_UNITS_TEMPERATURE=$(call SCPI_UNIT,TEMPERATURE) +CFLAGS += -DUSE_UNITS_RATIO=$(call SCPI_UNIT,RATIO) +CFLAGS += -DUSE_UNITS_POWER=$(call SCPI_UNIT,POWER) +CFLAGS += -DUSE_UNITS_FREQUENCY=$(call SCPI_UNIT,FREQUENCY) +CFLAGS += -DUSE_UNITS_ELECTRIC=$(call SCPI_UNIT,ELECTRIC) +CFLAGS += -DUSE_UNITS_ELECTRIC_CHARGE_CONDUCTANCE=$(SCPI_UECC) + ifneq ($(CONFIG_SCPI_PARSER_DEMO),) MAINSRC = $(SCPI_UNPACK)/examples/test-interactive/main.c CSRCS += scpi-def.c diff --git a/inertial/CMakeLists.txt b/inertial/CMakeLists.txt index 66cfc9e27..e3324c5cc 100644 --- a/inertial/CMakeLists.txt +++ b/inertial/CMakeLists.txt @@ -1,7 +1,7 @@ # ############################################################################## # apps/inertial/CMakeLists.txt # -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more contributor # license agreements. See the NOTICE file distributed with this work for @@ -20,4 +20,6 @@ # # ############################################################################## +nuttx_add_subdirectory() + nuttx_generate_kconfig(MENUDESC "Inertial Libraries Support") diff --git a/inertial/Make.defs b/inertial/Make.defs index d19af3397..d5c8f4ca5 100644 --- a/inertial/Make.defs +++ b/inertial/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # apps/inertial/Make.defs # -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/inertial/Makefile b/inertial/Makefile index e12c1d623..1d48a1ae3 100644 --- a/inertial/Makefile +++ b/inertial/Makefile @@ -1,7 +1,7 @@ ############################################################################ # apps/inertial/Makefile # -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/inertial/madgwick/.gitignore b/inertial/madgwick/.gitignore new file mode 100644 index 000000000..53b249722 --- /dev/null +++ b/inertial/madgwick/.gitignore @@ -0,0 +1,2 @@ +/fusion +/libmadgwick.tar.gz diff --git a/inertial/madgwick/CMakeLists.txt b/inertial/madgwick/CMakeLists.txt new file mode 100644 index 000000000..df20b7129 --- /dev/null +++ b/inertial/madgwick/CMakeLists.txt @@ -0,0 +1,58 @@ +# ############################################################################## +# apps/inertial/madgwick/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_LIB_MADGWICK) + + set(MADGWICK_DIR ${CMAKE_CURRENT_LIST_DIR}/fusion) + + if(NOT EXISTS ${MADGWICK_DIR}) + FetchContent_Declare( + madgwick_fetch + DOWNLOAD_NAME "libmadgwick.tar.gz" + DOWNLOAD_DIR ${CMAKE_CURRENT_LIST_DIR} + URL "https://github.com/xioTechnologies/Fusion/archive/refs/tags/v${CONFIG_LIB_MADGWICK_VER}.tar.gz" + SOURCE_DIR + ${MADGWICK_DIR} + BINARY_DIR + ${CMAKE_BINARY_DIR}/apps/inertial/madgwick/fusion + DOWNLOAD_NO_PROGRESS true + TIMEOUT 30) + + FetchContent_GetProperties(madgwick_fetch) + + if(NOT madgwick_fetch_POPULATED) + FetchContent_Populate(madgwick_fetch) + endif() + endif() + + set(CSRCS + ${MADGWICK_DIR}/Fusion/FusionAhrs.c + ${MADGWICK_DIR}/Fusion/FusionCompass.c + ${MADGWICK_DIR}/Fusion/FusionOffset.c) + + nuttx_add_library(madgwick STATIC) + target_sources(madgwick PRIVATE ${CSRCS}) + target_include_directories(madgwick PUBLIC ${MADGWICK_DIR}) + target_compile_options(madgwick PRIVATE -Wno-shadow -Wno-strict-prototypes + -Wno-unknown-pragmas) + +endif() diff --git a/inertial/madgwick/Make.defs b/inertial/madgwick/Make.defs index e16d25d84..bb48895dc 100644 --- a/inertial/madgwick/Make.defs +++ b/inertial/madgwick/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # apps/inertial/madgwick/Make.defs # -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/inertial/madgwick/Makefile b/inertial/madgwick/Makefile index 38dac5227..909d67b97 100644 --- a/inertial/madgwick/Makefile +++ b/inertial/madgwick/Makefile @@ -1,7 +1,7 @@ ############################################################################ # apps/inertial/madgwick/Makefile # -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: Apache-2.0 # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with diff --git a/interpreters/bas/CMakeLists.txt b/interpreters/bas/CMakeLists.txt index 86219418a..527353d7c 100644 --- a/interpreters/bas/CMakeLists.txt +++ b/interpreters/bas/CMakeLists.txt @@ -27,9 +27,9 @@ if(CONFIG_INTERPRETERS_BAS) SRCS bas_main.c STACKSIZE - ${CONFIG_INTERPRETER_BAS_STACKSIZE} + ${CONFIG_INTERPRETERS_BAS_STACKSIZE} PRIORITY - ${CONFIG_INTERPRETER_BAS_PRIORITY}) + ${CONFIG_INTERPRETERS_BAS_PRIORITY}) set(CSRCS bas_str.c @@ -42,7 +42,7 @@ if(CONFIG_INTERPRETERS_BAS) bas_global.c bas_program.c) - if(CONFIG_INTERPRETER_BAS_VT100) + if(CONFIG_INTERPRETERS_BAS_VT100) list(APPEND CSRCS bas_vt100.c) endif() diff --git a/interpreters/bas/Kconfig b/interpreters/bas/Kconfig index db84d3d07..39b502902 100644 --- a/interpreters/bas/Kconfig +++ b/interpreters/bas/Kconfig @@ -33,37 +33,37 @@ config INTERPRETERS_BAS if INTERPRETERS_BAS -config INTERPRETER_BAS_VERSION +config INTERPRETERS_BAS_VERSION string "Version number" default "2.4" -config INTERPRETER_BAS_PRIORITY +config INTERPRETERS_BAS_PRIORITY int "Basic interpreter priority" default 100 ---help--- Task priority of the Basic interpreter main task -config INTERPRETER_BAS_STACKSIZE +config INTERPRETERS_BAS_STACKSIZE int "Basic interpreter stack size" default 4096 ---help--- Size of the stack allocated for the Basic interpreter main task -config INTERPRETER_BAS_VT100 +config INTERPRETERS_BAS_VT100 bool "VT100 terminal support" default y -config INTERPRETER_BAS_USE_LR0 +config INTERPRETERS_BAS_USE_LR0 bool "LR0 parser" default n ---help--- Select if you want LR0 parser. -config INTERPRETER_BAS_USE_SELECT +config INTERPRETERS_BAS_USE_SELECT bool "Use select()" default n -config INTERPRETER_BAS_HAVE_FTRUNCATE +config INTERPRETERS_BAS_HAVE_FTRUNCATE bool default n ---help--- diff --git a/interpreters/bas/Makefile b/interpreters/bas/Makefile index 332cb17df..532c74043 100644 --- a/interpreters/bas/Makefile +++ b/interpreters/bas/Makefile @@ -27,7 +27,7 @@ include $(APPDIR)/Make.defs CSRCS = bas.c bas_auto.c bas_fs.c bas_global.c bas_program.c CSRCS += bas_str.c bas_token.c bas_value.c bas_var.c -ifeq ($(CONFIG_INTERPRETER_BAS_VT100),y) +ifeq ($(CONFIG_INTERPRETERS_BAS_VT100),y) CSRCS += bas_vt100.c endif @@ -36,8 +36,8 @@ MAINSRC = bas_main.c # BAS built-in application info PROGNAME = bas -PRIORITY = $(CONFIG_INTERPRETER_BAS_PRIORITY) -STACKSIZE = $(CONFIG_INTERPRETER_BAS_STACKSIZE) +PRIORITY = $(CONFIG_INTERPRETERS_BAS_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETERS_BAS_STACKSIZE) MODULE = $(CONFIG_INTERPRETERS_BAS) include $(APPDIR)/Application.mk diff --git a/interpreters/bas/bas.c b/interpreters/bas/bas.c index 3ccaea3b9..18b08876f 100644 --- a/interpreters/bas/bas.c +++ b/interpreters/bas/bas.c @@ -614,7 +614,7 @@ static struct Value *func(struct Value *value) return value; } -#ifdef CONFIG_INTERPRETER_BAS_USE_LR0 +#ifdef CONFIG_INTERPRETERS_BAS_USE_LR0 /* Grammar with LR(0) sets */ @@ -2368,7 +2368,7 @@ void bas_interpreter(void) { if (FS_istty(STDCHANNEL)) { - FS_putChars(STDCHANNEL, "bas " CONFIG_INTERPRETER_BAS_VERSION "\n"); + FS_putChars(STDCHANNEL, "bas " CONFIG_INTERPRETERS_BAS_VERSION "\n"); FS_putChars(STDCHANNEL, "Copyright 1999-2014 Michael Haardt.\n"); FS_putChars(STDCHANNEL, "This is free software with ABSOLUTELY NO WARRANTY.\n"); diff --git a/interpreters/bas/bas_fs.c b/interpreters/bas/bas_fs.c index 199f41bb9..ad708a912 100644 --- a/interpreters/bas/bas_fs.c +++ b/interpreters/bas/bas_fs.c @@ -270,7 +270,7 @@ static int edit(int chn, int nl) { if (f->inCapacity) { -#ifdef CONFIG_INTERPRETER_BAS_VT100 +#ifdef CONFIG_INTERPRETERS_BAS_VT100 /* Could use vt100_clrtoeol */ #endif /* Is the previous char in buffer 2 char escape sequence? */ @@ -319,7 +319,7 @@ static int edit(int chn, int nl) static int cls(int chn) { -#ifdef CONFIG_INTERPRETER_BAS_VT100 +#ifdef CONFIG_INTERPRETERS_BAS_VT100 vt100_clrscreen(chn); vt100_cursorhome(chn); return 0; @@ -331,7 +331,7 @@ static int cls(int chn) static int locate(int chn, int line, int column) { -#ifdef CONFIG_INTERPRETER_BAS_VT100 +#ifdef CONFIG_INTERPRETERS_BAS_VT100 vt100_setcursor(chn, line, column); return 0; #else @@ -342,7 +342,7 @@ static int locate(int chn, int line, int column) static int colour(int chn, int foreground, int background) { -#ifdef CONFIG_INTERPRETER_BAS_VT100 +#ifdef CONFIG_INTERPRETERS_BAS_VT100 if (foreground >= 0) { vt100_foreground_color(chn, foreground); @@ -362,7 +362,7 @@ static int colour(int chn, int foreground, int background) static int resetcolour(int chn) { -#ifdef CONFIG_INTERPRETER_BAS_VT100 +#ifdef CONFIG_INTERPRETERS_BAS_VT100 vt100_foreground_color(chn, VT100_DEFAULT); vt100_background_color(chn, VT100_DEFAULT); #endif @@ -837,7 +837,7 @@ int FS_lock(int chn, off_t offset, off_t length, int mode, int w) int FS_truncate(int chn) { -#ifdef CONFIG_INTERPRETER_BAS_HAVE_FTRUNCATE +#ifdef CONFIG_INTERPRETERS_BAS_HAVE_FTRUNCATE int fd; off_t o; @@ -1411,7 +1411,7 @@ int FS_inkeyChar(int dev, int ms) struct FileStream *f; char c; ssize_t len; -#ifdef CONFIG_INTERPRETER_BAS_USE_SELECT +#ifdef CONFIG_INTERPRETERS_BAS_USE_SELECT fd_set just_infd; struct timeval timeout; #endif @@ -1427,7 +1427,7 @@ int FS_inkeyChar(int dev, int ms) return f->inBuf[f->inSize++]; } -#ifdef CONFIG_INTERPRETER_BAS_USE_SELECT +#ifdef CONFIG_INTERPRETERS_BAS_USE_SELECT FD_ZERO(&just_infd); FD_SET(f->infd, &just_infd); timeout.tv_sec = ms / 1000; diff --git a/interpreters/bas/bas_main.c b/interpreters/bas/bas_main.c index 4bb2d0f12..c6c9a70ec 100644 --- a/interpreters/bas/bas_main.c +++ b/interpreters/bas/bas_main.c @@ -84,7 +84,7 @@ int main(int argc, FAR char *argv[]) break; case 'V': - printf("bas %s\n", CONFIG_INTERPRETER_BAS_VERSION); + printf("bas %s\n", CONFIG_INTERPRETERS_BAS_VERSION); exit(0); break; @@ -129,7 +129,7 @@ int main(int argc, FAR char *argv[]) fputs(_("-u Output all tokens in uppercase\n"), stdout); fputs(_("-h Display this help and exit\n"), stdout); - fputs(_("-V Ooutput version information and exit\n"), + fputs(_("-V Output version information and exit\n"), stdout); exit(0); } diff --git a/interpreters/berry/.gitattributes b/interpreters/berry/.gitattributes new file mode 100644 index 000000000..0a83e0afa --- /dev/null +++ b/interpreters/berry/.gitattributes @@ -0,0 +1 @@ +0001-Fix-Berry-default-port-for-NuttX.patch whitespace=cr-at-eol,-blank-at-eol diff --git a/interpreters/berry/0001-Fix-Berry-default-port-for-NuttX.patch b/interpreters/berry/0001-Fix-Berry-default-port-for-NuttX.patch new file mode 100644 index 000000000..5e720d07d --- /dev/null +++ b/interpreters/berry/0001-Fix-Berry-default-port-for-NuttX.patch @@ -0,0 +1,57 @@ +diff --git a/default/berry.c b/default/berry.c +index 203773f..0df2a99 100644 +--- a/default/berry.c ++++ b/default/berry.c +@@ -19,7 +19,9 @@ + #endif + + /* detect operating system name */ +-#if defined(__linux) ++#if defined(__NuttX__) ++ #define OS_NAME "NuttX" ++#elif defined(__linux) + #define OS_NAME "Linux" + #elif defined(__unix) + #define OS_NAME "Unix" +@@ -98,7 +100,7 @@ + struct arg_opts { + int idx; + const char *pattern; +- const char *optarg; ++ const char *optvalue; + const char *errarg; + const char *src; + const char *dst; +@@ -143,10 +145,10 @@ static int arg_getopt(struct arg_opts *opt, int argc, char *argv[]) + /* the '?' indicates an optional argument after the option */ + if (++opt->idx < argc && res != NULL + && res[1] == '?' && *argv[opt->idx] != '-') { +- opt->optarg = argv[opt->idx++]; /* save the argument */ ++ opt->optvalue = argv[opt->idx++]; /* save the argument */ + return *res; + } +- opt->optarg = NULL; ++ opt->optvalue = NULL; + opt->errarg = arg; + return res != NULL ? *res : '?'; + } +@@ -267,16 +269,16 @@ static int parse_arg(struct arg_opts *opt, int argc, char *argv[]) + case 's': args |= arg_s; break; + case 'm': + args |= arg_m; +- opt->modulepath = opt->optarg; ++ opt->modulepath = opt->optvalue; + break; + case '?': return args | arg_err; + case 'c': + args |= arg_c; +- opt->src = opt->optarg; ++ opt->src = opt->optvalue; + break; + case 'o': + args |= arg_o; +- opt->dst = opt->optarg; ++ opt->dst = opt->optvalue; + break; + default: + break; diff --git a/interpreters/berry/CMakeLists.txt b/interpreters/berry/CMakeLists.txt new file mode 100644 index 000000000..d4749851b --- /dev/null +++ b/interpreters/berry/CMakeLists.txt @@ -0,0 +1,148 @@ +# ############################################################################## +# apps/interpreters/berry/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_INTERPRETERS_BERRY) + set(BERRY_COMMIT_ID 4db341475df9e9ad18e6741ef978b4c467690e09) + set(BERRY_DIR ${CMAKE_CURRENT_LIST_DIR}/berry) + + if(NOT EXISTS ${BERRY_DIR}) + set(BERRY_URL_BASE https://github.com/berry-lang/berry/archive/) + + FetchContent_Declare( + berry_fetch + URL ${BERRY_URL_BASE}/${BERRY_COMMIT_ID}.zip SOURCE_DIR + ${CMAKE_CURRENT_LIST_DIR}/berry BINARY_DIR + ${CMAKE_BINARY_DIR}/apps/interpreters/berry/berry + PATCH_COMMAND + patch -l -p1 -d ${CMAKE_CURRENT_LIST_DIR}/berry -i + ${CMAKE_CURRENT_LIST_DIR}/0001-Fix-Berry-default-port-for-NuttX.patch + DOWNLOAD_NO_PROGRESS true + TIMEOUT 30) + + FetchContent_GetProperties(berry_fetch) + + if(NOT berry_fetch_POPULATED) + FetchContent_Populate(berry_fetch) + endif() + endif() + + find_package( + Python3 + COMPONENTS Interpreter + REQUIRED) + + set(BERRY_CONFIG_DIR ${CMAKE_CURRENT_LIST_DIR}/include) + set(BERRY_CONFIG ${BERRY_CONFIG_DIR}/berry_conf.h) + set(BERRY_GENERATE ${BERRY_DIR}/generate) + set(BERRY_GEN_STAMP ${BERRY_GENERATE}/.generated) + set(BERRY_FLAGS -Wno-unused-but-set-variable -Wno-shadow) + set(BERRY_INCDIR ${BERRY_CONFIG_DIR} ${BERRY_DIR}/src ${BERRY_DIR}/default) + + set(BERRY_CSRCS + ${BERRY_DIR}/src/be_api.c + ${BERRY_DIR}/src/be_baselib.c + ${BERRY_DIR}/src/be_bytecode.c + ${BERRY_DIR}/src/be_byteslib.c + ${BERRY_DIR}/src/be_class.c + ${BERRY_DIR}/src/be_code.c + ${BERRY_DIR}/src/be_debug.c + ${BERRY_DIR}/src/be_debuglib.c + ${BERRY_DIR}/src/be_exec.c + ${BERRY_DIR}/src/be_filelib.c + ${BERRY_DIR}/src/be_func.c + ${BERRY_DIR}/src/be_gc.c + ${BERRY_DIR}/src/be_gclib.c + ${BERRY_DIR}/src/be_globallib.c + ${BERRY_DIR}/src/be_introspectlib.c + ${BERRY_DIR}/src/be_jsonlib.c + ${BERRY_DIR}/src/be_lexer.c + ${BERRY_DIR}/src/be_libs.c + ${BERRY_DIR}/src/be_list.c + ${BERRY_DIR}/src/be_listlib.c + ${BERRY_DIR}/src/be_map.c + ${BERRY_DIR}/src/be_maplib.c + ${BERRY_DIR}/src/be_mathlib.c + ${BERRY_DIR}/src/be_mem.c + ${BERRY_DIR}/src/be_module.c + ${BERRY_DIR}/src/be_object.c + ${BERRY_DIR}/src/be_oslib.c + ${BERRY_DIR}/src/be_parser.c + ${BERRY_DIR}/src/be_rangelib.c + ${BERRY_DIR}/src/be_repl.c + ${BERRY_DIR}/src/be_solidifylib.c + ${BERRY_DIR}/src/be_strictlib.c + ${BERRY_DIR}/src/be_string.c + ${BERRY_DIR}/src/be_strlib.c + ${BERRY_DIR}/src/be_syslib.c + ${BERRY_DIR}/src/be_timelib.c + ${BERRY_DIR}/src/be_undefinedlib.c + ${BERRY_DIR}/src/be_var.c + ${BERRY_DIR}/src/be_vector.c + ${BERRY_DIR}/src/be_vm.c + ${BERRY_DIR}/default/be_modtab.c + ${BERRY_DIR}/default/be_port.c) + + add_custom_command( + OUTPUT ${BERRY_GEN_STAMP} + COMMAND ${CMAKE_COMMAND} -E make_directory ${BERRY_GENERATE} + COMMAND + ${Python3_EXECUTABLE} ${BERRY_DIR}/tools/coc/coc -o ${BERRY_GENERATE} + ${BERRY_DIR}/src ${BERRY_DIR}/default -c ${BERRY_CONFIG} + COMMAND ${CMAKE_COMMAND} -E touch ${BERRY_GEN_STAMP} + DEPENDS ${BERRY_CSRCS} ${BERRY_CONFIG} + WORKING_DIRECTORY ${BERRY_DIR} + COMMENT "Generating Berry constant objects") + + add_custom_target(berry_coc DEPENDS ${BERRY_GEN_STAMP}) + + nuttx_add_library(libberry) + target_sources(libberry PRIVATE ${BERRY_CSRCS}) + target_include_directories(libberry PRIVATE ${BERRY_INCDIR}) + target_compile_options(libberry PRIVATE ${BERRY_FLAGS}) + add_dependencies(libberry berry_coc) + nuttx_export_header(TARGET libberry INCLUDE_DIRECTORIES ${BERRY_DIR}/src) + + set_property( + TARGET nuttx + APPEND + PROPERTY NUTTX_INCLUDE_DIRECTORIES ${BERRY_DIR}/src) + + nuttx_add_application( + MODULE + ${CONFIG_INTERPRETERS_BERRY} + NAME + ${CONFIG_INTERPRETERS_BERRY_PROGNAME} + STACKSIZE + ${CONFIG_INTERPRETERS_BERRY_STACKSIZE} + PRIORITY + ${CONFIG_INTERPRETERS_BERRY_PRIORITY} + SRCS + ${BERRY_DIR}/default/berry.c + INCLUDE_DIRECTORIES + ${BERRY_INCDIR} + COMPILE_FLAGS + ${BERRY_FLAGS} + DEPENDS + libberry + berry_coc) + +endif() diff --git a/interpreters/berry/Kconfig b/interpreters/berry/Kconfig new file mode 100644 index 000000000..805c61866 --- /dev/null +++ b/interpreters/berry/Kconfig @@ -0,0 +1,43 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config INTERPRETERS_BERRY + tristate "Berry scripting language" + default n + depends on ARCH_SETJMP_H + depends on LIBC_FLOATINGPOINT + depends on SYSTEM_SYSTEM + ---help--- + Enable the Berry embedded scripting language as an NSH built-in + command. Berry provides a small ANSI C99 compiler, register-based + virtual machine, garbage collector, file I/O, bytecode loading and + saving, and built-in modules. + + The default NuttX configuration keeps Berry shared-library loading + disabled so boards do not need CONFIG_LIBC_DLFCN. The math module + is enabled, so select a NuttX math library or provide one from the + toolchain. + +if INTERPRETERS_BERRY + +config INTERPRETERS_BERRY_PROGNAME + string "Berry program name" + default "berry" + ---help--- + The command name used to start the Berry interpreter. + +config INTERPRETERS_BERRY_PRIORITY + int "Berry interpreter priority" + default 100 + ---help--- + Task priority of the Berry interpreter main task. + +config INTERPRETERS_BERRY_STACKSIZE + int "Berry interpreter stack size" + default 12288 + ---help--- + Size of the stack allocated for the Berry interpreter main task. + +endif # INTERPRETERS_BERRY diff --git a/interpreters/berry/Make.defs b/interpreters/berry/Make.defs new file mode 100644 index 000000000..48a5e64ef --- /dev/null +++ b/interpreters/berry/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/interpreters/berry/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_INTERPRETERS_BERRY),) +CONFIGURED_APPS += $(APPDIR)/interpreters/berry +endif diff --git a/interpreters/berry/Makefile b/interpreters/berry/Makefile new file mode 100644 index 000000000..510a5e86a --- /dev/null +++ b/interpreters/berry/Makefile @@ -0,0 +1,98 @@ +############################################################################ +# apps/interpreters/berry/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +BERRY_COMMIT_ID = 4db341475df9e9ad18e6741ef978b4c467690e09 +BERRY_UNPACK = berry +BERRY_ARCHIVE = berry-$(BERRY_COMMIT_ID).zip +BERRY_URL_BASE = https://github.com/berry-lang/berry/archive/ +BERRY_URL = $(BERRY_URL_BASE)/$(BERRY_COMMIT_ID).zip +BERRY_CONFIG = include$(DELIM)berry_conf.h +BERRY_COC = $(BERRY_UNPACK)$(DELIM)tools$(DELIM)coc$(DELIM)coc +BERRY_GENERATE = $(BERRY_UNPACK)$(DELIM)generate +BERRY_GEN_STAMP = $(BERRY_GENERATE)$(DELIM).generated + +BERRY_SRCS = be_api.c be_baselib.c be_bytecode.c be_byteslib.c +BERRY_SRCS += be_class.c be_code.c be_debug.c be_debuglib.c be_exec.c +BERRY_SRCS += be_filelib.c be_func.c be_gc.c be_gclib.c be_globallib.c +BERRY_SRCS += be_introspectlib.c be_jsonlib.c be_lexer.c be_libs.c +BERRY_SRCS += be_list.c be_listlib.c be_map.c be_maplib.c be_mathlib.c +BERRY_SRCS += be_mem.c be_module.c be_object.c be_oslib.c be_parser.c +BERRY_SRCS += be_rangelib.c be_repl.c be_solidifylib.c be_strictlib.c +BERRY_SRCS += be_string.c be_strlib.c be_syslib.c be_timelib.c +BERRY_SRCS += be_undefinedlib.c be_var.c be_vector.c be_vm.c + +CSRCS = $(BERRY_SRCS) +CSRCS += be_modtab.c be_port.c + +MAINSRC = berry.c + +VPATH += $(BERRY_UNPACK)$(DELIM)src +VPATH += $(BERRY_UNPACK)$(DELIM)default +DEPPATH += --dep-path $(BERRY_UNPACK)$(DELIM)src +DEPPATH += --dep-path $(BERRY_UNPACK)$(DELIM)default + +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)$(DELIM)interpreters$(DELIM)berry$(DELIM)include +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)$(DELIM)interpreters$(DELIM)berry$(DELIM)$(BERRY_UNPACK)$(DELIM)src +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)$(DELIM)interpreters$(DELIM)berry$(DELIM)$(BERRY_UNPACK)$(DELIM)default +CFLAGS += -Wno-unused-but-set-variable +CFLAGS += -Wno-shadow + +PROGNAME = $(CONFIG_INTERPRETERS_BERRY_PROGNAME) +PRIORITY = $(CONFIG_INTERPRETERS_BERRY_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETERS_BERRY_STACKSIZE) +MODULE = $(CONFIG_INTERPRETERS_BERRY) + +$(BERRY_ARCHIVE): + $(Q) echo "Downloading $(BERRY_ARCHIVE)" + $(Q) curl -L $(BERRY_URL) -o $(BERRY_ARCHIVE) + +$(BERRY_UNPACK): $(BERRY_ARCHIVE) + $(Q) echo "Unpacking $(BERRY_ARCHIVE) to $(BERRY_UNPACK)" + $(Q) unzip -q -o $(BERRY_ARCHIVE) + $(call DELDIR, $(BERRY_UNPACK)) + $(Q) mv berry-$(BERRY_COMMIT_ID) $(BERRY_UNPACK) + $(Q) patch -l -d $(BERRY_UNPACK) -p1 < 0001-Fix-Berry-default-port-for-NuttX.patch + +$(BERRY_GEN_STAMP): $(BERRY_UNPACK) $(BERRY_CONFIG) + $(Q) echo "Generating Berry constant objects" + $(Q) mkdir -p $(BERRY_GENERATE) + $(Q) python3 $(BERRY_COC) -o $(BERRY_GENERATE) \ + $(BERRY_UNPACK)$(DELIM)src \ + $(BERRY_UNPACK)$(DELIM)default \ + -c $(BERRY_CONFIG) + $(Q) touch $(BERRY_GEN_STAMP) + +ifeq ($(wildcard $(BERRY_UNPACK)/.git),) +context:: $(BERRY_GEN_STAMP) +endif + +depend:: $(BERRY_GEN_STAMP) + +distclean:: +ifeq ($(wildcard $(BERRY_UNPACK)/.git),) + $(call DELDIR, $(BERRY_UNPACK)) + $(call DELFILE, $(BERRY_ARCHIVE)) +endif + +include $(APPDIR)/Application.mk diff --git a/interpreters/berry/include/berry_conf.h b/interpreters/berry/include/berry_conf.h new file mode 100644 index 000000000..633d9cdc9 --- /dev/null +++ b/interpreters/berry/include/berry_conf.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * apps/interpreters/berry/include/berry_conf.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_INTERPRETERS_BERRY_INCLUDE_BERRY_CONF_H +#define __APPS_INTERPRETERS_BERRY_INCLUDE_BERRY_CONF_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BE_DEBUG 0 +#define BE_INTGER_TYPE 2 +#define BE_USE_SINGLE_FLOAT 0 +#define BE_BYTES_MAX_SIZE (32 * 1024) +#define BE_USE_PRECOMPILED_OBJECT 1 +#define BE_DEBUG_SOURCE_FILE 1 +#define BE_DEBUG_RUNTIME_INFO 1 +#define BE_DEBUG_VAR_INFO 1 +#define BE_USE_PERF_COUNTERS 1 +#define BE_VM_OBSERVABILITY_SAMPLING 20 +#define BE_STACK_TOTAL_MAX 20000 +#define BE_STACK_FREE_MIN 10 +#define BE_STACK_START 50 +#define BE_CONST_SEARCH_SIZE 50 +#define BE_USE_STR_HASH_CACHE 0 +#define BE_USE_FILE_SYSTEM 1 +#define BE_USE_SCRIPT_COMPILER 1 +#define BE_USE_BYTECODE_SAVER 1 +#define BE_USE_BYTECODE_LOADER 1 +#define BE_USE_SHARED_LIB 0 +#define BE_USE_OVERLOAD_HASH 1 +#define BE_USE_DEBUG_HOOK 0 +#define BE_USE_DEBUG_GC 0 +#define BE_USE_DEBUG_STACK 0 +#define BE_USE_MEM_ALIGNED 0 + +#define BE_USE_STRING_MODULE 1 +#define BE_USE_JSON_MODULE 1 +#define BE_USE_MATH_MODULE 1 +#define BE_USE_TIME_MODULE 1 +#define BE_USE_OS_MODULE 1 +#define BE_USE_GLOBAL_MODULE 1 +#define BE_USE_SYS_MODULE 1 +#define BE_USE_DEBUG_MODULE 1 +#define BE_USE_GC_MODULE 1 +#define BE_USE_SOLIDIFY_MODULE 1 +#define BE_USE_INTROSPECT_MODULE 1 +#define BE_USE_STRICT_MODULE 1 + +#define BE_EXPLICIT_ABORT abort +#define BE_EXPLICIT_EXIT exit +#define BE_EXPLICIT_MALLOC malloc +#define BE_EXPLICIT_FREE free +#define BE_EXPLICIT_REALLOC realloc + +#define be_assert(expr) assert(expr) + +#endif /* __APPS_INTERPRETERS_BERRY_INCLUDE_BERRY_CONF_H */ diff --git a/interpreters/jimtcl/CMakeLists.txt b/interpreters/jimtcl/CMakeLists.txt new file mode 100644 index 000000000..ba3b64697 --- /dev/null +++ b/interpreters/jimtcl/CMakeLists.txt @@ -0,0 +1,48 @@ +if(CONFIG_INTERPRETERS_JIMTCL) + set(JIMTCL_VERSION 0.83) + set(JIMTCL_TARBALL ${JIMTCL_VERSION}.tar.gz) + set(JIMTCL_UNPACK jimtcl) + set(JIMTCL_URL_BASE https://github.com/msteveb/jimtcl/archive/refs/tags) + set(JIMTCL_URL ${JIMTCL_URL_BASE}/${JIMTCL_TARBALL}) + set(JIMTCL_SRC ${CMAKE_CURRENT_BINARY_DIR}/${JIMTCL_UNPACK}) + + # We need the source available before declaring the application, because + # jimsh.c is added as an application source, so we can not integrate download + # into the external project. + FetchContent_Declare(jimtcl_fetch URL ${JIMTCL_URL} SOURCE_DIR + ${JIMTCL_UNPACK}) + + FetchContent_MakeAvailable(jimtcl_fetch) + + ExternalProject_Add( + jimtcl_build + SOURCE_DIR ${JIMTCL_SRC} + PATCH_COMMAND sed -i -e "/^void Jim_SetEnviron/,/^/{s/^/\\/* /}" -e + "/^void Jim_SetEnviron/,/^/{s/\$/ *\\//}" jim.c + CONFIGURE_COMMAND + ./configure --host=${CMAKE_C_COMPILER_TARGET} + "CFLAGS=$, > -D$, -D> -I$, -I> -isystem ${CMAKE_SOURCE_DIR}/include -isystem ${CMAKE_BINARY_DIR}/include" + BUILD_COMMAND make libjim.a initjimsh.o + INSTALL_COMMAND "" + BUILD_IN_SOURCE TRUE + BUILD_BYPRODUCTS ${JIMTCL_SRC}/libjim.a ${JIMTCL_SRC}/_initjimsh.c) + + nuttx_add_application( + NAME + jimsh + PRIORITY + 100 + STACKSIZE + 8192 + INCLUDE_DIRECTORIES + ${JIMTCL_SRC} + DEPENDS + jimtcl_build + SRCS + ${JIMTCL_SRC}/jimsh.c + ${JIMTCL_SRC}/_initjimsh.c) + + set_property(GLOBAL APPEND PROPERTY NUTTX_EXTRA_LIBRARIES + ${JIMTCL_SRC}/libjim.a) + +endif() diff --git a/interpreters/jimtcl/Kconfig b/interpreters/jimtcl/Kconfig new file mode 100644 index 000000000..16a70db70 --- /dev/null +++ b/interpreters/jimtcl/Kconfig @@ -0,0 +1,12 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config INTERPRETERS_JIMTCL + bool "The Jim (TCL) Interpreter" + default n + ---help--- + Enable support for The Jim interpreter. + Jim is an opensource small-footprint implementation of the + Tcl programming language. \ No newline at end of file diff --git a/interpreters/jimtcl/Make.defs b/interpreters/jimtcl/Make.defs new file mode 100644 index 000000000..eb5a44b0c --- /dev/null +++ b/interpreters/jimtcl/Make.defs @@ -0,0 +1,3 @@ +ifneq ($(CONFIG_INTERPRETERS_JIMTCL),) +CONFIGURED_APPS += $(APPDIR)/interpreters/jimtcl +endif \ No newline at end of file diff --git a/interpreters/jimtcl/Makefile b/interpreters/jimtcl/Makefile new file mode 100644 index 000000000..f470167f2 --- /dev/null +++ b/interpreters/jimtcl/Makefile @@ -0,0 +1,64 @@ +include $(APPDIR)/Make.defs + +PROGNAME = jimsh +PRIORITY = 100 +STACKSIZE = 8192 + +JIMTCL_VERSION = 0.83 +JIMTCL_TARBALL = $(JIMTCL_VERSION).tar.gz +JIMTCL_UNPACK = jimtcl +JIMTCL_URL_BASE = https://github.com/msteveb/jimtcl/archive/refs/tags +JIMTCL_URL = $(JIMTCL_URL_BASE)/$(JIMTCL_TARBALL) +JIMTCL_SRC = $(JIMTCL_UNPACK) + +MAINSRC = $(JIMTCL_SRC)$(DELIM)jimsh.c + +# Obtain object list from jimtcl Makefile +JIMTCL_OBJS := $(shell test -f $(JIMTCL_SRC)$(DELIM)Makefile \ + && ( cat $(JIMTCL_SRC)$(DELIM)Makefile; \ + printf 'print-%%:\n\techo $$($$*)' ) \ + | $(MAKE) -f - -s print-OBJS) initjimsh.o + +EXTOBJS = $(addprefix $(JIMTCL_SRC)$(DELIM), $(JIMTCL_OBJS)) + +CFLAGS += -I$(JIMTCL_SRC) + +$(JIMTCL_SRC)$(DELIM)%.o: $(JIMTCL_SRC)$(DELIM)Makefile + $(Q) $(MAKE) -C $(JIMTCL_SRC) $*.o + +$(JIMTCL_SRC)$(DELIM)jimsh.c: $(JIMTCL_SRC)$(DELIM)Makefile + +$(JIMTCL_SRC)$(DELIM)_initjimsh.c: $(JIMTCL_SRC)$(DELIM)Makefile + $(Q) $(MAKE) -C $(JIMTCL_SRC) $(notdir $@) + +$(JIMTCL_SRC)$(DELIM)Makefile: + $(Q) cd $(dir $@) \ + && ./configure --host=$(patsubst %-,%,$(CROSSDEV)) \ + CFLAGS="$(CFLAGS)" + +# Get the source code and apply a small patch because environ is +# not settable in Nuttx. This is not required for basic jimtcl +# but build would fail without commenting it out. +$(JIMTCL_TARBALL): + $(Q) echo "Downloading $(JIMTCL_TARBALL)" + $(Q) curl -O -L $(JIMTCL_URL) + $(Q) echo "Unpacking $(JIMTCL_TARBALL) to $(JIMTCL_UNPACK)" + $(Q) tar -xvzf $(JIMTCL_TARBALL) + $(Q) mv jimtcl-$(JIMTCL_VERSION) $(JIMTCL_UNPACK) + $(Q) sed -i '/^void Jim_SetEnviron/,/^}/{s/^/\/* /;s/$$/ *\//}' \ + $(JIMTCL_SRC)$(DELIM)jim.c + +# Download and unpack tarball if no git repo found +ifeq ($(wildcard $(JIMTCL_UNPACK)/.git),) +context:: $(JIMTCL_TARBALL) +endif + +clean:: + $(Q) test -f $(JIMTCL_SRC)$(DELIM)Makefile \ + && $(MAKE) -C $(JIMTCL_SRC) clean + +distclean:: clean + $(call DELDIR, $(JIMTCL_UNPACK)) + $(call DELFILE, $(JIMTCL_TARBALL)) + +include $(APPDIR)/Application.mk diff --git a/interpreters/lua/CMakeLists.txt b/interpreters/lua/CMakeLists.txt index 8083a605d..863c63514 100644 --- a/interpreters/lua/CMakeLists.txt +++ b/interpreters/lua/CMakeLists.txt @@ -32,7 +32,7 @@ if(CONFIG_INTERPRETERS_LUA) set(LUA_URL https://github.com/lua/lua/archive/refs/tags/) FetchContent_Declare( lua_fetch - URL ${LUA_URL}/v${CONFIG_INTERPRETER_LUA_VERSION}.tar.gz SOURCE_DIR + URL ${LUA_URL}/v${CONFIG_INTERPRETERS_LUA_VERSION}.tar.gz SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/lua BINARY_DIR ${CMAKE_BINARY_DIR}/apps/interpreters/lua/lua DOWNLOAD_NO_PROGRESS true @@ -49,17 +49,17 @@ if(CONFIG_INTERPRETERS_LUA) # Flags # ############################################################################ - set(CFLAGS -DLUA_MAXINPUT=${CONFIG_INTERPRETER_LUA_IOBUFSIZE} + set(CFLAGS -DLUA_MAXINPUT=${CONFIG_INTERPRETERS_LUA_IOBUFSIZE} -DLUA_PROGNAME=\"lua\") - if(CONFIG_INTERPRETER_LUA_32BITS) + if(CONFIG_INTERPRETERS_LUA_32BITS) list(APPEND CFLAGS -DLUA_32BITS) endif() - if(NOT "${CONFIG_INTERPRETER_LUA_PATH}" STREQUAL "") - list(APPEND CFLAGS -DLUA_PATH_DEFAULT=\"${CONFIG_INTERPRETER_LUA_PATH}\") + if(NOT "${CONFIG_INTERPRETERS_LUA_PATH}" STREQUAL "") + list(APPEND CFLAGS -DLUA_PATH_DEFAULT=\"${CONFIG_INTERPRETERS_LUA_PATH}\") endif() - if(NOT "${CONFIG_INTERPRETER_LUA_CPATH}" STREQUAL "") - list(APPEND CFLAGS -DLUA_CPATH_DEFAULT=\"${CONFIG_INTERPRETER_LUA_CPATH}\") + if(NOT "${CONFIG_INTERPRETERS_LUA_CPATH}" STREQUAL "") + list(APPEND CFLAGS -DLUA_CPATH_DEFAULT=\"${CONFIG_INTERPRETERS_LUA_CPATH}\") endif() if(CONFIG_SYSTEM_READLINE) @@ -96,7 +96,7 @@ if(CONFIG_INTERPRETERS_LUA) ${CORELIBS_SRCS}) list(APPEND CSRCS nuttx_linit.c) - if(CONFIG_INTERPRETER_LUA_CORELIBS) + if(CONFIG_INTERPRETERS_LUA_CORELIBS) list(APPEND CSRCS ${CORELIBS_SRCS}) set(LUA_LIB_H_PATH ${LUA_DIR}/lualib.h) @@ -135,9 +135,9 @@ if(CONFIG_INTERPRETERS_LUA) NAME lua STACKSIZE - ${CONFIG_INTERPRETER_LUA_STACKSIZE} + ${CONFIG_INTERPRETERS_LUA_STACKSIZE} PRIORITY - ${CONFIG_INTERPRETER_LUA_PRIORITY} + ${CONFIG_INTERPRETERS_LUA_PRIORITY} SRCS ${LUA_DIR}/lua.c ${CSRCS} diff --git a/interpreters/lua/Kconfig b/interpreters/lua/Kconfig index 8afeb0549..418cf8472 100644 --- a/interpreters/lua/Kconfig +++ b/interpreters/lua/Kconfig @@ -10,55 +10,55 @@ menuconfig INTERPRETERS_LUA select LIBC_LOCALE ---help--- Embed Lua language interpreter. - Select the Lua version with the INTERPRETER_LUA_VERSION config. + Select the Lua version with the INTERPRETERS_LUA_VERSION config. A math library is required. Use the LIBM config or a toolchain library. It's suggested to enable the SYSTEM_READLINE and LIBC_FLOATINGPOINT configs. if INTERPRETERS_LUA -config INTERPRETER_LUA_VERSION +config INTERPRETERS_LUA_VERSION string "Lua interpreter version" default "5.4.0" ---help--- Lua release version to fetch and build. Versions 5.2.0 and up are supported. -config INTERPRETER_LUA_CORELIBS +config INTERPRETERS_LUA_CORELIBS bool "Load core Lua modules" default y select SYSTEM_SYSTEM ---help--- Load core Lua modules like "os", "string", and "table". -config INTERPRETER_LUA_PATH +config INTERPRETERS_LUA_PATH string "Lua modules search path" ---help--- Override default package.path search path for Lua modules. -config INTERPRETER_LUA_CPATH +config INTERPRETERS_LUA_CPATH string "Lua C modules search path" ---help--- Override default package.cpath search path for C modules. -config INTERPRETER_LUA_32BIT +config INTERPRETERS_LUA_32BIT bool "Use 32-bit integers and floats" default y ---help--- "Use int and float instead of long and double for Lua numbers." -config INTERPRETER_LUA_PRIORITY +config INTERPRETERS_LUA_PRIORITY int "Lua interpreter priority" default 100 ---help--- Task priority of the Lua interpreter main task. -config INTERPRETER_LUA_STACKSIZE +config INTERPRETERS_LUA_STACKSIZE int "Lua interpreter stack size" default 32768 ---help--- Size of the stack allocated for the Lua interpreter main task. -config INTERPRETER_LUA_IOBUFSIZE +config INTERPRETERS_LUA_IOBUFSIZE int "I/O buffer size" default 1024 ---help--- diff --git a/interpreters/lua/Makefile b/interpreters/lua/Makefile index 86e446667..40daf76ed 100644 --- a/interpreters/lua/Makefile +++ b/interpreters/lua/Makefile @@ -25,13 +25,13 @@ include $(APPDIR)/Make.defs # Lua built-in application info PROGNAME = lua -PRIORITY = $(CONFIG_INTERPRETER_LUA_PRIORITY) -STACKSIZE = $(CONFIG_INTERPRETER_LUA_STACKSIZE) +PRIORITY = $(CONFIG_INTERPRETERS_LUA_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETERS_LUA_STACKSIZE) MODULE = $(CONFIG_INTERPRETERS_LUA) # Lua library -LUA_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETER_LUA_VERSION))) +LUA_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETERS_LUA_VERSION))) LUA_TARBALL = v$(LUA_VERSION).tar.gz LUA_UNPACK = lua LUA_URL_BASE = https://github.com/lua/lua/archive/refs/tags/ @@ -44,19 +44,19 @@ EXCLUDE_SRCS = $(MAINSRC) $(CORELIBS_SRCS) $(LUA_SRC)$(DELIM)onelua.c $(LUA_SRC CSRCS = $(filter-out $(EXCLUDE_SRCS),$(wildcard $(LUA_SRC)$(DELIM)*.c)) CSRCS += nuttx_linit.c -CFLAGS += -DLUA_MAXINPUT=$(CONFIG_INTERPRETER_LUA_IOBUFSIZE) +CFLAGS += -DLUA_MAXINPUT=$(CONFIG_INTERPRETERS_LUA_IOBUFSIZE) CFLAGS += -DLUA_PROGNAME=\"$(PROGNAME)\" -ifeq ($(CONFIG_INTERPRETER_LUA_32BITS),y) +ifeq ($(CONFIG_INTERPRETERS_LUA_32BITS),y) CFLAGS += -DLUA_32BITS endif -ifneq ($(CONFIG_INTERPRETER_LUA_PATH),"") -CFLAGS += -DLUA_PATH_DEFAULT=\"$(CONFIG_INTERPRETER_LUA_PATH)\" +ifneq ($(CONFIG_INTERPRETERS_LUA_PATH),"") +CFLAGS += -DLUA_PATH_DEFAULT=\"$(CONFIG_INTERPRETERS_LUA_PATH)\" endif -ifneq ($(CONFIG_INTERPRETER_LUA_CPATH),"") -CFLAGS += -DLUA_CPATH_DEFAULT=\"$(CONFIG_INTERPRETER_LUA_CPATH)\" +ifneq ($(CONFIG_INTERPRETERS_LUA_CPATH),"") +CFLAGS += -DLUA_CPATH_DEFAULT=\"$(CONFIG_INTERPRETERS_LUA_CPATH)\" endif ifeq ($(CONFIG_SYSTEM_READLINE),y) @@ -83,7 +83,7 @@ endif # Register core modules -ifeq ($(CONFIG_INTERPRETER_LUA_CORELIBS),y) +ifeq ($(CONFIG_INTERPRETERS_LUA_CORELIBS),y) CSRCS += $(CORELIBS_SRCS) register:: diff --git a/interpreters/lua/nuttx_linit.c b/interpreters/lua/nuttx_linit.c index 3e47a9c99..904eb9f81 100644 --- a/interpreters/lua/nuttx_linit.c +++ b/interpreters/lua/nuttx_linit.c @@ -48,7 +48,7 @@ static const luaL_Reg g_loadedlibs[] = { -#ifdef CONFIG_INTERPRETER_LUA_CORELIBS +#ifdef CONFIG_INTERPRETERS_LUA_CORELIBS {LUA_GNAME, luaopen_base}, #endif #include "luamod_list.h" diff --git a/interpreters/minibasic/CMakeLists.txt b/interpreters/minibasic/CMakeLists.txt index 7c1e1401d..c13cef5b1 100644 --- a/interpreters/minibasic/CMakeLists.txt +++ b/interpreters/minibasic/CMakeLists.txt @@ -28,7 +28,7 @@ if(CONFIG_INTERPRETERS_MINIBASIC) script.c basic.c STACKSIZE - ${CONFIG_INTERPRETER_MINIBASIC_STACKSIZE} + ${CONFIG_INTERPRETERS_MINIBASIC_STACKSIZE} PRIORITY - ${CONFIG_INTERPRETER_MINIBASIC_PRIORITY}) + ${CONFIG_INTERPRETERS_MINIBASIC_PRIORITY}) endif() diff --git a/interpreters/minibasic/Kconfig b/interpreters/minibasic/Kconfig index bab275fc0..0d54790b5 100644 --- a/interpreters/minibasic/Kconfig +++ b/interpreters/minibasic/Kconfig @@ -33,25 +33,25 @@ config INTERPRETERS_MINIBASIC if INTERPRETERS_MINIBASIC -config INTERPRETER_MINIBASIC_PRIORITY +config INTERPRETERS_MINIBASIC_PRIORITY int "Basic interpreter priority" default 100 ---help--- Task priority of the Basic interpreter main task -config INTERPRETER_MINIBASIC_STACKSIZE +config INTERPRETERS_MINIBASIC_STACKSIZE int "Basic interpreter stack size" default 4096 ---help--- Size of the stack allocated for the Basic interpreter main task -config INTERPRETER_MINIBASIC_IOBUFSIZE +config INTERPRETERS_MINIBASIC_IOBUFSIZE int "I/O buffer size" default 1024 ---help--- Size of the statically allocated I/O buffer. -config INTERPRETER_MINIBASIC_TESTSCRIPT +config INTERPRETERS_MINIBASIC_TESTSCRIPT bool "Test script" default n ---help--- diff --git a/interpreters/minibasic/Makefile b/interpreters/minibasic/Makefile index 40f597750..61a439873 100644 --- a/interpreters/minibasic/Makefile +++ b/interpreters/minibasic/Makefile @@ -31,8 +31,8 @@ MAINSRC = script.c # Mini Basic built-in application info PROGNAME = basic -PRIORITY = $(CONFIG_INTERPRETER_MINIBASIC_PRIORITY) -STACKSIZE = $(CONFIG_INTERPRETER_MINIBASIC_STACKSIZE) +PRIORITY = $(CONFIG_INTERPRETERS_MINIBASIC_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETERS_MINIBASIC_STACKSIZE) MODULE = $(CONFIG_INTERPRETERS_MINIBASIC) include $(APPDIR)/Application.mk diff --git a/interpreters/minibasic/basic.c b/interpreters/minibasic/basic.c index 791463a30..2547f963e 100644 --- a/interpreters/minibasic/basic.c +++ b/interpreters/minibasic/basic.c @@ -22,7 +22,7 @@ /**************************************************************************** * - * This file was taken from Mini Basic, versino 1.0 developed by Malcolm + * This file was taken from Mini Basic, version 1.0 developed by Malcolm * McLean, Leeds University. Mini Basic version 1.0 was released the * Creative Commons Attribution license which, from my reading, appears to * be compatible with the NuttX license: @@ -48,11 +48,11 @@ /* Configuration */ -#ifndef CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE -# define CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE 1024 +#ifndef CONFIG_INTERPRETERS_MINIBASIC_IOBUFSIZE +# define CONFIG_INTERPRETERS_MINIBASIC_IOBUFSIZE 1024 #endif -#define IOBUFSIZE CONFIG_INTERPRETER_MINIBASIC_IOBUFSIZE +#define IOBUFSIZE CONFIG_INTERPRETERS_MINIBASIC_IOBUFSIZE /* Tokens defined */ @@ -990,7 +990,7 @@ static int doif(void) * Name: dogoto * * Description: - * The GOTO satement + * The GOTO statement * Returns new line number * ****************************************************************************/ @@ -3804,7 +3804,7 @@ static int isstring(int tokenid) * Description: * Get a numerical value from the parse string * Params: str - the string to search - * len - return pinter for no chars read + * len - return pointer for no chars read * Returns: the value of the string. * ****************************************************************************/ diff --git a/interpreters/minibasic/script.c b/interpreters/minibasic/script.c index af3b668f9..3e0212464 100644 --- a/interpreters/minibasic/script.c +++ b/interpreters/minibasic/script.c @@ -22,7 +22,7 @@ /**************************************************************************** * - * This file was taken from Mini Basic, versino 1.0 developed by Malcolm + * This file was taken from Mini Basic, version 1.0 developed by Malcolm * McLean, Leeds University. Mini Basic version 1.0 was released the * Creative Commons Attribution license which, from my reading, appears to * be compatible with the NuttX BSD-style license: @@ -45,7 +45,7 @@ * Private Data ****************************************************************************/ -#ifdef CONFIG_INTERPRETER_MINIBASIC_TESTSCRIPT +#ifdef CONFIG_INTERPRETERS_MINIBASIC_TESTSCRIPT /* Here is a simple script to play with */ static FAR char *script = @@ -147,7 +147,7 @@ int main(int argc, FAR char *argv[]) if (argc == 1) { -#ifdef CONFIG_INTERPRETER_MINIBASIC_TESTSCRIPT +#ifdef CONFIG_INTERPRETERS_MINIBASIC_TESTSCRIPT basic(script, stdin, stdout, stderr); #else fprintf(stderr, "ERROR: Missing argument.\n"); diff --git a/interpreters/mquickjs/.gitignore b/interpreters/mquickjs/.gitignore new file mode 100644 index 000000000..87708e7fd --- /dev/null +++ b/interpreters/mquickjs/.gitignore @@ -0,0 +1,2 @@ +*.d +mquickjs/ diff --git a/interpreters/mquickjs/CMakeLists.txt b/interpreters/mquickjs/CMakeLists.txt new file mode 100644 index 000000000..51d505860 --- /dev/null +++ b/interpreters/mquickjs/CMakeLists.txt @@ -0,0 +1,123 @@ +# ############################################################################## +# apps/interpreters/mquickjs/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_INTERPRETERS_MQJS) + set(MQJS_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/mquickjs) + + # Prefer local git repo if it exists, otherwise download tarball + if(EXISTS ${MQJS_SOURCE_DIR}/.git) + # Use local git-managed mquickjs + set(MQJS_DIR ${MQJS_SOURCE_DIR}) + message(STATUS "Using local mquickjs git repo: ${MQJS_DIR}") + else() + # Download mquickjs to build directory + FetchContent_Declare( + mquickjs_fetch + URL https://github.com/bellard/mquickjs/archive/refs/heads/main.zip + DOWNLOAD_NO_PROGRESS true + TIMEOUT 30) + + FetchContent_MakeAvailable(mquickjs_fetch) + + # The source directory after FetchContent_MakeAvailable + set(MQJS_DIR ${mquickjs_fetch_SOURCE_DIR}) + message(STATUS "Using downloaded mquickjs: ${MQJS_DIR}") + endif() + + # Detect target architecture for header generation + if(CONFIG_ARCH_64BIT) + set(MQJS_BUILD_FLAGS "-m64") + else() + set(MQJS_BUILD_FLAGS "-m32") + endif() + + include(ExternalProject) + + # Build mqjs_stdlib host tool using mquickjs Makefile + ExternalProject_Add( + mqjs_stdlib_host + SOURCE_DIR ${MQJS_DIR} + BINARY_DIR ${MQJS_DIR} + CONFIGURE_COMMAND "" + BUILD_COMMAND make mqjs_stdlib + INSTALL_COMMAND "" + BUILD_BYPRODUCTS ${MQJS_DIR}/mqjs_stdlib) + + # Generate mqjs_stdlib.h + add_custom_command( + OUTPUT ${MQJS_DIR}/mqjs_stdlib.h + COMMAND ${MQJS_DIR}/mqjs_stdlib ${MQJS_BUILD_FLAGS} > + ${MQJS_DIR}/mqjs_stdlib.h + DEPENDS mqjs_stdlib_host + COMMENT "Generating mqjs_stdlib.h") + + # Generate mquickjs_atom.h + add_custom_command( + OUTPUT ${MQJS_DIR}/mquickjs_atom.h + COMMAND ${MQJS_DIR}/mqjs_stdlib -a ${MQJS_BUILD_FLAGS} > + ${MQJS_DIR}/mquickjs_atom.h + DEPENDS mqjs_stdlib_host + COMMENT "Generating mquickjs_atom.h") + + # Custom target for header generation + add_custom_target(mqjs_headers_gen DEPENDS ${MQJS_DIR}/mqjs_stdlib.h + ${MQJS_DIR}/mquickjs_atom.h) + + # Create mquickjs library + nuttx_add_library(libmquickjs) + target_sources( + libmquickjs + PRIVATE ${MQJS_DIR}/mquickjs.c ${MQJS_DIR}/dtoa.c ${MQJS_DIR}/libm.c + ${MQJS_DIR}/cutils.c ${MQJS_DIR}/readline.c + ${MQJS_DIR}/readline_tty.c) + target_include_directories(libmquickjs PRIVATE ${MQJS_DIR}) + nuttx_export_header(TARGET libmquickjs INCLUDE_DIRECTORIES ${MQJS_DIR}) + + # Ensure headers are generated before building library + add_dependencies(libmquickjs mqjs_headers_gen) + + # Add mquickjs include directory to global include paths + set_property( + TARGET nuttx + APPEND + PROPERTY NUTTX_INCLUDE_DIRECTORIES ${NUTTX_APPS_DIR}/interpreters/mquickjs) + + # Create mqjs NSH application + set(MQJS_PRIORITY ${CONFIG_INTERPRETERS_MQJS_PRIORITY}) + set(MQJS_STACKSIZE ${CONFIG_INTERPRETERS_MQJS_STACKSIZE}) + + nuttx_add_application( + NAME + mqjs + PRIORITY + ${MQJS_PRIORITY} + STACKSIZE + ${MQJS_STACKSIZE} + SRCS + ${MQJS_DIR}/mqjs.c + INCLUDE_DIRECTORIES + ${MQJS_DIR} + DEPENDS + libmquickjs + mqjs_headers_gen) + +endif() diff --git a/interpreters/mquickjs/Kconfig b/interpreters/mquickjs/Kconfig new file mode 100644 index 000000000..06e8906b7 --- /dev/null +++ b/interpreters/mquickjs/Kconfig @@ -0,0 +1,24 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config INTERPRETERS_MQJS + tristate "MicroQuickJS JavaScript interpreter" + default n + +if INTERPRETERS_MQJS + +config INTERPRETERS_MQJS_PRIORITY + int "MicroQuickJS interpreter priority" + default 100 + ---help--- + Task priority of the MicroQuickJS interpreter. + + config INTERPRETERS_MQJS_STACKSIZE + int "MicroQuickJS interpreter stack size" + default 8192 + ---help--- + Size of the stack allocated for MicroQuickJS interpreter. + + endif # INTERPRETERS_MQJS diff --git a/interpreters/mquickjs/Make.defs b/interpreters/mquickjs/Make.defs new file mode 100644 index 000000000..8850fa7b6 --- /dev/null +++ b/interpreters/mquickjs/Make.defs @@ -0,0 +1,30 @@ +############################################################################ +# apps/interpreters/mquickjs/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_INTERPRETERS_MQJS),) +CONFIGURED_APPS += $(APPDIR)/interpreters/mquickjs + +# It allows `` import. +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/interpreters/mquickjs/mquickjs +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/interpreters/mquickjs/mquickjs + +endif diff --git a/interpreters/mquickjs/Makefile b/interpreters/mquickjs/Makefile new file mode 100644 index 000000000..2c1ed3e49 --- /dev/null +++ b/interpreters/mquickjs/Makefile @@ -0,0 +1,92 @@ +############################################################################ +# apps/interpreters/mquickjs/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MQJS_UNPACK = mquickjs +MQJS_ARCHIVE = mquickjs-master.zip +MQJS_URL = https://github.com/bellard/mquickjs/archive/refs/heads/master.zip + +# Determine target word size for header generation +ifeq ($(CONFIG_ARCH_64BIT),y) +MQJS_BUILD_FLAGS = -m64 +else +MQJS_BUILD_FLAGS = -m32 +endif + +CSRCS = $(MQJS_UNPACK)/mquickjs.c \ + $(MQJS_UNPACK)/dtoa.c \ + $(MQJS_UNPACK)/libm.c \ + $(MQJS_UNPACK)/cutils.c \ + $(MQJS_UNPACK)/readline.c \ + $(MQJS_UNPACK)/readline_tty.c + +PROGNAME = mqjs +PRIORITY = $(CONFIG_INTERPRETERS_MQJS_PRIORITY) +STACKSIZE = $(CONFIG_INTERPRETERS_MQJS_STACKSIZE) +MODULE = $(CONFIG_INTERPRETERS_MQJS) +MAINSRC = $(MQJS_UNPACK)/mqjs.c + +# Download archive +$(MQJS_ARCHIVE): + $(Q) echo "Downloading $(MQJS_ARCHIVE)" + $(Q) curl -L $(MQJS_URL) -o $(MQJS_ARCHIVE) + +# Unpack archive +$(MQJS_UNPACK): $(MQJS_ARCHIVE) + $(Q) echo "Unpacking $(MQJS_ARCHIVE) to $(MQJS_UNPACK)" + $(Q) unzip -q -o $(MQJS_ARCHIVE) + $(Q) mv bellard-mquickjs-* $(MQJS_UNPACK) + $(Q) touch $(MQJS_UNPACK) + +# Build host tool via upstream Makefile +$(MQJS_UNPACK)/mqjs_stdlib: + $(Q) echo "Building mquickjs host tool via upstream Makefile" + $(Q) rm -f $(MQJS_UNPACK)/mqjs_stdlib + $(Q) $(MAKE) -C $(MQJS_UNPACK) HOSTCC=$(HOSTCC) mqjs_stdlib + +# Generate stdlib header using built host tool +$(MQJS_UNPACK)/mqjs_stdlib.h: $(MQJS_UNPACK)/mqjs_stdlib + $(Q) echo "Generating mquickjs stdlib header" + $(MQJS_UNPACK)/mqjs_stdlib $(MQJS_BUILD_FLAGS) > $@ + +# Generate atom header using built host tool +$(MQJS_UNPACK)/mquickjs_atom.h: $(MQJS_UNPACK)/mqjs_stdlib + $(Q) echo "Generating mquickjs atom header" + $(MQJS_UNPACK)/mqjs_stdlib -a $(MQJS_BUILD_FLAGS) > $@ + +# Download and unpack only if not a git repository +context:: $(MQJS_UNPACK)/mqjs_stdlib.h $(MQJS_UNPACK)/mquickjs_atom.h + +# Clean up on distclean +distclean:: +ifneq ($(wildcard $(MQJS_UNPACK)/.git),) + $(Q) $(MAKE) -C $(MQJS_UNPACK) clean +else + $(call DELDIR, $(MQJS_UNPACK)) +endif + $(call DELFILE, $(MQJS_ARCHIVE)) + +# Dependencies +$(MQJS_UNPACK)/mquickjs.c: $(MQJS_UNPACK)/mquickjs_atom.h + +include $(APPDIR)/Application.mk diff --git a/interpreters/python/Kconfig b/interpreters/python/Kconfig index 947a71e6d..752bb08b5 100644 --- a/interpreters/python/Kconfig +++ b/interpreters/python/Kconfig @@ -3,9 +3,10 @@ # see the file kconfig-language.txt in the NuttX tools repository. # -config INTERPRETER_CPYTHON +config INTERPRETERS_CPYTHON tristate "CPython" depends on LIB_ZLIB + depends on !LIBC_DLFCN || LIB_LIBFFI depends on EXPERIMENTAL default n ---help--- @@ -13,28 +14,52 @@ config INTERPRETER_CPYTHON interpreter to NuttX. Initially, it is tweaked to work with the RISC-V QEMU virtual board (`rv-virt`). -if INTERPRETER_CPYTHON +if INTERPRETERS_CPYTHON -config INTERPRETER_CPYTHON_VERSION +config INTERPRETERS_CPYTHON_VERSION string "Python Version" default "3.13.0" -config INTERPRETER_CPYTHON_STACKSIZE +config INTERPRETERS_CPYTHON_STACKSIZE int "CPython stack size" default 307200 ---help--- This is the stack size allocated when the CPython task runs. -config INTERPRETER_CPYTHON_PRIORITY +config INTERPRETERS_CPYTHON_PRIORITY int "CPython task priority" default 100 ---help--- This is the priority of the CPython task. -config INTERPRETER_CPYTHON_PROGNAME +config INTERPRETERS_CPYTHON_PROGNAME string "CPython name" default "python" ---help--- This is the name of the program that will be used from the nsh. +config INTERPRETERS_CPYTHON_ENABLE_PIP + bool "Enable bundled pip" + default n + ---help--- + Enable bundling pip into the CPython module image. When enabled, the + build downloads the pip wheel and pre-installs it through a + site-packages .pth entry that points to ensurepip's bundled wheel. + Disable this to skip pip wheel download/integration entirely. + +config INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE + bool "Install NuttX Python package" + default y + ---help--- + Installs the nuttx-periphery Python package into the CPython module image. + This allows importing the nuttx-periphery package into the CPython interpreter, + which provides a Python interface to the NuttX's peripheral drivers. + +config INTERPRETERS_CPYTHON_PYTHONPATH + string "CPython Python path" + default "/tmp" + ---help--- + This is the Python default search path for modules files. This is + required to be a writable path. + endif diff --git a/interpreters/python/Make.defs b/interpreters/python/Make.defs index 39472e7a1..dceeb5267 100644 --- a/interpreters/python/Make.defs +++ b/interpreters/python/Make.defs @@ -20,13 +20,15 @@ # ############################################################################ -ifneq ($(CONFIG_INTERPRETER_CPYTHON),) +ifneq ($(CONFIG_INTERPRETERS_CPYTHON),) -CPYTHON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETER_CPYTHON_VERSION))) +CPYTHON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETERS_CPYTHON_VERSION))) CPYTHON_VERSION_MINOR=$(basename $(CPYTHON_VERSION)) EXTRA_LIBPATHS += -L$(APPDIR)/interpreters/python/install/target EXTRA_LIBS += -lpython$(CPYTHON_VERSION_MINOR) +EXTRA_LIBS += $(APPDIR)/interpreters/python/build/target/Modules/_hacl/libHacl_Hash_SHA2.a +EXTRA_LIBS += $(APPDIR)/interpreters/python/build/target/Modules/expat/libexpat.a CONFIGURED_APPS += $(APPDIR)/interpreters/python endif diff --git a/interpreters/python/Makefile b/interpreters/python/Makefile index bf20c9ae4..bac4013f8 100644 --- a/interpreters/python/Makefile +++ b/interpreters/python/Makefile @@ -23,7 +23,7 @@ include $(APPDIR)/Make.defs CPYTHON_URL ?= "https://github.com/python/cpython/archive" -CPYTHON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETER_CPYTHON_VERSION))) +CPYTHON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_INTERPRETERS_CPYTHON_VERSION))) CPYTHON_VERSION_MINOR=$(basename $(CPYTHON_VERSION)) CPYTHON_ZIP = v$(CPYTHON_VERSION).zip @@ -56,7 +56,7 @@ CFLAGS += -Wno-shadow -Wno-undef -Wno-format -Wno-builtin-macro-redefined CFLAGS += -Wno-type-limits -Wno-implicit-fallthrough -Wno-char-subscripts CFLAGS += -Wno-sign-compare -Wno-unused-const-variable -Wno-unused-function CFLAGS += -Wno-unused-variable -Wno-overflow -Wno-unused-but-set-variable -CFLAGS += -Wno-strict-prototypes -nostdlib +CFLAGS += -Wno-strict-prototypes -Wno-maybe-uninitialized -nostdlib DEPPATH += --dep-path $(CPYTHON_UNPACKNAME)$(DELIM)Programs VPATH += :$(CPYTHON_UNPACKNAME)$(DELIM)Programs @@ -84,6 +84,13 @@ $(CPYTHON_UNPACKNAME): $(CPYTHON_ZIP) $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0012-hack-place-_PyRuntime-structure-into-PSRAM-bss-regio.patch $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0013-transform-functions-used-by-NuttX-to-lowercase.patch $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0014-insert-prefix-to-list_length-to-avoid-symbol-collisi.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0015-keep-ensurepip-in-stdlib-archive.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0016-fix-timezone-offset-check-when-time-t-is-unsigned.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0017-stdlib-zip-keep-pydecimal-and-trim-tooling-extras.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0018-ignore-chmod-on-nuttx-like-wasi.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0019-undef-get_errno-set_errno-to-avoid-nuttx-macro-coll.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0020-ctypes-skip-pythonapi-on-nuttx.patch + $(Q) patch -p1 -d $(CPYTHON_UNPACKNAME) < patch$(DELIM)0021-posixmodule-ignore-utime-enosys-on-nuttx.patch $(HOSTPYTHON): mkdir -p $(HOSTBUILD) @@ -92,6 +99,7 @@ $(HOSTPYTHON): cd $(HOSTBUILD) && $(CPYTHON_PATH)/configure \ --with-pydebug \ --prefix=$(HOSTINSTALL) \ + --disable-test-modules \ ) $(MAKE) -C $(HOSTBUILD) install @@ -111,6 +119,13 @@ ifeq ($(CONFIG_SYSTEM_SYSTEM),y) else @echo "export ac_cv_func_system=\"no\"" >> $@ endif +ifeq ($(CONFIG_NET),y) + @echo "export ac_cv_func_getaddrinfo=\"yes\"" >> $@ + @echo "export ac_cv_func_getnameinfo=\"yes\"" >> $@ +else + @echo "export ac_cv_func_getaddrinfo=\"no\"" >> $@ + @echo "export ac_cv_func_getnameinfo=\"no\"" >> $@ +endif # The `Setup.local` file enables or disables Python modules. # Depending on the features enabled on NuttX, this file may need to be @@ -123,6 +138,12 @@ $(SETUP_LOCAL): ifneq ($(CONFIG_ARCH_HAVE_FORK),y) @echo "_posixsubprocess" >> $@ endif +ifneq ($(CONFIG_LIBC_DLFCN),y) + @echo "_ctypes" >> $@ +endif +ifneq ($(CONFIG_NET),y) + @echo "_socket" >> $@ +endif # For the Python's `configure` script, please consider the following # when building for NuttX: @@ -135,6 +156,12 @@ endif # Python/Modules/getpath.c (issue will be filed soon to track this # problem). +ifneq ($(CONFIG_NET),y) +PYTHON_CONFIGURE_EXTRAS = --disable-ipv6 +else +PYTHON_CONFIGURE_EXTRAS = +endif + $(TARGETBUILD)/Makefile: $(HOSTPYTHON) $(CONFIG_SITE) $(SETUP_LOCAL) $(Q) mkdir -p $(TARGETBUILD)/Modules $(Q) mkdir -p $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR) @@ -152,7 +179,7 @@ $(TARGETBUILD)/Makefile: $(HOSTPYTHON) $(CONFIG_SITE) $(SETUP_LOCAL) AR="$(AR)" \ ARFLAGS=" " \ MACHDEP="$(MACHDEP)" \ - OPT="-g -O0 -Wall" \ + OPT="-Os" \ CONFIG_SITE="$(CONFIG_SITE)" \ $(CPYTHON_PATH)/configure \ --prefix=${TARGETINSTALL} \ @@ -163,19 +190,71 @@ $(TARGETBUILD)/Makefile: $(HOSTPYTHON) $(CONFIG_SITE) $(SETUP_LOCAL) --without-mimalloc \ --without-pymalloc \ --disable-test-modules \ + --with-ensurepip=no \ + $(PYTHON_CONFIGURE_EXTRAS) \ ) + $(Q) sed -i 's/^#define HAVE_LIBB2 1/\/* #undef HAVE_LIBB2 *\//' $(TARGETBUILD)/pyconfig.h + $(Q) sed -i 's/-lb2//g' $(TARGETBUILD)/Makefile + +BUNDLED_WHEELS_DIR = $(CPYTHON_PATH)/Lib/ensurepip/_bundled +# PyPI downloads use the host system pip, not $(HOSTPYTHON)'s ensurepip bundle: +# pip 24.x vendors urllib3 that breaks on Python 3.13 (zlib.Decompress.unused_data). +HOSTPIP = python3 -m pip $(TARGETLIBPYTHON): $(TARGETBUILD)/Makefile +ifneq ($(or $(filter y,$(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP)),$(filter y,$(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE))),) + $(Q) mkdir -p $(BUNDLED_WHEELS_DIR) +endif +ifeq ($(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP),y) + $(Q) ( \ + PIP_WHEEL_VERSION=$$($(HOSTPYTHON) -c "import ensurepip; print(ensurepip._PIP_VERSION)"); \ + PIP_WHEEL=$(BUNDLED_WHEELS_DIR)/pip-$${PIP_WHEEL_VERSION}-py3-none-any.whl; \ + if [ ! -f "$${PIP_WHEEL}" ]; then \ + echo "Fetching pip wheel $${PIP_WHEEL_VERSION} for ensurepip bundle"; \ + $(HOSTPIP) download --only-binary=:all: --no-deps --dest $(BUNDLED_WHEELS_DIR) pip==$${PIP_WHEEL_VERSION}; \ + fi; \ + echo "Pre-compiling pip wheel with build Python (must match embedded CPython version)"; \ + $(HOSTPYTHON) $(CURDIR)/repack_wheel_add_pyc.py --package pip "$${PIP_WHEEL}"; \ + ) +endif +ifeq ($(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE),y) + $(Q) ( \ + set -e; \ + NUTTX_WHEEL=$$(ls $(BUNDLED_WHEELS_DIR)/nuttx_periphery-*-py3-none-any.whl 2>/dev/null | head -1 || true); \ + if [ -z "$${NUTTX_WHEEL}" ]; then \ + echo "Fetching latest nuttx-periphery wheel for ensurepip bundle"; \ + $(HOSTPIP) download --only-binary=:all: --no-deps --dest $(BUNDLED_WHEELS_DIR) nuttx-periphery; \ + NUTTX_WHEEL=$$(ls $(BUNDLED_WHEELS_DIR)/nuttx_periphery-*-py3-none-any.whl); \ + fi; \ + echo "Pre-compiling nuttx-periphery wheel with build Python (must match embedded CPython version)"; \ + $(HOSTPYTHON) $(CURDIR)/repack_wheel_add_pyc.py --package nuttx_periphery "$${NUTTX_WHEEL}"; \ + ) +endif $(MAKE) -C $(TARGETBUILD) regen-frozen $(MAKE) -C $(TARGETBUILD) libpython$(CPYTHON_VERSION_MINOR).a wasm_stdlib $(Q) ( cp $(TARGETBUILD)/libpython$(CPYTHON_VERSION_MINOR).a $(TARGETLIBPYTHON) ) $(Q) $(UNPACK) $(TARGETMODULESPACK) -d $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR) +ifneq ($(or $(filter y,$(CONFIG_INTERPRETERS_CPYTHON_ENABLE_PIP)),$(filter y,$(CONFIG_INTERPRETERS_CPYTHON_INSTALL_NUTTX_PACKAGE))),) + $(Q) mkdir -p $(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)/site-packages + $(Q) ( \ + set -e; \ + BUNDLED_DIR=$(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)/ensurepip/_bundled; \ + SITE_PACKAGES=$(TARGETMODULES)/python$(CPYTHON_VERSION_MINOR)/site-packages; \ + : > "$${SITE_PACKAGES}/bundled_wheels.pth"; \ + for wheel in $${BUNDLED_DIR}/*.whl; do \ + [ -f "$${wheel}" ] || continue; \ + whl_name=$$(basename "$${wheel}"); \ + echo "Pre-installing wheel via zipimport into target sys.path: $${whl_name}"; \ + echo "../ensurepip/_bundled/$${whl_name}" >> "$${SITE_PACKAGES}/bundled_wheels.pth"; \ + done; \ + ) +endif -MODULE = $(CONFIG_INTERPRETER_CPYTHON) +MODULE = $(CONFIG_INTERPRETERS_CPYTHON) -PROGNAME += $(CONFIG_INTERPRETER_CPYTHON_PROGNAME) -PRIORITY += $(CONFIG_INTERPRETER_CPYTHON_PRIORITY) -STACKSIZE += $(CONFIG_INTERPRETER_CPYTHON_STACKSIZE) +PROGNAME += $(CONFIG_INTERPRETERS_CPYTHON_PROGNAME) +PRIORITY += $(CONFIG_INTERPRETERS_CPYTHON_PRIORITY) +STACKSIZE += $(CONFIG_INTERPRETERS_CPYTHON_STACKSIZE) MAINSRC += python_wrapper.c diff --git a/interpreters/python/Setup.local.in b/interpreters/python/Setup.local.in index 5ff7cd3fa..d8c27292b 100644 --- a/interpreters/python/Setup.local.in +++ b/interpreters/python/Setup.local.in @@ -4,7 +4,6 @@ *disabled* _asyncio -_blake2 _bz2 _codecs_cn _codecs_hk @@ -12,22 +11,14 @@ _codecs_iso2022 _codecs_jp _codecs_kr _codecs_tw -_ctypes _decimal _elementtree -_hashlib _heapq _interpchannels _interpqueues _lsprof _lzma -_md5 _multibytecodec -_sha1 -_sha2 -_sha2 -_sha3 -_sha3 _sqlite3 _ssl _statistics @@ -41,9 +32,7 @@ _testlimitedcapi _uuid _xxtestfuzz _zoneinfo -mmap pwd -pyexpat readline resource xxsubtype diff --git a/interpreters/python/config.site.in b/interpreters/python/config.site.in index eb37e5a88..d19337ea6 100644 --- a/interpreters/python/config.site.in +++ b/interpreters/python/config.site.in @@ -21,4 +21,17 @@ export ac_cv_func_pipe="yes" export ac_cv_enable_strict_prototypes_warning="no" export ac_cv_func_getnameinfo="yes" export ac_cv_func_poll="yes" -export ac_cv_func_gethostname="yes" \ No newline at end of file +export ac_cv_func_gethostname="yes" +export ac_cv_func_lstat="yes" +export ac_cv_func_readlink="yes" +export ac_cv_func_realpath="yes" +export ac_cv_func_getpid="yes" +export ac_cv_func_utime="yes" +export ac_cv_func_utimes="yes" +export ac_cv_func_getuid="yes" +export ac_cv_func_sysconf="yes" +export ac_cv_func_umask="yes" +export ac_cv_func_ffi_prep_cif_var="yes" +export ac_cv_func_ffi_prep_closure_loc="yes" +export ac_cv_func_ffi_closure_alloc="yes" +export ac_cv_func_utimes="no" diff --git a/interpreters/python/patch/0015-keep-ensurepip-in-stdlib-archive.patch b/interpreters/python/patch/0015-keep-ensurepip-in-stdlib-archive.patch new file mode 100644 index 000000000..a74e892ef --- /dev/null +++ b/interpreters/python/patch/0015-keep-ensurepip-in-stdlib-archive.patch @@ -0,0 +1,25 @@ +--- a/Tools/wasm/wasm_assets.py ++++ b/Tools/wasm/wasm_assets.py +@@ -40,7 +40,6 @@ OMIT_FILES = ( + # regression tests + "test/", + # package management +- "ensurepip/", + "venv/", + # other platforms + "_aix_support.py", +@@ -148,6 +147,13 @@ def create_stdlib_zip( + if entry.name.endswith(".py") or entry.is_dir(): + # writepy() writes .pyc files (bytecode). + pzf.writepy(entry, filterfunc=filterfunc) ++ ++ # Preserve ensurepip wheel payloads so `python -m ensurepip` can ++ # bootstrap pip on targets that consume this stdlib zip archive. ++ bundled_wheels = args.srcdir_lib / "ensurepip" / "_bundled" ++ if bundled_wheels.is_dir(): ++ for wheel in sorted(bundled_wheels.glob("*.whl")): ++ pzf.write(wheel, arcname=f"ensurepip/_bundled/{wheel.name}") + + + def detect_extension_modules(args: argparse.Namespace) -> Dict[str, bool]: + diff --git a/interpreters/python/patch/0016-fix-timezone-offset-check-when-time-t-is-unsigned.patch b/interpreters/python/patch/0016-fix-timezone-offset-check-when-time-t-is-unsigned.patch new file mode 100644 index 000000000..81924297b --- /dev/null +++ b/interpreters/python/patch/0016-fix-timezone-offset-check-when-time-t-is-unsigned.patch @@ -0,0 +1,21 @@ +--- a/Modules/timemodule.c ++++ b/Modules/timemodule.c +@@ -1800,15 +1800,15 @@ static int + static const time_t YEAR = (365 * 24 + 6) * 3600; + time_t t; + struct tm p; +- time_t janzone_t, julyzone_t; ++ long long janzone_t, julyzone_t; + char janname[10], julyname[10]; + t = (time((time_t *)0) / YEAR) * YEAR; + _PyTime_localtime(t, &p); + get_zone(janname, 9, &p); +- janzone_t = -get_gmtoff(t, &p); ++ janzone_t = -(long long)get_gmtoff(t, &p); + janname[9] = '\0'; + t += YEAR/2; + _PyTime_localtime(t, &p); + get_zone(julyname, 9, &p); +- julyzone_t = -get_gmtoff(t, &p); ++ julyzone_t = -(long long)get_gmtoff(t, &p); + julyname[9] = '\0'; diff --git a/interpreters/python/patch/0017-stdlib-zip-keep-pydecimal-and-trim-tooling-extras.patch b/interpreters/python/patch/0017-stdlib-zip-keep-pydecimal-and-trim-tooling-extras.patch new file mode 100644 index 000000000..333bb983a --- /dev/null +++ b/interpreters/python/patch/0017-stdlib-zip-keep-pydecimal-and-trim-tooling-extras.patch @@ -0,0 +1,35 @@ +--- a/Tools/wasm/wasm_assets.py ++++ b/Tools/wasm/wasm_assets.py +@@ -47,13 +47,20 @@ + # webbrowser + "antigravity.py", + "webbrowser.py", +- # Pure Python implementations of C extensions +- "_pydecimal.py", ++ # Pure Python implementations of C extensions. ++ # NOTE: keep "_pydecimal.py" so decimal.py can fall back to it when the ++ # _decimal C extension is not built (NuttX targets do not link libmpdec). + "_pyio.py", + # concurrent threading + "concurrent/futures/thread.py", + # Misc unused or large files + "pydoc_data/", ++ # Tooling/REPL extras not needed on a constrained embedded target. ++ "unittest/", ++ "_pyrepl/", ++ "idlelib/", ++ "turtledemo/", ++ "wsgiref/", + ) + + # Synchronous network I/O and protocols are not supported; for example, +@@ -80,7 +87,8 @@ + "_asyncio": ["asyncio/"], + "_curses": ["curses/"], + "_ctypes": ["ctypes/"], +- "_decimal": ["decimal.py"], ++ # decimal.py is intentionally NOT omitted here: it ships a pure-Python ++ # fallback (_pydecimal) used when the _decimal C ext is unavailable. + "_dbm": ["dbm/ndbm.py"], + "_gdbm": ["dbm/gnu.py"], + "_json": ["json/"], diff --git a/interpreters/python/patch/0018-ignore-chmod-on-nuttx-like-wasi.patch b/interpreters/python/patch/0018-ignore-chmod-on-nuttx-like-wasi.patch new file mode 100644 index 000000000..4e2afb410 --- /dev/null +++ b/interpreters/python/patch/0018-ignore-chmod-on-nuttx-like-wasi.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tiago Medicci +Date: Thu, 7 May 2026 14:00:00 -0300 +Subject: [PATCH] posixmodule: ignore chmod on NuttX like WASI + +NuttX's tmpfs does not implement chstat, so chmod fails with ENOSYS. +Apply the same workaround already used for WASI: silently succeed +when HAVE_CHMOD is not defined. + +--- + Modules/posixmodule.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -3608,8 +3608,8 @@ + #ifdef HAVE_CHMOD + result = chmod(path->narrow, mode); +-#elif defined(__wasi__) +- // WASI SDK 15.0 does not support chmod. ++#elif defined(__wasi__) || defined(__NuttX__) ++ // WASI SDK 15.0 and NuttX do not fully support chmod. + // Ignore missing syscall for now. + result = 0; + #else diff --git a/interpreters/python/patch/0019-undef-get_errno-set_errno-to-avoid-nuttx-macro-coll.patch b/interpreters/python/patch/0019-undef-get_errno-set_errno-to-avoid-nuttx-macro-coll.patch new file mode 100644 index 000000000..905827466 --- /dev/null +++ b/interpreters/python/patch/0019-undef-get_errno-set_errno-to-avoid-nuttx-macro-coll.patch @@ -0,0 +1,17 @@ +--- a/Modules/_ctypes/callproc.c ++++ b/Modules/_ctypes/callproc.c +@@ -91,6 +91,14 @@ + #include + #endif + ++/* NuttX errno.h defines get_errno() and set_errno(e) as macros. ++ Undef them so that CPython's static functions of the same name ++ in this file compile without conflict. */ ++#ifdef __NuttX__ ++#undef get_errno ++#undef set_errno ++#endif ++ + #ifdef _Py_MEMORY_SANITIZER + #include + #endif diff --git a/interpreters/python/patch/0020-ctypes-skip-pythonapi-on-nuttx.patch b/interpreters/python/patch/0020-ctypes-skip-pythonapi-on-nuttx.patch new file mode 100644 index 000000000..99ca460d9 --- /dev/null +++ b/interpreters/python/patch/0020-ctypes-skip-pythonapi-on-nuttx.patch @@ -0,0 +1,11 @@ +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -481,6 +481,8 @@ + pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2]) + elif _sys.platform == "cygwin": + pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) ++elif _sys.platform == "nuttx": ++ pythonapi = None + else: + pythonapi = PyDLL(None) + diff --git a/interpreters/python/patch/0021-posixmodule-ignore-utime-enosys-on-nuttx.patch b/interpreters/python/patch/0021-posixmodule-ignore-utime-enosys-on-nuttx.patch new file mode 100644 index 000000000..664d0f174 --- /dev/null +++ b/interpreters/python/patch/0021-posixmodule-ignore-utime-enosys-on-nuttx.patch @@ -0,0 +1,16 @@ +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -6666,6 +6666,13 @@ + } + #endif + ++#if defined(__NuttX__) ++ if (result == -1 && errno == ENOSYS) { ++ /* SmartFS and other NuttX fs do not implement chstat. */ ++ Py_RETURN_NONE; ++ } ++#endif ++ + if (result < 0) { + path_error(path); + return NULL; diff --git a/interpreters/python/python_wrapper.c b/interpreters/python/python_wrapper.c index 1eb1d3cc3..be7454514 100644 --- a/interpreters/python/python_wrapper.c +++ b/interpreters/python/python_wrapper.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include @@ -198,5 +198,7 @@ int main(int argc, FAR char *argv[]) setenv("PYTHON_BASIC_REPL", "1", 1); + setenv("PYTHONPATH", CONFIG_INTERPRETERS_CPYTHON_PYTHONPATH, 1); + return py_bytesmain(argc, argv); } diff --git a/interpreters/python/repack_wheel_add_pyc.py b/interpreters/python/repack_wheel_add_pyc.py new file mode 100644 index 000000000..ba27f7d07 --- /dev/null +++ b/interpreters/python/repack_wheel_add_pyc.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# +# Repack a wheel in-place: compile package/*.py to legacy sibling *.pyc +# (compileall -b: required for zipimport, which does not read PEP 3147 +# __pycache__/ names), remove the .py sources, and rewrite *.dist-info/RECORD. + +from __future__ import annotations + +import argparse +import base64 +import hashlib +import shutil +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path + + +def wheel_record_hash(data: bytes) -> str: + digest = hashlib.sha256(data).digest() + return base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=") + + +def wheel_has_py_sources(zf: zipfile.ZipFile, package: str) -> bool: + prefix = f"{package}/" + return any(n.startswith(prefix) and n.endswith(".py") for n in zf.namelist()) + + +def wheel_has_legacy_bytecode(zf: zipfile.ZipFile, package: str) -> bool: + return f"{package}/__init__.pyc" in zf.namelist() + + +def strip_py_sources(pkg_dir: Path, package: str) -> int: + """Remove package/**/*.py after sibling legacy *.pyc exists (compileall -b output).""" + removed = 0 + for path in sorted(pkg_dir.rglob("*.py")): + if not path.is_file(): + continue + pyc = path.with_suffix(".pyc") + if not pyc.is_file(): + rel = path.relative_to(pkg_dir) + raise SystemExit( + f"missing legacy .pyc for {package}/{rel.as_posix()}, " + "refusing to delete source" + ) + path.unlink() + removed += 1 + return removed + + +def rebuild_record(root: Path) -> None: + dist_infos = sorted(root.glob("*.dist-info")) + if len(dist_infos) != 1: + raise SystemExit( + f"expected one *.dist-info, got {[p.name for p in dist_infos]}" + ) + di = dist_infos[0] + record_path = di / "RECORD" + record_rel = f"{di.name}/RECORD" + lines: list[str] = [] + for path in sorted(root.rglob("*")): + if not path.is_file(): + continue + rel = path.relative_to(root).as_posix() + if rel == record_rel: + continue + body = path.read_bytes() + lines.append(f"{rel},sha256={wheel_record_hash(body)},{len(body)}") + lines.append(f"{record_rel},,") + record_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def repack(whl_path: Path, *, package: str, force: bool) -> None: + whl_path = whl_path.resolve() + if not whl_path.is_file(): + raise SystemExit(f"missing wheel: {whl_path}") + + with zipfile.ZipFile(whl_path) as zf: + has_py = wheel_has_py_sources(zf, package) + if not has_py: + if not wheel_has_legacy_bytecode(zf, package): + raise SystemExit( + f"repack_wheel_add_pyc: wheel has no {package}/*.py and no " + f"{package}/__init__.pyc (corrupt or old tool output). " + f"Delete the bundled wheel and rebuild." + ) + if not force: + print( + f"repack_wheel_add_pyc: skip ({package} already bytecode-only): " + f"{whl_path.name}" + ) + return + + tmpdir = tempfile.mkdtemp(prefix=f"{package}-whl-pyc-") + try: + root = Path(tmpdir) + with zipfile.ZipFile(whl_path) as zf: + zf.extractall(root) + + pkg_dir = root / package + if not pkg_dir.is_dir(): + raise SystemExit(f"wheel has no {package}/ top-level package") + + subprocess.run( + [sys.executable, "-m", "compileall", "-q", "-f", "-b", str(pkg_dir)], + cwd=str(root), + check=True, + ) + n_py = strip_py_sources(pkg_dir, package) + rebuild_record(root) + + out_path = whl_path.with_suffix(whl_path.suffix + ".tmp") + with zipfile.ZipFile(out_path, "w", compression=zipfile.ZIP_DEFLATED) as out: + for path in sorted(root.rglob("*")): + if path.is_file(): + arcname = path.relative_to(root).as_posix() + out.write(path, arcname) + + out_path.replace(whl_path) + print( + f"repack_wheel_add_pyc: bytecode-only {package} ({n_py} .py removed) -> " + f"{whl_path.name}" + ) + finally: + shutil.rmtree(tmpdir, ignore_errors=True) + + +def main() -> None: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("wheel", type=Path, help="path to .whl (updated in place)") + ap.add_argument( + "-p", + "--package", + default="pip", + help="top-level package directory inside the wheel (default: pip)", + ) + ap.add_argument( + "-f", + "--force", + action="store_true", + help="repack even if the package is already .pyc-only", + ) + args = ap.parse_args() + repack(args.wheel, package=args.package, force=args.force) + + +if __name__ == "__main__": + main() diff --git a/interpreters/quickjs/Kconfig b/interpreters/quickjs/Kconfig index cc10f7799..43a31c055 100644 --- a/interpreters/quickjs/Kconfig +++ b/interpreters/quickjs/Kconfig @@ -14,7 +14,7 @@ choice default INTERPRETERS_QUICKJS_NONE config INTERPRETERS_QUICKJS_NONE - bool "distable quickjs interpreter" + bool "Disable quickjs interpreter" config INTERPRETERS_QUICKJS_MINI bool "Minimal quickjs interpreter" @@ -40,11 +40,11 @@ config INTERPRETERS_QUICKJS_STACKSIZE default 8192 config INTERPRETERS_QUICKJS_EXT_HOOK - bool "External init/destory hook" + bool "External init/destroy hook" default n depends on INTERPRETERS_QUICKJS_MINI ---help--- Since minimal interpreter only support 'console.log', - you can export custom module by implement init/destory hook. + you can export custom module by implement init/destroy hook. endif # INTERPRETERS_QUICKJS diff --git a/interpreters/quickjs/Makefile b/interpreters/quickjs/Makefile index 7b84a08bb..457cdee35 100644 --- a/interpreters/quickjs/Makefile +++ b/interpreters/quickjs/Makefile @@ -35,6 +35,8 @@ VERSION=\"$(QUICKJS_VERSION)\" CFLAGS += -Dmp_add=qjs_mp_add -Dmp_sub=qjs_mp_sub -Dmp_mul=qjs_mp_mul CFLAGS += -DCONFIG_VERSION=$(VERSION) -Wno-shadow CFLAGS += -Wno-array-bounds -I$(QUICKJS_UNPACK) +CFLAGS += -Wno-strict-prototypes -Wno-unused-variable +CFLAGS += -Wno-unused-but-set-variable CFLAGS += -D__linux__ -include alloca.h CFLAGS += -Wno-incompatible-pointer-types CFLAGS += -Wno-implicit-function-declaration diff --git a/logging/embedlog/Kconfig b/logging/embedlog/Kconfig index c6cad947c..ce5dd19d3 100644 --- a/logging/embedlog/Kconfig +++ b/logging/embedlog/Kconfig @@ -189,7 +189,7 @@ config EMBEDLOG_LOG_MAX int "Max length of log message" default 128 ---help--- - Maximum length of single log message. This defines length of finall + Maximum length of single log message. This defines length of final message, so message "foo() returned %s" may consume for example 200 bytes since '%s' may be a long string. Metadata like timestamp or file info uses this space too. Output log will be truncated if @@ -205,7 +205,7 @@ config EMBEDLOG_MEM_LINE_SIZE for more information about this. config EMBEDLOG_DEMO_PROGRAMS - bool "Compile demo programs" + tristate "Compile demo programs" default n ---help--- Compile demo programs that visualise how embedlog works. Also good diff --git a/logging/embedlog/Makefile b/logging/embedlog/Makefile index ef7b306f7..45d499bc0 100644 --- a/logging/embedlog/Makefile +++ b/logging/embedlog/Makefile @@ -168,10 +168,11 @@ CFLAGS += -DENABLE_OUT_NET=0 # nuttx does not implement clock() function CFLAGS += -DENABLE_CLOCK=0 -ifeq ($(CONFIG_EMBEDLOG_DEMO_PROGRAMS),y) +ifneq ($(CONFIG_EMBEDLOG_DEMO_PROGRAMS),) # build demo programs as library instead of standalone programs CFLAGS += -DEMBEDLOG_DEMO_LIBRARY + MODULE = $(CONFIG_EMBEDLOG_DEMO_PROGRAMS) PROGNAME = el_demo_print_memory PRIORITY = $(CONFIG_EMBEDLOG_DEMO_PROGRAMS_PRIORITY) STACKSIZE = $(CONFIG_EMBEDLOG_DEMO_PROGRAMS_STACKSIZE) diff --git a/logging/nxscope/CMakeLists.txt b/logging/nxscope/CMakeLists.txt index 378ebefd5..98594accd 100644 --- a/logging/nxscope/CMakeLists.txt +++ b/logging/nxscope/CMakeLists.txt @@ -27,6 +27,10 @@ if(CONFIG_LOGGING_NXSCOPE) list(APPEND CSRCS nxscope_iser.c) endif() + if(CONFIG_LOGGING_NXSCOPE_INTF_UDP) + list(APPEND CSRCS nxscope_iudp.c) + endif() + if(CONFIG_LOGGING_NXSCOPE_INTF_DUMMY) list(APPEND CSRCS nxscope_idummy.c) endif() diff --git a/logging/nxscope/Kconfig b/logging/nxscope/Kconfig index 3649b25e9..bb187ea43 100644 --- a/logging/nxscope/Kconfig +++ b/logging/nxscope/Kconfig @@ -15,6 +15,12 @@ config LOGGING_NXSCOPE_INTF_SERIAL ---help--- For details, see logging/nxscope/nxscope_iserial.c +config LOGGING_NXSCOPE_INTF_UDP + bool "NxScope UDP interface support" + default n + ---help--- + For details, see logging/nxscope/nxscope_iudp.c + config LOGGING_NXSCOPE_INTF_DUMMY bool "NxScope dummy interface support" default n diff --git a/logging/nxscope/Makefile b/logging/nxscope/Makefile index 3e45148fc..c8fc05489 100644 --- a/logging/nxscope/Makefile +++ b/logging/nxscope/Makefile @@ -34,6 +34,10 @@ ifeq ($(CONFIG_LOGGING_NXSCOPE_INTF_DUMMY),y) CSRCS += nxscope_idummy.c endif +ifeq ($(CONFIG_LOGGING_NXSCOPE_INTF_UDP),y) +CSRCS += nxscope_iudp.c +endif + ifeq ($(CONFIG_LOGGING_NXSCOPE_PROTO_SER),y) CSRCS += nxscope_pser.c endif diff --git a/logging/nxscope/nxscope.c b/logging/nxscope/nxscope.c index f919bf62c..094395dd8 100644 --- a/logging/nxscope/nxscope.c +++ b/logging/nxscope/nxscope.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -150,7 +150,7 @@ static int nxscope_chinfo_send(FAR struct nxscope_s *s, uint8_t ch) namelen = strnlen(s->chinfo[ch].name, CHAN_NAMELEN_MAX); memcpy(&data[tmp], s->chinfo[ch].name, namelen); - /* Treminate name wit a null-character */ + /* Terminate name with a null character */ txlen = tmp + namelen + 1; data[txlen - 1] = '\0'; diff --git a/logging/nxscope/nxscope_chan.c b/logging/nxscope/nxscope_chan.c index 10c30ad25..5fb9e9b92 100644 --- a/logging/nxscope/nxscope_chan.c +++ b/logging/nxscope/nxscope_chan.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -332,7 +332,6 @@ static int nxscope_put_vector(FAR uint8_t *buff, uint8_t type, FAR void *val, break; } -#ifdef CONFIG_HAVE_LONG_LONG case NXSCOPE_TYPE_UINT64: case NXSCOPE_TYPE_INT64: case NXSCOPE_TYPE_DOUBLE: @@ -358,7 +357,6 @@ static int nxscope_put_vector(FAR uint8_t *buff, uint8_t type, FAR void *val, break; } -#endif case NXSCOPE_TYPE_CHAR: { diff --git a/logging/nxscope/nxscope_idummy.c b/logging/nxscope/nxscope_idummy.c index 8615748af..f139ce4dd 100644 --- a/logging/nxscope/nxscope_idummy.c +++ b/logging/nxscope/nxscope_idummy.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include diff --git a/logging/nxscope/nxscope_internals.c b/logging/nxscope/nxscope_internals.c index 43ffdd610..b001b4187 100644 --- a/logging/nxscope/nxscope_internals.c +++ b/logging/nxscope/nxscope_internals.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include diff --git a/logging/nxscope/nxscope_iser.c b/logging/nxscope/nxscope_iser.c index b1722042b..492617723 100644 --- a/logging/nxscope/nxscope_iser.c +++ b/logging/nxscope/nxscope_iser.c @@ -26,7 +26,7 @@ #include -#include +#include #include #include #include diff --git a/logging/nxscope/nxscope_iudp.c b/logging/nxscope/nxscope_iudp.c new file mode 100644 index 000000000..5a5e1eed0 --- /dev/null +++ b/logging/nxscope/nxscope_iudp.c @@ -0,0 +1,271 @@ +/**************************************************************************** + * apps/logging/nxscope/nxscope_iudp.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct nxscope_intf_udp_s +{ + FAR struct nxscope_udp_cfg_s *cfg; + int fd; + bool peer_valid; + struct sockaddr_in peer; +}; + +/**************************************************************************** + * Private Function Protototypes + ****************************************************************************/ + +static int nxscope_udp_send(FAR struct nxscope_intf_s *intf, + FAR uint8_t *buff, int len); +static int nxscope_udp_recv(FAR struct nxscope_intf_s *intf, + FAR uint8_t *buff, int len); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct nxscope_intf_ops_s g_nxscope_udp_ops = +{ + nxscope_udp_send, + nxscope_udp_recv +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxscope_udp_send + ****************************************************************************/ + +static int nxscope_udp_send(FAR struct nxscope_intf_s *intf, + FAR uint8_t *buff, int len) +{ + FAR struct nxscope_intf_udp_s *priv = NULL; + + DEBUGASSERT(intf); + DEBUGASSERT(intf->priv); + + /* Get priv data */ + + priv = (FAR struct nxscope_intf_udp_s *)intf->priv; + + if (!priv->peer_valid) + { + return -ENOTCONN; + } + + return sendto(priv->fd, buff, len, 0, + (FAR const struct sockaddr *)&priv->peer, + sizeof(priv->peer)); +} + +/**************************************************************************** + * Name: nxscope_udp_recv + ****************************************************************************/ + +static int nxscope_udp_recv(FAR struct nxscope_intf_s *intf, + FAR uint8_t *buff, int len) +{ + FAR struct nxscope_intf_udp_s *priv = NULL; + struct sockaddr_in from; + socklen_t fromlen; + int ret = OK; + + DEBUGASSERT(intf); + DEBUGASSERT(intf->priv); + + /* Get priv data */ + + priv = (FAR struct nxscope_intf_udp_s *)intf->priv; + + fromlen = sizeof(from); + ret = recvfrom(priv->fd, buff, len, 0, (FAR struct sockaddr *)&from, + &fromlen); + + if (ret < 0) + { + if (priv->cfg->nonblock && (errno == EAGAIN || errno == EWOULDBLOCK)) + { + ret = 0; + } + + return ret; + } + + priv->peer = from; + priv->peer_valid = true; + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxscope_udp_init + ****************************************************************************/ + +int nxscope_udp_init(FAR struct nxscope_intf_s *intf, + FAR struct nxscope_udp_cfg_s *cfg) +{ + FAR struct nxscope_intf_udp_s *priv = NULL; + struct sockaddr_in addr; + int ret = OK; + int flags = 0; + + DEBUGASSERT(intf); + DEBUGASSERT(cfg); + + /* Allocate priv data */ + + intf->priv = zalloc(sizeof(struct nxscope_intf_udp_s)); + if (intf->priv == NULL) + { + _err("ERROR: intf->priv alloc failed %d\n", errno); + ret = -errno; + goto errout; + } + + /* Get priv data */ + + priv = (FAR struct nxscope_intf_udp_s *)intf->priv; + priv->fd = -1; + + /* Connect configuration */ + + priv->cfg = (FAR struct nxscope_udp_cfg_s *)cfg; + + /* Connect ops */ + + intf->ops = &g_nxscope_udp_ops; + + /* Open UDP socket */ + + priv->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (priv->fd < 0) + { + _err("ERROR: failed to create UDP socket %d\n", errno); + ret = -errno; + goto errout; + } + + /* Configure non-blocking if enabled */ + + if (priv->cfg->nonblock) + { + flags = fcntl(priv->fd, F_GETFL, 0); + if (flags < 0) + { + _err("ERROR: failed to get socket flags %d\n", errno); + ret = -errno; + goto errout; + } + + ret = fcntl(priv->fd, F_SETFL, flags | O_NONBLOCK); + if (ret < 0) + { + _err("ERROR: failed to set O_NONBLOCK %d\n", errno); + ret = -errno; + goto errout; + } + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(priv->cfg->port); + + ret = bind(priv->fd, (FAR struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) + { + _err("ERROR: failed to bind UDP port %u errno=%d\n", + priv->cfg->port, + errno); + ret = -errno; + goto errout; + } + + /* Initialized */ + + intf->initialized = true; + return OK; + +errout: + if (priv != NULL && priv->fd >= 0) + { + close(priv->fd); + priv->fd = -1; + } + + return ret; +} + +/**************************************************************************** + * Name: nxscope_udp_deinit + ****************************************************************************/ + +void nxscope_udp_deinit(FAR struct nxscope_intf_s *intf) +{ + FAR struct nxscope_intf_udp_s *priv = NULL; + + DEBUGASSERT(intf); + + /* Get priv data */ + + priv = (FAR struct nxscope_intf_udp_s *)intf->priv; + + if (priv != NULL && priv->fd != -1) + { + close(priv->fd); + } + + if (priv != NULL) + { + free(priv); + } + + /* Reset structure */ + + memset(intf, 0, sizeof(struct nxscope_intf_s)); +} diff --git a/logging/nxscope/nxscope_pser.c b/logging/nxscope/nxscope_pser.c index 2d0917d51..2f8d5ad41 100644 --- a/logging/nxscope/nxscope_pser.c +++ b/logging/nxscope/nxscope_pser.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -274,7 +274,7 @@ int nxscope_proto_ser_init(FAR struct nxscope_proto_s *proto, FAR void *cfg) { DEBUGASSERT(proto); - /* cfg argument not used, but keept here for compatibility with + /* cfg argument not used, but kept here for compatibility with * future protocol implementations. */ diff --git a/lte/alt1250/alt1250_dbg.h b/lte/alt1250/alt1250_dbg.h index 82b9bb27a..0034bea82 100644 --- a/lte/alt1250/alt1250_dbg.h +++ b/lte/alt1250/alt1250_dbg.h @@ -29,7 +29,7 @@ #include #include -#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/lte/alt1250/alt1250_devif.c b/lte/alt1250/alt1250_devif.c index a742938fb..39c490667 100644 --- a/lte/alt1250/alt1250_devif.c +++ b/lte/alt1250/alt1250_devif.c @@ -123,7 +123,7 @@ int altdevice_send_command(FAR struct alt1250_s *dev, int fd, } else { - /* In case of send successed */ + /* In case of send succeeded */ ret = container->outparam ? REP_NO_ACK_WOFREE : REP_NO_ACK; } @@ -173,7 +173,7 @@ int altdevice_seteventbuff(int fd, FAR struct alt_evtbuffer_s *buffer) ****************************************************************************/ int altdevice_getevent(int fd, FAR uint64_t *evtbitmap, - FAR struct alt_container_s **replys) + FAR struct alt_container_s **replies) { int ret = -EIO; struct alt_readdata_s dat; @@ -183,7 +183,7 @@ int altdevice_getevent(int fd, FAR uint64_t *evtbitmap, { ret = OK; *evtbitmap = dat.evtbitmap; - *replys = dat.head; + *replies = dat.head; } return ret; diff --git a/lte/alt1250/alt1250_devif.h b/lte/alt1250/alt1250_devif.h index c537754df..01c689625 100644 --- a/lte/alt1250/alt1250_devif.h +++ b/lte/alt1250/alt1250_devif.h @@ -20,7 +20,7 @@ * ****************************************************************************/ -#ifndef __APSS_LTE_ALT1250_ALT1250_DEVIF_H +#ifndef __APPS_LTE_ALT1250_ALT1250_DEVIF_H #define __APPS_LTE_ALT1250_ALT1250_DEVIF_H /**************************************************************************** @@ -48,7 +48,7 @@ int altdevice_send_command(FAR struct alt1250_s *dev, int fd, int altdevice_powercontrol(int fd, uint32_t cmd); int altdevice_seteventbuff(int fd, FAR struct alt_evtbuffer_s *buffers); int altdevice_getevent(int fd, FAR uint64_t *evtbitmap, - FAR struct alt_container_s **replys); + FAR struct alt_container_s **replies); void altdevice_reset(int fd); #ifdef CONFIG_LTE_ALT1250_ENABLE_HIBERNATION_MODE int altdevice_powerresponse(int fd, uint32_t cmd, int resp); diff --git a/lte/alt1250/usock_handlers/alt1250_ioctl_lwm2m.c b/lte/alt1250/usock_handlers/alt1250_ioctl_lwm2m.c index e68704a87..3e76e43bd 100644 --- a/lte/alt1250/usock_handlers/alt1250_ioctl_lwm2m.c +++ b/lte/alt1250/usock_handlers/alt1250_ioctl_lwm2m.c @@ -610,7 +610,7 @@ static int perform_m2m_applysetting(FAR struct alt1250_s *dev, } *usock_result = OK; - dev->lwm2m_apply_xid = (int64_t)xid; + dev->lwm2m_apply_xid = xid; MODEM_STATE_INTENTRST(dev); altdevice_reset(dev->altfd); diff --git a/lte/lapi/src/lapi_dbg.h b/lte/lapi/src/lapi_dbg.h index 403847653..ebe02523a 100644 --- a/lte/lapi/src/lapi_dbg.h +++ b/lte/lapi/src/lapi_dbg.h @@ -27,7 +27,7 @@ * Included Files ****************************************************************************/ -#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/lte/lapi/src/lapi_lwm2m.c b/lte/lapi/src/lapi_lwm2m.c index 47a5d9f59..889e16018 100644 --- a/lte/lapi/src/lapi_lwm2m.c +++ b/lte/lapi/src/lapi_lwm2m.c @@ -95,7 +95,7 @@ int lte_m2m_connection(int cmd) return -EINVAL; } - return lapi_req(LTE_CMDID_LWM2M_CONNECT, (FAR void **)cmd, + return lapi_req(LTE_CMDID_LWM2M_CONNECT, (FAR void **)(uintptr_t)cmd, 1, NULL, 0, NULL); } @@ -107,7 +107,8 @@ int lte_m2m_readresponse(int seq_no, FAR struct lwm2mstub_instance_s *inst, int resp, FAR char *readvalue, int len) { FAR void *inarg[5] = { - (FAR void *)seq_no, (FAR void *)resp, inst, readvalue, (FAR void *)len + (FAR void *)(uintptr_t)seq_no, (FAR void *)(uintptr_t)resp, inst, + readvalue, (FAR void *)(uintptr_t)len }; if (!inst || !readvalue || len <= 0 || check_instance(inst) != OK) @@ -127,7 +128,7 @@ int lte_m2m_writeresponse(int seq_no, FAR struct lwm2mstub_instance_s *inst, int resp) { FAR void *inarg[3] = { - (FAR void *)seq_no, (FAR void *)resp, inst + (FAR void *)(uintptr_t)seq_no, (FAR void *)(uintptr_t)resp, inst }; if (!inst || check_instance(inst) != OK) @@ -147,7 +148,7 @@ int lte_m2m_executeresp(int seq_no, FAR struct lwm2mstub_instance_s *inst, int resp) { FAR void *inarg[3] = { - (FAR void *)seq_no, (FAR void *)resp, inst + (FAR void *)(uintptr_t)seq_no, (FAR void *)(uintptr_t)resp, inst }; if (!inst || check_instance(inst) != OK) @@ -166,7 +167,7 @@ int lte_m2m_executeresp(int seq_no, FAR struct lwm2mstub_instance_s *inst, int lte_m2m_observeresp(int seq_no, int resp) { FAR void *inarg[2] = { - (FAR void *)seq_no, (FAR void *)resp + (FAR void *)(uintptr_t)seq_no, (FAR void *)(uintptr_t)resp }; return lapi_req(LTE_CMDID_LWM2M_OBSERVERESP, @@ -182,7 +183,7 @@ int lte_m2m_observeupdate(FAR char *token, FAR char *value, int len) { FAR void *inarg[4] = { - token, inst, value, (FAR void *)len + token, inst, value, (FAR void *)(uintptr_t)len }; if (!token || !inst || !value || len <= 0) @@ -211,7 +212,7 @@ int lte_setm2m_endpointname(FAR char *name) int lte_getm2m_endpointname(FAR char *name, int len) { FAR void *outarg[2] = { - name, (FAR void *)len + name, (FAR void *)(uintptr_t)len }; if (!name || len <= 0) @@ -241,7 +242,7 @@ int lte_getm2m_servernum(void) int lte_setm2m_serverinfo(FAR struct lwm2mstub_serverinfo_s *info, int id) { FAR void *inarg[2] = { - info, (FAR void *)id + info, (FAR void *)(uintptr_t)id }; if (!info) @@ -259,7 +260,7 @@ int lte_setm2m_serverinfo(FAR struct lwm2mstub_serverinfo_s *info, int id) int lte_getm2m_serverinfo(FAR struct lwm2mstub_serverinfo_s *info, int id) { FAR void *outarg[2] = { - info, (FAR void *)id + info, (FAR void *)(uintptr_t)id }; if (!info) @@ -288,7 +289,7 @@ int lte_getm2m_enabled_objectnum(void) int lte_getm2m_enabled_objects(FAR uint16_t *objids, int objnum) { FAR void *outarg[2] = { - objids, (FAR void *)objnum + objids, (FAR void *)(uintptr_t)objnum }; if (!objids || objnum <= 0) @@ -306,7 +307,7 @@ int lte_getm2m_enabled_objects(FAR uint16_t *objids, int objnum) int lte_enablem2m_objects(FAR uint16_t *objids, int objnum) { FAR void *inarg[2] = { - objids, (FAR void *)objnum + objids, (FAR void *)(uintptr_t)objnum }; if (!objids || objnum <= 0) @@ -338,7 +339,7 @@ int lte_getm2m_objresourceinfo(uint16_t objid, int res_num, FAR struct lwm2mstub_resource_s *reses) { FAR void *outarg[2] = { - reses, (FAR void *)res_num + reses, (FAR void *)(uintptr_t)res_num }; if (!reses || res_num <= 0) @@ -391,7 +392,7 @@ int lte_setm2m_objectdefinition(uint16_t objids, int res_num, FAR struct lwm2mstub_resource_s *resucs) { FAR void *inarg[3] = { - (FAR void *)(uint32_t)objids, (FAR void *)res_num, resucs + (FAR void *)(uintptr_t)objids, (FAR void *)(uintptr_t)res_num, resucs }; return lapi_req(LTE_CMDID_LWM2M_SETOBJRESOURCE, inarg, 3, NULL, 0, NULL); @@ -487,5 +488,5 @@ bool lte_getm2m_qmode(void) int lte_setm2m_qmode(bool en) { return lapi_req(LTE_CMDID_LWM2M_SETQMODE, - (FAR void **)(en ? 1 : 0), 1, NULL, 0, NULL); + (FAR void **)(uintptr_t)(en ? 1 : 0), 1, NULL, 0, NULL); } diff --git a/lte/lapi/src/lapi_other.c b/lte/lapi/src/lapi_other.c index b7d6a2b64..14a0734ec 100644 --- a/lte/lapi/src/lapi_other.c +++ b/lte/lapi/src/lapi_other.c @@ -143,12 +143,12 @@ int lte_send_atcmd_sync(FAR const char *cmd, int cmdlen, int32_t ret; FAR void *inarg[] = { - (FAR void *)cmd, (FAR void *)cmdlen + (FAR void *)cmd, (FAR void *)(uintptr_t)cmdlen }; FAR void *outarg[] = { - respbuff, (FAR void *)respbufflen, resplen + respbuff, (FAR void *)(uintptr_t)respbufflen, resplen }; if (!cmd diff --git a/lte/lapi/src/lapi_power.c b/lte/lapi/src/lapi_power.c index 31ae86df0..8f5ea6c14 100644 --- a/lte/lapi/src/lapi_power.c +++ b/lte/lapi/src/lapi_power.c @@ -102,6 +102,7 @@ static inline void lapi_unlock(FAR sem_t *lock) * Name: is_daemon_running ****************************************************************************/ +#ifdef CONFIG_MODEM_ALT1250 static bool is_daemon_running(void) { int sock; @@ -127,6 +128,7 @@ static bool is_daemon_running(void) return is_run; } +#endif /* CONFIG_MODEM_ALT1250 */ /**************************************************************************** * Public Functions @@ -140,6 +142,15 @@ int lte_initialize(void) { int ret = 0; +#ifndef CONFIG_MODEM_ALT1250 + /* The modem backend can be kernel-resident (e.g. a built-in modem + * served by kernel side usrsock handler, such as the nRF91). LTE + * commands reach the modem directly via the SIOCLTECMD socket ioctl. + */ + + UNUSED(ret); + return OK; +#else lapi_lock(&g_lock); if (!is_daemon_running()) @@ -178,6 +189,7 @@ int lte_initialize(void) lapi_unlock(&g_lock); return ret; +#endif } /**************************************************************************** diff --git a/math/libtommath/Makefile b/math/libtommath/Makefile index 91b47d463..0d9fe26e4 100644 --- a/math/libtommath/Makefile +++ b/math/libtommath/Makefile @@ -65,6 +65,7 @@ DEPPATH += --dep-path $(LIBTOMMATH_UNPACKNAME)/demo ifneq ($(CONFIG_LIBTOMMATH_TEST),) MAINSRC += test.c +MODULE += $(CONFIG_LIBTOMMATH_TEST) PROGNAME += $(CONFIG_LIBTOMMATH_TEST_PROGNAME) PRIORITY += $(CONFIG_LIBTOMMATH_TEST_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMMATH_TEST_STACKSIZE) @@ -73,6 +74,7 @@ endif ifneq ($(CONFIG_LIBTOMMATH_MTEST_OPPONENT),) MAINSRC += mtest_opponent.c +MODULE += $(CONFIG_LIBTOMMATH_MTEST_OPPONENT) PROGNAME += $(CONFIG_LIBTOMMATH_MTEST_OPPONENT_PROGNAME) PRIORITY += $(CONFIG_LIBTOMMATH_MTEST_OPPONENT_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMMATH_MTEST_OPPONENT_STACKSIZE) @@ -81,6 +83,7 @@ endif ifneq ($(CONFIG_LIBTOMMATH_TIMING),) MAINSRC += timing.c +MODULE += $(CONFIG_LIBTOMMATH_TIMING) PROGNAME += $(CONFIG_LIBTOMMATH_TIMING_PROGNAME) PRIORITY += $(CONFIG_LIBTOMMATH_TIMING_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMMATH_TIMING_STACKSIZE) @@ -91,6 +94,7 @@ MAINSRC += mtest.c VPATH += $(LIBTOMMATH_UNPACKNAME)/mtest DEPPATH += --dep-path $(LIBTOMMATH_UNPACKNAME)/mtest +MODULE += $(CONFIG_LIBTOMMATH_MTEST) PROGNAME += $(CONFIG_LIBTOMMATH_MTEST_PROGNAME) PRIORITY += $(CONFIG_LIBTOMMATH_MTEST_PRIORITY) STACKSIZE += $(CONFIG_LIBTOMMATH_MTEST_STACKSIZE) diff --git a/netutils/bare/Kconfig b/netutils/bare/Kconfig new file mode 100644 index 000000000..2f0fa3df2 --- /dev/null +++ b/netutils/bare/Kconfig @@ -0,0 +1,22 @@ +config NETUTILS_BARE + bool "Binary Application Record Encoding (BARE)" + default n + depends on ALLOW_MIT_COMPONENTS + ---help--- + BARE is a simple binary representation for structured + application data. + + See https://baremessages.org/ + + cbare is an MIT-licensed I/O agnostic C implementation of the + BARE message encoding that is used when this option is enabled. + +if NETUTILS_BARE + +config NETUTILS_BARE_TEST + bool "Enable baretest" + default n + ---help--- + Enable baretest -- BARE test application. + +endif diff --git a/netutils/bare/Make.defs b/netutils/bare/Make.defs new file mode 100644 index 000000000..9a6b4f538 --- /dev/null +++ b/netutils/bare/Make.defs @@ -0,0 +1,27 @@ +############################################################################ +# apps/netutils/bare/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_NETUTILS_BARE),) +CONFIGURED_APPS += $(APPDIR)/netutils/bare +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/bare/cbare/src +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/bare/cbare/src +endif diff --git a/netutils/bare/Makefile b/netutils/bare/Makefile new file mode 100644 index 000000000..e851edf15 --- /dev/null +++ b/netutils/bare/Makefile @@ -0,0 +1,58 @@ +############################################################################ +# apps/netutils/bare/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +BARE_CBARE_DIR = cbare + +DEPPATH += --dep-path $(BARE_CBARE_DIR)$(DELIM)src +VPATH += :$(BARE_CBARE_DIR)$(DELIM)src + +CFLAGS += -I$(BARE_CBARE_DIR)/src + +CSRCS = $(BARE_CBARE_DIR)/src/alloc.c +CSRCS += $(BARE_CBARE_DIR)/src/cbare.c +CSRCS += $(BARE_CBARE_DIR)/src/die.c + +ifneq ($(CONFIG_NETUTILS_BARE_TEST),) + +PROGNAME = baretest +PRIORITY = 100 +STACKSIZE = 2048 +MODULE = y + +MAINSRC = $(BARE_CBARE_DIR)/test/baretest.c + +endif + +$(BARE_CBARE_DIR): + $(Q) echo "Cloning cbare repo..." + $(Q) git clone --depth=1 https://git.sr.ht/~fsx/cbare + $(Q) patch -p1 --directory=cbare < cbare_porting_for_nuttx.patch + $(Q) touch $(BARE_CBARE_DIR) + +context:: $(BARE_CBARE_DIR) + +distclean:: + $(call DELDIR, $(BARE_CBARE_DIR)) + +include $(APPDIR)/Application.mk diff --git a/netutils/bare/cbare_porting_for_nuttx.patch b/netutils/bare/cbare_porting_for_nuttx.patch new file mode 100644 index 000000000..73d16f7fd --- /dev/null +++ b/netutils/bare/cbare_porting_for_nuttx.patch @@ -0,0 +1,28 @@ +diff --git i/src/cbare.c w/src/cbare.c +index 208639f..a3081fb 100644 +--- i/src/cbare.c ++++ w/src/cbare.c +@@ -4,7 +4,9 @@ + #include "cbare.h" + #include "utf8.h" + ++#ifndef UNUSED + #define UNUSED(x) (void)(x) ++#endif + + enum { + U8SZ = 1, +diff --git i/test/baretest.c w/test/baretest.c +index 1561f4c..9df740f 100644 +--- i/test/baretest.c ++++ w/test/baretest.c +@@ -10,7 +10,9 @@ + #include "alloc.h" + #include "die.h" + ++#ifndef UNUSED + #define UNUSED(x) (void)(x) ++#endif + + #define ARRLEN(a) sizeof(a)/sizeof(a[0]) + diff --git a/netutils/chat/chat.c b/netutils/chat/chat.c index 3849631df..94f79c500 100644 --- a/netutils/chat/chat.c +++ b/netutils/chat/chat.c @@ -45,7 +45,7 @@ #include #include -#include +#include #include #include #include @@ -108,6 +108,8 @@ static int chat_tokenise(FAR struct chat *priv, int tok_on_delimiter(void) { + FAR struct chat_token *newtok; + if (!tok_pos && !quoted && !no_termin) { /* a) the first character in the script is a delimiter or @@ -122,9 +124,29 @@ static int chat_tokenise(FAR struct chat *priv, /* Terminate the temporary */ tok_str[tok_pos] = '\0'; + newtok = malloc(sizeof(struct chat_token)); + if (newtok == NULL) + { + /* out of memory */ + + return -ENOMEM; + } + + /* Copy the temporary */ + + newtok->string = strdup(tok_str); + if (newtok->string == NULL) + { + free(newtok); + return -ENOMEM; + } + + newtok->no_termin = no_termin; + newtok->next = NULL; + if (tok) { - tok->next = malloc(sizeof(struct chat_token)); + tok->next = newtok; /* The terminated token becomes previous */ @@ -134,26 +156,10 @@ static int chat_tokenise(FAR struct chat *priv, { /* There was no previous token */ - *first_tok = malloc(sizeof(struct chat_token)); + *first_tok = newtok; tok = *first_tok; } - if (!tok) - { - /* out of memory */ - - return -ENOMEM; - } - - /* Copy the temporary */ - - tok->string = strdup(tok_str); - tok->no_termin = no_termin; - - /* Initialize the next token */ - - tok->next = NULL; - /* Reset the buffer position */ tok_pos = 0; diff --git a/netutils/cjson/CMakeLists.txt b/netutils/cjson/CMakeLists.txt index 83a6475cd..315326793 100644 --- a/netutils/cjson/CMakeLists.txt +++ b/netutils/cjson/CMakeLists.txt @@ -23,7 +23,8 @@ if(CONFIG_NETUTILS_CJSON) if(NOT CONFIG_NETUTILS_CJSON_URL) - set(CONFIG_NETUTILS_CJSON_URL "https://github.com/DaveGamble/cJSON/archive") + set(CONFIG_NETUTILS_CJSON_URL + "https://github.com/DaveGamble/cJSON/archive/refs/tags") endif() if(NOT CONFIG_NETUTILS_CJSON_VERSION) @@ -39,7 +40,7 @@ if(CONFIG_NETUTILS_CJSON) SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/cJSON BINARY_DIR - ${CMAKE_BINARY_DIR}/apps/netutils/cjson/cJSON + ${NUTTX_APPS_BINDIR}/netutils/cjson/cJSON DOWNLOAD_NO_PROGRESS true TIMEOUT 30) @@ -50,13 +51,13 @@ if(CONFIG_NETUTILS_CJSON) endif() endif() - if(NOT EXISTS ${CMAKE_BINARY_DIR}/apps/include/netutils/cJSON.h - OR NOT EXISTS ${CMAKE_BINARY_DIR}/apps/include/netutils/cJSON_Utils.h) - file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/apps/include/netutils) + if(NOT EXISTS ${NUTTX_APPS_BINDIR}/include/netutils/cJSON.h + OR NOT EXISTS ${NUTTX_APPS_BINDIR}/include/netutils/cJSON_Utils.h) + file(MAKE_DIRECTORY ${NUTTX_APPS_BINDIR}/include/netutils) file(CREATE_LINK ${CMAKE_CURRENT_LIST_DIR}/cJSON/cJSON.h - ${CMAKE_BINARY_DIR}/apps/include/netutils/cJSON.h SYMBOLIC) + ${NUTTX_APPS_BINDIR}/include/netutils/cJSON.h SYMBOLIC) file(CREATE_LINK ${CMAKE_CURRENT_LIST_DIR}/cJSON/cJSON_Utils.h - ${CMAKE_BINARY_DIR}/apps/include/netutils/cJSON_Utils.h SYMBOLIC) + ${NUTTX_APPS_BINDIR}/include/netutils/cJSON_Utils.h SYMBOLIC) endif() target_sources(apps PRIVATE cJSON/cJSON.c cJSON/cJSON_Utils.c) diff --git a/netutils/cjson/Kconfig b/netutils/cjson/Kconfig index 4a360de6a..05773f157 100644 --- a/netutils/cjson/Kconfig +++ b/netutils/cjson/Kconfig @@ -14,7 +14,7 @@ if NETUTILS_CJSON config NETUTILS_CJSON_URL string "URL where cJSON library can be downloaded" - default "https://github.com/DaveGamble/cJSON/archive" + default "https://github.com/DaveGamble/cJSON/archive/refs/tags" config NETUTILS_CJSON_VERSION string "Version number" diff --git a/netutils/cjson/Makefile b/netutils/cjson/Makefile index d8993373d..050e5cf86 100644 --- a/netutils/cjson/Makefile +++ b/netutils/cjson/Makefile @@ -28,7 +28,7 @@ include $(APPDIR)/Make.defs WD := ${shell echo $(CURDIR) | sed -e 's/ /\\ /g'} -CONFIG_NETUTILS_CJSON_URL ?= "https://github.com/DaveGamble/cJSON/archive" +CONFIG_NETUTILS_CJSON_URL ?= "https://github.com/DaveGamble/cJSON/archive/refs/tags" CONFIG_NETUTILS_CJSON_VERSION ?= "1.7.10" CJSON_VERSION = $(patsubst "%",%,$(strip $(CONFIG_NETUTILS_CJSON_VERSION))) diff --git a/netutils/cmux/cmux.h b/netutils/cmux/cmux.h index 470aacfa4..7758f83b5 100644 --- a/netutils/cmux/cmux.h +++ b/netutils/cmux/cmux.h @@ -29,7 +29,7 @@ #include #include -#include +#include #include #define CMUX_BIT0 (0) diff --git a/netutils/codecs/Kconfig b/netutils/codecs/Kconfig index 4b22e1d3f..08f5ed816 100644 --- a/netutils/codecs/Kconfig +++ b/netutils/codecs/Kconfig @@ -16,7 +16,8 @@ config CODECS_BASE64 default n ---help--- Enables support for the following interfaces: base64_encode(), - base64_decode(), base64w_encode(), and base64w_decode(), + base64_decode(), base64w_encode(), base64w_decode(), + base64url_encode(), and base64url_decode(), Contributed NuttX by Darcy Gong. diff --git a/netutils/codecs/base64.c b/netutils/codecs/base64.c index 36485b85a..731283857 100644 --- a/netutils/codecs/base64.c +++ b/netutils/codecs/base64.c @@ -55,6 +55,7 @@ #include #include +#include #include #include "netutils/base64.h" @@ -317,4 +318,150 @@ FAR void *base64w_decode(FAR const void *src, size_t len, FAR void *dst, return _base64_decode(src, len, dst, out_len, true); } +/**************************************************************************** + * Name: base64url_val + ****************************************************************************/ + +static int base64url_val(char c) +{ + if (c >= 'A' && c <= 'Z') + { + return c - 'A'; + } + + if (c >= 'a' && c <= 'z') + { + return c - 'a' + 26; + } + + if (c >= '0' && c <= '9') + { + return c - '0' + 52; + } + + if (c == '-') + { + return 62; + } + + if (c == '_') + { + return 63; + } + + return -1; +} + +/**************************************************************************** + * Name: base64url_encode + * + * Description: + * Encode binary data as unpadded base64url (RFC 4648 section 5). + * + ****************************************************************************/ + +int base64url_encode(FAR const void *src, size_t len, FAR char *dst, + size_t dstlen) +{ + FAR const uint8_t *in = src; + uint32_t acc = 0; + size_t i; + size_t o = 0; + int bits = 0; + + static const char g_base64url[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + for (i = 0; i < len; i++) + { + acc = (acc << 8) | in[i]; + bits += 8; + + while (bits >= 6) + { + if (o + 1 >= dstlen) + { + return -E2BIG; + } + + bits -= 6; + dst[o++] = g_base64url[(acc >> bits) & 0x3f]; + } + } + + if (bits > 0) + { + if (o + 1 >= dstlen) + { + return -E2BIG; + } + + dst[o++] = g_base64url[(acc << (6 - bits)) & 0x3f]; + } + + if (o >= dstlen) + { + return -E2BIG; + } + + dst[o] = '\0'; + return 0; +} + +/**************************************************************************** + * Name: base64url_decode + * + * Description: + * Decode unpadded base64url (RFC 4648 section 5). + * + ****************************************************************************/ + +int base64url_decode(FAR const char *src, FAR void *dst, size_t dstmax, + FAR size_t *out_len) +{ + FAR uint8_t *out = dst; + uint32_t acc = 0; + size_t o = 0; + int bits = 0; + int v; + + *out_len = 0; + + while (*src != '\0') + { + if (*src == '$' || *src == ':') + { + break; + } + + v = base64url_val(*src++); + if (v < 0) + { + return -EINVAL; + } + + acc = (acc << 6) | (uint32_t)v; + bits += 6; + + if (bits >= 8) + { + bits -= 8; + if (o >= dstmax) + { + return -E2BIG; + } + + out[o++] = (uint8_t)((acc >> bits) & 0xff); + } + } + + if (bits >= 6) + { + return -EINVAL; + } + + *out_len = o; + return 0; +} + #endif /* CONFIG_CODECS_BASE64 */ diff --git a/netutils/codecs/md5.c b/netutils/codecs/md5.c index 75dfe3f0c..dade6abd1 100644 --- a/netutils/codecs/md5.c +++ b/netutils/codecs/md5.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "netutils/md5.h" diff --git a/netutils/dhcp6c/dhcp6c.c b/netutils/dhcp6c/dhcp6c.c index 5418c0a98..3989bee0c 100644 --- a/netutils/dhcp6c/dhcp6c.c +++ b/netutils/dhcp6c/dhcp6c.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/netutils/dhcpc/dhcpc.c b/netutils/dhcpc/dhcpc.c index dc6b46230..a449c96ca 100644 --- a/netutils/dhcpc/dhcpc.c +++ b/netutils/dhcpc/dhcpc.c @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include @@ -91,6 +91,7 @@ #define DHCP_OPTION_ROUTER 3 #define DHCP_OPTION_DNS_SERVER 6 #define DHCP_OPTION_HOST_NAME 12 +#define DHCP_OPTION_NTP_SERVER 42 #define DHCP_OPTION_REQ_IPADDR 50 #define DHCP_OPTION_LEASE_TIME 51 #define DHCP_OPTION_MSG_TYPE 53 @@ -210,10 +211,11 @@ static FAR uint8_t *dhcpc_addclientid(FAR uint8_t *clientid, static FAR uint8_t *dhcpc_addreqoptions(FAR uint8_t *optptr) { *optptr++ = DHCP_OPTION_REQ_LIST; - *optptr++ = 3; + *optptr++ = 4; *optptr++ = DHCP_OPTION_SUBNET_MASK; *optptr++ = DHCP_OPTION_ROUTER; *optptr++ = DHCP_OPTION_DNS_SERVER; + *optptr++ = DHCP_OPTION_NTP_SERVER; return optptr; } @@ -411,6 +413,39 @@ static uint8_t dhcpc_parseoptions(FAR struct dhcpc_state *presult, } break; + case DHCP_OPTION_NTP_SERVER: + + /* Get the NTP server addresses in network order. + * DHCP option 42 can contain multiple IPv4 addresses, + * each 4 bytes long. + */ + + if (optptr + 2 <= end) + { + uint8_t optlen = *(optptr + 1); + uint8_t num_ntp = optlen / 4; + uint8_t i; + + if (num_ntp > CONFIG_NETUTILS_DHCPC_NTP_SERVERS) + { + num_ntp = CONFIG_NETUTILS_DHCPC_NTP_SERVERS; + } + + presult->num_ntpaddr = 0; + for (i = 0; i < num_ntp && (optptr + 2 + i * 4 + 4) <= end; + i++) + { + memcpy(&presult->ntpaddr[i].s_addr, optptr + 2 + i * 4, + 4); + presult->num_ntpaddr++; + } + } + else + { + nerr("Packet too short (NTP address missing)\n"); + } + break; + case DHCP_OPTION_MSG_TYPE: /* Get message type */ @@ -533,8 +568,16 @@ static void *dhcpc_run(void *args) struct dhcpc_state result; int ret; +#ifndef CONFIG_ENABLE_ALL_SIGNALS + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); +#endif + while (1) { +#ifndef CONFIG_ENABLE_ALL_SIGNALS + pthread_testcancel(); +#endif ret = dhcpc_request(pdhcpc, &result); if (ret == OK) { @@ -701,7 +744,9 @@ void dhcpc_close(FAR void *handle) void dhcpc_cancel(FAR void *handle) { struct dhcpc_state_s *pdhcpc = (struct dhcpc_state_s *)handle; +#ifdef CONFIG_ENABLE_ALL_SIGNALS sighandler_t old; +#endif int ret; if (pdhcpc) @@ -710,6 +755,7 @@ void dhcpc_cancel(FAR void *handle) if (pdhcpc->thread) { +#ifdef CONFIG_ENABLE_ALL_SIGNALS old = signal(SIGQUIT, SIG_IGN); /* Signal the dhcpc_run */ @@ -719,6 +765,9 @@ void dhcpc_cancel(FAR void *handle) { nerr("ERROR: pthread_kill DHCPC thread\n"); } +#else + pthread_cancel(pdhcpc->thread); +#endif /* Wait for the end of dhcpc_run */ @@ -729,7 +778,9 @@ void dhcpc_cancel(FAR void *handle) } pdhcpc->thread = 0; +#ifdef CONFIG_ENABLE_ALL_SIGNALS signal(SIGQUIT, old); +#endif } } } @@ -983,6 +1034,21 @@ int dhcpc_request(FAR void *handle, FAR struct dhcpc_state *presult) } } + /* Print all NTP servers received */ + + if (presult->num_ntpaddr > 0) + { + uint8_t i; + for (i = 0; i < presult->num_ntpaddr; i++) + { + ninfo("Got NTP server %d: %u.%u.%u.%u\n", i, + ip4_addr1(presult->ntpaddr[i].s_addr), + ip4_addr2(presult->ntpaddr[i].s_addr), + ip4_addr3(presult->ntpaddr[i].s_addr), + ip4_addr4(presult->ntpaddr[i].s_addr)); + } + } + ninfo("Got default router %u.%u.%u.%u\n", ip4_addr1(presult->default_router.s_addr), ip4_addr2(presult->default_router.s_addr), diff --git a/netutils/dhcpd/dhcpd.c b/netutils/dhcpd/dhcpd.c index 367dda526..5d909a2e6 100644 --- a/netutils/dhcpd/dhcpd.c +++ b/netutils/dhcpd/dhcpd.c @@ -40,13 +40,14 @@ # define OK (0) #else # include /* NuttX configuration */ -# include /* For nerr, info */ +# include /* For nerr, info */ # include /* For CONFIG_CPP_HAVE_WARNING */ # include "netutils/dhcpd.h" /* Advertised DHCPD APIs */ #endif #include #include +#include #include #include @@ -276,8 +277,6 @@ struct dhcpd_state_s struct dhcpd_daemon_s { uint8_t ds_state; /* See enum dhcpd_daemon_e */ - sem_t ds_lock; /* Used to protect the whole structure */ - sem_t ds_sync; /* Used to synchronize start and stop events */ pid_t ds_pid; /* Task ID of the DHCPD daemon */ FAR struct dhcpd_state_s *ds_data; /* DHCPD daemon data */ }; @@ -314,8 +313,6 @@ static const uint8_t g_magiccookie[4] = static struct dhcpd_daemon_s g_dhcpd_daemon = { DHCPD_NOT_RUNNING, - SEM_INITIALIZER(1), - SEM_INITIALIZER(0), -1, NULL }; @@ -664,7 +661,7 @@ static inline bool dhcpd_parseoptions(void) if (optlen >= 6 && optlen < remaining) { memcpy(&tmp, &ptr[DHCPD_OPTION_DATA], 4); - g_state.ds_optleasetime = (time_t)ntohl(tmp); + g_state.ds_optleasetime = ntohl(tmp); } break; @@ -1019,7 +1016,7 @@ static int dhcpd_sendpacket(int sockfd, int bbroadcast) #endif /* Create a socket to respond with a packet to the client. We - * cannot re-use the listener socket because it is not bound correctly + * cannot reuse the listener socket because it is not bound correctly */ /* Then send the response to the DHCP client port at that address */ @@ -1407,7 +1404,7 @@ static inline int dhcpd_decline(void) lease = dhcpd_findbymac(g_state.ds_inpacket.chaddr); if (lease) { - /* Disassociate the IP from the MAC, but prevent re-used of this + /* Disassociate the IP from the MAC, but prevent reused of this * address for a period of time. */ @@ -1497,10 +1494,76 @@ static inline int dhcpd_openlistener(FAR const char *interface) * Name: dhcpd_task_run ****************************************************************************/ +#ifndef CONFIG_BUILD_KERNEL static int dhcpd_task_run(int argc, char **argv) { return dhcpd_run(argv[1]); } +#endif + +/**************************************************************************** + * Name: dhcpd_get_pid + * + * Description: + * Use /proc filesystem to get dhcpd process PID + * + * Returned Value: + * PID of dhcpd process on success, -1 on failure + * + ****************************************************************************/ + +#if !defined(CONFIG_BUILD_KERNEL) || defined(CONFIG_SCHED_WAITPID) +static pid_t dhcpd_get_pid(void) +{ + pid_t pid = -1; + +#if !defined(CONFIG_BUILD_KERNEL) + pid = g_dhcpd_daemon.ds_pid; +#elif defined(CONFIG_SYSTEM_POPEN) + FAR FILE *fp; + char line[32]; + + fp = popen(("pidof dhcpd"), "r"); + if (fp == NULL) + { + return -errno; + } + + if (fgets(line, sizeof(line), fp) != NULL) + { + pid = atoi(line); + } + + pclose(fp); +#endif + + return pid; +} +#endif + +/**************************************************************************** + * Name: dhcpd_signal_handler + * + * Description: + * Signal handler for CONFIG_NETUTILS_DHCPD_SIGWAKEUP + * This function allows dhcpd_stop to terminate dhcpd daemon + * + * Input Parameters: + * signo - Signal number + * info - Signal information + * ctx - Signal context + * + ****************************************************************************/ + +#ifdef CONFIG_ENABLE_ALL_SIGNALS +static void +dhcpd_signal_handler(int signo, FAR siginfo_t *info, FAR void *ctx) +{ + /* Set global flag to indicate stop request */ + + g_dhcpd_daemon.ds_state = DHCPD_STOP_REQUESTED; +} +#endif /**************************************************************************** * Public Functions @@ -1512,7 +1575,10 @@ static int dhcpd_task_run(int argc, char **argv) int dhcpd_run(FAR const char *interface) { - int sockfd; +#ifdef CONFIG_ENABLE_ALL_SIGNALS + struct sigaction act; +#endif + int sockfd = -1; int nbytes; ninfo("Started\n"); @@ -1538,15 +1604,20 @@ int dhcpd_run(FAR const char *interface) g_dhcpd_daemon.ds_pid = getpid(); + /* Install signal handler for CONFIG_NETUTILS_DHCPD_SIGWAKEUP */ + +#ifdef CONFIG_ENABLE_ALL_SIGNALS + memset(&act, 0, sizeof(act)); + act.sa_sigaction = dhcpd_signal_handler; + act.sa_flags = SA_SIGINFO; + sigaction(CONFIG_NETUTILS_DHCPD_SIGWAKEUP, &act, NULL); +#endif /* Indicate that we have started */ g_dhcpd_daemon.ds_state = DHCPD_RUNNING; - sem_post(&g_dhcpd_daemon.ds_sync); - /* Now loop indefinitely, reading packets from the DHCP server socket */ - sockfd = -1; while (g_dhcpd_daemon.ds_state != DHCPD_STOP_REQUESTED) { /* Create a socket to listen for requests from DHCP clients */ @@ -1631,7 +1702,6 @@ int dhcpd_run(FAR const char *interface) g_dhcpd_daemon.ds_data = NULL; g_dhcpd_daemon.ds_pid = -1; g_dhcpd_daemon.ds_state = DHCPD_STOPPED; - sem_post(&g_dhcpd_daemon.ds_sync); return OK; } @@ -1648,6 +1718,7 @@ int dhcpd_run(FAR const char *interface) * ****************************************************************************/ +#ifndef CONFIG_BUILD_KERNEL int dhcpd_start(FAR const char *interface) { FAR char *argv[2]; @@ -1658,17 +1729,13 @@ int dhcpd_start(FAR const char *interface) /* Is the DHCPD in a non-running state? */ - sem_wait(&g_dhcpd_daemon.ds_lock); - if (g_dhcpd_daemon.ds_state == DHCPD_NOT_RUNNING || - g_dhcpd_daemon.ds_state == DHCPD_STOPPED) + if (dhcpd_get_pid() < 0) { /* Start the DHCPD daemon */ - g_dhcpd_daemon.ds_state = DHCPD_STARTED; - pid = - task_create("DHCPD daemon", CONFIG_NETUTILS_DHCPD_PRIORITY, - CONFIG_NETUTILS_DHCPD_STACKSIZE, dhcpd_task_run, - argv); + pid = task_create("dhcpd", CONFIG_NETUTILS_DHCPD_PRIORITY, + CONFIG_NETUTILS_DHCPD_STACKSIZE, dhcpd_task_run, + argv); /* Handle failures to start the DHCPD daemon */ @@ -1676,24 +1743,14 @@ int dhcpd_start(FAR const char *interface) { int errval = errno; - g_dhcpd_daemon.ds_state = DHCPD_STOPPED; nerr("ERROR: Failed to start the DHCPD daemon: %d\n", errval); - sem_post(&g_dhcpd_daemon.ds_lock); return -errval; } - - /* Wait for any daemon state change */ - - do - { - sem_wait(&g_dhcpd_daemon.ds_sync); - } - while (g_dhcpd_daemon.ds_state == DHCPD_STARTED); } - sem_post(&g_dhcpd_daemon.ds_lock); return OK; } +#endif /**************************************************************************** * Name: dhcpd_stop @@ -1707,46 +1764,36 @@ int dhcpd_start(FAR const char *interface) * ****************************************************************************/ +#ifdef CONFIG_SCHED_WAITPID int dhcpd_stop(void) { - int ret; + pid_t pid; /* Is the DHCPD in a running state? */ - sem_wait(&g_dhcpd_daemon.ds_lock); - if (g_dhcpd_daemon.ds_state == DHCPD_STARTED || - g_dhcpd_daemon.ds_state == DHCPD_RUNNING) + pid = dhcpd_get_pid(); + if (pid >= 0) { /* Yes.. request that the daemon stop. */ - g_dhcpd_daemon.ds_state = DHCPD_STOP_REQUESTED; - - /* Wait for any daemon state change */ - - do + if (kill(pid, CONFIG_NETUTILS_DHCPD_SIGWAKEUP) < 0) { - /* Signal the DHCPD client */ - - ret = kill(g_dhcpd_daemon.ds_pid, - CONFIG_NETUTILS_DHCPD_SIGWAKEUP); - - if (ret < 0) - { - nerr("ERROR: kill pid %d failed: %d\n", - g_dhcpd_daemon.ds_pid, errno); - break; - } - - /* Wait for the DHCPD client to respond to the stop request */ - - sem_wait(&g_dhcpd_daemon.ds_sync); + nerr("ERROR: kill pid %d failed: %d\n", pid, errno); + return -errno; + } + + /* Wait for the DHCPD client to respond to the stop request */ + + if (waitpid(pid, NULL, 0) < 0) + { + nerr("ERROR: waitpid failed: %d\n", errno); + return -errno; } - while (g_dhcpd_daemon.ds_state == DHCPD_STOP_REQUESTED); } - sem_post(&g_dhcpd_daemon.ds_lock); return OK; } +#endif /**************************************************************************** * Name: dhcpd_set_startip diff --git a/netutils/discover/discover.c b/netutils/discover/discover.c index 6bb3f675b..ba11fbe87 100644 --- a/netutils/discover/discover.c +++ b/netutils/discover/discover.c @@ -24,7 +24,7 @@ * Included Files ****************************************************************************/ -#include +#include #include #include #include diff --git a/netutils/dropbear/.gitignore b/netutils/dropbear/.gitignore new file mode 100644 index 000000000..1dc4834a1 --- /dev/null +++ b/netutils/dropbear/.gitignore @@ -0,0 +1,6 @@ +/dropbear +/*.zip +*.o +.built +.depend +Make.dep diff --git a/netutils/dropbear/CMakeLists.txt b/netutils/dropbear/CMakeLists.txt new file mode 100644 index 000000000..c429a2a0d --- /dev/null +++ b/netutils/dropbear/CMakeLists.txt @@ -0,0 +1,256 @@ +# ############################################################################## +# apps/netutils/dropbear/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_NETUTILS_DROPBEAR) + + set(DROPBEAR_COMMIT "${CONFIG_NETUTILS_DROPBEAR_COMMIT}") + string(REPLACE "\"" "" DROPBEAR_COMMIT "${DROPBEAR_COMMIT}") + + set(DROPBEAR_ZIP "${DROPBEAR_COMMIT}.zip") + set(DROPBEAR_URL "https://github.com/mkj/dropbear/archive") + set(DROPBEAR_UNPACKNAME "dropbear") + + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}") + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_ZIP}") + message(STATUS "Downloading Dropbear: ${DROPBEAR_URL}/${DROPBEAR_ZIP}") + file(DOWNLOAD "${DROPBEAR_URL}/${DROPBEAR_ZIP}" + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_ZIP}") + endif() + message(STATUS "Unpacking Dropbear: ${DROPBEAR_ZIP}") + execute_process( + COMMAND unzip -q -o "${DROPBEAR_ZIP}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE result) + if(result EQUAL 0) + file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/dropbear-${DROPBEAR_COMMIT}" + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0001-guard-platform-declarations.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0002-use-nuttx-passwd-auth.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0003-allow-localoptions-to-override-tracking-malloc.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + execute_process( + COMMAND + patch -s -N -l -p1 -d "${DROPBEAR_UNPACKNAME}" -i + "${CMAKE_CURRENT_SOURCE_DIR}/patch/0004-use-nuttx-unused-macro.patch" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + message(STATUS "Generating default_options_guard.h") + execute_process( + COMMAND + sh + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/ifndef_wrapper.sh" + INPUT_FILE + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/default_options.h" + OUTPUT_FILE + "${CMAKE_CURRENT_SOURCE_DIR}/${DROPBEAR_UNPACKNAME}/src/default_options_guard.h" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + endif() + + set(PROGNAME "${CONFIG_NETUTILS_DROPBEAR_PROGNAME}") + string(REPLACE "\"" "" PROGNAME "${PROGNAME}") + + set(DROPBEAR_SRCS + dropbear_nshsession.c + port/nuttx_auth.c + port/dropbear_utils.c + dropbear/src/dbutil.c + dropbear/src/buffer.c + dropbear/src/dbhelpers.c + dropbear/src/bignum.c + dropbear/src/signkey.c + dropbear/src/dbrandom.c + dropbear/src/queue.c + dropbear/src/atomicio.c + dropbear/src/compat.c + dropbear/src/fake-rfc2553.c + dropbear/src/curve25519.c + dropbear/src/chachapoly.c + dropbear/src/ltc_prng.c + dropbear/src/ecc.c + dropbear/src/ecdsa.c + dropbear/src/crypto_desc.c + dropbear/src/dbmalloc.c + dropbear/src/gensignkey.c + dropbear/src/common-session.c + dropbear/src/packet.c + dropbear/src/common-algo.c + dropbear/src/common-kex.c + dropbear/src/common-channel.c + dropbear/src/common-chansession.c + dropbear/src/termcodes.c + dropbear/src/tcp-accept.c + dropbear/src/listener.c + dropbear/src/process-packet.c + dropbear/src/common-runopts.c + dropbear/src/circbuffer.c + dropbear/src/list.c + dropbear/src/netio.c + dropbear/src/gcm.c + dropbear/src/kex-x25519.c + dropbear/src/svr-kex.c + dropbear/src/svr-auth.c + dropbear/src/svr-authpasswd.c + dropbear/src/svr-session.c + dropbear/src/svr-service.c + dropbear/src/svr-runopts.c + dropbear/src/svr-tcpfwd.c + dropbear/src/svr-forward.c + dropbear/src/svr-streamfwd.c + dropbear/src/svr-authpam.c) + + file(GLOB LIBTOMMATH_SRCS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtommath/*.c") + list(APPEND DROPBEAR_SRCS ${LIBTOMMATH_SRCS}) + + file(GLOB_RECURSE LIBTOMCRYPT_SRCS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtomcrypt/src/*.c") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/prngs/sober128tab\\.c$") + + # Drop the bundled libtomcrypt HMAC modules replaced by the NuttX adapter. + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_init\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_process\\.c$") + list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_done\\.c$") + list(APPEND DROPBEAR_SRCS port/dropbear_ltc_hmac_sha256.c) + + # Replace the bundled chacha20-poly1305 with the /dev/crypto adapter. + list(REMOVE_ITEM DROPBEAR_SRCS dropbear/src/chachapoly.c) + list(APPEND DROPBEAR_SRCS port/dropbear_chachapoly.c) + + list(APPEND DROPBEAR_SRCS ${LIBTOMCRYPT_SRCS}) + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + list(APPEND DROPBEAR_SRCS dropbear/src/scpmisc.c port/nuttx_scp.c) + endif() + + nuttx_add_application( + NAME + ${PROGNAME} + SRCS + ${DROPBEAR_SRCS} + dropbear_main.c + STACKSIZE + ${CONFIG_NETUTILS_DROPBEAR_STACKSIZE} + PRIORITY + ${CONFIG_NETUTILS_DROPBEAR_PRIORITY} + DEPENDS + ${DROPBEAR_UNPACKNAME}) + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + nuttx_add_application( + NAME + scp + SRCS + dropbear/src/scp.c + STACKSIZE + ${CONFIG_NETUTILS_DROPBEAR_SCP_STACKSIZE} + PRIORITY + ${CONFIG_NETUTILS_DROPBEAR_SCP_PRIORITY} + DEPENDS + ${DROPBEAR_UNPACKNAME}) + endif() + + target_include_directories( + ${PROGNAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/port + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/src + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtomcrypt/src/headers + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/libtommath + ${CMAKE_CURRENT_SOURCE_DIR}/../../nshlib) + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + target_include_directories( + scp + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/port + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear + ${CMAKE_CURRENT_SOURCE_DIR}/dropbear/src) + endif() + + if(CONFIG_NETUTILS_DROPBEAR_COMPRESSION) + target_include_directories( + ${PROGNAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../system/zlib/zlib) + endif() + + target_compile_definitions( + ${PROGNAME} + PRIVATE LOCALOPTIONS_H_EXISTS=1 + DROPBEAR_NUTTX=1 + DROPBEAR_NUTTX_PASSWD=1 + base64_encode=dropbear_ltc_base64_encode + base64_decode=dropbear_ltc_base64_decode + ecc_make_key=dropbear_ltc_ecc_make_key) + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + target_compile_definitions( + scp PRIVATE LOCALOPTIONS_H_EXISTS=1 DROPBEAR_NUTTX=1 + DROPBEAR_NUTTX_PASSWD=1) + endif() + + set_source_files_properties( + dropbear_nshsession.c + PROPERTIES COMPILE_DEFINITIONS + "Channel=dropbear_channel;ChanType=dropbear_chantype") + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + set_source_files_properties( + dropbear/src/scp.c + PROPERTIES + COMPILE_DEFINITIONS + "xmalloc=dropbear_scp_xmalloc;xrealloc=dropbear_scp_xrealloc;xfree=dropbear_scp_xfree;execvp=dropbear_scp_execvp" + ) + set_source_files_properties( + dropbear/src/scpmisc.c + PROPERTIES + COMPILE_DEFINITIONS + "xmalloc=dropbear_scp_xmalloc;xrealloc=dropbear_scp_xrealloc;xfree=dropbear_scp_xfree" + ) + endif() + + # LTC_SOURCE must be set only for libtomcrypt sources. + set_source_files_properties(${LIBTOMCRYPT_SRCS} PROPERTIES COMPILE_DEFINITIONS + LTC_SOURCE=1) + + target_compile_options(${PROGNAME} PRIVATE -Wno-pointer-sign -Wno-format) + + if(CONFIG_NETUTILS_DROPBEAR_SCP) + target_compile_options( + scp + PRIVATE -Wno-pointer-sign -Wno-format -include + ${CMAKE_CURRENT_SOURCE_DIR}/port/nuttx_scp.h + -Wno-strict-prototypes) + endif() + + target_sources(apps PRIVATE ${DROPBEAR_SRCS}) + +endif() diff --git a/netutils/dropbear/Kconfig b/netutils/dropbear/Kconfig new file mode 100644 index 000000000..99066ee2e --- /dev/null +++ b/netutils/dropbear/Kconfig @@ -0,0 +1,157 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig NETUTILS_DROPBEAR + tristate "Dropbear SSH server" + default n + depends on NET && NET_TCP + depends on !DISABLE_PSEUDOFS_OPERATIONS + depends on !DISABLE_PTHREAD + depends on SCHED_WAITPID + depends on SCHED_HAVE_PARENT + depends on SCHED_CHILD_STATUS + depends on NSH_LIBRARY + depends on FSUTILS_PASSWD + depends on PSEUDOTERM + depends on SERIAL + depends on DEV_URANDOM + depends on LIBC_NETDB + depends on LIBC_GAISTRERROR + depends on CRYPTO + depends on CRYPTO_RANDOM_POOL + depends on ALLOW_BSD_COMPONENTS + depends on CRYPTO_CRYPTODEV + depends on CRYPTO_CRYPTODEV_SOFTWARE_CRYPTO + ---help--- + Enable a minimal Dropbear SSH server port for NuttX. This initial + port is based on the ESP-IDF MCU test port and provides a single + foreground SSH server process with SSH sessions backed by NSH. + + The port computes the hmac-sha2-256 packet MAC through the NuttX + crypto device (/dev/crypto, CRYPTO_SHA2_256_HMAC) instead of the + bundled libtomcrypt implementation, which is dropped from the + build. The SHA-256 hash descriptor itself stays in the + application: Dropbear's key derivation clones partially-updated + hash states (hashkeys() in common-kex.c), which cannot be + represented by a kernel crypto session. + + The port also implements the chacha20-poly1305@openssh.com cipher + through the NuttX crypto device (/dev/crypto), using the + CRYPTO_CHACHA20_DJB and CRYPTO_POLY1305 algorithms instead of + the bundled libtomcrypt implementation, which is dropped from + the build. + +if NETUTILS_DROPBEAR + +config NETUTILS_DROPBEAR_STACKSIZE + int "Dropbear main stack size" + default 65536 + ---help--- + Stack size for the Dropbear server built-in. + This is architecture-specific, so adjust it according to your setup. + +config NETUTILS_DROPBEAR_PRIORITY + int "Dropbear main priority" + default 100 + +config NETUTILS_DROPBEAR_SHELL_PRIORITY + int "Dropbear NSH session priority" + default 100 + +config NETUTILS_DROPBEAR_PROGNAME + string "Dropbear program name" + default "dropbear" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config NETUTILS_DROPBEAR_LISTEN_RETRIES + int "Dropbear listen retries" + default 0 + ---help--- + Number of times to retry listen setup when no listen socket could + be opened. Zero means to retry forever. + +config NETUTILS_DROPBEAR_LISTEN_RETRY_MAX + int "Dropbear maximum listen retry interval" + default 120 + range 1 3600 + ---help--- + Maximum number of seconds to wait between listen setup retries. + The retry delay starts at one second and doubles until it reaches + this value. + +config NETUTILS_DROPBEAR_SHELL_STACKSIZE + int "Dropbear NSH session task stack size" + default 8192 + +config NETUTILS_DROPBEAR_SCP + bool "Enable scp remote copy helper" + default y + depends on PIPES + ---help--- + Build Dropbear's scp program and allow SSH exec requests so a host + scp client can copy files to and from NuttX using the legacy scp + protocol. This does not build a full SSH client for initiating scp + transfers from the target. + +config NETUTILS_DROPBEAR_SCP_STACKSIZE + int "Dropbear scp stack size" + default 32768 + depends on NETUTILS_DROPBEAR_SCP + +config NETUTILS_DROPBEAR_SCP_PRIORITY + int "Dropbear scp priority" + default 100 + depends on NETUTILS_DROPBEAR_SCP + +config NETUTILS_DROPBEAR_PORT + int "Dropbear listen port" + default 2222 + +config NETUTILS_DROPBEAR_HOSTKEY_PATH + string "Dropbear ECDSA P-256 host key path" + default "/etc/dropbear/dropbear_ecdsa_host_key" + ---help--- + Path to the persistent ECDSA P-256 host key used by the Dropbear + server. The file is stored in Dropbear's native host key format. + +config NETUTILS_DROPBEAR_GENERATE_HOSTKEY + bool "Generate host key if missing" + default y + ---help--- + Pass -R so Dropbear generates an ECDSA P-256 host key on demand and + persists it at NETUTILS_DROPBEAR_HOSTKEY_PATH when it does not exist. + Product builds can disable this and provision the host key + externally (loaded with -r). + +config NETUTILS_DROPBEAR_COMPRESSION + bool "Enable SSH compression (zlib)" + default n + depends on LIB_ZLIB + ---help--- + Enable zlib compression for SSH sessions. Requires the zlib + library (LIB_ZLIB). When disabled, Dropbear is built with + DISABLE_ZLIB and negotiates no compression. + + WARNING: each session allocates a zlib deflate state of about + 256 KiB (DROPBEAR_ZLIB_WINDOW_BITS=15, DROPBEAR_ZLIB_MEM_LEVEL=8), + and the state is allocated even for the delayed zlib@openssh.com + method, right after key exchange. + +config NETUTILS_DROPBEAR_SYSLOG + bool "Log via syslog" + default n + ---help--- + Route Dropbear log messages through syslog(). When disabled, + Dropbear is built with DISABLE_SYSLOG. + +config NETUTILS_DROPBEAR_COMMIT + string "Dropbear upstream commit" + default "54ef47adf8c99b422be6a8f694f2866e62f88b9e" + ---help--- + Upstream Dropbear (mkj/dropbear) commit to download and build. + +endif diff --git a/netutils/dropbear/Make.defs b/netutils/dropbear/Make.defs new file mode 100644 index 000000000..9fcb635f7 --- /dev/null +++ b/netutils/dropbear/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/netutils/dropbear/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_NETUTILS_DROPBEAR),) +CONFIGURED_APPS += $(APPDIR)/netutils/dropbear +endif diff --git a/netutils/dropbear/Makefile b/netutils/dropbear/Makefile new file mode 100644 index 000000000..f36398088 --- /dev/null +++ b/netutils/dropbear/Makefile @@ -0,0 +1,205 @@ +############################################################################ +# apps/netutils/dropbear/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +ifneq ($(CONFIG_NETUTILS_DROPBEAR),) + +DROPBEAR_COMMIT = $(patsubst "%",%,$(strip $(CONFIG_NETUTILS_DROPBEAR_COMMIT))) +DROPBEAR_ZIP = $(DROPBEAR_COMMIT).zip +DROPBEAR_UNPACKNAME = dropbear +DROPBEAR_URL = https://github.com/mkj/dropbear/archive +UNPACK ?= unzip -q -o +ifeq ($(CONFIG_HOST_MACOS),y) +PATCH ?= gpatch +else +PATCH ?= patch +endif + +MODULE = $(CONFIG_NETUTILS_DROPBEAR) +PROGNAME = $(CONFIG_NETUTILS_DROPBEAR_PROGNAME) +PRIORITY = $(CONFIG_NETUTILS_DROPBEAR_PRIORITY) +STACKSIZE = $(CONFIG_NETUTILS_DROPBEAR_STACKSIZE) + +ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),) +PROGNAME += scp +PRIORITY += $(CONFIG_NETUTILS_DROPBEAR_SCP_PRIORITY) +STACKSIZE += $(CONFIG_NETUTILS_DROPBEAR_SCP_STACKSIZE) +endif + +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)port" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)src" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)libtomcrypt$(DELIM)src$(DELIM)headers" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)dropbear$(DELIM)libtommath" +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)nshlib" + +ifneq ($(CONFIG_NETUTILS_DROPBEAR_COMPRESSION),) +CFLAGS += ${INCDIR_PREFIX}"$(APPDIR)$(DELIM)system$(DELIM)zlib$(DELIM)zlib" +endif + +CFLAGS += ${DEFINE_PREFIX}LOCALOPTIONS_H_EXISTS=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX=1 +CFLAGS += ${DEFINE_PREFIX}DROPBEAR_NUTTX_PASSWD=1 +CFLAGS += -Wno-pointer-sign -Wno-format + +dropbear_nshsession.c_CFLAGS += ${DEFINE_PREFIX}Channel=dropbear_channel +dropbear_nshsession.c_CFLAGS += ${DEFINE_PREFIX}ChanType=dropbear_chantype + +CSRCS = dropbear_nshsession.c +CSRCS += port/nuttx_auth.c +CSRCS += port/dropbear_utils.c + +CSRCS += \ + dropbear/src/dbutil.c \ + dropbear/src/buffer.c \ + dropbear/src/dbhelpers.c \ + dropbear/src/bignum.c \ + dropbear/src/signkey.c \ + dropbear/src/dbrandom.c \ + dropbear/src/queue.c \ + dropbear/src/atomicio.c \ + dropbear/src/compat.c \ + dropbear/src/fake-rfc2553.c \ + dropbear/src/curve25519.c \ + dropbear/src/chachapoly.c \ + dropbear/src/ltc_prng.c \ + dropbear/src/ecc.c \ + dropbear/src/ecdsa.c \ + dropbear/src/crypto_desc.c \ + dropbear/src/dbmalloc.c \ + dropbear/src/gensignkey.c \ + dropbear/src/common-session.c \ + dropbear/src/packet.c \ + dropbear/src/common-algo.c \ + dropbear/src/common-kex.c \ + dropbear/src/common-channel.c \ + dropbear/src/common-chansession.c \ + dropbear/src/termcodes.c \ + dropbear/src/tcp-accept.c \ + dropbear/src/listener.c \ + dropbear/src/process-packet.c \ + dropbear/src/common-runopts.c \ + dropbear/src/circbuffer.c \ + dropbear/src/list.c \ + dropbear/src/netio.c \ + dropbear/src/gcm.c \ + dropbear/src/kex-x25519.c \ + dropbear/src/svr-kex.c \ + dropbear/src/svr-auth.c \ + dropbear/src/svr-authpasswd.c \ + dropbear/src/svr-session.c \ + dropbear/src/svr-service.c \ + dropbear/src/svr-runopts.c \ + dropbear/src/svr-tcpfwd.c \ + dropbear/src/svr-forward.c \ + dropbear/src/svr-streamfwd.c \ + dropbear/src/svr-authpam.c + +TOMMATH_SRCS = $(shell if [ -d "$(DROPBEAR_UNPACKNAME)/libtommath" ]; then find "$(DROPBEAR_UNPACKNAME)/libtommath" -name "*.c"; fi) +TOMCRYPT_SRCS = $(shell if [ -d "$(DROPBEAR_UNPACKNAME)/libtomcrypt/src" ]; then find "$(DROPBEAR_UNPACKNAME)/libtomcrypt/src" -name "*.c" ! -name "sober128tab.c"; fi) + +# Drop the bundled libtomcrypt HMAC modules replaced by the NuttX adapter. + +TOMCRYPT_SRCS := $(filter-out \ + %/mac/hmac/hmac_init.c \ + %/mac/hmac/hmac_process.c \ + %/mac/hmac/hmac_done.c, \ + $(TOMCRYPT_SRCS)) + +CSRCS += port/dropbear_ltc_hmac_sha256.c + +# Replace the bundled chacha20-poly1305 with the /dev/crypto adapter. + +CSRCS := $(filter-out dropbear/src/chachapoly.c,$(CSRCS)) +CSRCS += port/dropbear_chachapoly.c + +CSRCS += $(TOMMATH_SRCS) +CSRCS += $(TOMCRYPT_SRCS) + +ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),) +CSRCS += dropbear/src/scpmisc.c +CSRCS += port/nuttx_scp.c +endif + +# Match the ESP-IDF port behavior: LTC_SOURCE is only for libtomcrypt sources. +$(foreach src,$(TOMCRYPT_SRCS),$(eval $(src)_CFLAGS += ${DEFINE_PREFIX}LTC_SOURCE=1)) + +# Avoid duplicate symbols when NuttX/apps also provide these APIs. +# Apply to all dropbear sources so libtomcrypt definitions and dropbear +# callers (e.g. signkey.c) use the same renamed symbols. +CFLAGS += ${DEFINE_PREFIX}base64_encode=dropbear_ltc_base64_encode +CFLAGS += ${DEFINE_PREFIX}base64_decode=dropbear_ltc_base64_decode +CFLAGS += ${DEFINE_PREFIX}ecc_make_key=dropbear_ltc_ecc_make_key + +MAINSRC = dropbear_main.c + +ifneq ($(CONFIG_NETUTILS_DROPBEAR_SCP),) +MAINSRC += dropbear/src/scp.c + +dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xmalloc=dropbear_scp_xmalloc +dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xrealloc=dropbear_scp_xrealloc +dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}xfree=dropbear_scp_xfree +dropbear/src/scp.c_CFLAGS += ${DEFINE_PREFIX}execvp=dropbear_scp_execvp +dropbear/src/scp.c_CFLAGS += -include port/nuttx_scp.h +dropbear/src/scp.c_CFLAGS += -Wno-strict-prototypes + +dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xmalloc=dropbear_scp_xmalloc +dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xrealloc=dropbear_scp_xrealloc +dropbear/src/scpmisc.c_CFLAGS += ${DEFINE_PREFIX}xfree=dropbear_scp_xfree +endif + +$(DROPBEAR_ZIP): + @echo "Downloading: $(DROPBEAR_ZIP)" + $(Q) curl -L -o $(DROPBEAR_ZIP) $(DROPBEAR_URL)/$(DROPBEAR_ZIP) + +$(DROPBEAR_UNPACKNAME): $(DROPBEAR_ZIP) + @echo "Unpacking: $(DROPBEAR_ZIP) -> $(DROPBEAR_UNPACKNAME)" + $(Q) $(UNPACK) $(DROPBEAR_ZIP) + $(Q) rm -rf $(DROPBEAR_UNPACKNAME) + $(Q) mv dropbear-$(DROPBEAR_COMMIT) $(DROPBEAR_UNPACKNAME) + @echo "Patching $(DROPBEAR_UNPACKNAME)" + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0001-guard-platform-declarations.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0002-use-nuttx-passwd-auth.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0003-allow-localoptions-to-override-tracking-malloc.patch; true + $(Q) $(PATCH) -s -N -l -p1 -d $(DROPBEAR_UNPACKNAME) -i $(APPDIR)$(DELIM)netutils$(DELIM)dropbear$(DELIM)patch$(DELIM)0004-use-nuttx-unused-macro.patch; true + @echo "Generating default_options_guard.h" + $(Q) sh $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)ifndef_wrapper.sh \ + < $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)default_options.h \ + > $(DROPBEAR_UNPACKNAME)$(DELIM)src$(DELIM)default_options_guard.h + $(Q) touch $(DROPBEAR_UNPACKNAME) + +ifeq ($(wildcard $(DROPBEAR_UNPACKNAME)/.git),) +context:: $(DROPBEAR_UNPACKNAME) + +clean:: + $(call DELFILE, port$(DELIM)*.o) + +distclean:: + $(call DELDIR, $(DROPBEAR_UNPACKNAME)) + $(call DELFILE, $(DROPBEAR_ZIP)) +endif + +endif + +include $(APPDIR)/Application.mk diff --git a/netutils/dropbear/dropbear_main.c b/netutils/dropbear/dropbear_main.c new file mode 100644 index 000000000..798ba2b55 --- /dev/null +++ b/netutils/dropbear/dropbear_main.c @@ -0,0 +1,329 @@ +/**************************************************************************** + * apps/netutils/dropbear/dropbear_main.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "includes.h" +#include "algo.h" +#include "crypto_desc.h" +#define dropbear_main dropbear_multi_entry +#include "dbutil.h" +#undef dropbear_main +#include "dbrandom.h" +#include "netio.h" +#include "runopts.h" +#include "session.h" +#include "signkey.h" +#include "gensignkey.h" +#include "ssh.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_PORT_STRING_HELPER(n) #n +#define DROPBEAR_PORT_STRING(n) DROPBEAR_PORT_STRING_HELPER(n) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +typedef void (*dropbear_exit_handler_t)(int exitcode, FAR const char *format, + va_list param) ATTRIB_NORETURN; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static jmp_buf g_session_exit_jmp; +static int g_session_exitcode; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void dropbear_session_exit(int exitcode, FAR const char *format, + va_list param) noreturn_function; +static void dropbear_session_exit(int exitcode, FAR const char *format, + va_list param) +{ + char exitmsg[150]; + char fullmsg[300]; + char fromaddr[60]; + int signal_pipe[2]; + + vsnprintf(exitmsg, sizeof(exitmsg), format, param); + + fromaddr[0] = '\0'; + + if (svr_ses.addrstring != NULL) + { + snprintf(fromaddr, sizeof(fromaddr), " from <%s>", svr_ses.addrstring); + } + + if (!ses.init_done) + { + snprintf(fullmsg, sizeof(fullmsg), "Early exit%s: %s", fromaddr, + exitmsg); + } + else if (ses.authstate.authdone) + { + snprintf(fullmsg, sizeof(fullmsg), "Exit (%s)%s: %s", + ses.authstate.pw_name, fromaddr, exitmsg); + } + else if (ses.authstate.pw_name != NULL) + { + snprintf(fullmsg, sizeof(fullmsg), + "Exit before auth%s: (user '%s', %u fails): %s", + fromaddr, ses.authstate.pw_name, ses.authstate.failcount, + exitmsg); + } + else + { + snprintf(fullmsg, sizeof(fullmsg), "Exit before auth%s: %s", fromaddr, + exitmsg); + } + + dropbear_log(LOG_INFO, "%s", fullmsg); + + signal_pipe[0] = ses.signal_pipe[0]; + signal_pipe[1] = ses.signal_pipe[1]; + session_cleanup(); + + if (signal_pipe[0] > STDERR_FILENO) + { + m_close(signal_pipe[0]); + } + + if (signal_pipe[1] > STDERR_FILENO && signal_pipe[1] != signal_pipe[0]) + { + m_close(signal_pipe[1]); + } + + memset(&ses, 0, sizeof(ses)); + memset(&svr_ses, 0, sizeof(svr_ses)); + + g_session_exitcode = exitcode; + longjmp(g_session_exit_jmp, 1); + + while (1) + { + } +} + +static void dropbear_setup(FAR const char *port) +{ + /* Load the host key with -r so it lives in the persistent (epoch 0) + * allocation rather than being created lazily inside a per-session malloc + * epoch (-R), which would free it when the first session ends and leave a + * dangling svr_opts.hostkey for the next connection. + */ + + FAR char *argv[] = + { + "dropbear", + "-F", + "-p", + (FAR char *)port, + "-r", + CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH, + NULL + }; + + _dropbear_exit = svr_dropbear_exit; + _dropbear_log = svr_dropbear_log; + + disallow_core(); + svr_getopts(6, argv); + seedrandom(); + crypto_init(); + +#ifdef CONFIG_NETUTILS_DROPBEAR_GENERATE_HOSTKEY + /* Generate the ECDSA host key now if it is missing. signkey_generate() + * with skip_exist=1 silently succeeds when the file already exists. + */ + + if (signkey_generate(DROPBEAR_SIGNKEY_ECDSA_NISTP256, 0, + CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH, 1) + == DROPBEAR_FAILURE) + { + dropbear_exit("failed to generate host key"); + } +#endif + + load_all_hostkeys(); + + if (dropbear_auth_initialize() < 0) + { + dropbear_exit("failed to initialize password auth"); + } +} + +static void dropbear_run_session(int childsock) +{ + dropbear_exit_handler_t saved_exit; + + saved_exit = _dropbear_exit; + _dropbear_exit = dropbear_session_exit; + + m_malloc_set_epoch(1); + + if (setjmp(g_session_exit_jmp) == 0) + { + svr_session(childsock, -1); + } + + m_malloc_free_epoch(1, 1); + m_malloc_set_epoch(0); + + _dropbear_exit = saved_exit; + + if (g_session_exitcode != EXIT_SUCCESS) + { + dropbear_log(LOG_WARNING, "session exited with status %d", + g_session_exitcode); + } +} + +static size_t dropbear_listen_sockets(FAR int *socks, size_t sockcount, + FAR int *maxfd) +{ + FAR char *errstring = NULL; + size_t sockpos = 0; + unsigned int i; + + for (i = 0; i < svr_opts.portcount; i++) + { + int nsock; + unsigned int n; + + nsock = dropbear_listen(svr_opts.addresses[i], svr_opts.ports[i], + &socks[sockpos], sockcount - sockpos, + &errstring, maxfd, svr_opts.interface); + + if (nsock < 0) + { + dropbear_log(LOG_WARNING, "failed listening on '%s': %s", + svr_opts.ports[i], + errstring != NULL ? errstring : "unknown error"); + m_free(errstring); + errstring = NULL; + continue; + } + + for (n = 0; n < (unsigned int)nsock; n++) + { + set_sock_priority(socks[sockpos + n], DROPBEAR_PRIO_LOWDELAY); + } + + sockpos += (size_t)nsock; + } + + return sockpos; +} + +static size_t dropbear_wait_listen_sockets(FAR int *socks, size_t sockcount, + FAR int *maxfd) +{ + int retries = 0; + int retry_delay = 1; + + while (1) + { + size_t count; + + *maxfd = -1; + count = dropbear_listen_sockets(socks, sockcount, maxfd); + + if (count > 0) + { + return count; + } + +#if CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES > 0 + if (retries++ >= CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRIES) + { + dropbear_exit("no listening ports available"); + } +#else + retries++; +#endif + + dropbear_log(LOG_WARNING, "Retry %d in %d seconds...", retries, + retry_delay); + sleep(retry_delay); + + retry_delay *= 2; + + if (retry_delay > CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX) + { + retry_delay = CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *port = DROPBEAR_PORT_STRING(CONFIG_NETUTILS_DROPBEAR_PORT); + int listensocks[MAX_LISTEN_ADDR]; + int maxfd = -1; + + if (argc > 1) + { + port = argv[1]; + } + + dropbear_setup(port); + + dropbear_wait_listen_sockets(listensocks, MAX_LISTEN_ADDR, &maxfd); + + printf("dropbear: listening on port %s\n", port); + + while (1) + { + struct sockaddr_storage remoteaddr; + socklen_t remoteaddrlen = sizeof(remoteaddr); + FAR char *remote_host = NULL; + FAR char *remote_port = NULL; + int childsock; + + childsock = accept(listensocks[0], (FAR struct sockaddr *)&remoteaddr, + &remoteaddrlen); + + if (childsock < 0) + { + continue; + } + + getaddrstring(&remoteaddr, &remote_host, &remote_port, 0); + dropbear_log(LOG_INFO, "connection from %s:%s", + remote_host != NULL ? remote_host : "?", + remote_port != NULL ? remote_port : "?"); + m_free(remote_host); + m_free(remote_port); + + seedrandom(); + dropbear_run_session(childsock); + close(childsock); + } + + return EXIT_SUCCESS; +} diff --git a/netutils/dropbear/dropbear_nshsession.c b/netutils/dropbear/dropbear_nshsession.c new file mode 100644 index 000000000..0af7e68ea --- /dev/null +++ b/netutils/dropbear/dropbear_nshsession.c @@ -0,0 +1,829 @@ +/**************************************************************************** + * apps/netutils/dropbear/dropbear_nshsession.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "includes.h" +#include "channel.h" +#include "chansession.h" +#include "dbutil.h" +#include "session.h" + +#include "nsh.h" +#include "nsh_console.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dropbear_nshsession_s +{ + int pty_readfd; + int pty_writefd; + pid_t nsh_pid; + volatile bool done; + pthread_t waiter; + bool waiter_started; + bool have_winsize; + bool has_pty; + struct winsize win; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_nsh_main(int argc, FAR char *argv[]) +{ + FAR struct console_stdio_s *pstate; + int ret; + + pstate = nsh_newconsole(true); + if (pstate == NULL) + { + dropbear_log(LOG_WARNING, "failed to create NSH console"); + return -ENOMEM; + } + + ret = nsh_session(pstate, NSH_LOGIN_NONE, argc, argv); + dropbear_log(LOG_INFO, "NSH session exited: %d", ret); + + nsh_exit(&pstate->cn_vtbl, ret); + return ret; +} + +static FAR void *dropbear_nsh_waiter(FAR void *arg) +{ + FAR struct dropbear_nshsession_s *sess = arg; + unsigned char ch = 0; + int status; + int ret; + + ret = waitpid(sess->nsh_pid, &status, 0); + if (ret < 0) + { + dropbear_log(LOG_WARNING, "NSH session wait failed: %s", + strerror(errno)); + } + + sess->nsh_pid = -1; + sess->done = true; + + /* Match Dropbear's SIGCHLD wakeup path. The session loop checks channel + * close conditions when this pipe becomes readable. + */ + + if (ses.signal_pipe[1] >= 0) + { + write(ses.signal_pipe[1], &ch, sizeof(ch)); + } + + return NULL; +} + +static int dropbear_newchansess(FAR struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess; + + sess = m_malloc(sizeof(*sess)); + memset(sess, 0, sizeof(*sess)); + sess->pty_readfd = -1; + sess->pty_writefd = -1; + sess->nsh_pid = -1; + + channel->typedata = sess; + channel->prio = DROPBEAR_PRIO_LOWDELAY; + return 0; +} + +static int dropbear_sesscheckclose(FAR struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + return sess != NULL && sess->done; +} + +static int dropbear_setup_spawn_attrs(FAR posix_spawnattr_t *attr, + FAR const char **errmsg) +{ + struct sched_param param; + int rc; + + param.sched_priority = CONFIG_NETUTILS_DROPBEAR_SHELL_PRIORITY; + *errmsg = "spawn priority setup"; + rc = posix_spawnattr_setschedparam(attr, ¶m); + if (rc != 0) + { + return rc; + } + + *errmsg = "spawn stack setup"; + rc = posix_spawnattr_setstacksize( + attr, CONFIG_NETUTILS_DROPBEAR_SHELL_STACKSIZE); + if (rc != 0) + { + return rc; + } + + *errmsg = "spawn flags setup"; + return posix_spawnattr_setflags(attr, POSIX_SPAWN_SETSCHEDPARAM); +} + +static int +dropbear_setup_spawn_stdio(FAR posix_spawn_file_actions_t *actions, + int slavefd) +{ + static const int stdio_fds[] = + { + STDIN_FILENO, + STDOUT_FILENO, + STDERR_FILENO + }; + + int i; + int rc; + + for (i = 0; i < nitems(stdio_fds); i++) + { + rc = posix_spawn_file_actions_adddup2(actions, slavefd, stdio_fds[i]); + if (rc != 0) + { + return rc; + } + } + + return 0; +} + +#ifdef CONFIG_NETUTILS_DROPBEAR_SCP +static int +dropbear_setup_spawn_stdio3(FAR posix_spawn_file_actions_t *actions, + int stdinfd, int stdoutfd, int stderrfd) +{ + int rc; + + rc = posix_spawn_file_actions_adddup2(actions, stdinfd, STDIN_FILENO); + if (rc != 0) + { + return rc; + } + + rc = posix_spawn_file_actions_adddup2(actions, stdoutfd, STDOUT_FILENO); + if (rc != 0) + { + return rc; + } + + return posix_spawn_file_actions_adddup2(actions, stderrfd, STDERR_FILENO); +} +#endif /* CONFIG_NETUTILS_DROPBEAR_SCP */ + +static int +dropbear_setup_spawn_close(FAR posix_spawn_file_actions_t *actions, + int fd) +{ + if (fd <= STDERR_FILENO) + { + return 0; + } + + return posix_spawn_file_actions_addclose(actions, fd); +} + +static void dropbear_close_fd(FAR int *fd) +{ + if (*fd >= 0) + { + close(*fd); + *fd = -1; + } +} + +static int dropbear_start_nsh(FAR struct dropbear_channel *channel, + FAR struct dropbear_nshsession_s *sess) +{ + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + FAR const char *errmsg; + int masterfd; + int slavefd; + int writefd; + int rc; + + if (sess->nsh_pid >= 0) + { + dropbear_log(LOG_WARNING, "NSH session already running"); + return DROPBEAR_FAILURE; + } + + rc = openpty(&masterfd, &slavefd, NULL, NULL, + sess->have_winsize ? &sess->win : NULL); + if (rc < 0) + { + dropbear_log(LOG_WARNING, "openpty failed: %s", strerror(errno)); + return DROPBEAR_FAILURE; + } + + writefd = dup(masterfd); + if (writefd < 0) + { + dropbear_log(LOG_WARNING, "pty dup failed: %s", strerror(errno)); + close(masterfd); + close(slavefd); + return DROPBEAR_FAILURE; + } + + rc = posix_spawn_file_actions_init(&actions); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn actions init failed: %s", + strerror(rc)); + close(masterfd); + close(writefd); + close(slavefd); + return DROPBEAR_FAILURE; + } + + rc = posix_spawnattr_init(&attr); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn attr init failed: %s", strerror(rc)); + goto err_with_actions; + } + + rc = dropbear_setup_spawn_attrs(&attr, &errmsg); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "%s failed: %s", errmsg, strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_stdio(&actions, slavefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdio setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, masterfd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn master close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, writefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn write close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, slavefd); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn slave close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + sess->nsh_pid = task_spawn("dropbear nsh", dropbear_nsh_main, &actions, + &attr, NULL, NULL); + if (sess->nsh_pid < 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH task: %s", + strerror(-sess->nsh_pid)); + goto err_with_attr; + } + + close(slavefd); + slavefd = -1; + + rc = pthread_create(&sess->waiter, NULL, dropbear_nsh_waiter, sess); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH waiter: %s", + strerror(rc)); + close(masterfd); + close(writefd); + masterfd = -1; + writefd = -1; + kill(sess->nsh_pid, SIGTERM); + waitpid(sess->nsh_pid, NULL, 0); + sess->nsh_pid = -1; + goto err_with_attr; + } + + sess->waiter_started = true; + sess->has_pty = true; + sess->pty_readfd = masterfd; + sess->pty_writefd = writefd; + + channel->readfd = masterfd; + channel->writefd = writefd; + channel->bidir_fd = 0; + + setnonblocking(channel->readfd); + setnonblocking(channel->writefd); + ses.maxfd = MAX(ses.maxfd, channel->readfd); + ses.maxfd = MAX(ses.maxfd, channel->writefd); + + posix_spawnattr_destroy(&attr); + posix_spawn_file_actions_destroy(&actions); + + dropbear_log(LOG_INFO, "NSH PTY session started"); + return DROPBEAR_SUCCESS; + +err_with_attr: + posix_spawnattr_destroy(&attr); + +err_with_actions: + posix_spawn_file_actions_destroy(&actions); + + if (masterfd >= 0) + { + close(masterfd); + } + + if (writefd >= 0) + { + close(writefd); + } + + if (slavefd >= 0) + { + close(slavefd); + } + + sess->nsh_pid = -1; + return DROPBEAR_FAILURE; +} + +#ifdef CONFIG_NETUTILS_DROPBEAR_SCP +static int dropbear_start_exec(FAR struct dropbear_channel *channel, + FAR struct dropbear_nshsession_s *sess, + FAR char *cmd) +{ + posix_spawn_file_actions_t actions; + posix_spawnattr_t attr; + FAR const char *errmsg; + FAR char * const argv[] = + { + "-c", + cmd, + NULL + }; + + int inpipe[2] = + { + -1, + -1 + }; + + int outpipe[2] = + { + -1, + -1 + }; + + int errpipe[2] = + { + -1, + -1 + }; + + int rc; + + if (sess->nsh_pid >= 0) + { + dropbear_log(LOG_WARNING, "NSH exec already running"); + return DROPBEAR_FAILURE; + } + + if (pipe(inpipe) < 0 || pipe(outpipe) < 0 || pipe(errpipe) < 0) + { + dropbear_log(LOG_WARNING, "exec pipe setup failed: %s", + strerror(errno)); + goto err_with_pipes; + } + + rc = posix_spawn_file_actions_init(&actions); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn actions init failed: %s", + strerror(rc)); + goto err_with_pipes; + } + + rc = posix_spawnattr_init(&attr); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn attr init failed: %s", strerror(rc)); + goto err_with_actions; + } + + rc = dropbear_setup_spawn_attrs(&attr, &errmsg); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "%s failed: %s", errmsg, strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_stdio3(&actions, inpipe[0], outpipe[1], + errpipe[1]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdio setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, inpipe[0]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdin close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, inpipe[1]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdin writer close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, outpipe[0]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdout reader close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, outpipe[1]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stdout close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, errpipe[0]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stderr reader close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + rc = dropbear_setup_spawn_close(&actions, errpipe[1]); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "spawn stderr close setup failed: %s", + strerror(rc)); + goto err_with_attr; + } + + sess->nsh_pid = task_spawn("dropbear exec", dropbear_nsh_main, &actions, + &attr, argv, NULL); + if (sess->nsh_pid < 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH exec task: %s", + strerror(-sess->nsh_pid)); + goto err_with_attr; + } + + dropbear_close_fd(&inpipe[0]); + dropbear_close_fd(&outpipe[1]); + dropbear_close_fd(&errpipe[1]); + + rc = pthread_create(&sess->waiter, NULL, dropbear_nsh_waiter, sess); + if (rc != 0) + { + dropbear_log(LOG_WARNING, "failed to create NSH exec waiter: %s", + strerror(rc)); + dropbear_close_fd(&inpipe[1]); + dropbear_close_fd(&outpipe[0]); + dropbear_close_fd(&errpipe[0]); + kill(sess->nsh_pid, SIGTERM); + waitpid(sess->nsh_pid, NULL, 0); + sess->nsh_pid = -1; + goto err_with_attr; + } + + sess->waiter_started = true; + sess->has_pty = false; + sess->pty_readfd = -1; + sess->pty_writefd = -1; + + channel->readfd = outpipe[0]; + channel->writefd = inpipe[1]; + channel->errfd = errpipe[0]; + channel->bidir_fd = 0; + + setnonblocking(channel->readfd); + setnonblocking(channel->writefd); + setnonblocking(channel->errfd); + ses.maxfd = MAX(ses.maxfd, channel->readfd); + ses.maxfd = MAX(ses.maxfd, channel->writefd); + ses.maxfd = MAX(ses.maxfd, channel->errfd); + + posix_spawnattr_destroy(&attr); + posix_spawn_file_actions_destroy(&actions); + + dropbear_log(LOG_INFO, "NSH exec started: %s", cmd); + return DROPBEAR_SUCCESS; + +err_with_attr: + posix_spawnattr_destroy(&attr); + +err_with_actions: + posix_spawn_file_actions_destroy(&actions); + +err_with_pipes: + dropbear_close_fd(&inpipe[0]); + dropbear_close_fd(&inpipe[1]); + dropbear_close_fd(&outpipe[0]); + dropbear_close_fd(&outpipe[1]); + dropbear_close_fd(&errpipe[0]); + dropbear_close_fd(&errpipe[1]); + sess->nsh_pid = -1; + return DROPBEAR_FAILURE; +} +#endif /* CONFIG_NETUTILS_DROPBEAR_SCP */ + +static void dropbear_parse_winsize(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int cols = buf_getint(ses.payload); + unsigned int rows = buf_getint(ses.payload); + unsigned int width = buf_getint(ses.payload); + unsigned int height = buf_getint(ses.payload); + + sess->win.ws_col = cols; + sess->win.ws_row = rows; + sess->win.ws_xpixel = width; + sess->win.ws_ypixel = height; + sess->have_winsize = true; + + if (sess->pty_readfd >= 0) + { + ioctl(sess->pty_readfd, TIOCSWINSZ, (unsigned long)&sess->win); + } +} + +static int dropbear_handle_pty_req(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int len; + FAR char *term; + FAR char *modes; + + term = buf_getstring(ses.payload, &len); + dropbear_parse_winsize(sess); + modes = buf_getstring(ses.payload, &len); + + m_free(term); + m_free(modes); + return DROPBEAR_SUCCESS; +} + +static int dropbear_signal_from_name(FAR const char *name) +{ + int i; + + for (i = 0; signames[i].name != NULL; i++) + { + if (strcmp(name, signames[i].name) == 0) + { + return signames[i].signal; + } + } + + return -EINVAL; +} + +static int dropbear_write_terminal_signal( + FAR struct dropbear_nshsession_s *sess, + int signo) +{ +#ifdef CONFIG_TTY_SIGINT + unsigned char ch; + ssize_t nwritten; + + if (signo == SIGINT && sess->has_pty && sess->pty_writefd >= 0) + { + ch = CONFIG_TTY_SIGINT_CHAR; + nwritten = write(sess->pty_writefd, &ch, sizeof(ch)); + if (nwritten == sizeof(ch)) + { + return DROPBEAR_SUCCESS; + } + + dropbear_log(LOG_WARNING, "SSH terminal INT failed: %s", + strerror(errno)); + return DROPBEAR_FAILURE; + } +#endif + + return DROPBEAR_FAILURE; +} + +static int dropbear_handle_signal(FAR struct dropbear_nshsession_s *sess) +{ + unsigned int len; + FAR char *name; + int signo; + int ret; + + name = buf_getstring(ses.payload, &len); + signo = dropbear_signal_from_name(name); + if (signo < 0) + { + dropbear_log(LOG_WARNING, "unsupported SSH signal '%s'", name); + m_free(name); + return DROPBEAR_FAILURE; + } + + ret = dropbear_write_terminal_signal(sess, signo); + if (ret == DROPBEAR_SUCCESS) + { + dropbear_log(LOG_INFO, "SSH signal '%s' sent to PTY", name); + m_free(name); + return DROPBEAR_SUCCESS; + } + + if (sess->nsh_pid <= 0) + { + dropbear_log(LOG_WARNING, "SSH signal '%s' has no NSH session", name); + m_free(name); + return DROPBEAR_FAILURE; + } + + ret = kill(sess->nsh_pid, signo); + if (ret < 0) + { + dropbear_log(LOG_WARNING, "SSH signal '%s' failed: %s", + name, strerror(errno)); + m_free(name); + return DROPBEAR_FAILURE; + } + + dropbear_log(LOG_INFO, "SSH signal '%s' sent to NSH session", name); + m_free(name); + return DROPBEAR_SUCCESS; +} + +static void dropbear_chansessionrequest(FAR struct dropbear_channel *channel) +{ + unsigned int typelen; + FAR char *type = buf_getstring(ses.payload, &typelen); + unsigned char wantreply = buf_getbool(ses.payload); + FAR struct dropbear_nshsession_s *sess = channel->typedata; + int ret = DROPBEAR_FAILURE; + + TRACE(("dropbear_chansessionrequest: type='%s'", type)) + + if (strcmp(type, "pty-req") == 0) + { + ret = dropbear_handle_pty_req(sess); + } + else if (strcmp(type, "shell") == 0) + { + ret = dropbear_start_nsh(channel, sess); + } + else if (strcmp(type, "exec") == 0) + { +#ifdef CONFIG_NETUTILS_DROPBEAR_SCP + unsigned int cmdlen; + FAR char *cmd; + + cmd = buf_getstring(ses.payload, &cmdlen); + ret = dropbear_start_exec(channel, sess, cmd); + m_free(cmd); +#else + dropbear_log(LOG_WARNING, "SSH exec requests are not supported"); +#endif + } + else if (strcmp(type, "window-change") == 0) + { + dropbear_parse_winsize(sess); + ret = DROPBEAR_SUCCESS; + } + else if (strcmp(type, "signal") == 0) + { + ret = dropbear_handle_signal(sess); + } + else if (strcmp(type, "break") == 0) + { + ret = DROPBEAR_SUCCESS; + } + else + { + TRACE(("dropbear_chansessionrequest: unhandled type '%s'", type)) + } + + if (wantreply) + { + if (ret == DROPBEAR_SUCCESS) + { + send_msg_channel_success(channel); + } + else + { + send_msg_channel_failure(channel); + } + } + + m_free(type); +} + +static void +dropbear_closechansess(FAR const struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + if (sess != NULL) + { + sess->done = true; + } +} + +static void +dropbear_cleanupchansess(FAR const struct dropbear_channel *channel) +{ + FAR struct dropbear_nshsession_s *sess = channel->typedata; + + if (sess == NULL) + { + return; + } + + sess->done = true; + + if (sess->nsh_pid > 0) + { + kill(sess->nsh_pid, SIGTERM); + } + + if (sess->waiter_started) + { + pthread_join(sess->waiter, NULL); + sess->waiter_started = false; + } + + m_free(sess); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Chansession lifecycle hooks invoked from svr-session.c. Upstream + * svr-chansession.c uses these to set up and reap the global childpids[] / + * SIGCHLD machinery; the NSH bridge tracks each session's child through its + * own waiter thread (dropbear_nsh_waiter), so both are no-ops here. + */ + +void svr_chansessinitialise(void) +{ +} + +void svr_chansess_checksignal(void) +{ +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_chantype svrchansess = +{ + "session", + dropbear_newchansess, + dropbear_sesscheckclose, + dropbear_chansessionrequest, + dropbear_closechansess, + dropbear_cleanupchansess, +}; diff --git a/netutils/dropbear/patch/0001-guard-platform-declarations.patch b/netutils/dropbear/patch/0001-guard-platform-declarations.patch new file mode 100644 index 000000000..7eb1dfcd2 --- /dev/null +++ b/netutils/dropbear/patch/0001-guard-platform-declarations.patch @@ -0,0 +1,26 @@ +--- a/src/dbutil.c ++++ b/src/dbutil.c +@@ -66,6 +66,10 @@ + #include "session.h" + #include "atomicio.h" + ++#ifdef DROPBEAR_NUTTX ++int execv(const char *path, char * const argv[]); ++#endif ++ + #define MAX_FMT 100 + + static void generic_dropbear_exit(int exitcode, const char* format, +--- a/src/signkey.h ++++ b/src/signkey.h +@@ -150,9 +150,9 @@ + void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf); + #if DROPBEAR_SIGNKEY_VERIFY + int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf); + int sk_buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf, char* app, unsigned int applen); +-char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); + #endif ++char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen); + int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, + const unsigned char* algoname, unsigned int algolen, + const buffer * line, char ** fingerprint); diff --git a/netutils/dropbear/patch/0002-use-nuttx-passwd-auth.patch b/netutils/dropbear/patch/0002-use-nuttx-passwd-auth.patch new file mode 100644 index 000000000..5e71d73da --- /dev/null +++ b/netutils/dropbear/patch/0002-use-nuttx-passwd-auth.patch @@ -0,0 +1,87 @@ +--- a/src/svr-authpasswd.c ++++ b/src/svr-authpasswd.c +@@ -33,6 +33,77 @@ + + #if DROPBEAR_SVR_PASSWORD_AUTH + ++#if DROPBEAR_NUTTX_PASSWD ++ ++/* Process a password auth request, sending success or failure messages as ++ * appropriate */ ++void svr_auth_password(int valid_user) { ++ ++ char * password = NULL; ++ unsigned int passwordlen; ++ unsigned int changepw; ++ int auth_ok = 0; ++ ++ /* check if client wants to change password */ ++ changepw = buf_getbool(ses.payload); ++ if (changepw) { ++ /* not implemented by this server */ ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ password = buf_getstring(ses.payload, &passwordlen); ++ if (valid_user && passwordlen <= DROPBEAR_MAX_PASSWORD_LEN && ++ strlen(password) == passwordlen) { ++ auth_ok = dropbear_verify_password(ses.authstate.pw_name, password); ++ } ++ m_burn(password, passwordlen); ++ m_free(password); ++ ++ /* After we have got the payload contents we can exit if the username ++ is invalid. Invalid users have already been logged. */ ++ if (!valid_user) { ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ if (passwordlen > DROPBEAR_MAX_PASSWORD_LEN) { ++ dropbear_log(LOG_WARNING, ++ "Too-long password attempt for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_failure(0, 1); ++ return; ++ } ++ ++ if (auth_ok == DROPBEAR_SUCCESS) { ++ if (svr_opts.multiauthmethod && (ses.authstate.authtypes & ~AUTH_TYPE_PASSWORD)) { ++ /* successful password authentication, but extra auth required */ ++ dropbear_log(LOG_NOTICE, ++ "Password auth succeeded for '%s' from %s, extra auth required", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ ses.authstate.authtypes &= ~AUTH_TYPE_PASSWORD; /* password auth ok, delete the method flag */ ++ send_msg_userauth_failure(1, 0); /* Send partial success */ ++ } else { ++ /* successful authentication */ ++ dropbear_log(LOG_NOTICE, ++ "Password auth succeeded for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_success(); ++ } ++ } else { ++ dropbear_log(LOG_WARNING, ++ "Bad password attempt for '%s' from %s", ++ ses.authstate.pw_name, ++ svr_ses.addrstring); ++ send_msg_userauth_failure(0, 1); ++ } ++} ++ ++#else ++ + /* not constant time when strings are differing lengths. + string content isn't leaked, and crypt hashes are predictable length. */ + static int constant_time_strcmp(const char* a, const char* b) { +@@ -131,4 +202,6 @@ + } + } + ++#endif /* DROPBEAR_NUTTX_PASSWD */ ++ + #endif diff --git a/netutils/dropbear/patch/0003-allow-localoptions-to-override-tracking-malloc.patch b/netutils/dropbear/patch/0003-allow-localoptions-to-override-tracking-malloc.patch new file mode 100644 index 000000000..b1dc3e52c --- /dev/null +++ b/netutils/dropbear/patch/0003-allow-localoptions-to-override-tracking-malloc.patch @@ -0,0 +1,24 @@ +--- a/src/sysoptions.h ++++ b/src/sysoptions.h +@@ -183,7 +183,9 @@ defined(__has_feature) + #define LTC_ECC521 + #endif + +-#define DROPBEAR_LTC_PRNG (DROPBEAR_ECC) ++#ifndef DROPBEAR_LTC_PRNG ++#define DROPBEAR_LTC_PRNG (DROPBEAR_ECC) ++#endif + + /* RSA can be vulnerable to timing attacks which use the time required for + * signing to guess the private key. Blinding avoids this attack, though makes +@@ -436,7 +438,9 @@ defined(__has_feature) + #define DROPBEAR_CLIENT_TCP_FAST_OPEN 0 + #endif + +-#define DROPBEAR_TRACKING_MALLOC (DROPBEAR_FUZZ) ++#ifndef DROPBEAR_TRACKING_MALLOC ++#define DROPBEAR_TRACKING_MALLOC (DROPBEAR_FUZZ) ++#endif + + /* Used to work around Memory Sanitizer false positives */ + #if defined(__has_feature) diff --git a/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch b/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch new file mode 100644 index 000000000..2ea588057 --- /dev/null +++ b/netutils/dropbear/patch/0004-use-nuttx-unused-macro.patch @@ -0,0 +1,202 @@ +--- a/src/compat.c ++++ b/src/compat.c +@@ -89,6 +89,6 @@ + #ifndef HAVE_GETUSERSHELL + static char **curshell, **shells, *strings; +-static char **initshells(); ++static char **initshells(void); + #endif + + #ifndef HAVE_STRLCPY +@@ -234,7 +234,7 @@ + curshell = initshells(); + } + +-static char **initshells() { ++static char **initshells(void) { + static const char *okshells[] = { COMPAT_USER_SHELLS, NULL }; + register char **sp, *cp; + register FILE *fp; +--- a/src/common-algo.c ++++ b/src/common-algo.c +@@ -39,16 +39,25 @@ + /* This file (algo.c) organises the ciphers which can be used, and is used to + * decide which ciphers/hashes/compression/signing to use during key exchange*/ + +-static int void_cipher(const unsigned char* in, unsigned char* out, +- unsigned long len, void* UNUSED(cipher_state)) { ++static int void_cipher(const unsigned char* in, unsigned char* out, ++ unsigned long len, void* cipher_state) { ++ UNUSED(cipher_state); ++ + if (in != out) { + memmove(out, in, len); + } + return CRYPT_OK; + } + +-static int void_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), +- const unsigned char* UNUSED(key), +- int UNUSED(keylen), int UNUSED(num_rounds), void* UNUSED(cipher_state)) { ++static int void_start(int cipher, const unsigned char* IV, ++ const unsigned char* key, ++ int keylen, int num_rounds, void* cipher_state) { ++ UNUSED(cipher); ++ UNUSED(IV); ++ UNUSED(key); ++ UNUSED(keylen); ++ UNUSED(num_rounds); ++ UNUSED(cipher_state); ++ + return CRYPT_OK; + } + +--- a/src/common-channel.c ++++ b/src/common-channel.c +@@ -410,7 +410,9 @@ + + #ifndef HAVE_WRITEV + static int writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, +- const unsigned char *UNUSED(moredata), unsigned int *morelen) { ++ const unsigned char *moredata, unsigned int *morelen) { ++ UNUSED(moredata); ++ + + unsigned char *circ_p1, *circ_p2; + unsigned int circ_len1, circ_len2; +--- a/src/dbutil.c ++++ b/src/dbutil.c +@@ -138,7 +138,9 @@ + } + +-static void generic_dropbear_log(int UNUSED(priority), const char* format, ++static void generic_dropbear_log(int priority, const char* format, + va_list param) { ++ UNUSED(priority); ++ + + char printbuf[1024]; + +--- a/src/netio.c ++++ b/src/netio.c +@@ -47,7 +47,10 @@ + } + } + +-static void cancel_callback(int result, int sock, void* UNUSED(data), const char* UNUSED(errstring)) { ++static void cancel_callback(int result, int sock, void* data, const char* errstring) { ++ UNUSED(data); ++ UNUSED(errstring); ++ + if (result == DROPBEAR_SUCCESS) + { + m_close(sock); +--- a/src/ltc_prng.c ++++ b/src/ltc_prng.c +@@ -32,8 +32,10 @@ + @param prng [out] The PRNG state to initialize + @return CRYPT_OK if successful + */ +-int dropbear_prng_start(prng_state* UNUSED(prng)) ++int dropbear_prng_start(prng_state* prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -44,8 +46,12 @@ + @param prng PRNG state to update + @return CRYPT_OK if successful + */ +-int dropbear_prng_add_entropy(const unsigned char* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(prng)) ++int dropbear_prng_add_entropy(const unsigned char* in, unsigned long inlen, prng_state* prng) + { ++ UNUSED(in); ++ UNUSED(inlen); ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -54,8 +60,10 @@ + @param prng The PRNG to make active + @return CRYPT_OK if successful + */ +-int dropbear_prng_ready(prng_state* UNUSED(prng)) ++int dropbear_prng_ready(prng_state* prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -66,8 +74,10 @@ + @param prng The active PRNG to read from + @return Number of octets read + */ +-unsigned long dropbear_prng_read(unsigned char* out, unsigned long outlen, prng_state* UNUSED(prng)) ++unsigned long dropbear_prng_read(unsigned char* out, unsigned long outlen, prng_state* prng) + { ++ UNUSED(prng); ++ + LTC_ARGCHK(out != NULL); + genrandom(out, outlen); + return outlen; +@@ -78,8 +88,10 @@ + @param prng The PRNG to terminate + @return CRYPT_OK if successful + */ +-int dropbear_prng_done(prng_state* UNUSED(prng)) ++int dropbear_prng_done(prng_state* prng) + { ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +@@ -90,8 +102,11 @@ + @param prng The PRNG to export + @return CRYPT_OK if successful + */ +-int dropbear_prng_export(unsigned char* UNUSED(out), unsigned long* outlen, prng_state* UNUSED(prng)) ++int dropbear_prng_export(unsigned char* out, unsigned long* outlen, prng_state* prng) + { ++ UNUSED(out); ++ UNUSED(prng); ++ + LTC_ARGCHK(outlen != NULL); + + *outlen = 0; +@@ -105,8 +120,12 @@ + @param prng The PRNG to import + @return CRYPT_OK if successful + */ +-int dropbear_prng_import(const unsigned char* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(prng)) ++int dropbear_prng_import(const unsigned char* in, unsigned long inlen, prng_state* prng) + { ++ UNUSED(in); ++ UNUSED(inlen); ++ UNUSED(prng); ++ + return CRYPT_OK; + } + +--- a/src/chachapoly.c ++++ b/src/chachapoly.c +@@ -43,9 +43,13 @@ + const struct dropbear_cipher dropbear_chachapoly = + {&dummy, CHACHA20_KEY_LEN*2, CHACHA20_BLOCKSIZE}; + +-static int dropbear_chachapoly_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), ++static int dropbear_chachapoly_start(int cipher, const unsigned char* IV, + const unsigned char *key, int keylen, +- int UNUSED(num_rounds), dropbear_chachapoly_state *state) { ++ int num_rounds, dropbear_chachapoly_state *state) { ++ UNUSED(cipher); ++ UNUSED(IV); ++ UNUSED(num_rounds); ++ + int err; + + TRACE2(("enter dropbear_chachapoly_start")) diff --git a/netutils/dropbear/port/config.h b/netutils/dropbear/port/config.h new file mode 100644 index 000000000..0f5355fd7 --- /dev/null +++ b/netutils/dropbear/port/config.h @@ -0,0 +1,20 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/config.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H +#define __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H + +/* Dropbear looks for a file named config.h. Keep this wrapper name stable; + * NuttX-specific autoconf replacements live in nuttx_config.h. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "nuttx_config.h" + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_CONFIG_H */ diff --git a/netutils/dropbear/port/dropbear_chachapoly.c b/netutils/dropbear/port/dropbear_chachapoly.c new file mode 100644 index 000000000..f670d14ca --- /dev/null +++ b/netutils/dropbear/port/dropbear_chachapoly.c @@ -0,0 +1,389 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_chachapoly.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* chacha20-poly1305@openssh.com backed by the NuttX crypto device. + * + * The SSH construction (see OpenSSH PROTOCOL.chacha20poly1305) uses the + * original DJB ChaCha20 parameterization: a 64-bit block counter in state + * words 12..13 and a 64-bit nonce (the packet sequence number, big endian) + * in words 14..15. This maps to the kernel CRYPTO_CHACHA20_DJB transform, + * whose 16-byte IV is loaded verbatim into words 12..15 as a 64-bit + * little-endian counter followed by the 64-bit nonce. The Poly1305 tag is + * computed with the kernel CRYPTO_POLY1305 transform, keyed with the first + * keystream block (counter 0) of the main key, as the protocol requires. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include +#include +#include +#include + +#include + +#include "algo.h" +#include "dbutil.h" +#include "chachapoly.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CHACHA20_KEY_LEN 32 +#define CHACHA20_BLOCKSIZE 8 +#define CHACHA20_IV_LEN 16 +#define POLY1305_KEY_LEN 32 +#define POLY1305_TAG_LEN 16 + +/* The keystream comes from the NuttX crypto device, so each upstream + * chacha_state is unused and its input[] buffer just stores the 32-byte key, + * keeping the upstream header unpatched. + */ + +#define KEY_MAIN(s) ((FAR unsigned char *)(s)->chacha.input) +#define KEY_HEADER(s) ((FAR unsigned char *)(s)->header.input) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct ltc_cipher_descriptor g_dropbear_chachapoly_dummy = +{ + .name = NULL +}; + +static const struct dropbear_hash g_dropbear_chachapoly_mac = +{ + NULL, + POLY1305_KEY_LEN, + POLY1305_TAG_LEN +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_cipher dropbear_chachapoly = +{ + &g_dropbear_chachapoly_dummy, + CHACHA20_KEY_LEN * 2, + CHACHA20_BLOCKSIZE +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Open a fresh /dev/crypto session descriptor. NuttX file descriptors are + * owned by the task group, so the fd is never cached across calls: each + * operation opens it in the task that uses it and closes it when done. + */ + +static int dropbear_cryptodev_open(void) +{ + int fd; + int cfd; + + fd = open("/dev/crypto", O_RDWR); + if (fd < 0) + { + return -1; + } + + if (ioctl(fd, CRIOGET, &cfd) < 0) + { + close(fd); + return -1; + } + + close(fd); + + /* Keep the descriptor out of the NSH shells Dropbear forks and execs for + * each SSH session. + */ + + if (fcntl(cfd, F_SETFD, FD_CLOEXEC) < 0) + { + close(cfd); + return -1; + } + + return cfd; +} + +/* One ChaCha20 (DJB layout) pass: encrypt/decrypt len bytes with the given + * 64-bit block counter and the packet sequence number as nonce. + */ + +static int dropbear_chacha(int cfd, FAR const unsigned char *key, + unsigned int seq, uint64_t counter, + FAR const unsigned char *in, + FAR unsigned char *out, size_t len) +{ + struct session_op session; + struct crypt_op cryp; + unsigned char iv[CHACHA20_IV_LEN]; + int ret = CRYPT_ERROR; + int i; + + /* IV = 64-bit little-endian block counter || 64-bit nonce. The nonce is + * the packet sequence number stored big endian, as OpenSSH does. + */ + + for (i = 0; i < 8; i++) + { + iv[i] = (unsigned char)(counter >> (8 * i)); + } + + STORE64H((uint64_t)seq, iv + 8); + + memset(&session, 0, sizeof(session)); + session.cipher = CRYPTO_CHACHA20_DJB; + session.key = (caddr_t)key; + session.keylen = CHACHA20_KEY_LEN; + if (ioctl(cfd, CIOCGSESSION, &session) < 0) + { + return CRYPT_ERROR; + } + + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = session.ses; + cryp.op = COP_ENCRYPT; + cryp.len = len; + cryp.src = (caddr_t)in; + cryp.dst = (caddr_t)out; + cryp.iv = (caddr_t)iv; + cryp.ivlen = sizeof(iv); + if (ioctl(cfd, CIOCCRYPT, &cryp) == 0) + { + ret = CRYPT_OK; + } + + ioctl(cfd, CIOCFSESSION, &session.ses); + return ret; +} + +static int dropbear_poly1305(int cfd, FAR const unsigned char *key, + FAR const unsigned char *in, size_t len, + FAR unsigned char *tag) +{ + struct session_op session; + struct crypt_op cryp; + int ret = CRYPT_ERROR; + + memset(&session, 0, sizeof(session)); + session.mac = CRYPTO_POLY1305; + session.mackey = (caddr_t)key; + session.mackeylen = POLY1305_KEY_LEN; + if (ioctl(cfd, CIOCGSESSION, &session) < 0) + { + return CRYPT_ERROR; + } + + /* Plain (non-HMAC) MACs are driven in two steps through /dev/crypto: + * COP_FLAG_UPDATE feeds the data, then a final call without the flag + * writes out the tag. + */ + + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = session.ses; + cryp.op = COP_ENCRYPT; + cryp.flags = COP_FLAG_UPDATE; + cryp.len = len; + cryp.src = (caddr_t)in; + if (ioctl(cfd, CIOCCRYPT, &cryp) == 0) + { + cryp.flags = 0; + cryp.len = 0; + cryp.src = NULL; + cryp.mac = (caddr_t)tag; + if (ioctl(cfd, CIOCCRYPT, &cryp) == 0) + { + ret = CRYPT_OK; + } + } + + ioctl(cfd, CIOCFSESSION, &session.ses); + return ret; +} + +static int dropbear_chachapoly_start(int cipher, + FAR const unsigned char *iv, + FAR const unsigned char *key, + int keylen, int num_rounds, + FAR void *cipher_state) +{ + FAR dropbear_chachapoly_state *state = cipher_state; + int cfd; + + UNUSED(cipher); + UNUSED(iv); + + if (keylen != CHACHA20_KEY_LEN * 2 || num_rounds != 0) + { + return CRYPT_ERROR; + } + + /* Validate that the crypto device is reachable at cipher setup. */ + + cfd = dropbear_cryptodev_open(); + if (cfd < 0) + { + return CRYPT_ERROR; + } + + close(cfd); + + memcpy(KEY_MAIN(state), key, CHACHA20_KEY_LEN); + memcpy(KEY_HEADER(state), key + CHACHA20_KEY_LEN, CHACHA20_KEY_LEN); + return CRYPT_OK; +} + +static int dropbear_chachapoly_crypt(unsigned int seq, + FAR const unsigned char *in, + FAR unsigned char *out, + unsigned long len, unsigned long taglen, + FAR void *cipher_state, + int direction) +{ + FAR dropbear_chachapoly_state *state = cipher_state; + unsigned char key[POLY1305_KEY_LEN]; + unsigned char tag[POLY1305_TAG_LEN]; + unsigned char zero[POLY1305_KEY_LEN]; + int cfd; + int ret = CRYPT_ERROR; + + if (len < 4 || taglen != POLY1305_TAG_LEN) + { + return CRYPT_ERROR; + } + + cfd = dropbear_cryptodev_open(); + if (cfd < 0) + { + return CRYPT_ERROR; + } + + /* Poly1305 key = first keystream block of the main key at counter 0 */ + + memset(zero, 0, sizeof(zero)); + if (dropbear_chacha(cfd, KEY_MAIN(state), seq, 0, zero, key, + sizeof(key)) != CRYPT_OK) + { + goto out; + } + + if (direction == LTC_DECRYPT) + { + if (dropbear_poly1305(cfd, key, in, len, tag) != CRYPT_OK) + { + goto out; + } + + if (constant_time_memcmp(in + len, tag, sizeof(tag)) != 0) + { + goto out; + } + } + + /* Packet length: header key, counter 0. Payload: main key, counter 1. */ + + if (dropbear_chacha(cfd, KEY_HEADER(state), seq, 0, in, out, 4) != + CRYPT_OK) + { + goto out; + } + + if (dropbear_chacha(cfd, KEY_MAIN(state), seq, 1, in + 4, out + 4, + len - 4) != CRYPT_OK) + { + goto out; + } + + if (direction == LTC_ENCRYPT) + { + if (dropbear_poly1305(cfd, key, out, len, out + len) != CRYPT_OK) + { + goto out; + } + } + + ret = CRYPT_OK; + +out: + close(cfd); + zeromem(key, sizeof(key)); + zeromem(tag, sizeof(tag)); + return ret; +} + +static int +dropbear_chachapoly_getlength(unsigned int seq, FAR const unsigned char *in, + FAR unsigned int *outlen, unsigned long len, + FAR void *cipher_state) +{ + FAR dropbear_chachapoly_state *state = cipher_state; + unsigned char buf[4]; + int cfd; + int ret; + + if (len < sizeof(buf)) + { + return CRYPT_ERROR; + } + + cfd = dropbear_cryptodev_open(); + if (cfd < 0) + { + return CRYPT_ERROR; + } + + ret = dropbear_chacha(cfd, KEY_HEADER(state), seq, 0, in, buf, + sizeof(buf)); + close(cfd); + if (ret != CRYPT_OK) + { + return CRYPT_ERROR; + } + + LOAD32H(*outlen, buf); + return CRYPT_OK; +} + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct dropbear_cipher_mode dropbear_mode_chachapoly = +{ + dropbear_chachapoly_start, + NULL, + NULL, + dropbear_chachapoly_crypt, + dropbear_chachapoly_getlength, + &g_dropbear_chachapoly_mac +}; diff --git a/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c b/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c new file mode 100644 index 000000000..dda511d5f --- /dev/null +++ b/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c @@ -0,0 +1,255 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_ltc_hmac_sha256.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* LibTomCrypt-compatible incremental HMAC API backed by a + * CRYPTO_SHA2_256_HMAC /dev/crypto session (hmac-sha2-256, the only MAC the + * NuttX Dropbear configuration enables): init opens the session, process + * feeds data with COP_FLAG_UPDATE, done reads the tag and frees the session. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "includes.h" + +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_HMAC_SHA256_DIGESTLEN 32 +#define DROPBEAR_HMAC_SHA256_BLOCKLEN 64 + +/* Stash the process-local /dev/crypto descriptor and session id in the + * unused md hash-state buffer so the upstream hmac_state needs no patch. + */ + +static_assert(sizeof(int) + sizeof(uint32_t) <= sizeof(hash_state), + "cryptodev state does not fit in hmac_state.md"); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_hmac_cryptodev_open(void) +{ + int fd; + int cfd; + + fd = open("/dev/crypto", O_RDWR); + if (fd < 0) + { + return -1; + } + + if (ioctl(fd, CRIOGET, &cfd) < 0) + { + close(fd); + return -1; + } + + close(fd); + + if (fcntl(cfd, F_SETFD, FD_CLOEXEC) < 0) + { + close(cfd); + return -1; + } + + return cfd; +} + +static int dropbear_hmac_hash_is_sha256(int hash) +{ + int ret; + + ret = hash_is_valid(hash); + if (ret != CRYPT_OK) + { + return ret; + } + + if (hash_descriptor[hash].hashsize != DROPBEAR_HMAC_SHA256_DIGESTLEN || + hash_descriptor[hash].blocksize != DROPBEAR_HMAC_SHA256_BLOCKLEN) + { + return CRYPT_INVALID_HASH; + } + + return CRYPT_OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, + unsigned long keylen) +{ + struct session_op session; + int cfd; + int ret; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(key != NULL); + + if (keylen == 0 || keylen > INT_MAX) + { + return CRYPT_INVALID_KEYSIZE; + } + + ret = dropbear_hmac_hash_is_sha256(hash); + if (ret != CRYPT_OK) + { + return ret; + } + + zeromem(hmac, sizeof(*hmac)); + cfd = dropbear_hmac_cryptodev_open(); + if (cfd < 0) + { + return CRYPT_ERROR; + } + + memset(&session, 0, sizeof(session)); + session.mac = CRYPTO_SHA2_256_HMAC; + session.mackey = (caddr_t)key; + session.mackeylen = (int)keylen; + if (ioctl(cfd, CIOCGSESSION, &session) < 0) + { + close(cfd); + zeromem(hmac, sizeof(*hmac)); + return CRYPT_ERROR; + } + + memcpy(&hmac->md, &cfd, sizeof(cfd)); + memcpy((FAR uint8_t *)&hmac->md + sizeof(cfd), + &session.ses, sizeof(session.ses)); + hmac->hash = hash; + return CRYPT_OK; +} + +int hmac_process(hmac_state *hmac, const unsigned char *in, + unsigned long inlen) +{ + struct crypt_op cryp; + uint32_t ses; + int cfd; + int ret; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(in != NULL || inlen == 0); + + ret = dropbear_hmac_hash_is_sha256(hmac->hash); + if (ret != CRYPT_OK) + { + return ret; + } + + if (inlen > UINT_MAX) + { + return CRYPT_OVERFLOW; + } + + memcpy(&cfd, &hmac->md, sizeof(cfd)); + memcpy(&ses, (FAR uint8_t *)&hmac->md + sizeof(cfd), sizeof(ses)); + if (cfd < 0) + { + return CRYPT_ERROR; + } + + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = ses; + cryp.op = COP_ENCRYPT; + cryp.flags = COP_FLAG_UPDATE; + cryp.len = (unsigned int)inlen; + cryp.src = (caddr_t)in; + if (ioctl(cfd, CIOCCRYPT, &cryp) < 0) + { + return CRYPT_ERROR; + } + + return CRYPT_OK; +} + +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen) +{ + unsigned char digest[DROPBEAR_HMAC_SHA256_DIGESTLEN]; + struct crypt_op cryp; + unsigned long copylen; + uint32_t ses; + int cfd; + int ret = CRYPT_ERROR; + + LTC_ARGCHK(hmac != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + memcpy(&cfd, &hmac->md, sizeof(cfd)); + memcpy(&ses, (FAR uint8_t *)&hmac->md + sizeof(cfd), sizeof(ses)); + if (cfd < 0) + { + goto out; + } + + ret = dropbear_hmac_hash_is_sha256(hmac->hash); + if (ret != CRYPT_OK) + { + goto out; + } + + memset(&cryp, 0, sizeof(cryp)); + cryp.ses = ses; + cryp.op = COP_ENCRYPT; + cryp.len = 0; + cryp.src = (caddr_t)digest; + cryp.mac = (caddr_t)digest; + if (ioctl(cfd, CIOCCRYPT, &cryp) < 0) + { + ret = CRYPT_ERROR; + goto out; + } + + copylen = MIN(*outlen, DROPBEAR_HMAC_SHA256_DIGESTLEN); + memcpy(out, digest, copylen); + *outlen = copylen; + ret = CRYPT_OK; + +out: + if (cfd >= 0) + { + ioctl(cfd, CIOCFSESSION, &ses); + close(cfd); + } + + zeromem(digest, sizeof(digest)); + zeromem(hmac, sizeof(*hmac)); + return ret; +} diff --git a/netutils/dropbear/port/dropbear_utils.c b/netutils/dropbear/port/dropbear_utils.c new file mode 100644 index 000000000..73619cbe7 --- /dev/null +++ b/netutils/dropbear/port/dropbear_utils.c @@ -0,0 +1,149 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_utils.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "dropbear_utils.h" + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int dropbear_hex_value(char ch) +{ + if (ch >= '0' && ch <= '9') + { + return ch - '0'; + } + + if (ch >= 'a' && ch <= 'f') + { + return ch - 'a' + 10; + } + + if (ch >= 'A' && ch <= 'F') + { + return ch - 'A' + 10; + } + + return -1; +} + +static int dropbear_try_mkdir(FAR const char *path) +{ + if (mkdir(path, 0700) < 0 && errno != EEXIST) + { + return -errno; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void dropbear_hex_encode(FAR char *dst, FAR const uint8_t *src, + size_t srclen) +{ + static const char hex[] = "0123456789abcdef"; + size_t i; + + for (i = 0; i < srclen; i++) + { + dst[i * 2] = hex[src[i] >> 4]; + dst[i * 2 + 1] = hex[src[i] & 0x0f]; + } + + dst[srclen * 2] = '\0'; +} + +int dropbear_hex_decode(FAR const char *src, size_t srclen, + FAR uint8_t *dst, size_t dstlen) +{ + size_t i; + + if (srclen != dstlen * 2) + { + return -EINVAL; + } + + for (i = 0; i < dstlen; i++) + { + int hi = dropbear_hex_value(src[i * 2]); + int lo = dropbear_hex_value(src[i * 2 + 1]); + + if (hi < 0 || lo < 0) + { + return -EINVAL; + } + + dst[i] = (uint8_t)((hi << 4) | lo); + } + + return OK; +} + +int dropbear_try_prepare_parent(FAR const char *path) +{ + char dir[PATH_MAX]; + struct stat st; + FAR char *slash; + FAR char *p; + int ret; + + if (strlcpy(dir, path, sizeof(dir)) >= sizeof(dir)) + { + return -ENAMETOOLONG; + } + + slash = strrchr(dir, '/'); + if (slash == NULL || slash == dir) + { + return OK; + } + + *slash = '\0'; + if (stat(dir, &st) == 0) + { + return OK; + } + + for (p = dir + 1; *p != '\0'; p++) + { + if (*p == '/') + { + *p = '\0'; + ret = dropbear_try_mkdir(dir); + if (ret < 0) + { + return ret; + } + + *p = '/'; + } + } + + return dropbear_try_mkdir(dir); +} + +#ifndef CONFIG_SCHED_USER_IDENTITY +int dropbear_getgroups(int size, gid_t list[]) +{ + (void)size; + (void)list; + set_errno(ENOSYS); + return ERROR; +} +#endif diff --git a/netutils/dropbear/port/dropbear_utils.h b/netutils/dropbear/port/dropbear_utils.h new file mode 100644 index 000000000..3e7d2722e --- /dev/null +++ b/netutils/dropbear/port/dropbear_utils.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/dropbear_utils.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H +#define __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void dropbear_hex_encode(FAR char *dst, FAR const uint8_t *src, + size_t srclen); + +int dropbear_hex_decode(FAR const char *src, size_t srclen, + FAR uint8_t *dst, size_t dstlen); + +int dropbear_try_prepare_parent(FAR const char *path); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_DROPBEAR_UTILS_H */ diff --git a/netutils/dropbear/port/localoptions.h b/netutils/dropbear/port/localoptions.h new file mode 100644 index 000000000..28ac5a3b5 --- /dev/null +++ b/netutils/dropbear/port/localoptions.h @@ -0,0 +1,81 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/localoptions.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H +#define __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_TRACKING_MALLOC 1 + +/* Persist the native Dropbear ECDSA host key at the configured path. */ + +#define ECDSA_PRIV_FILENAME CONFIG_NETUTILS_DROPBEAR_HOSTKEY_PATH + +#define DROPBEAR_SVR_DROP_PRIVS 0 + +#ifdef CONFIG_SCHED_USER_IDENTITY +# define DROPBEAR_SVR_MULTIUSER 1 +#else +# define DROPBEAR_SVR_MULTIUSER 0 +#endif + +#define DROPBEAR_SVR_PASSWORD_AUTH 1 +#define DROPBEAR_SVR_PUBKEY_AUTH 0 +#define DROPBEAR_SVR_PUBKEY_OPTIONS 0 + +#define DROPBEAR_REEXEC 0 +#define DROPBEAR_SMALL_CODE 1 +#define DROPBEAR_USER_ALGO_LIST 0 + +#define DROPBEAR_X11FWD 0 +#define DROPBEAR_SVR_AGENTFWD 0 +#define DROPBEAR_SVR_LOCALTCPFWD 0 +#define DROPBEAR_SVR_REMOTETCPFWD 0 +#define DROPBEAR_SVR_LOCALSTREAMFWD 0 +#define DROPBEAR_SVR_REMOTESTREAMFWD 0 + +#define DROPBEAR_DSS 0 +#define DROPBEAR_RSA 0 +#define DROPBEAR_ECDSA 1 +#define DROPBEAR_ED25519 0 +#define DROPBEAR_SK_KEYS 0 +#define DROPBEAR_ECC_256 1 +#define DROPBEAR_ECC_384 0 +#define DROPBEAR_ECC_521 0 + +#define DROPBEAR_AES128 1 +#define DROPBEAR_AES256 0 +#define DROPBEAR_CHACHA20POLY1305 1 +#define DROPBEAR_ENABLE_CBC_MODE 0 +#define DROPBEAR_ENABLE_GCM_MODE 0 + +#define DROPBEAR_SHA1_HMAC 0 +#define DROPBEAR_SHA2_256_HMAC 1 +#define DROPBEAR_SHA2_512_HMAC 0 +#define DROPBEAR_SHA1_96_HMAC 0 + +#define DROPBEAR_CURVE25519 1 +#define DROPBEAR_DH_GROUP14_SHA1 0 +#define DROPBEAR_DH_GROUP14_SHA256 0 +#define DROPBEAR_DH_GROUP16 0 +#define DROPBEAR_DH_GROUP1 0 +#define DROPBEAR_ECDH 0 +#define DROPBEAR_SNTRUP761 0 +#define DROPBEAR_MLKEM768 0 + +#define DROPBEAR_DEFAULT_CLI_AUTHKEY "/etc/dropbear/authorized_keys" +#define DROPBEAR_SFTPSERVER 0 + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_LOCALOPTIONS_H */ diff --git a/netutils/dropbear/port/nuttx_auth.c b/netutils/dropbear/port/nuttx_auth.c new file mode 100644 index 000000000..83becdcae --- /dev/null +++ b/netutils/dropbear/port/nuttx_auth.c @@ -0,0 +1,109 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_auth.c + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "fsutils/passwd.h" + +#include "dbutil.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct passwd g_dropbear_pw; +static char g_dropbear_name[64]; +static char g_dropbear_dir[] = "/"; +static char g_dropbear_shell[] = "/bin/sh"; +static char g_dropbear_password_marker[] = "x"; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR struct passwd *dropbear_fill_pw(FAR const char *name, uid_t uid) +{ + memset(&g_dropbear_pw, 0, sizeof(g_dropbear_pw)); + + snprintf(g_dropbear_name, sizeof(g_dropbear_name), "%s", name); + + g_dropbear_pw.pw_uid = uid; + g_dropbear_pw.pw_gid = 0; + g_dropbear_pw.pw_name = g_dropbear_name; + g_dropbear_pw.pw_passwd = g_dropbear_password_marker; + g_dropbear_pw.pw_gecos = g_dropbear_name; + g_dropbear_pw.pw_dir = g_dropbear_dir; + g_dropbear_pw.pw_shell = g_dropbear_shell; + + return &g_dropbear_pw; +} + +FAR struct passwd *dropbear_getpwuid(uid_t uid) +{ + return dropbear_fill_pw("root", uid); +} + +FAR struct passwd *dropbear_getpwnam(FAR const char *name) +{ + if (name == NULL || name[0] == '\0') + { + return NULL; + } + + return dropbear_fill_pw(name, 0); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +uid_t dropbear_getuid(void) +{ + return 0; +} + +uid_t dropbear_geteuid(void) +{ + return 0; +} + +int dropbear_auth_initialize(void) +{ + dropbear_log(LOG_INFO, "using NuttX passwd auth at %s", + CONFIG_FSUTILS_PASSWD_PATH); + return OK; +} + +int dropbear_verify_password(FAR const char *username, + FAR const char *password) +{ + int ret; + + ret = passwd_verify(username, password); + if (PASSWORD_VERIFY_MATCH(ret)) + { + return DROPBEAR_SUCCESS; + } + + if (PASSWORD_VERIFY_ERROR(ret) && ret != -ENOENT) + { + dropbear_log(LOG_WARNING, "passwd_verify failed for '%s': %d", + username, ret); + } + + return DROPBEAR_FAILURE; +} diff --git a/netutils/dropbear/port/nuttx_config.h b/netutils/dropbear/port/nuttx_config.h new file mode 100644 index 000000000..d91709d1e --- /dev/null +++ b/netutils/dropbear/port/nuttx_config.h @@ -0,0 +1,142 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_config.h + * + * SPDX-License-Identifier: Apache-2.0 + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H +#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DROPBEAR_SERVER 1 +#define BUNDLED_LIBTOM 1 +#define DISABLE_LASTLOG 1 +#define DISABLE_PAM 1 +#define DISABLE_PUTUTLINE 1 +#define DISABLE_PUTUTXLINE 1 +#define DISABLE_UTMP 1 +#define DISABLE_UTMPX 1 +#define DISABLE_WTMP 1 +#define DISABLE_WTMPX 1 + +#ifndef CONFIG_NETUTILS_DROPBEAR_SYSLOG +# define DISABLE_SYSLOG 1 +#endif + +#ifndef CONFIG_NETUTILS_DROPBEAR_COMPRESSION +# define DISABLE_ZLIB 1 +#endif + +#define DROPBEAR_FUZZ 0 +#define DROPBEAR_PLUGIN 0 + +#define HAVE_BASENAME 1 +#define HAVE_CLOCK_GETTIME 1 +#define HAVE_CONST_GAI_STRERROR_PROTO 1 +#define HAVE_CRYPT 1 +#define HAVE_DECL_HTOLE64 1 +#define HAVE_ENDIAN_H 1 +#define HAVE_EXPLICIT_BZERO 1 +#define HAVE_FREEADDRINFO 1 +#define HAVE_GAI_STRERROR 1 +#define HAVE_GETADDRINFO 1 +#define HAVE_GETNAMEINFO 1 +#define HAVE_GETRANDOM 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LIBGEN_H 1 +#define HAVE_NETDB_H 1 +#define HAVE_NETINET_IN_H 1 +#define HAVE_NETINET_TCP_H 1 +#define HAVE_PATHS_H 1 +#define HAVE_PUTENV 1 +#define HAVE_STATIC_ASSERT 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDIO_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRLCAT 1 +#define HAVE_STRLCPY 1 +#define HAVE_STRUCT_ADDRINFO 1 +#define HAVE_STRUCT_IN6_ADDR 1 +#define HAVE_STRUCT_SOCKADDR_IN6 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#define HAVE_SYS_RANDOM_H 1 +#define HAVE_SYS_SELECT_H 1 +#define HAVE_SYS_SOCKET_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_UIO_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_UINT16_T 1 +#define HAVE_UINT32_T 1 +#define HAVE_UINT8_T 1 +#define HAVE_U_INT16_T 1 +#define HAVE_U_INT32_T 1 +#define HAVE_U_INT8_T 1 +#define HAVE_UNDERSCORE_STATIC_ASSERT 1 +#define HAVE_UNISTD_H 1 +/* NuttX exposes writev(), but keep this port on Dropbear's simpler write() + * path until the SSH-to-NSH channel bridge is validated with vectored + * writes. + */ + +#undef HAVE_WRITEV +#define STDC_HEADERS 1 + +#define PACKAGE_BUGREPORT "" +#define PACKAGE_NAME "" +#define PACKAGE_STRING "" +#define PACKAGE_TARNAME "" +#define PACKAGE_URL "" +#define PACKAGE_VERSION "" + +#define SELECT_TYPE_ARG1 int +#define SELECT_TYPE_ARG234 (fd_set *) +#define SELECT_TYPE_ARG5 (struct timeval *) + +#ifndef PF_UNIX +# define PF_UNIX AF_UNIX +#endif + +#ifndef GRND_NONBLOCK +# define GRND_NONBLOCK O_NONBLOCK +#endif + +#define IPPORT_RESERVED 1024 + +#define getuid dropbear_getuid +#define geteuid dropbear_geteuid +#define getpwuid dropbear_getpwuid +#define getpwnam dropbear_getpwnam +uid_t getuid(void); +uid_t geteuid(void); +struct passwd *getpwuid(uid_t uid); +struct passwd *getpwnam(const char *name); +int dropbear_auth_initialize(void); +int dropbear_verify_password(const char *username, const char *password); + +#ifndef CONFIG_SCHED_USER_IDENTITY +/* NuttX has no supplementary-group support, so getgroups() does not exist. */ + +int dropbear_getgroups(int size, gid_t list[]); + +# define getgroups dropbear_getgroups +#endif + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_CONFIG_H */ diff --git a/netutils/dropbear/port/nuttx_scp.c b/netutils/dropbear/port/nuttx_scp.c new file mode 100644 index 000000000..6d7606abe --- /dev/null +++ b/netutils/dropbear/port/nuttx_scp.c @@ -0,0 +1,49 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_scp.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "nuttx_scp.h" + +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* scp only runs in server mode (-t/-f) on NuttX; execvp would only be + * reached in client mode to spawn a local ssh, which is not supported. + */ + +int dropbear_scp_execvp(FAR const char *file, FAR char * const argv[]) +{ + (void)file; + (void)argv; + + set_errno(ENOSYS); + return ERROR; +} diff --git a/netutils/dropbear/port/nuttx_scp.h b/netutils/dropbear/port/nuttx_scp.h new file mode 100644 index 000000000..c5edf799a --- /dev/null +++ b/netutils/dropbear/port/nuttx_scp.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/netutils/dropbear/port/nuttx_scp.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H +#define __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int dropbear_scp_execvp(FAR const char *file, FAR char * const argv[]); + +#endif /* __APPS_NETUTILS_DROPBEAR_PORT_NUTTX_SCP_H */ diff --git a/netutils/esp8266/esp8266.c b/netutils/esp8266/esp8266.c index 51de514b7..051dd15fc 100644 --- a/netutils/esp8266/esp8266.c +++ b/netutils/esp8266/esp8266.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/ftpc/ftpc_chmod.c b/netutils/ftpc/ftpc_chmod.c index 5710b7c8b..d9b490b0a 100644 --- a/netutils/ftpc/ftpc_chmod.c +++ b/netutils/ftpc/ftpc_chmod.c @@ -26,7 +26,7 @@ #include "ftpc_config.h" -#include +#include #include "netutils/ftpc.h" #include "ftpc_internal.h" diff --git a/netutils/ftpc/ftpc_cmd.c b/netutils/ftpc/ftpc_cmd.c index b6b7556b5..1e3ff9ea8 100644 --- a/netutils/ftpc/ftpc_cmd.c +++ b/netutils/ftpc/ftpc_cmd.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "ftpc_internal.h" diff --git a/netutils/ftpc/ftpc_connect.c b/netutils/ftpc/ftpc_connect.c index 8624f4905..f28c40b61 100644 --- a/netutils/ftpc/ftpc_connect.c +++ b/netutils/ftpc/ftpc_connect.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include @@ -108,6 +108,11 @@ SESSION ftpc_connect(FAR union ftpc_sockaddr_u *server) */ session->homeldir = strdup(ftpc_lpwd()); + if (session->homeldir == NULL) + { + nerr("ERROR: Failed to allocate local home directory\n"); + goto errout_with_alloc; + } /* And (Re-)connect to the server */ diff --git a/netutils/ftpc/ftpc_getfile.c b/netutils/ftpc/ftpc_getfile.c index 83524df4e..3ca989a80 100644 --- a/netutils/ftpc/ftpc_getfile.c +++ b/netutils/ftpc/ftpc_getfile.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "netutils/ftpc.h" diff --git a/netutils/ftpc/ftpc_getreply.c b/netutils/ftpc/ftpc_getreply.c index d5a3d0983..e63e0ed2b 100644 --- a/netutils/ftpc/ftpc_getreply.c +++ b/netutils/ftpc/ftpc_getreply.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include "ftpc_internal.h" diff --git a/netutils/ftpc/ftpc_idle.c b/netutils/ftpc/ftpc_idle.c index 42e2fdbef..dd5ab5c23 100644 --- a/netutils/ftpc/ftpc_idle.c +++ b/netutils/ftpc/ftpc_idle.c @@ -26,7 +26,7 @@ #include "ftpc_config.h" -#include +#include #include "netutils/ftpc.h" #include "ftpc_internal.h" diff --git a/netutils/ftpc/ftpc_listdir.c b/netutils/ftpc/ftpc_listdir.c index 92461f6e3..01ef7dd3a 100644 --- a/netutils/ftpc/ftpc_listdir.c +++ b/netutils/ftpc/ftpc_listdir.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "netutils/ftpc.h" @@ -257,6 +257,7 @@ FAR struct ftpc_dirlist_s *ftpc_listdir(SESSION handle, FAR char *tmpfname; bool iscurrdir; unsigned int nnames; + unsigned int i; int allocsize; int ret; @@ -365,6 +366,17 @@ FAR struct ftpc_dirlist_s *ftpc_listdir(SESSION handle, ftpc_nlstparse(filestream, ftpc_addname, dirlist); DEBUGASSERT(nnames == dirlist->nnames); + + for (i = 0; i < dirlist->nnames; i++) + { + if (dirlist->name[i] == NULL) + { + nerr("ERROR: Failed to allocate directory name\n"); + ftpc_dirfree(dirlist); + dirlist = NULL; + break; + } + } } errout: diff --git a/netutils/ftpc/ftpc_login.c b/netutils/ftpc/ftpc_login.c index 9e99c0dc5..80a97044a 100644 --- a/netutils/ftpc/ftpc_login.c +++ b/netutils/ftpc/ftpc_login.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include "netutils/ftpc.h" @@ -172,6 +172,10 @@ int ftpc_relogin(FAR struct ftpc_session_s *session) if (session->homerdir != NULL) { session->currdir = strdup(session->homerdir); + if (session->currdir == NULL) + { + return -ENOMEM; + } } /* If the user has requested a special start up directory, then change to diff --git a/netutils/ftpc/ftpc_putfile.c b/netutils/ftpc/ftpc_putfile.c index 9ac75cc0e..a0ea6bc3e 100644 --- a/netutils/ftpc/ftpc_putfile.c +++ b/netutils/ftpc/ftpc_putfile.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "netutils/ftpc.h" diff --git a/netutils/ftpc/ftpc_rpwd.c b/netutils/ftpc/ftpc_rpwd.c index 8ff608d9b..c2673b20e 100644 --- a/netutils/ftpc/ftpc_rpwd.c +++ b/netutils/ftpc/ftpc_rpwd.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include "netutils/ftpc.h" diff --git a/netutils/ftpc/ftpc_socket.c b/netutils/ftpc/ftpc_socket.c index db9b4ccd0..4a769a3d3 100644 --- a/netutils/ftpc/ftpc_socket.c +++ b/netutils/ftpc/ftpc_socket.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include "ftpc_internal.h" diff --git a/netutils/ftpc/ftpc_transfer.c b/netutils/ftpc/ftpc_transfer.c index 7d294d52d..b6ef5a65c 100644 --- a/netutils/ftpc/ftpc_transfer.c +++ b/netutils/ftpc/ftpc_transfer.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include "netutils/ftpc.h" diff --git a/netutils/ftpd/ftpd.c b/netutils/ftpd/ftpd.c index 12491030a..c62d9ca73 100644 --- a/netutils/ftpd/ftpd.c +++ b/netutils/ftpd/ftpd.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,7 @@ #include #include #include -#include +#include #include @@ -139,11 +140,11 @@ static uint8_t ftpd_listoption(FAR char **param); static int ftpd_listbuffer(FAR struct ftpd_session_s *session, FAR char *path, FAR struct stat *st, FAR char *buffer, size_t buflen, - unsigned int opton); + unsigned int option); static int fptd_listscan(FAR struct ftpd_session_s *session, - FAR char *path, unsigned int opton); + FAR char *path, unsigned int option); static int ftpd_list(FAR struct ftpd_session_s *session, - unsigned int opton); + unsigned int option); /* Command handlers */ @@ -626,6 +627,11 @@ static bool ftpd_account_login(FAR struct ftpd_session_s *session, if (account->home != NULL) { home = strdup(account->home); + if (home == NULL) + { + ftpd_account_free(account); + return false; + } } flags = account->flags; @@ -644,6 +650,11 @@ static bool ftpd_account_login(FAR struct ftpd_session_s *session, { home = strdup(home); } + + if (home == NULL) + { + return false; + } } if ((flags & FTPD_ACCOUNTFLAG_ADMIN) != 0) @@ -651,6 +662,12 @@ static bool ftpd_account_login(FAR struct ftpd_session_s *session, /* admin user */ session->home = strdup("/"); + if (session->home == NULL) + { + free(home); + return false; + } + session->work = home; } else @@ -659,6 +676,12 @@ static bool ftpd_account_login(FAR struct ftpd_session_s *session, session->home = home; session->work = strdup("/"); + if (session->work == NULL) + { + free(home); + session->home = NULL; + return false; + } } return true; @@ -2144,7 +2167,7 @@ static uint8_t ftpd_listoption(FAR char **param) static int ftpd_listbuffer(FAR struct ftpd_session_s *session, FAR char *path, FAR struct stat *st, FAR char *buffer, - size_t buflen, unsigned int opton) + size_t buflen, unsigned int option) { UNUSED(session); @@ -2153,7 +2176,7 @@ static int ftpd_listbuffer(FAR struct ftpd_session_s *session, name = basename(path); - if ((opton & FTPD_LISTOPTION_L) != 0) + if ((option & FTPD_LISTOPTION_L) != 0) { FAR const char *str; struct tm tm; @@ -2304,11 +2327,11 @@ static int ftpd_listbuffer(FAR struct ftpd_session_s *session, /* time */ - memcpy(&tm, localtime((FAR const time_t *)&st->st_mtime), sizeof(tm)); + memcpy(&tm, localtime(&st->st_mtime), sizeof(tm)); offset += snprintf(&buffer[offset], buflen - offset, " %s %2u", g_monthtab[tm.tm_mon], tm.tm_mday); now = time(0); - if ((now - st->st_mtime) > (time_t)(60 * 60 * 24 * 180)) + if (now - st->st_mtime > 60 * 60 * 24 * 180) { offset += snprintf(&buffer[offset], buflen - offset, " %5u", tm.tm_year + 1900); @@ -2366,7 +2389,7 @@ static int ftpd_listbuffer(FAR struct ftpd_session_s *session, ****************************************************************************/ static int fptd_listscan(FAR struct ftpd_session_s *session, FAR char *path, - unsigned int opton) + unsigned int option) { FAR char *temp; DIR *dir; @@ -2383,7 +2406,7 @@ static int fptd_listscan(FAR struct ftpd_session_s *session, FAR char *path, if (!S_ISDIR(st.st_mode)) { ret = ftpd_listbuffer(session, path, &st, session->data.buffer, - session->data.buflen, opton); + session->data.buflen, option); if (ret == 0) { ret = ftpd_response(session->data.sd, session->txtimeout, @@ -2411,7 +2434,7 @@ static int fptd_listscan(FAR struct ftpd_session_s *session, FAR char *path, if (entry->d_name[0] == '.') { - if ((opton & FTPD_LISTOPTION_A) == 0) + if ((option & FTPD_LISTOPTION_A) == 0) { continue; } @@ -2431,7 +2454,7 @@ static int fptd_listscan(FAR struct ftpd_session_s *session, FAR char *path, } ret = ftpd_listbuffer(session, temp, &st, session->data.buffer, - session->data.buflen, opton); + session->data.buflen, option); if (ret >= 0) { ret = ftpd_response(session->data.sd, session->txtimeout, @@ -2453,7 +2476,7 @@ static int fptd_listscan(FAR struct ftpd_session_s *session, FAR char *path, * Name: ftpd_list ****************************************************************************/ -static int ftpd_list(FAR struct ftpd_session_s *session, unsigned int opton) +static int ftpd_list(FAR struct ftpd_session_s *session, unsigned int option) { FAR char *abspath; int ret; @@ -2461,7 +2484,7 @@ static int ftpd_list(FAR struct ftpd_session_s *session, unsigned int opton) ret = ftpd_getpath(session, session->param, &abspath, NULL); if (ret >= 0) { - ret = fptd_listscan(session, abspath, opton); + ret = fptd_listscan(session, abspath, option); free(abspath); } @@ -2516,6 +2539,17 @@ static int ftpd_command_user(FAR struct ftpd_session_s *session) session->loggedin = false; session->home = strdup(home == NULL ? "/" : home); session->work = strdup("/"); + if (session->home == NULL || session->work == NULL) + { + free(session->home); + free(session->work); + session->home = NULL; + session->work = NULL; + + return ftpd_response(session->cmd.sd, session->txtimeout, + g_respfmt1, 451, ' ', + "Memory exhausted !"); + } return ftpd_response(session->cmd.sd, session->txtimeout, g_respfmt1, 230, ' ', "Login successful."); @@ -3416,7 +3450,7 @@ static int ftpd_command_epsv(FAR struct ftpd_session_s *session) static int ftpd_command_list(FAR struct ftpd_session_s *session) { - uint8_t opton = FTPD_LISTOPTION_L; + uint8_t option = FTPD_LISTOPTION_L; int ret; ret = ftpd_dataopen(session); @@ -3434,8 +3468,8 @@ static int ftpd_command_list(FAR struct ftpd_session_s *session) return ret; } - opton |= ftpd_listoption((char **)(&session->param)); - ftpd_list(session, opton); + option |= ftpd_listoption((char **)(&session->param)); + ftpd_list(session, option); ret = ftpd_response(session->cmd.sd, session->txtimeout, g_respfmt1, 226, ' ', "Transfer complete"); @@ -3450,7 +3484,7 @@ static int ftpd_command_list(FAR struct ftpd_session_s *session) static int ftpd_command_nlst(FAR struct ftpd_session_s *session) { - uint8_t opton = 0; + uint8_t option = 0; int ret; ret = ftpd_dataopen(session); @@ -3468,8 +3502,8 @@ static int ftpd_command_nlst(FAR struct ftpd_session_s *session) return ret; } - opton |= ftpd_listoption((char **)(&session->param)); - ftpd_list(session, opton); + option |= ftpd_listoption((char **)(&session->param)); + ftpd_list(session, option); ret = ftpd_response(session->cmd.sd, session->txtimeout, g_respfmt1, 226, ' ', "Transfer complete"); @@ -3734,11 +3768,7 @@ static int ftpd_command_appe(FAR struct ftpd_session_s *session) static int ftpd_command_rest(FAR struct ftpd_session_s *session) { -#ifdef CONFIG_HAVE_LONG_LONG session->restartpos = (off_t)atoll(session->param); -#else - session->restartpos = (off_t)atoi(session->param); -#endif session->flags |= FTPD_SESSIONFLAG_RESTARTPOS; return ftpd_response(session->cmd.sd, session->txtimeout, diff --git a/netutils/iperf/iperf.c b/netutils/iperf/iperf.c index cda61f68e..97ebf639f 100644 --- a/netutils/iperf/iperf.c +++ b/netutils/iperf/iperf.c @@ -48,7 +48,7 @@ #define IPERF_TRAFFIC_TASK_PRIORITY 100 #define IPERF_TRAFFIC_TASK_STACK 4096 #define IPERF_REPORT_TASK_NAME "iperf_report" -#define IPERF_REPORT_TASK_PRIORITY 100 +#define IPERF_REPORT_TASK_PRIORITY 101 #define IPERF_REPORT_TASK_STACK 4096 #define IPERF_UDP_TX_LEN (1472) @@ -220,6 +220,7 @@ static void iperf_print_addr(FAR const char *str, FAR struct sockaddr *addr) { switch (addr->sa_family) { +#ifdef CONFIG_NET_IPv4 case AF_INET: { FAR struct sockaddr_in *inaddr = (FAR struct sockaddr_in *)addr; @@ -227,6 +228,7 @@ static void iperf_print_addr(FAR const char *str, FAR struct sockaddr *addr) inet_ntoa(inaddr->sin_addr), htons(inaddr->sin_port)); return; } +#endif case AF_LOCAL: { diff --git a/netutils/iperf/iperf_main.c b/netutils/iperf/iperf_main.c index 67ff3855e..b87a122bc 100644 --- a/netutils/iperf/iperf_main.c +++ b/netutils/iperf/iperf_main.c @@ -140,7 +140,10 @@ int main(int argc, FAR char *argv[]) struct iperf_cfg_t cfg; struct in_addr addr; int nerrors; + +#ifdef CONFIG_NET_IPv4 char inetaddr[INET_ADDRSTRLEN]; +#endif bzero(&addr, sizeof(struct in_addr)); bzero(&cfg, sizeof(cfg)); @@ -226,6 +229,7 @@ int main(int argc, FAR char *argv[]) } else { +#ifdef CONFIG_NET_IPv4 if (iperf_args.bind->count > 0) { addr.s_addr = inet_addr(iperf_args.bind->sval[0]); @@ -248,6 +252,10 @@ int main(int argc, FAR char *argv[]) printf(" IP: %s\n", inet_ntoa_r(addr, inetaddr, sizeof(inetaddr))); cfg.sip = addr.s_addr; +#else + printf("ERROR: IPv4 Not Enabled\n"); + goto out; +#endif } if (iperf_args.udp->count == 0) diff --git a/netutils/libcurl4nx/curl4nx_easy_cleanup.c b/netutils/libcurl4nx/curl4nx_easy_cleanup.c index 1c3000671..0533693e8 100644 --- a/netutils/libcurl4nx/curl4nx_easy_cleanup.c +++ b/netutils/libcurl4nx/curl4nx_easy_cleanup.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_duphandle.c b/netutils/libcurl4nx/curl4nx_easy_duphandle.c index 4de16e0af..9bf44580c 100644 --- a/netutils/libcurl4nx/curl4nx_easy_duphandle.c +++ b/netutils/libcurl4nx/curl4nx_easy_duphandle.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_escape.c b/netutils/libcurl4nx/curl4nx_easy_escape.c index ff76d6d8c..8c5919b72 100644 --- a/netutils/libcurl4nx/curl4nx_easy_escape.c +++ b/netutils/libcurl4nx/curl4nx_easy_escape.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_getinfo.c b/netutils/libcurl4nx/curl4nx_easy_getinfo.c index ed8e90a9d..ff64739ad 100644 --- a/netutils/libcurl4nx/curl4nx_easy_getinfo.c +++ b/netutils/libcurl4nx/curl4nx_easy_getinfo.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_init.c b/netutils/libcurl4nx/curl4nx_easy_init.c index 1817bf734..661cfde76 100644 --- a/netutils/libcurl4nx/curl4nx_easy_init.c +++ b/netutils/libcurl4nx/curl4nx_easy_init.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_perform.c b/netutils/libcurl4nx/curl4nx_easy_perform.c index 466b3efe7..529aaa22d 100644 --- a/netutils/libcurl4nx/curl4nx_easy_perform.c +++ b/netutils/libcurl4nx/curl4nx_easy_perform.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_reset.c b/netutils/libcurl4nx/curl4nx_easy_reset.c index 7ece89011..9ee355a2f 100644 --- a/netutils/libcurl4nx/curl4nx_easy_reset.c +++ b/netutils/libcurl4nx/curl4nx_easy_reset.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_setopt.c b/netutils/libcurl4nx/curl4nx_easy_setopt.c index 03f8ff5d1..378862b9e 100644 --- a/netutils/libcurl4nx/curl4nx_easy_setopt.c +++ b/netutils/libcurl4nx/curl4nx_easy_setopt.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libcurl4nx/curl4nx_easy_unescape.c b/netutils/libcurl4nx/curl4nx_easy_unescape.c index 23f169afe..7e3955497 100644 --- a/netutils/libcurl4nx/curl4nx_easy_unescape.c +++ b/netutils/libcurl4nx/curl4nx_easy_unescape.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/netutils/libshvc/Makefile b/netutils/libshvc/Makefile index 3db2a49cb..af2669c88 100644 --- a/netutils/libshvc/Makefile +++ b/netutils/libshvc/Makefile @@ -82,7 +82,7 @@ ifeq ($(wildcard $(SHVDIR)/.git),) $(eval $(call RUN_OMK_MAKE, context, default-config include-pass)) distclean:: - make -C $(SHVDIR) distclean + if [ -d $(SHVDIR) ]; then make -C $(SHVDIR) distclean; fi $(call DELDIR, $(SHVDIR)) $(call DELFILE, $(SHV_LIBS4C_TARGZ)) endif diff --git a/netutils/libulut/Makefile b/netutils/libulut/Makefile index d3bdb498f..6582158f2 100644 --- a/netutils/libulut/Makefile +++ b/netutils/libulut/Makefile @@ -82,7 +82,7 @@ ifeq ($(wildcard $(ULUTDIR)/.git),) $(eval $(call RUN_OMK_MAKE, context, default-config include-pass)) distclean:: - make -C $(ULUTDIR) distclean + if [ -d $(ULUTDIR) ]; then make -C $(ULUTDIR) distclean; fi $(call DELDIR, $(ULUTDIR)) $(call DELFILE, $(ULUT_TARGZ)) endif diff --git a/netutils/libwebsockets/CMakeLists.txt b/netutils/libwebsockets/CMakeLists.txt new file mode 100644 index 000000000..8f308e292 --- /dev/null +++ b/netutils/libwebsockets/CMakeLists.txt @@ -0,0 +1,219 @@ +# ############################################################################## +# apps/netutils/libwebsockets/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_NETUTILS_LIBWEBSOCKETS) + + # ############################################################################ + # Config and Fetch libwebsockets + # ############################################################################ + + set(LIBWEBSOCKETS_DIR ${CMAKE_CURRENT_LIST_DIR}/libwebsockets) + + # Match Makefile: strip kconfig quotes; default when olddefconfig has not run + set(LWS_VERSION ${CONFIG_NETUTILS_LIBWEBSOCKETS_VERSION}) + string(REGEX REPLACE "^\"(.*)\"$" "\\1" LWS_VERSION "${LWS_VERSION}") + if(NOT LWS_VERSION) + set(LWS_VERSION "4.3.1") + endif() + + if(NOT EXISTS "${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-caps.c") + set(LIBWEBSOCKETS_URL "https://github.com/warmcat/libwebsockets/archive") + FetchContent_Declare( + libwebsockets_fetch + URL ${LIBWEBSOCKETS_URL}/v${LWS_VERSION}.zip SOURCE_DIR + ${LIBWEBSOCKETS_DIR} BINARY_DIR + ${CMAKE_BINARY_DIR}/apps/netutils/libwebsockets/libwebsockets + DOWNLOAD_NO_PROGRESS true + TIMEOUT 30) + + FetchContent_GetProperties(libwebsockets_fetch) + + if(NOT libwebsockets_fetch_POPULATED) + FetchContent_Populate(libwebsockets_fetch) + endif() + + execute_process( + COMMAND sh -c "patch -p0 < ${CMAKE_CURRENT_LIST_DIR}/libwebsockets.patch" + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + message("patching done") + endif() + + # ############################################################################ + # Flags + # ############################################################################ + + string(REPLACE "." ";" LWS_VERSION_PARTS "${LWS_VERSION}") + list(GET LWS_VERSION_PARTS 0 VERSION_MAJOR) + list(LENGTH LWS_VERSION_PARTS LWS_VERSION_PARTS_LEN) + if(LWS_VERSION_PARTS_LEN GREATER 1) + list(GET LWS_VERSION_PARTS 1 VERSION_MINOR) + else() + set(VERSION_MINOR 0) + endif() + if(LWS_VERSION_PARTS_LEN GREATER 2) + list(GET LWS_VERSION_PARTS 2 VERSION_PATCH) + else() + set(VERSION_PATCH 0) + endif() + + set(FLAGS + -DLWS_LIBRARY_VERSION_MAJOR=${VERSION_MAJOR} + -DLWS_LIBRARY_VERSION_MINOR=${VERSION_MINOR} + -DLWS_LIBRARY_VERSION_PATCH=${VERSION_PATCH} + -DLWS_LIBRARY_VERSION_PATCH_ELABORATED=${VERSION_PATCH}-unknown + -DLWS_LIBRARY_VERSION="${LWS_VERSION}-unknown" + -Wno-shadow + -Wno-format) + + set(INCDIR + ${CMAKE_CURRENT_LIST_DIR} + ${LIBWEBSOCKETS_DIR}/lib/core + ${LIBWEBSOCKETS_DIR}/lib/plat/unix + ${LIBWEBSOCKETS_DIR}/lib/event-libs + ${LIBWEBSOCKETS_DIR}/lib/system/smd + ${LIBWEBSOCKETS_DIR}/lib/system/metrics + ${LIBWEBSOCKETS_DIR}/lib/core-net + ${LIBWEBSOCKETS_DIR}/lib/roles + ${LIBWEBSOCKETS_DIR}/lib/roles/http + ${LIBWEBSOCKETS_DIR}/lib/roles/h1 + ${LIBWEBSOCKETS_DIR}/lib/roles/h2 + ${LIBWEBSOCKETS_DIR}/lib/roles/ws + ${LIBWEBSOCKETS_DIR}/lib/tls) + + set(CSRCS + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-caps.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-misc.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-init.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-file.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-pipe.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-service.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-sockets.c + ${LIBWEBSOCKETS_DIR}/lib/plat/unix/unix-fds.c + ${LIBWEBSOCKETS_DIR}/lib/core/alloc.c + ${LIBWEBSOCKETS_DIR}/lib/core/buflist.c + ${LIBWEBSOCKETS_DIR}/lib/core/context.c + ${LIBWEBSOCKETS_DIR}/lib/core/lws_dll2.c + ${LIBWEBSOCKETS_DIR}/lib/core/lws_map.c + ${LIBWEBSOCKETS_DIR}/lib/core/libwebsockets.c + ${LIBWEBSOCKETS_DIR}/lib/core/logs.c + ${LIBWEBSOCKETS_DIR}/lib/core/vfs.c + ${LIBWEBSOCKETS_DIR}/lib/misc/base64-decode.c + ${LIBWEBSOCKETS_DIR}/lib/misc/cache-ttl/lws-cache-ttl.c + ${LIBWEBSOCKETS_DIR}/lib/misc/cache-ttl/heap.c + ${LIBWEBSOCKETS_DIR}/lib/misc/cache-ttl/file.c + ${LIBWEBSOCKETS_DIR}/lib/misc/dir.c + ${LIBWEBSOCKETS_DIR}/lib/misc/prng.c + ${LIBWEBSOCKETS_DIR}/lib/misc/lws-ring.c + ${LIBWEBSOCKETS_DIR}/lib/misc/lwsac/lwsac.c + ${LIBWEBSOCKETS_DIR}/lib/misc/lwsac/cached-file.c + ${LIBWEBSOCKETS_DIR}/lib/misc/lejp.c + ${LIBWEBSOCKETS_DIR}/lib/misc/sha-1.c + ${LIBWEBSOCKETS_DIR}/lib/system/system.c + ${LIBWEBSOCKETS_DIR}/lib/system/smd/smd.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/dummy-callback.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/output.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/close.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/network.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/vhost.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/pollfd.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/service.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/sorted-usec-list.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/wsi.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/wsi-timeout.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/adopt.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/state.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/client.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/connect.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/connect2.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/connect3.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/connect4.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/sort-dns.c + ${LIBWEBSOCKETS_DIR}/lib/core-net/client/conmon.c + ${LIBWEBSOCKETS_DIR}/lib/roles/pipe/ops-pipe.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/header.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/date.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/parsers.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/cookie.c + ${LIBWEBSOCKETS_DIR}/lib/roles/h1/ops-h1.c + ${LIBWEBSOCKETS_DIR}/lib/roles/h2/http2.c + ${LIBWEBSOCKETS_DIR}/lib/roles/h2/hpack.c + ${LIBWEBSOCKETS_DIR}/lib/roles/h2/ops-h2.c + ${LIBWEBSOCKETS_DIR}/lib/roles/ws/ops-ws.c + ${LIBWEBSOCKETS_DIR}/lib/roles/ws/client-ws.c + ${LIBWEBSOCKETS_DIR}/lib/roles/ws/client-parser-ws.c + ${LIBWEBSOCKETS_DIR}/lib/roles/raw-skt/ops-raw-skt.c + ${LIBWEBSOCKETS_DIR}/lib/roles/raw-file/ops-raw-file.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/client/client-http.c + ${LIBWEBSOCKETS_DIR}/lib/event-libs/poll/poll.c) + + if(CONFIG_NETUTILS_LIBWEBSOCKETS_SERVER) + list(APPEND INCDIR ${LIBWEBSOCKETS_DIR}/lib/roles/http/server) + list(APPEND CSRCS ${LIBWEBSOCKETS_DIR}/lib/roles/ws/server-ws.c + ${LIBWEBSOCKETS_DIR}/lib/roles/http/server/server.c + ${LIBWEBSOCKETS_DIR}/lib/roles/listen/ops-listen.c) + endif() + + if(CONFIG_NETUTILS_LIBWEBSOCKETS_TLS) + list(APPEND INCDIR ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/wrapper/include + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/wrapper/include/internal) + list( + APPEND + CSRCS + ${LIBWEBSOCKETS_DIR}/lib/tls/tls.c + ${LIBWEBSOCKETS_DIR}/lib/tls/tls-network.c + ${LIBWEBSOCKETS_DIR}/lib/tls/tls-sessions.c + ${LIBWEBSOCKETS_DIR}/lib/tls/tls-client.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-tls.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-extensions.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-x509.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-ssl.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-session.c + ${LIBWEBSOCKETS_DIR}/lib/tls/mbedtls/mbedtls-client.c) + endif() + + if(CONFIG_NETUTILS_MQTTC) + list(APPEND INCDIR ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt) + list( + APPEND + CSRCS + ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt/mqtt.c + ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt/ops-mqtt.c + ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt/primitives.c + ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt/client/client-mqtt.c + ${LIBWEBSOCKETS_DIR}/lib/roles/mqtt/client/client-mqtt-handshake.c) + + endif() + + # ############################################################################ + # Library Configuration + # ############################################################################ + + set_property( + TARGET nuttx + APPEND + PROPERTY NUTTX_INCLUDE_DIRECTORIES ${LIBWEBSOCKETS_DIR}/include + ${CMAKE_CURRENT_LIST_DIR}) + + nuttx_add_library(libwebsockets STATIC) + target_sources(libwebsockets PRIVATE ${CSRCS}) + target_compile_options(libwebsockets PRIVATE ${FLAGS}) + target_include_directories(libwebsockets PRIVATE ${INCDIR}) + +endif() diff --git a/netutils/libwebsockets/Kconfig b/netutils/libwebsockets/Kconfig index 07686db68..36c904906 100644 --- a/netutils/libwebsockets/Kconfig +++ b/netutils/libwebsockets/Kconfig @@ -6,7 +6,7 @@ config NETUTILS_LIBWEBSOCKETS bool "libwebsockets library (current version)" default n - depends on NET && OPENSSL_MBEDTLS_WRAPPER + depends on NET ---help--- Enables the libwebsockets library. @@ -16,4 +16,20 @@ config NETUTILS_LIBWEBSOCKETS_VERSION string "Version number" default "4.3.1" +config NETUTILS_LIBWEBSOCKETS_SERVER + bool "Enable server support" + default n + ---help--- + Enable WebSocket/HTTP server support in libwebsockets. + When disabled, only client-side code is built. + +config NETUTILS_LIBWEBSOCKETS_TLS + bool "Enable TLS support" + default y + depends on OPENSSL_MBEDTLS_WRAPPER + ---help--- + Enable TLS in libwebsockets via the OpenSSL-mbedTLS + wrapper. Requires CONFIG_OPENSSL_MBEDTLS_WRAPPER. + When disabled, only plain ws:// connections are supported. + endif diff --git a/netutils/libwebsockets/Make.defs b/netutils/libwebsockets/Make.defs index 13fe3849c..58ebdbb3e 100644 --- a/netutils/libwebsockets/Make.defs +++ b/netutils/libwebsockets/Make.defs @@ -25,7 +25,9 @@ CONFIGURED_APPS += $(APPDIR)/netutils/libwebsockets # Allows `.h>` import. -CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets/libwebsockets/include -CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets/libwebsockets/include +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets +CFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets/libwebsockets/include +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets +CXXFLAGS += ${INCDIR_PREFIX}$(APPDIR)/netutils/libwebsockets/libwebsockets/include endif diff --git a/netutils/libwebsockets/Makefile b/netutils/libwebsockets/Makefile index ad8c0c5e6..ff28433ce 100644 --- a/netutils/libwebsockets/Makefile +++ b/netutils/libwebsockets/Makefile @@ -35,7 +35,8 @@ CFLAGS += \ -DLWS_LIBRARY_VERSION_MINOR=$(call word-dot,$(LIBWEBSOCKETS_VERSION),2) \ -DLWS_LIBRARY_VERSION_PATCH=$(call word-dot,$(LIBWEBSOCKETS_VERSION),3) \ -DLWS_LIBRARY_VERSION_PATCH_ELABORATED=$(call word-dot,$(LIBWEBSOCKETS_VERSION),3)-unknown \ - -DLWS_LIBRARY_VERSION=\"$(LIBWEBSOCKETS_VERSION)-unknown\" + -DLWS_LIBRARY_VERSION=\"$(LIBWEBSOCKETS_VERSION)-unknown\" \ + -Wno-shadow -Wno-format CFLAGS += \ -I. \ @@ -118,6 +119,16 @@ CSRCS += \ $(LIBWEBSOCKETS_UNPACK)/lib/roles/http/client/client-http.c \ $(LIBWEBSOCKETS_UNPACK)/lib/event-libs/poll/poll.c +ifneq ($(CONFIG_NETUTILS_LIBWEBSOCKETS_SERVER),) +CSRCS += \ + $(LIBWEBSOCKETS_UNPACK)/lib/roles/ws/server-ws.c \ + $(LIBWEBSOCKETS_UNPACK)/lib/roles/http/server/server.c \ + $(LIBWEBSOCKETS_UNPACK)/lib/roles/listen/ops-listen.c + +CFLAGS += -I$(LIBWEBSOCKETS_UNPACK)/lib/roles/http/server +endif + +ifneq ($(CONFIG_NETUTILS_LIBWEBSOCKETS_TLS),) CSRCS += \ $(LIBWEBSOCKETS_UNPACK)/lib/tls/tls.c \ $(LIBWEBSOCKETS_UNPACK)/lib/tls/tls-network.c \ @@ -128,7 +139,8 @@ CSRCS += \ $(LIBWEBSOCKETS_UNPACK)/lib/tls/mbedtls/mbedtls-x509.c \ $(LIBWEBSOCKETS_UNPACK)/lib/tls/mbedtls/mbedtls-ssl.c \ $(LIBWEBSOCKETS_UNPACK)/lib/tls/mbedtls/mbedtls-session.c \ - $(LIBWEBSOCKETS_UNPACK)/lib/tls/mbedtls/mbedtls-client.c \ + $(LIBWEBSOCKETS_UNPACK)/lib/tls/mbedtls/mbedtls-client.c +endif ifneq ($(CONFIG_NETUTILS_MQTTC),) CFLAGS += -I$(LIBWEBSOCKETS_UNPACK)/lib/roles/mqtt diff --git a/netutils/libwebsockets/libwebsockets.patch b/netutils/libwebsockets/libwebsockets.patch index 3576e8c4e..f2468fa6f 100644 --- a/netutils/libwebsockets/libwebsockets.patch +++ b/netutils/libwebsockets/libwebsockets.patch @@ -33,22 +33,6 @@ if (!unix_skt && setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *)&optval, optlen) < 0) return 1; #elif !defined(__APPLE__) && \ -@@ -190,6 +190,7 @@ lws_plat_set_socket_options(struct lws_vhost *vhost, int fd, int unix_skt) - return lws_plat_set_nonblocking(fd); - } - -+#if !defined(__NuttX__) - static const int ip_opt_lws_flags[] = { - LCCSCF_IP_LOW_LATENCY, LCCSCF_IP_HIGH_THROUGHPUT, - LCCSCF_IP_HIGH_RELIABILITY -@@ -210,6 +211,7 @@ static const char *ip_opt_names[] = { - #endif - }; - #endif -+#endif - - int - lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) @@ -237,7 +239,8 @@ lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) !defined(__sun) && \ !defined(__HAIKU__) && \ @@ -59,22 +43,6 @@ /* the BSDs don't have SO_PRIORITY */ -@@ -255,6 +258,7 @@ lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) - } - #endif - -+#if !defined(__NuttX__) - for (n = 0; n < 4; n++) { - if (!(lws_flags & ip_opt_lws_flags[n])) - continue; -@@ -272,6 +276,7 @@ lws_plat_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags) - lwsl_notice("%s: set ip flag %s\n", __func__, - ip_opt_names[n]); - } -+#endif - - return ret; - } --- libwebsockets/lib/roles/ws/client-ws.c +++ libwebsockets/lib/roles/ws/client-ws.c diff --git a/netutils/libwebsockets/lws_config.h b/netutils/libwebsockets/lws_config.h index 3fe970b47..02fb9ef2c 100644 --- a/netutils/libwebsockets/lws_config.h +++ b/netutils/libwebsockets/lws_config.h @@ -106,7 +106,7 @@ #define LWS_HAVE_MALLOC_H /* #define LWS_HAVE_MALLOC_TRIM */ #define LWS_HAVE_MALLOC_USABLE_SIZE -#ifdef CONFIG_CRYPTO_MBEDTLS +#ifdef CONFIG_NETUTILS_LIBWEBSOCKETS_TLS #define LWS_HAVE_mbedtls_md_setup #define LWS_HAVE_mbedtls_net_init #define LWS_HAVE_mbedtls_rsa_complete @@ -187,8 +187,10 @@ /* #undef LWS_NO_CLIENT */ #define LWS_NO_DAEMONIZE +#ifdef CONFIG_NETUTILS_LIBWEBSOCKETS_TLS #define LWS_OPENSSL_CLIENT_CERTS "/etc/ssl/certs" #define LWS_OPENSSL_SUPPORT +#endif /* #undef LWS_PLAT_OPTEE */ #define LWS_PLAT_UNIX /* #undef LWS_PLAT_FREERTOS */ @@ -281,7 +283,7 @@ /* #undef LWS_WITH_SDEVENT */ #define LWS_WITH_LWSAC #define LWS_LOGS_TIMESTAMP -#ifdef CONFIG_CRYPTO_MBEDTLS +#ifdef CONFIG_NETUTILS_LIBWEBSOCKETS_TLS #define LWS_WITH_MBEDTLS #endif /* #undef LWS_WITH_MINIZ */ @@ -292,7 +294,9 @@ #define LWS_WITH_CACHE_NSCOOKIEJAR #define LWS_WITH_CLIENT #define LWS_WITHOUT_EXTENSIONS -/* #undef LWS_WITH_SERVER */ +#ifdef CONFIG_NETUTILS_LIBWEBSOCKETS_SERVER +#define LWS_WITH_SERVER +#endif /* #undef LWS_WITH_SPAWN */ @@ -355,7 +359,7 @@ /* #undef LWS_WITH_SYS_NTPCLIENT */ #define LWS_WITH_SYS_STATE /* #undef LWS_WITH_THREADPOOL */ -#ifdef CONFIG_CRYPTO_MBEDTLS +#ifdef CONFIG_NETUTILS_LIBWEBSOCKETS_TLS #define LWS_WITH_TLS /* #undef LWS_WITH_TLS_JIT_TRUST */ #define LWS_WITH_TLS_SESSIONS diff --git a/netutils/mdns/Kconfig b/netutils/mdns/Kconfig index bd2003bdb..58f25acd2 100644 --- a/netutils/mdns/Kconfig +++ b/netutils/mdns/Kconfig @@ -51,11 +51,11 @@ config NETUTILS_MDNS_PRIORITY config NETUTILS_MDNS_STACKSIZE int "mDNS stack size" - default DEFAULT_TASK_STACKSIZE + default 8192 ---help--- - The default (4KiB) is adequate for simple networks but this will - most likely need to be increased if there are many network devices - attached that could send queries. + Stack size for the mDNS responder task. 8KiB is adequate for + typical networks; increase it if many network devices send + queries. config NETUTILS_MDNS_VERBOSE bool "Enable verbose printf output from built-in mdns demo" diff --git a/netutils/mdns/mdnsd.c b/netutils/mdns/mdnsd.c index 1f648950b..d060a8849 100644 --- a/netutils/mdns/mdnsd.c +++ b/netutils/mdns/mdnsd.c @@ -32,7 +32,7 @@ #include #include #include -#include /* For nerr, info */ +#include /* For nerr, info */ #include "netutils/netlib.h" #include "netutils/mdnsd.h" diff --git a/netutils/mqttc/CMakeLists.txt b/netutils/mqttc/CMakeLists.txt index 246cd1919..420c987b1 100644 --- a/netutils/mqttc/CMakeLists.txt +++ b/netutils/mqttc/CMakeLists.txt @@ -1,24 +1,24 @@ -############################################################################ +# ############################################################################## # apps/netutils/mqttc/CMakeLists.txt # # SPDX-License-Identifier: Apache-2.0 # -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. The -# ASF licenses this file to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance with the -# License. You may obtain a copy of the License at +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# License for the specific language governing permissions and limitations under +# the License. # -############################################################################ +# ############################################################################## if(CONFIG_NETUTILS_MQTTC) @@ -33,7 +33,7 @@ if(CONFIG_NETUTILS_MQTTC) FetchContent_Declare( mqttc_fetch URL ${MQTTC_URL}/${CONFIG_NETUTILS_MQTTC_VERSION}.tar.gz SOURCE_DIR - ${MQTTC_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/apps/netutils/mqttc/MQTT-C + ${MQTTC_DIR} BINARY_DIR ${NUTTX_APPS_BINDIR}/netutils/mqttc/MQTT-C DOWNLOAD_NO_PROGRESS true TIMEOUT 30) @@ -44,9 +44,8 @@ if(CONFIG_NETUTILS_MQTTC) endif() execute_process( - COMMAND - sh -c - "cat 0001_add_connection_status.patch | patch -s -N -d $(MQTTC_DIR) -p1" + COMMAND patch -s -N -d ${MQTTC_DIR} -p1 -i + ${CMAKE_CURRENT_LIST_DIR}/0001_add_connection_status.patch WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) endif() @@ -65,6 +64,10 @@ if(CONFIG_NETUTILS_MQTTC) target_sources(mqttc PRIVATE ${CSRCS}) target_compile_options(mqttc PRIVATE ${CFLAGS}) + if(CONFIG_NETUTILS_MQTTC_WITH_MBEDTLS AND CONFIG_CRYPTO_MBEDTLS) + nuttx_add_dependencies(TARGET mqttc DEPENDS mbedtls) + endif() + if(CONFIG_NETUTILS_MQTTC_EXAMPLE) if(CONFIG_NETUTILS_MQTTC_WITH_MBEDTLS) nuttx_add_application( @@ -79,7 +82,8 @@ if(CONFIG_NETUTILS_MQTTC) COMPILE_FLAGS ${CFLAGS} DEPENDS - mqttc) + mqttc + mbedtls) else() set(MQTT_PUB_CFLAGS ${CFLAGS} @@ -124,4 +128,24 @@ if(CONFIG_NETUTILS_MQTTC) mqttc) endif() endif() + + if(CONFIG_NETUTILS_MQTTC_TEST) + set(MQTT_TEST_CFLAGS ${CFLAGS} -Dopen_nb_socket=test_open_nb_socket + -Dpublish_callback=test_publish_callback) + + nuttx_add_application( + NAME + cmocka_mqttc_test + STACKSIZE + ${CONFIG_NETUTILS_MQTTC_TEST_STACKSIZE} + PRIORITY + ${SCHED_PRIORITY_DEFAULT} + SRCS + ${MQTTC_DIR}/tests.c + COMPILE_FLAGS + ${MQTT_TEST_CFLAGS} + DEPENDS + cmocka + mqttc) + endif() endif() diff --git a/netutils/mqttc/Kconfig b/netutils/mqttc/Kconfig index 2c1d0b883..973c5bb4a 100644 --- a/netutils/mqttc/Kconfig +++ b/netutils/mqttc/Kconfig @@ -17,6 +17,11 @@ config NETUTILS_MQTTC_EXAMPLE ---help--- Enable MQTT-C example +config NETUTILS_MQTTC_TEST + tristate "Enable MQTT-C test" + depends on !NETUTILS_MQTTC_WITH_MBEDTLS + default n + if NETUTILS_MQTTC_EXAMPLE config NETUTILS_MQTTC_EXAMPLE_STACKSIZE @@ -25,6 +30,14 @@ config NETUTILS_MQTTC_EXAMPLE_STACKSIZE endif +if NETUTILS_MQTTC_TEST + +config NETUTILS_MQTTC_TEST_STACKSIZE + int "Task's stack size" + default 8192 + +endif + config NETUTILS_MQTTC_WITH_MBEDTLS bool "Enable MQTT-C with mbedtls" default n diff --git a/netutils/mqttc/Makefile b/netutils/mqttc/Makefile index 730ce5139..d8d2a3638 100644 --- a/netutils/mqttc/Makefile +++ b/netutils/mqttc/Makefile @@ -83,4 +83,17 @@ endif endif +ifneq ($(CONFIG_NETUTILS_MQTTC_TEST),) +$(MQTTC_UNPACK)$(DELIM)tests.c_CFLAGS += \ + -I$(APPDIR)$(DELIM)testing$(DELIM)cmocka$(DELIM)cmocka$(DELIM)include \ + -Dopen_nb_socket=test_open_nb_socket \ + -Dpublish_callback=test_publish_callback + +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_NETUTILS_MQTTC_TEST_STACKSIZE) +MODULE = $(CONFIG_NETUTILS_MQTTC_TEST) +MAINSRC += $(MQTTC_UNPACK)$(DELIM)tests.c +PROGNAME += cmocka_mqttc_test +endif + include $(APPDIR)/Application.mk diff --git a/netutils/netinit/Kconfig b/netutils/netinit/Kconfig index 2031c7bae..8d91b0b50 100644 --- a/netutils/netinit/Kconfig +++ b/netutils/netinit/Kconfig @@ -488,6 +488,13 @@ config NETINIT_SWMAC With this choice, you can assign a fixed MAC address determined by a NuttX configuration option. +config NETINIT_MACADDR + bool "Device MAC address" + depends on BOARDCTL_MACADDR + ---help--- + With this choice, you can assign a fixed MAC address in the file + device.info (DEVICE_INFO_PATH) defined by user. + endchoice # MAC address selection config NETINIT_MACADDR_1 diff --git a/netutils/netinit/netinit.c b/netutils/netinit/netinit.c index 4433eb4a5..6120f4a67 100644 --- a/netutils/netinit/netinit.c +++ b/netutils/netinit/netinit.c @@ -36,7 +36,7 @@ #endif #include -#include +#include #include #include #include @@ -204,11 +204,7 @@ * signal indicating a change in network status. */ -#ifdef CONFIG_SYSTEM_TIME64 -# define LONG_TIME_SEC (60*60) /* One hour in seconds */ -#else -# define LONG_TIME_SEC (5*60) /* Five minutes in seconds */ -#endif +#define LONG_TIME_SEC (60*60) /* One hour in seconds */ #define SHORT_TIME_SEC (2) /* 2 seconds */ @@ -286,7 +282,9 @@ static const uint16_t g_ipv6_netmask[8] = defined(HAVE_MAC) static void netinit_set_macaddr(void) { -#if defined(CONFIG_NETINIT_UIDMAC) +#if defined(CONFIG_NETINIT_MACADDR) + struct boardioc_macaddr_s req; +#elif defined(CONFIG_NETINIT_UIDMAC) uint8_t uid[CONFIG_BOARDCTL_UNIQUEID_SIZE]; #elif defined(CONFIG_NET_ETHERNET) uint8_t mac[IFHWADDRLEN]; @@ -296,7 +294,14 @@ static void netinit_set_macaddr(void) /* Many embedded network interfaces must have a software assigned MAC */ -#if defined(CONFIG_NETINIT_UIDMAC) +#if defined(CONFIG_NETINIT_MACADDR) + strlcpy(req.ifname, NET_DEVNAME, IFNAMSIZ); + if (boardctl(BOARDIOC_MACADDR, (uintptr_t)&req) == 0) + { + netlib_setmacaddr(NET_DEVNAME, req.macaddr); + } + +#elif defined(CONFIG_NETINIT_UIDMAC) boardctl(BOARDIOC_UNIQUEID, (uintptr_t)&uid); uid[0] = (uid[0] & 0b11110000) | 2; /* Locally Administered MAC */ netlib_setmacaddr(NET_DEVNAME, uid); diff --git a/netutils/netlib/CMakeLists.txt b/netutils/netlib/CMakeLists.txt index 66bc927a0..0a6735121 100644 --- a/netutils/netlib/CMakeLists.txt +++ b/netutils/netlib/CMakeLists.txt @@ -57,6 +57,7 @@ if(CONFIG_NETUTILS_NETLIB) endif() if(CONFIG_NETUTILS_DHCPC) list(APPEND SRCS netlib_obtainipv4addr.c) + list(APPEND SRCS netlib_dhcp_ntp.c) endif() endif() @@ -118,6 +119,10 @@ if(CONFIG_NETUTILS_NETLIB) list(APPEND SRCS netlib_setmacaddr.c netlib_getmacaddr.c) endif() + if(CONFIG_NET_VLAN) + list(APPEND SRCS netlib_addvlan.c netlib_delvlan.c) + endif() + list(APPEND SRCS netlib_setmtu.c) if(CONFIG_WIRELESS_IEEE802154) @@ -143,6 +148,24 @@ if(CONFIG_NETUTILS_NETLIB) endif() endif() + # Network check support + + if(CONFIG_NET_ARP_ACD) + list(APPEND SRCS netlib_checkifconflict.c) + endif() + + if(CONFIG_MM_IOB) + list(APPEND SRCS netlib_getiobinfo.c) + endif() + + list(APPEND SRCS netlib_checkipconnectivity.c) + + if(CONFIG_NETUTILS_PING) + list(APPEND SRCS netlib_checkifconnectivity.c) + endif() + + list(APPEND SRCS netlib_checkhttpconnectivity.c) + target_sources(apps PRIVATE ${SRCS}) endif() diff --git a/netutils/netlib/Makefile b/netutils/netlib/Makefile index c590970e5..5867d9190 100644 --- a/netutils/netlib/Makefile +++ b/netutils/netlib/Makefile @@ -57,6 +57,7 @@ CSRCS += netlib_iptables.c endif ifeq ($(CONFIG_NETUTILS_DHCPC),y) CSRCS += netlib_obtainipv4addr.c +CSRCS += netlib_dhcp_ntp.c endif endif @@ -123,6 +124,10 @@ ifeq ($(CONFIG_NET_ETHERNET),y) CSRCS += netlib_setmacaddr.c netlib_getmacaddr.c endif +ifeq ($(CONFIG_NET_VLAN),y) +CSRCS += netlib_addvlan.c netlib_delvlan.c +endif + CSRCS += netlib_setmtu.c ifeq ($(CONFIG_WIRELESS_IEEE802154),y) @@ -142,4 +147,22 @@ CSRCS += netlib_ipmsfilter.c endif endif +# Network check support + +ifeq ($(CONFIG_NET_ARP_ACD),y) +CSRCS += netlib_checkifconflict.c +endif + +ifeq ($(CONFIG_MM_IOB),y) +CSRCS += netlib_getiobinfo.c +endif + +CSRCS += netlib_checkipconnectivity.c + +ifeq ($(CONFIG_NETUTILS_PING),y) +CSRCS += netlib_checkifconnectivity.c +endif + +CSRCS += netlib_checkhttpconnectivity.c + include $(APPDIR)/Application.mk diff --git a/netutils/netlib/netlib_addvlan.c b/netutils/netlib/netlib_addvlan.c new file mode 100644 index 000000000..db1369578 --- /dev/null +++ b/netutils/netlib/netlib_addvlan.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_addvlan.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET_VLAN + +#include + +#include "netutils/netlib.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_add_vlan + * + * Description: + * Add a VLAN interface to an existing network device. + * + * Parameters: + * ifname - The name of the existing network device + * vlanid - The VLAN identifier to be added + * prio - The default VLAN priority (PCP) + * + * Return: + * 0 on success; -1 on failure + * + ****************************************************************************/ + +int netlib_add_vlan(FAR const char *ifname, int vlanid, int prio) +{ + int ret = ERROR; + + if (ifname && vlanid > 0) + { + int sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); + if (sockfd >= 0) + { + struct vlan_ioctl_args ifv; + + strlcpy(ifv.device1, ifname, sizeof(ifv.device1)); + ifv.u.VID = vlanid; + ifv.vlan_qos = prio; + ifv.cmd = ADD_VLAN_CMD; + + ret = ioctl(sockfd, SIOCSIFVLAN, &ifv); + close(sockfd); + } + } + + return ret; +} + +#endif /* CONFIG_NET_VLAN */ diff --git a/netutils/netlib/netlib_checkhttpconnectivity.c b/netutils/netlib/netlib_checkhttpconnectivity.c new file mode 100644 index 000000000..a4ed482e5 --- /dev/null +++ b/netutils/netlib/netlib_checkhttpconnectivity.c @@ -0,0 +1,265 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_checkhttpconnectivity.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_LIBC_NETDB +# include +#endif + +#include +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: eai_to_errno + * + * Description: + * Convert EAI_* error code to errno value for unified error handling. + * + * Parameters: + * eai_error The EAI_* error code from getaddrinfo(). + * + * Return: + * Corresponding errno value. + * + ****************************************************************************/ + +#ifdef CONFIG_LIBC_NETDB +static int eai_to_errno(int eai_error) +{ + switch (eai_error) + { + case EAI_AGAIN: + return EAGAIN; /* Temporary failure, try again */ + + case EAI_BADFLAGS: + return EINVAL; /* Invalid flags */ + + case EAI_FAIL: + return EHOSTUNREACH; /* Non-recoverable failure */ + + case EAI_FAMILY: + return EAFNOSUPPORT; /* Address family not supported */ + + case EAI_MEMORY: + return ENOMEM; /* Memory allocation failure */ + + case EAI_NONAME: + return EHOSTUNREACH; /* Host name not found */ + + case EAI_SERVICE: + return EINVAL; /* Service not recognized */ + + case EAI_SOCKTYPE: + return EINVAL; /* Socket type not supported */ + + case EAI_SYSTEM: + return errno; /* System error, use errno */ + + case EAI_OVERFLOW: + return EOVERFLOW; /* Buffer overflow */ + + default: + return EINVAL; /* Unknown error, use invalid argument */ + } +} +#endif + +/**************************************************************************** + * Name: httpget_gethostip + * + * Description: + * Check the http status code + * + * Parameters: + * hostname The host name to use in the nslookup. + * dest The location to return the IPv4 address. + * + * Return: + * 0 on success; a negative errno value on failure. + * + ****************************************************************************/ + +static int httpget_gethostip(FAR const char *hostname, + FAR struct in_addr *dest) +{ +#ifdef CONFIG_LIBC_NETDB + /* Netdb DNS client support is enabled */ + + int ret; + FAR struct addrinfo hint; + FAR struct addrinfo *info; + FAR struct sockaddr_in *addr; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + + ret = getaddrinfo(hostname, NULL, &hint, &info); + if (ret != OK) + { + return -eai_to_errno(ret); + } + + addr = (FAR struct sockaddr_in *)info->ai_addr; + memcpy(dest, &addr->sin_addr, sizeof(struct in_addr)); + + freeaddrinfo(info); + return OK; + +#else /* CONFIG_LIBC_NETDB */ + /* No host name support */ + + /* Convert strings to numeric IPv4 address */ + + int ret = inet_pton(AF_INET, hostname, dest); + + /* The inet_pton() function returns 1 if the conversion succeeds. It will + * return 0 if the input is not a valid IPv4 dotted-decimal string or -1 + * with errno set to EAFNOSUPPORT if the address family argument is + * unsupported. + */ + + if (ret != 1) + { + /* ret == 0: invalid IP address string, set errno to EINVAL + * ret == -1: system error, errno is already set to EAFNOSUPPORT + */ + + if (ret == 0) + { + set_errno(EINVAL); + } + + return -errno; + } + + return OK; + +#endif /* CONFIG_LIBC_NETDB */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_check_httpconnectivity + * + * Description: + * Check the http status code + * + * Parameters: + * host The remote host to check + * getmsg The http GET message + * port The port of remote host + * expect_code The except http code of http code + * + * Return: + * 0 on success; a nagtive on failure. + * + ****************************************************************************/ + +int netlib_check_httpconnectivity(FAR const char *host, + FAR const char *getmsg, + int port, int expect_code) +{ + int sock; + int ret; + int status_code = -1; + struct sockaddr_in server_addr; + char buf[256]; + + ret = httpget_gethostip(host, &server_addr.sin_addr); + if (ret < 0) + { + nerr("Failed to get host ip"); + return ret; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + nerr("Failed to create socket"); + return -errno; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + + ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (ret < 0) + { + nerr("Connection failed: %s", strerror(errno)); + goto exit_with_socket; + } + + snprintf(buf, sizeof(buf), + "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", + getmsg, host); + + ret = send(sock, buf, strlen(buf), 0); + if (ret < 0) + { + nerr("send failed: %s", strerror(errno)); + goto exit_with_socket; + } + + if ((ret = recv(sock, buf, sizeof(buf), 0)) < 0) + { + nerr("recv failed: %s", strerror(errno)); + goto exit_with_socket; + } + + /* get the http code, and check with except code */ + + if (sscanf(buf, "HTTP/1.1 %d", &status_code) == 1 && + status_code == expect_code) + { + ninfo("HTTP connectivity is ok"); + ret = OK; + } + else + { + nerr("HTTP connectivity is not ok status code: %d\n", status_code); + ret = -status_code; + } + +exit_with_socket: + close(sock); + return ret; +} diff --git a/netutils/netlib/netlib_checkifconflict.c b/netutils/netlib/netlib_checkifconflict.c new file mode 100644 index 000000000..c8c8fc2c1 --- /dev/null +++ b/netutils/netlib/netlib_checkifconflict.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_checkifconflict.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "netutils/netlib.h" + +#ifdef CONFIG_NET_ARP_ACD + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Determines the size of an intermediate buffer that must be large enough + * to handle the longest line generated by this logic. + */ + +#define PROCFS_LINELEN 80 +#define PROCFS_NET_PATH "/proc/net/" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_check_ifconflict + * + * Description: + * Check the network driver ip conflict + * + * Parameters: + * ifname The name of the interface to use + * + * Return: + * 0 on no conflict; a nagtive on failure; 1 on conflict + * + ****************************************************************************/ + +int netlib_check_ifconflict(FAR const char *ifname) +{ + int conflict = 0; + char path[32]; + char line[PROCFS_LINELEN]; + FAR FILE *stream; + + if (ifname == NULL) + { + fprintf(stderr, "ERROR: ifname is NULL \n"); + return -EINVAL; + } + + snprintf(path, sizeof(path), "%s%s", PROCFS_NET_PATH, ifname); + ninfo("get dev statistics from %s \n", path); + + stream = fopen(path, "r"); + if (stream == NULL) + { + fprintf(stderr, "ERROR: Failed to open path:%s \n", path); + return -errno; + } + + while (fgets(line, sizeof(line), stream) != NULL) + { + if (strstr(line, "conflict!") != NULL) + { + conflict = 1; + break; + } + } + + fclose(stream); + return conflict; +} + +#endif /* CONFIG_NET_ARP_ACD */ diff --git a/netutils/netlib/netlib_checkifconnectivity.c b/netutils/netlib/netlib_checkifconnectivity.c new file mode 100644 index 000000000..d0bc8c3e3 --- /dev/null +++ b/netutils/netlib/netlib_checkifconnectivity.c @@ -0,0 +1,90 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_checkifconnectivity.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "netutils/netlib.h" + +#ifdef CONFIG_NETUTILS_PING + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_check_ifconnectivity + * + * Description: + * Check network connectivity by pinging the default gateway + * of the specified network interface. + * + * Parameters: + * ifname The name of the interface to use + * timeout The timeout of ping + * retry The retry times of ping + * + * Return: + * nums of gateway reply of ping; a negative on failure. + * + ****************************************************************************/ + +int netlib_check_ifconnectivity(FAR const char *ifname, + int timeout, int retry) +{ + int ret; + char destip[INET_ADDRSTRLEN]; + struct in_addr addr; + + ret = netlib_get_dripv4addr(ifname, &addr); + if (ret < 0) + { + nerr("ERROR: failed to get gateway ip %s\n", ifname); + return ret; + } + + if (net_ipv4addr_cmp(&addr, INADDR_ANY)) + { + nerr("ERROR: failed to get gateway ip %s\n", ifname); + return -EINVAL; + } + + inet_ntop(AF_INET, &addr, destip, sizeof(destip)); + ninfo("ping gateway %s \n", destip); + + return netlib_check_ipconnectivity(destip, timeout, retry); +} + +#endif /* CONFIG_NETUTILS_PING */ diff --git a/netutils/netlib/netlib_checkipconnectivity.c b/netutils/netlib/netlib_checkipconnectivity.c new file mode 100644 index 000000000..ce1a334b8 --- /dev/null +++ b/netutils/netlib/netlib_checkipconnectivity.c @@ -0,0 +1,425 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_checkipconnectivity.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "netutils/netlib.h" +#include "netutils/icmp_ping.h" +#include "netutils/icmpv6_ping.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#define PING_IPADDR_DATALEN 56 + +#ifdef CONFIG_NETUTILS_PING + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ping_ipaddr_callback + * + * Description: + * ping ipaddr callback + * + * Parameters: + * result The result of ping + * + ****************************************************************************/ + +static void ping_ipaddr_callback(FAR const struct ping_result_s *result) +{ + FAR int *count = result->info->priv; + + if (result->code == ICMP_I_FINISH) + { + *count = result->nreplies; + } +} + +#endif /* CONFIG_NETUTILS_PING */ + +#ifdef CONFIG_NETUTILS_PING6 + +/**************************************************************************** + * Name: ping6_ipaddr_callback + * + * Description: + * ping6 ipaddr callback + * + * Parameters: + * result The result of ping6 + * + ****************************************************************************/ + +static void ping6_ipaddr_callback(FAR const struct ping6_result_s *result) +{ + FAR int *count = result->info->priv; + + if (result->code == ICMPv6_I_FINISH) + { + *count = result->nreplies; + } +} + +#endif /* CONFIG_NETUTILS_PING6 */ + +/**************************************************************************** + * Name: ping_gateway + * + * Description: + * Ping the default gateway of each network interface (IPv4 and IPv6). + * + * Parameters: + * info Pointer to the IPv4 ping info structure + * info6 Pointer to the IPv6 ping info structure + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_IFINDEX +static void ping_gateway(FAR struct ping_info_s *info, + FAR struct ping6_info_s *info6) +{ + FAR int *replies = info->priv; + FAR struct ifaddrs *ifap; + FAR struct ifaddrs *ifa; + + if (getifaddrs(&ifap) < 0) + { + return; + } + + for (ifa = ifap; ifa != NULL && *replies == 0; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) + { + continue; + } + + /* Skip interfaces that are not up and running */ + + if ((ifa->ifa_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) != 0) + { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) + { + FAR struct sockaddr_in *sin; + char destip[INET_ADDRSTRLEN]; + + /* Skip interfaces with INADDR_ANY address and loopback flag */ + + sin = (FAR struct sockaddr_in *)ifa->ifa_addr; + if (sin->sin_addr.s_addr == INADDR_ANY) + { + continue; + } + + /* No ARP means point-to-point or tunnel, treat as reachable */ + + if ((ifa->ifa_flags & IFF_NOARP) != 0) + { + (*replies)++; + break; + } + + sin = (FAR struct sockaddr_in *)ifa->ifa_dstaddr; + if (sin == NULL || + sin->sin_family != AF_INET || + sin->sin_addr.s_addr == INADDR_ANY) + { + continue; + } + + inet_ntop(AF_INET, &sin->sin_addr, destip, sizeof(destip)); + ninfo("ping gateway %s on %s\n", destip, ifa->ifa_name); + info->hostname = destip; +#ifdef CONFIG_NETUTILS_PING + icmp_ping(info); +#else + (*replies)++; +#endif + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + FAR struct sockaddr_in6 *sin6; + char destip[INET6_ADDRSTRLEN]; + + /* Skip interfaces with unspecified address and loopback flag */ + + sin6 = (FAR struct sockaddr_in6 *)ifa->ifa_addr; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) + { + continue; + } + + /* No ARP means point-to-point or tunnel, treat as reachable */ + + if ((ifa->ifa_flags & IFF_NOARP) != 0) + { + (*replies)++; + break; + } + + sin6 = (FAR struct sockaddr_in6 *)ifa->ifa_dstaddr; + if (sin6 == NULL || + sin6->sin6_family != AF_INET6 || + IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) + { + continue; + } + + inet_ntop(AF_INET6, &sin6->sin6_addr, destip, sizeof(destip)); + ninfo("ping6 gateway %s on %s\n", destip, ifa->ifa_name); + info6->hostname = destip; +#ifdef CONFIG_NETUTILS_PING6 + icmp6_ping(info6); +#else + (*replies)++; +#endif + } + } + + freeifaddrs(ifap); +} +#endif /* CONFIG_NETDEV_IFINDEX */ + +/**************************************************************************** + * Name: ping_route + * + * Description: + * Ping the router addresses from the IPv4 routing table. + * + * Parameters: + * info Pointer to the ping info structure + * + ****************************************************************************/ + +#if defined(HAVE_ROUTE_PROCFS) && defined(CONFIG_NETUTILS_PING) +static void ping_route(FAR struct ping_info_s *info) +{ + FAR int *replies = info->priv; + FAR FILE *stream; + struct netlib_ipv4_route_s route; + + stream = netlib_open_ipv4route(); + if (stream == NULL) + { + return; + } + + while (*replies == 0 && netlib_read_ipv4route(stream, &route) > 0) + { + char destip[INET_ADDRSTRLEN]; + + if (route.router == INADDR_ANY) + { + continue; + } + + inet_ntop(AF_INET, &route.router, destip, sizeof(destip)); + ninfo("ping route gateway %s\n", destip); + info->hostname = destip; + icmp_ping(info); + } + + netlib_close_ipv4route(stream); +} +#endif /* HAVE_ROUTE_PROCFS && CONFIG_NETUTILS_PING */ + +/**************************************************************************** + * Name: ping6_route + * + * Description: + * Ping the router addresses from the IPv6 routing table. + * + * Parameters: + * info6 Pointer to the ping6 info structure + * + ****************************************************************************/ + +#if defined(HAVE_ROUTE_PROCFS) && defined(CONFIG_NETUTILS_PING6) +static void ping6_route(FAR struct ping6_info_s *info6) +{ + FAR int *replies = info6->priv; + FAR FILE *stream; + struct netlib_ipv6_route_s route; + + stream = netlib_open_ipv6route(); + if (stream == NULL) + { + return; + } + + while (*replies == 0 && netlib_read_ipv6route(stream, &route) > 0) + { + char destip[INET6_ADDRSTRLEN]; + struct in6_addr router; + + memcpy(&router, route.router, sizeof(router)); + if (IN6_IS_ADDR_UNSPECIFIED(&router)) + { + continue; + } + + inet_ntop(AF_INET6, &router, destip, sizeof(destip)); + ninfo("ping6 route gateway %s\n", destip); + info6->hostname = destip; + icmp6_ping(info6); + } + + netlib_close_ipv6route(stream); +} +#endif /* HAVE_ROUTE_PROCFS && CONFIG_NETUTILS_PING6 */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_check_ipconnectivity + * + * Description: + * Check network connectivity by pinging a remote IP address. + * If ip is NULL, ping the gateway of each network interface, + * and optionally the routers from the routing table. If ping + * is disabled, just check the status of the IP network card. + * + * Parameters: + * ip The ipv4 address to check, or NULL to ping gateways + * timeout The max timeout of each ping + * retry The retry times of ping + * + * Return: + * nums of remote reply of ping; a negative or ZERO on failure + * + ****************************************************************************/ + +int netlib_check_ipconnectivity(FAR const char *ip, int timeout, int retry) +{ + int replies = 0; +#if defined(CONFIG_NETDEV_IFINDEX) || defined(CONFIG_NETUTILS_PING) + struct ping_info_s info; +#endif +#if defined(CONFIG_NETDEV_IFINDEX) || defined(CONFIG_NETUTILS_PING6) + struct ping6_info_s info6; +#endif + +#ifdef CONFIG_NETUTILS_PING + info.count = retry; + info.datalen = PING_IPADDR_DATALEN; + info.delay = 0; + info.timeout = timeout; + info.callback = ping_ipaddr_callback; + info.hostname = ip; +# ifdef CONFIG_NET_BINDTODEVICE + info.devname = NULL; +# endif +#endif +#if defined(CONFIG_NETDEV_IFINDEX) || defined(CONFIG_NETUTILS_PING) + info.priv = &replies; +#endif + +#ifdef CONFIG_NETUTILS_PING6 + info6.count = retry; + info6.datalen = PING_IPADDR_DATALEN; + info6.delay = 0; + info6.timeout = timeout; + info6.hostname = ip; + info6.callback = ping6_ipaddr_callback; +# ifdef CONFIG_NET_BINDTODEVICE + info6.devname = NULL; +# endif +#endif +#if defined(CONFIG_NETDEV_IFINDEX) || defined(CONFIG_NETUTILS_PING6) + info6.priv = &replies; +#endif + + if (ip != NULL) + { + struct in6_addr addr6; + + ninfo("ping ipaddr test %s\n", ip); + if (inet_pton(AF_INET6, ip, &addr6) == 1) + { +#ifdef CONFIG_NETUTILS_PING6 + icmp6_ping(&info6); + return replies; +#else + /* Fallback to network card status check */ +#endif + } + else + { +#ifdef CONFIG_NETUTILS_PING + icmp_ping(&info); + return replies; +#else + /* Fallback to network card status check */ +#endif + } + } + +#ifdef CONFIG_NETDEV_IFINDEX + ping_gateway(&info, &info6); +#endif + +#if defined(HAVE_ROUTE_PROCFS) && defined(CONFIG_NETUTILS_PING) + if (replies == 0) + { + ping_route(&info); + } +#endif + +#if defined(HAVE_ROUTE_PROCFS) && defined(CONFIG_NETUTILS_PING6) + if (replies == 0) + { + ping6_route(&info6); + } +#endif + +#if !defined(CONFIG_NETDEV_IFINDEX) && !defined(HAVE_ROUTE_PROCFS) + replies = 1; +#endif + + return replies; +} diff --git a/netutils/netlib/netlib_delarp.c b/netutils/netlib/netlib_delarp.c index 7a263211c..ac8fc1ad6 100644 --- a/netutils/netlib/netlib_delarp.c +++ b/netutils/netlib/netlib_delarp.c @@ -76,8 +76,7 @@ int netlib_del_arpmapping(FAR const struct sockaddr_in *inaddr, memset(&req.arp_ha, 0, sizeof(struct sockaddr_in)); if (ifname != NULL) { - strlcpy((FAR char *)&req.arp_dev, ifname, - sizeof(req.arp_dev)); + strlcpy(req.arp_dev, ifname, sizeof(req.arp_dev)); } else { diff --git a/netutils/netlib/netlib_delvlan.c b/netutils/netlib/netlib_delvlan.c new file mode 100644 index 000000000..1b6a2ceb3 --- /dev/null +++ b/netutils/netlib/netlib_delvlan.c @@ -0,0 +1,72 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_delvlan.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET_VLAN + +#include + +#include "netutils/netlib.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_del_vlan + * + * Description: + * Remove a VLAN interface from an existing network device. + * + * Parameters: + * vlanif - The name of the VLAN network device + * + * Return: + * 0 on success; -1 on failure + * + ****************************************************************************/ + +int netlib_del_vlan(FAR const char *vlanif) +{ + int ret = ERROR; + + if (vlanif) + { + int sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); + if (sockfd >= 0) + { + struct vlan_ioctl_args ifv; + + strlcpy(ifv.device1, vlanif, sizeof(ifv.device1)); + ifv.cmd = DEL_VLAN_CMD; + + ret = ioctl(sockfd, SIOCSIFVLAN, &ifv); + close(sockfd); + } + } + + return ret; +} + +#endif /* CONFIG_NET_VLAN */ diff --git a/netutils/netlib/netlib_dhcp_ntp.c b/netutils/netlib/netlib_dhcp_ntp.c new file mode 100644 index 000000000..b1d608db0 --- /dev/null +++ b/netutils/netlib/netlib_dhcp_ntp.c @@ -0,0 +1,182 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_dhcp_ntp.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_NETUTILS_DHCPC + +#include +#include +#include +#include + +#include "netutils/netlib.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pthread_mutex_t g_dhcp_ntp_lock = PTHREAD_MUTEX_INITIALIZER; +static FAR char *g_dhcp_ntp_servers; +static netlib_dhcp_ntp_callback_t g_dhcp_ntp_callback; +static FAR void *g_dhcp_ntp_callback_arg; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR char *netlib_dhcp_ntp_dup(FAR const char *ntp_server_list) +{ + if (ntp_server_list == NULL || ntp_server_list[0] == '\0') + { + return NULL; + } + + return strdup(ntp_server_list); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int netlib_set_ntp_servers_from_dhcp(FAR const char *ntp_server_list) +{ + FAR char *new_servers; + FAR char *notify_servers = NULL; + FAR char *old_servers; + netlib_dhcp_ntp_callback_t callback; + FAR void *arg; + int ret = OK; + + new_servers = netlib_dhcp_ntp_dup(ntp_server_list); + if (ntp_server_list != NULL && ntp_server_list[0] != '\0' && + new_servers == NULL) + { + return -ENOMEM; + } + + pthread_mutex_lock(&g_dhcp_ntp_lock); + + if ((g_dhcp_ntp_servers == NULL && new_servers == NULL) || + (g_dhcp_ntp_servers != NULL && new_servers != NULL && + strcmp(g_dhcp_ntp_servers, new_servers) == 0)) + { + pthread_mutex_unlock(&g_dhcp_ntp_lock); + free(new_servers); + return OK; + } + + callback = g_dhcp_ntp_callback; + arg = g_dhcp_ntp_callback_arg; + + if (callback != NULL && new_servers != NULL) + { + notify_servers = strdup(new_servers); + if (notify_servers == NULL) + { + ret = -ENOMEM; + goto errout_with_lock; + } + } + + old_servers = g_dhcp_ntp_servers; + g_dhcp_ntp_servers = new_servers; + pthread_mutex_unlock(&g_dhcp_ntp_lock); + + free(old_servers); + + if (callback != NULL) + { + callback(notify_servers, arg); + } + + free(notify_servers); + return OK; + +errout_with_lock: + pthread_mutex_unlock(&g_dhcp_ntp_lock); + free(new_servers); + return ret; +} + +int netlib_register_dhcp_ntp_callback(netlib_dhcp_ntp_callback_t callback, + FAR void *arg) +{ + FAR char *notify_servers = NULL; + + if (callback == NULL) + { + return -EINVAL; + } + + pthread_mutex_lock(&g_dhcp_ntp_lock); + + if (g_dhcp_ntp_callback != NULL && + (g_dhcp_ntp_callback != callback || g_dhcp_ntp_callback_arg != arg)) + { + pthread_mutex_unlock(&g_dhcp_ntp_lock); + return -EBUSY; + } + + if (g_dhcp_ntp_servers != NULL) + { + notify_servers = strdup(g_dhcp_ntp_servers); + if (notify_servers == NULL) + { + pthread_mutex_unlock(&g_dhcp_ntp_lock); + return -ENOMEM; + } + } + + g_dhcp_ntp_callback = callback; + g_dhcp_ntp_callback_arg = arg; + + pthread_mutex_unlock(&g_dhcp_ntp_lock); + + callback(notify_servers, arg); + free(notify_servers); + return OK; +} + +int netlib_unregister_dhcp_ntp_callback(netlib_dhcp_ntp_callback_t callback, + FAR void *arg) +{ + int ret = -ENOENT; + + pthread_mutex_lock(&g_dhcp_ntp_lock); + + if (g_dhcp_ntp_callback == callback && g_dhcp_ntp_callback_arg == arg) + { + g_dhcp_ntp_callback = NULL; + g_dhcp_ntp_callback_arg = NULL; + ret = OK; + } + + pthread_mutex_unlock(&g_dhcp_ntp_lock); + return ret; +} + +#endif /* CONFIG_NETUTILS_DHCPC */ diff --git a/netutils/netlib/netlib_getarp.c b/netutils/netlib/netlib_getarp.c index d023bce30..b98d99f9b 100644 --- a/netutils/netlib/netlib_getarp.c +++ b/netutils/netlib/netlib_getarp.c @@ -77,8 +77,7 @@ int netlib_get_arpmapping(FAR const struct sockaddr_in *inaddr, memcpy(&req.arp_pa, inaddr, sizeof(struct sockaddr_in)); if (ifname != NULL) { - strlcpy((FAR char *)&req.arp_dev, ifname, - sizeof(req.arp_dev)); + strlcpy(req.arp_dev, ifname, sizeof(req.arp_dev)); } else { diff --git a/netutils/netlib/netlib_getifstatistics.c b/netutils/netlib/netlib_getifstatistics.c index b838d556d..e6c52abba 100644 --- a/netutils/netlib/netlib_getifstatistics.c +++ b/netutils/netlib/netlib_getifstatistics.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include diff --git a/netutils/netlib/netlib_getiobinfo.c b/netutils/netlib/netlib_getiobinfo.c new file mode 100644 index 000000000..fe9a97983 --- /dev/null +++ b/netutils/netlib/netlib_getiobinfo.c @@ -0,0 +1,108 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_getiobinfo.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "netutils/netlib.h" + +#ifdef CONFIG_MM_IOB + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Determines the size of an intermediate buffer that must be large enough + * to handle the longest line generated by this logic. + */ + +#define PROCFS_LINELEN 80 +#define PROCFS_IOB_PATH "/proc/iobinfo" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_get_iobinfo + * + * Description: + * Get the network iob info. + * + * Parameters: + * iob The pointer to return iobinfo. + * + * Return: + * 0 on success; a nagtive on failure. + * + ****************************************************************************/ + +int netlib_get_iobinfo(FAR struct iob_stats_s *iob) +{ + int ret = OK; + FAR FILE *stream; + char line[PROCFS_LINELEN]; + + stream = fopen(PROCFS_IOB_PATH, "r"); + if (stream == NULL) + { + fprintf(stderr, "ERROR: Failed to open path:%s \n", PROCFS_IOB_PATH); + return -errno; + } + + /* Read ioninfo header and next is the data */ + + if (fgets(line, sizeof(line), stream) != NULL) + { + if (fgets(line, sizeof(line), stream) == NULL) + { + ret = -EINVAL; + fclose(stream); + return ret; + } + + if (sscanf(line, "%d %d %d %d", &iob->ntotal, &iob->nfree, + &iob->nwait, &iob->nthrottle) <= 0) + { + ret = -errno; + fclose(stream); + return ret; + } + } + + fclose(stream); + return ret; +} + +#endif /* CONFIG_MM_IOB */ \ No newline at end of file diff --git a/netutils/netlib/netlib_ipmsfilter.c b/netutils/netlib/netlib_ipmsfilter.c index d7f9b0ff1..9fa2f23aa 100644 --- a/netutils/netlib/netlib_ipmsfilter.c +++ b/netutils/netlib/netlib_ipmsfilter.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/netlib/netlib_ipv4addrconv.c b/netutils/netlib/netlib_ipv4addrconv.c index 27325ca0a..e27a21f76 100644 --- a/netutils/netlib/netlib_ipv4addrconv.c +++ b/netutils/netlib/netlib_ipv4addrconv.c @@ -68,9 +68,9 @@ bool netlib_ipv4addrconv(FAR const char *addrstr, FAR uint8_t *ipaddr) { ch = *addrstr++; if (++j > 4) - { - return false; - } + { + return false; + } if (ch == '.' || ch == 0) { diff --git a/netutils/netlib/netlib_ipv6route.c b/netutils/netlib/netlib_ipv6route.c index a0e10fa7c..14bcc2e9f 100644 --- a/netutils/netlib/netlib_ipv6route.c +++ b/netutils/netlib/netlib_ipv6route.c @@ -49,14 +49,14 @@ /* The form of the entry from the routing table file: * - * 11111111112222222222333333333344444444445555 - * 12345678901234567890123456789012345678901234567890123 - * nnnn. target: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx - * netmask: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx - * router: xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx + * 1111111111222222222233333333334444444444555 + * 1234567890123456789012345678901234567890123456789012 + * nnnn. TARGET xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx + * NETMASK xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx + * ROUTER xxxx:xxxx:xxxx:xxxxxxxx:xxxx:xxxx:xxxx */ -#define ADDR_OFFSET 15 +#define ADDR_OFFSET 14 /**************************************************************************** * Private Functions diff --git a/netutils/netlib/netlib_listenon.c b/netutils/netlib/netlib_listenon.c index 608937ff7..a80cd8158 100644 --- a/netutils/netlib/netlib_listenon.c +++ b/netutils/netlib/netlib_listenon.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/netlib/netlib_nodeaddrconv.c b/netutils/netlib/netlib_nodeaddrconv.c index 67c6ee2f7..1df293788 100644 --- a/netutils/netlib/netlib_nodeaddrconv.c +++ b/netutils/netlib/netlib_nodeaddrconv.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "nuttx/wireless/pktradio.h" #include "netutils/netlib.h" diff --git a/netutils/netlib/netlib_obtainipv4addr.c b/netutils/netlib/netlib_obtainipv4addr.c index 5390466fe..df07fef59 100644 --- a/netutils/netlib/netlib_obtainipv4addr.c +++ b/netutils/netlib/netlib_obtainipv4addr.c @@ -24,7 +24,11 @@ * Included Files ****************************************************************************/ -#include +#include +#include +#include +#include +#include #include #include "netutils/dhcpc.h" @@ -105,6 +109,68 @@ static int dhcp_setup_result(FAR const char *ifname, return OK; } +#ifdef CONFIG_NETUTILS_NTPCLIENT +static int dhcp_set_ntp_servers(FAR const struct dhcpc_state *ds) +{ + char ntp_server_list[CONFIG_NETUTILS_DHCPC_NTP_SERVERS * + (INET_ADDRSTRLEN + 1)]; + size_t offset = 0; + uint8_t i; + + /* Clear the DHCP-provided NTP server list, + * consider case: that device has joined another dhcp domain, + * it need refresh related settings. + */ + + if (ds->num_ntpaddr == 0) + { + return netlib_set_ntp_servers_from_dhcp(NULL); + } + + ntp_server_list[0] = '\0'; + + for (i = 0; i < ds->num_ntpaddr; i++) + { + char addrbuf[INET_ADDRSTRLEN]; + int ret; + + /* Skip empty entries */ + + if (ds->ntpaddr[i].s_addr == 0) + { + continue; + } + + if (inet_ntop(AF_INET, &ds->ntpaddr[i], addrbuf, sizeof(addrbuf)) == + NULL) + { + return -EINVAL; + } + + /* Append the server to the list */ + + ret = snprintf(ntp_server_list + offset, + sizeof(ntp_server_list) - offset, + "%s%s", offset == 0 ? "" : ";", addrbuf); + if (ret < 0 || (size_t)ret >= sizeof(ntp_server_list) - offset) + { + return -E2BIG; + } + + offset += (size_t)ret; + } + + /* Clear the list if all entries were empty */ + + if (offset == 0) + { + return netlib_set_ntp_servers_from_dhcp(NULL); + } + + return netlib_set_ntp_servers_from_dhcp(ntp_server_list); +} +#endif + /**************************************************************************** * Name: dhcp_obtain_statefuladdr * @@ -156,6 +222,18 @@ static int dhcp_obtain_statefuladdr(FAR const char *ifname) if (ret == OK) { ret = dhcp_setup_result(ifname, &ds); +#ifdef CONFIG_NETUTILS_NTPCLIENT + if (ret == OK) + { + ret = dhcp_set_ntp_servers(&ds); + if (ret < 0) + { + nwarn("WARNING: failed to update DHCP NTP server list: %d\n", + ret); + ret = OK; + } + } +#endif } else { diff --git a/netutils/netlib/netlib_obtainipv6addr.c b/netutils/netlib/netlib_obtainipv6addr.c index f55667705..c1a74b8ce 100644 --- a/netutils/netlib/netlib_obtainipv6addr.c +++ b/netutils/netlib/netlib_obtainipv6addr.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/netlib/netlib_server.c b/netutils/netlib/netlib_server.c index 98ff5e907..9436481bc 100644 --- a/netutils/netlib/netlib_server.c +++ b/netutils/netlib/netlib_server.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include diff --git a/netutils/netlib/netlib_setarp.c b/netutils/netlib/netlib_setarp.c index 88dbba810..a62ac9a9e 100644 --- a/netutils/netlib/netlib_setarp.c +++ b/netutils/netlib/netlib_setarp.c @@ -78,10 +78,10 @@ int netlib_set_arpmapping(FAR const struct sockaddr_in *inaddr, req.arp_ha.sa_family = ARPHRD_ETHER; memcpy(&req.arp_ha.sa_data, macaddr, ETHER_ADDR_LEN); + req.arp_flags = ATF_PERM; if (ifname != NULL) { - strlcpy((FAR char *)&req.arp_dev, ifname, - sizeof(req.arp_dev)); + strlcpy(req.arp_dev, ifname, sizeof(req.arp_dev)); } else { diff --git a/netutils/netlib/netlib_setifstatus.c b/netutils/netlib/netlib_setifstatus.c index 8a3f58db1..d2f56dd8b 100644 --- a/netutils/netlib/netlib_setifstatus.c +++ b/netutils/netlib/netlib_setifstatus.c @@ -74,12 +74,16 @@ int netlib_ifup(const char *ifname) /* Put the driver name into the request */ strlcpy(req.ifr_name, ifname, IFNAMSIZ); + ret = ioctl(sockfd, SIOCGIFFLAGS, (unsigned long)&req); + if (ret >= 0) + { + /* Perform the ioctl to ifup flag */ - /* Perform the ioctl to ifup flag */ + IFF_SET_UP(req.ifr_flags); - req.ifr_flags |= IFF_UP; + ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); + } - ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); close(sockfd); } } @@ -108,6 +112,53 @@ int netlib_ifdown(const char *ifname) { /* Get a socket (only so that we get access to the INET subsystem) */ + int sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); + if (sockfd >= 0) + { + struct ifreq req; + memset(&req, 0, sizeof(struct ifreq)); + + /* Put the driver name into the request */ + + strlcpy(req.ifr_name, ifname, IFNAMSIZ); + ret = ioctl(sockfd, SIOCGIFFLAGS, (unsigned long)&req); + if (ret >= 0) + { + /* Perform the ioctl to ifdown flag */ + + IFF_CLR_UP(req.ifr_flags); + + ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); + } + + close(sockfd); + } + } + + return ret; +} + +/**************************************************************************** + * Name: netlib_ifarp + * + * Description: + * Enable ARP learning capability on the interface + * + * Parameters: + * ifname The name of the interface to use + * + * Return: + * 0 on success; -1 on failure + * + ****************************************************************************/ + +int netlib_ifarp(const char *ifname) +{ + int ret = ERROR; + if (ifname) + { + /* Get a socket (only so that we get access to the INET subsystem) */ + int sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); if (sockfd >= 0) { @@ -120,7 +171,62 @@ int netlib_ifdown(const char *ifname) /* Perform the ioctl to ifup flag */ - ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); + ret = ioctl(sockfd, SIOCGIFFLAGS, (unsigned long)&req); + if (ret >= 0) + { + IFF_CLR_NOARP(req.ifr_flags); + + ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); + } + + close(sockfd); + } + } + + return ret; +} + +/**************************************************************************** + * Name: netlib_ifnoarp + * + * Description: + * Disable ARP learning capability on the interface + * + * Parameters: + * ifname The name of the interface to use + * + * Return: + * 0 on success; -1 on failure + * + ****************************************************************************/ + +int netlib_ifnoarp(const char *ifname) +{ + int ret = ERROR; + if (ifname) + { + /* Get a socket (only so that we get access to the INET subsystem) */ + + int sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); + if (sockfd >= 0) + { + struct ifreq req; + memset(&req, 0, sizeof(struct ifreq)); + + /* Put the driver name into the request */ + + strlcpy(req.ifr_name, ifname, IFNAMSIZ); + + /* Perform the ioctl to ifup flag */ + + ret = ioctl(sockfd, SIOCGIFFLAGS, (unsigned long)&req); + if (ret >= 0) + { + IFF_SET_NOARP(req.ifr_flags); + + ret = ioctl(sockfd, SIOCSIFFLAGS, (unsigned long)&req); + } + close(sockfd); } } diff --git a/netutils/nng/Kconfig b/netutils/nng/Kconfig index 525cf2f45..8d4dfb7c1 100644 --- a/netutils/nng/Kconfig +++ b/netutils/nng/Kconfig @@ -11,6 +11,12 @@ config NETUTILS_NNG offering a simple API to solve common recurring messaging problems, such as publish/subscribe, RPC-style request/reply, or service discovery. + Consider the following configuration when enabling NNG: + - CONFIG_LIBC_NETDB=y + - CONFIG_DEV_URANDOM=y + - CONFIG_NET_LOCAL=y + - CONFIG_NET_LOCAL_STREAM=y + if NETUTILS_NNG config NETUTILS_NNG_VERSION diff --git a/netutils/ntpclient/ntpclient.c b/netutils/ntpclient/ntpclient.c index 19962f4d5..5a48b0586 100644 --- a/netutils/ntpclient/ntpclient.c +++ b/netutils/ntpclient/ntpclient.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include @@ -52,6 +52,7 @@ #include +#include "netutils/netlib.h" #include "netutils/ntpclient.h" #include "ntpv3.h" @@ -62,10 +63,6 @@ /* Configuration ************************************************************/ -#ifndef CONFIG_HAVE_LONG_LONG -# error "64-bit integer support required for NTP client" -#endif - #if defined(CONFIG_LIBC_NETDB) && !defined(CONFIG_NETUTILS_NTPCLIENT_SERVER) # error "CONFIG_NETUTILS_NTPCLIENT_SERVER must be provided" #endif @@ -103,6 +100,8 @@ #define MAX_SERVER_SELECTION_RETRIES 3 +#define MAX_RETRY_INTERVAL 120 + #ifndef STR # define STR2(x) #x # define STR(x) STR2(x) @@ -123,6 +122,14 @@ enum ntpc_daemon_e NTP_STOPPED }; +enum ntpc_server_source_e +{ + NTP_SERVER_SOURCE_NONE = 0, + NTP_SERVER_SOURCE_CONFIG, + NTP_SERVER_SOURCE_DHCP, + NTP_SERVER_SOURCE_EXPLICIT +}; + /* This type describes the state of the NTP client daemon. Only one * instance of the NTP daemon is permitted in this implementation. */ @@ -130,11 +137,17 @@ enum ntpc_daemon_e struct ntpc_daemon_s { uint8_t state; /* See enum ntpc_daemon_e */ + uint8_t source; /* See enum ntpc_server_source_e */ + bool dhcp_registered; sem_t lock; /* Used to protect the whole structure */ sem_t sync; /* Used to synchronize start and stop events */ pid_t pid; /* Task ID of the NTP daemon */ sq_queue_t kod_list; /* KoD excluded server addresses */ int family; /* Allowed address family */ + + /* DHCP-provided server list */ + + FAR char *dhcp_servers; }; union ntp_addr_u @@ -191,11 +204,14 @@ struct ntp_kod_exclude_s static struct ntpc_daemon_s g_ntpc_daemon = { NTP_NOT_RUNNING, + NTP_SERVER_SOURCE_NONE, + false, SEM_INITIALIZER(1), SEM_INITIALIZER(0), -1, { NULL, NULL }, AF_UNSPEC, /* Default is both IPv4 and IPv6 */ + NULL, }; static struct ntp_sample_s g_last_samples @@ -206,6 +222,117 @@ unsigned int g_last_nsamples = 0; * Private Functions ****************************************************************************/ +static int ntpc_daemon(int argc, FAR char **argv); +#ifdef CONFIG_NETUTILS_DHCPC +static int ntpc_set_servers_from_dhcp(FAR const char *ntp_server_list); + +static FAR char *ntpc_dup_server_list(FAR const char *ntp_server_list) +{ + if (ntp_server_list == NULL || ntp_server_list[0] == '\0') + { + return NULL; + } + + return strdup(ntp_server_list); +} +#endif + +static FAR const char *ntpc_select_server_list(FAR uint8_t *source) +{ + if (g_ntpc_daemon.dhcp_servers != NULL && + g_ntpc_daemon.dhcp_servers[0] != '\0') + { + *source = NTP_SERVER_SOURCE_DHCP; + return g_ntpc_daemon.dhcp_servers; + } + + if (CONFIG_NETUTILS_NTPCLIENT_SERVER[0] != '\0') + { + *source = NTP_SERVER_SOURCE_CONFIG; + return CONFIG_NETUTILS_NTPCLIENT_SERVER; + } + + *source = NTP_SERVER_SOURCE_NONE; + return NULL; +} + +#ifdef CONFIG_NETUTILS_DHCPC +static void ntpc_dhcp_notify(FAR const char *ntp_server_list, FAR void *arg) +{ + int ret; + + UNUSED(arg); + + ret = ntpc_set_servers_from_dhcp(ntp_server_list); + if (ret < 0) + { + nwarn("WARNING: failed to update DHCP NTP server list: %d\n", ret); + } +} + +static int ntpc_register_dhcp(void) +{ + int ret; + + sem_wait(&g_ntpc_daemon.lock); + if (g_ntpc_daemon.dhcp_registered) + { + sem_post(&g_ntpc_daemon.lock); + return OK; + } + + g_ntpc_daemon.dhcp_registered = true; + sem_post(&g_ntpc_daemon.lock); + + ret = netlib_register_dhcp_ntp_callback(ntpc_dhcp_notify, NULL); + if (ret < 0) + { + sem_wait(&g_ntpc_daemon.lock); + g_ntpc_daemon.dhcp_registered = false; + sem_post(&g_ntpc_daemon.lock); + return ret; + } + + return OK; +} +#endif + +static int ntpc_start_selected(FAR const char *ntp_server_list, + uint8_t source) +{ + FAR char *task_argv[] = + { + (FAR char *)ntp_server_list, + NULL + }; + + g_ntpc_daemon.state = NTP_STARTED; + g_ntpc_daemon.source = source; + g_ntpc_daemon.pid = + task_create("NTP daemon", CONFIG_NETUTILS_NTPCLIENT_SERVERPRIO, + CONFIG_NETUTILS_NTPCLIENT_STACKSIZE, ntpc_daemon, + task_argv); + + if (g_ntpc_daemon.pid < 0) + { + int errval = errno; + DEBUGASSERT(errval > 0); + + g_ntpc_daemon.state = NTP_STOPPED; + g_ntpc_daemon.source = NTP_SERVER_SOURCE_NONE; + nerr("ERROR: Failed to start the NTP daemon: %d\n", errval); + return -errval; + } + + do + { + sem_wait(&g_ntpc_daemon.sync); + } + while (g_ntpc_daemon.state == NTP_STARTED); + + return g_ntpc_daemon.pid; +} + /**************************************************************************** * Name: sample_cmp ****************************************************************************/ @@ -440,7 +567,7 @@ static int32_t ntp_nsecpart(int64_t time) { /* Get fraction part converted to nanoseconds. */ - return (((int64_t)((uint64_t)time << 32) >> 32) * NSEC_PER_SEC) >> 32; + return (((time << 32) >> 32) * NSEC_PER_SEC) >> 32; } /**************************************************************************** @@ -462,7 +589,7 @@ static uint64_t timespec2ntp(FAR const struct timespec *ts) /* Set seconds part. */ - ntp_time += (uint64_t)(ts->tv_sec) << 32; + ntp_time += ts->tv_sec << 32; return ntp_time; } @@ -520,8 +647,8 @@ static void ntpc_calculate_offset(FAR int64_t *offset, FAR int64_t *delay, * http://nicolas.aimon.fr/2014/12/05/timesync/ */ - *offset = (int64_t)((remote_recvtime / 2 - local_xmittime / 2) + - (remote_xmittime / 2 - local_recvtime / 2)); + *offset = (remote_recvtime / 2 - local_xmittime / 2) + + (remote_xmittime / 2 - local_recvtime / 2); /* Calculate roundtrip delay. */ @@ -580,13 +707,13 @@ static void ntpc_settime(int64_t offset, FAR struct timespec *start_realtime, diffms_real = curr_realtime.tv_sec - start_realtime->tv_sec; diffms_real *= 1000; - diffms_real += (int64_t)(curr_realtime.tv_nsec - - start_realtime->tv_nsec) / (1000 * 1000); + diffms_real += (curr_realtime.tv_nsec - + start_realtime->tv_nsec) / (1000 * 1000); diffms_mono = curr_monotonic.tv_sec - start_monotonic->tv_sec; diffms_mono *= 1000; - diffms_mono += (int64_t)(curr_monotonic.tv_nsec - - start_monotonic->tv_nsec) / (1000 * 1000); + diffms_mono += (curr_monotonic.tv_nsec - + start_monotonic->tv_nsec) / (1000 * 1000); /* Detect if real-time has been altered by other task. */ @@ -1233,6 +1360,7 @@ static int ntpc_daemon(int argc, FAR char **argv) FAR struct ntp_servers_s *srvs; int exitcode = EXIT_SUCCESS; int retries = 0; + int retry_delay = 1; int nsamples; int ret; @@ -1302,21 +1430,24 @@ static int ntpc_daemon(int argc, FAR char **argv) clock_gettime(CLOCK_REALTIME, &start_realtime); clock_gettime(CLOCK_MONOTONIC, &start_monotonic); - /* Collect samples. */ - nsamples = 0; - for (i = 0; i < CONFIG_NETUTILS_NTPCLIENT_NUM_SAMPLES; i++) + if (netlib_check_ipconnectivity(NULL, 1, 1) > 0) { - /* Get next sample. */ + /* Collect samples. */ - ret = ntpc_get_ntp_sample(srvs, samples, nsamples); - if (ret < 0) + for (i = 0; i < CONFIG_NETUTILS_NTPCLIENT_NUM_SAMPLES; i++) { - errval = errno; - } - else - { - ++nsamples; + /* Get next sample. */ + + ret = ntpc_get_ntp_sample(srvs, samples, nsamples); + if (ret < 0) + { + errval = errno; + } + else + { + ++nsamples; + } } } @@ -1358,14 +1489,14 @@ static int ntpc_daemon(int argc, FAR char **argv) if (offset1 > 0 && offset2 > 0) { - offset = ((uint64_t)offset1 + (uint64_t)offset2) / 2; + offset = (offset1 + offset2) / 2; } else if (offset1 < 0 && offset2 < 0) { offset1 = -offset1; offset2 = -offset2; - offset = ((uint64_t)offset1 + (uint64_t)offset2) / 2; + offset = (offset1 + offset2) / 2; offset = -offset; } @@ -1405,6 +1536,7 @@ static int ntpc_daemon(int argc, FAR char **argv) sleep(CONFIG_NETUTILS_NTPCLIENT_POLLDELAYSEC); retries = 0; + retry_delay = 1; } continue; @@ -1424,7 +1556,14 @@ static int ntpc_daemon(int argc, FAR char **argv) if (errval != EINTR) { - sleep(1); + ninfo("Retry %d in %d seconds...\n", retries, retry_delay); + sleep(retry_delay); + + retry_delay *= 2; + if (retry_delay > MAX_RETRY_INTERVAL) + { + retry_delay = MAX_RETRY_INTERVAL; + } } /* Keep retrying. */ @@ -1475,11 +1614,12 @@ void ntpc_dualstack_family(int family) int ntpc_start_with_list(FAR const char *ntp_server_list) { - FAR char *task_argv[] = + int ret; + + if (ntp_server_list == NULL || ntp_server_list[0] == '\0') { - (FAR char *)ntp_server_list, - NULL - }; + return -EINVAL; + } /* Is the NTP in a non-running state? */ @@ -1487,34 +1627,10 @@ int ntpc_start_with_list(FAR const char *ntp_server_list) if (g_ntpc_daemon.state == NTP_NOT_RUNNING || g_ntpc_daemon.state == NTP_STOPPED) { - /* Start the NTP daemon */ - - g_ntpc_daemon.state = NTP_STARTED; - g_ntpc_daemon.pid = - task_create("NTP daemon", CONFIG_NETUTILS_NTPCLIENT_SERVERPRIO, - CONFIG_NETUTILS_NTPCLIENT_STACKSIZE, ntpc_daemon, - task_argv); - - /* Handle failures to start the NTP daemon */ - - if (g_ntpc_daemon.pid < 0) - { - int errval = errno; - DEBUGASSERT(errval > 0); - - g_ntpc_daemon.state = NTP_STOPPED; - nerr("ERROR: Failed to start the NTP daemon: %d\n", errval); - sem_post(&g_ntpc_daemon.lock); - return -errval; - } - - /* Wait for any daemon state change */ - - do - { - sem_wait(&g_ntpc_daemon.sync); - } - while (g_ntpc_daemon.state == NTP_STARTED); + ret = ntpc_start_selected(ntp_server_list, + NTP_SERVER_SOURCE_EXPLICIT); + sem_post(&g_ntpc_daemon.lock); + return ret; } sem_post(&g_ntpc_daemon.lock); @@ -1535,9 +1651,105 @@ int ntpc_start_with_list(FAR const char *ntp_server_list) int ntpc_start(void) { - return ntpc_start_with_list(CONFIG_NETUTILS_NTPCLIENT_SERVER); + FAR const char *ntp_server_list; + uint8_t source; + int ret; + +#ifdef CONFIG_NETUTILS_DHCPC + ret = ntpc_register_dhcp(); + if (ret < 0) + { + return ret; + } +#endif + + sem_wait(&g_ntpc_daemon.lock); + if (g_ntpc_daemon.state != NTP_NOT_RUNNING && + g_ntpc_daemon.state != NTP_STOPPED) + { + ret = g_ntpc_daemon.pid; + sem_post(&g_ntpc_daemon.lock); + return ret; + } + + ntp_server_list = ntpc_select_server_list(&source); + if (ntp_server_list == NULL) + { + g_ntpc_daemon.source = NTP_SERVER_SOURCE_NONE; + sem_post(&g_ntpc_daemon.lock); + return -ENOENT; + } + + ret = ntpc_start_selected(ntp_server_list, source); + sem_post(&g_ntpc_daemon.lock); + return ret; } +#ifdef CONFIG_NETUTILS_DHCPC +static int ntpc_set_servers_from_dhcp(FAR const char *ntp_server_list) +{ + FAR char *new_servers; + FAR char *old_servers; + bool start = false; + bool restart = false; + int ret = OK; + + new_servers = ntpc_dup_server_list(ntp_server_list); + if (ntp_server_list != NULL && ntp_server_list[0] != '\0' && + new_servers == NULL) + { + return -ENOMEM; + } + + sem_wait(&g_ntpc_daemon.lock); + + if ((g_ntpc_daemon.dhcp_servers == NULL && new_servers == NULL) || + (g_ntpc_daemon.dhcp_servers != NULL && new_servers != NULL && + strcmp(g_ntpc_daemon.dhcp_servers, new_servers) == 0)) + { + sem_post(&g_ntpc_daemon.lock); + free(new_servers); + return OK; + } + + old_servers = g_ntpc_daemon.dhcp_servers; + g_ntpc_daemon.dhcp_servers = new_servers; + + if ((g_ntpc_daemon.source == NTP_SERVER_SOURCE_DHCP || + g_ntpc_daemon.source == NTP_SERVER_SOURCE_CONFIG) && + (g_ntpc_daemon.state == NTP_STARTED || + g_ntpc_daemon.state == NTP_RUNNING)) + { + restart = true; + } + else if (g_ntpc_daemon.source == NTP_SERVER_SOURCE_NONE && + new_servers != NULL && + (g_ntpc_daemon.state == NTP_NOT_RUNNING || + g_ntpc_daemon.state == NTP_STOPPED)) + { + start = true; + } + + sem_post(&g_ntpc_daemon.lock); + free(old_servers); + + if (restart) + { + ret = ntpc_stop(); + if (ret >= 0 && new_servers != NULL) + { + ret = ntpc_start(); + } + } + else if (start) + { + ret = ntpc_start(); + } + + return ret; +} +#endif + /**************************************************************************** * Name: ntpc_stop * diff --git a/netutils/paho_mqtt/CMakeLists.txt b/netutils/paho_mqtt/CMakeLists.txt new file mode 100644 index 000000000..d3360390b --- /dev/null +++ b/netutils/paho_mqtt/CMakeLists.txt @@ -0,0 +1,166 @@ +# ############################################################################## +# apps/netutils/paho_mqtt/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_LIB_MQTT5) + + # ############################################################################ + # Config and Fetch Paho MQTT C library + # ############################################################################ + + set(PAHO_MQTT_DIR ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt) + + if(NOT EXISTS ${PAHO_MQTT_DIR}) + # Default version if not specified in config + if(DEFINED CONFIG_NETUTILS_PAHO_MQTT_VERSION) + set(PAHO_MQTT_VERSION ${CONFIG_NETUTILS_PAHO_MQTT_VERSION}) + else() + set(PAHO_MQTT_VERSION "1.3.15") + endif() + + set(PAHO_MQTT_URL "https://github.com/eclipse-paho/paho.mqtt.c/archive") + FetchContent_Declare( + paho_mqtt_fetch + URL ${PAHO_MQTT_URL}/v${PAHO_MQTT_VERSION}.zip SOURCE_DIR ${PAHO_MQTT_DIR} + BINARY_DIR ${CMAKE_BINARY_DIR}/apps/netutils/paho_mqtt/paho_mqtt + DOWNLOAD_NO_PROGRESS true + TIMEOUT 30) + + FetchContent_GetProperties(paho_mqtt_fetch) + + if(NOT paho_mqtt_fetch_POPULATED) + FetchContent_Populate(paho_mqtt_fetch) + + # GitHub ZIP files extract with a versioned top-level directory Move + # contents from paho.mqtt.c- to paho_mqtt if needed + file(GLOB extracted_dirs "${PAHO_MQTT_DIR}/paho.mqtt.c-*") + if(extracted_dirs) + list(GET extracted_dirs 0 extracted_dir) + # Move all contents from the versioned directory to paho_mqtt + file(GLOB extracted_contents "${extracted_dir}/*") + foreach(item ${extracted_contents}) + get_filename_component(item_name ${item} NAME) + execute_process( + COMMAND ${CMAKE_COMMAND} -E rename ${item} + ${PAHO_MQTT_DIR}/${item_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + endforeach() + # Remove the empty versioned directory + execute_process( + COMMAND ${CMAKE_COMMAND} -E remove_directory ${extracted_dir} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + message("Moved contents from versioned directory to paho_mqtt") + endif() + + # Remove downloaded zip file if exists in current directory + file(GLOB zip_files "${CMAKE_CURRENT_LIST_DIR}/v${PAHO_MQTT_VERSION}.zip") + if(zip_files) + file(REMOVE ${zip_files}) + message("Removed downloaded zip file") + endif() + endif() + + # Apply paho_mqtt_01 patch if exists + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt_01.patch) + execute_process( + COMMAND + sh -c + "patch -p1 --forward --ignore-whitespace < ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt_01.patch || true" + WORKING_DIRECTORY ${PAHO_MQTT_DIR}) + message("paho_mqtt_01 patching done") + endif() + + message("paho_mqtt download done") + endif() + + configure_file(paho_mqtt/src/VersionInfo.h.in + ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h @ONLY) + + set(MQTT5_INCDIR ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt/src) + list(APPEND MQTT5_INCDIR ${CMAKE_CURRENT_BINARY_DIR}) + + file(GLOB CSRCS paho_mqtt/src/*.c) + + if(CONFIG_UTILS_MQTT5) + list(APPEND CSRCS + ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt/src/samples/pubsub_opts.c) + endif() + + if(CONFIG_OPENSSL_MBEDTLS_WRAPPER) + list(REMOVE_ITEM CSRCS ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt/src/SHA1.c) + endif() + + list(REMOVE_ITEM CSRCS ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt/src/MQTTClient.c + ${CMAKE_CURRENT_LIST_DIR}/paho_mqtt/src/MQTTVersion.c) + + nuttx_add_library(mqtt5) + + target_sources(mqtt5 PRIVATE ${CSRCS}) + + target_include_directories(mqtt5 PRIVATE ${MQTT5_INCDIR}) + + target_compile_options(mqtt5 PRIVATE ${MQTT5_FLAGS}) + + if(CONFIG_UTILS_MQTT5) + + set(MQTT_PUB_FLAGS + -DmessageArrived=mqtt_pub_messageArrived + -DonDisconnect=mqtt_pub_onDisconnect + -DonConnectFailure5=mqtt_pub_onConnectFailure5 + -DonConnectFailure=mqtt_pub_onConnectFailure + -DonConnect5=mqtt_pub_onConnect5 + -DonConnect=mqtt_pub_onConnect + -Dmysleep=mqtt_pub_mysleep + -DtoStop=mqtt_pub_toStop + -Dopts=mqtt_pub_opts + -Dmyconnect=mqtt_pub_myconnect + -Dcfinish=mqtt_pub_cfinish + -Dtrace_callback=mqtt_pub_trace_callback) + + nuttx_add_application( + NAME + mqtt_pub + SRCS + paho_mqtt/src/samples/paho_c_pub.c + DEPENDS + mqtt5 + INCLUDE_DIRECTORIES + ${MQTT5_INCDIR} + COMPILE_FLAGS + ${MQTT_PUB_FLAGS} + STACKSIZE + ${CONFIG_UTILS_MQTT5_STACKSIZE} + PRIORITY + ${CONFIG_UTILS_MQTT5_PRIORITY}) + + nuttx_add_application( + NAME + mqtt_sub + SRCS + paho_mqtt/src/samples/paho_c_sub.c + DEPENDS + mqtt5 + INCLUDE_DIRECTORIES + ${MQTT5_INCDIR} + STACKSIZE + ${CONFIG_UTILS_MQTT5_STACKSIZE} + PRIORITY + ${CONFIG_UTILS_MQTT5_PRIORITY}) + endif() +endif() diff --git a/netutils/paho_mqtt/Kconfig b/netutils/paho_mqtt/Kconfig new file mode 100644 index 000000000..987fccb76 --- /dev/null +++ b/netutils/paho_mqtt/Kconfig @@ -0,0 +1,37 @@ +# +# Copyright (C) 2020 Xiaomi Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +config LIB_MQTT5 + bool "Enable mqtt5" + default n + ---help--- + A library for accessing mqtt5 client services through C libraries calls in a simple manner. + +config UTILS_MQTT5 + tristate "Enable mqtt5 tool" + depends on LIB_MQTT5 + ---help--- + Enable mqtt utility + +if UTILS_MQTT5 +config UTILS_MQTT5_PRIORITY + int "mqtt utility priority" + default 100 + +config UTILS_MQTT5_STACKSIZE + int "mqtt utility statcksize" + default 16384 +endif diff --git a/netutils/paho_mqtt/Make.defs b/netutils/paho_mqtt/Make.defs new file mode 100644 index 000000000..a91777976 --- /dev/null +++ b/netutils/paho_mqtt/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/netutils/paho_mqtt/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_LIB_MQTT5),) +CONFIGURED_APPS += $(APPDIR)/netutils/paho_mqtt +endif diff --git a/netutils/paho_mqtt/Makefile b/netutils/paho_mqtt/Makefile new file mode 100644 index 000000000..21c9d904f --- /dev/null +++ b/netutils/paho_mqtt/Makefile @@ -0,0 +1,111 @@ +############################################################################ +# apps/netutils/paho_mqtt/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PAHO_MQTT_URL ?= "https://github.com/eclipse-paho/paho.mqtt.c/archive" + +# Default version if not specified in config +ifdef CONFIG_NETUTILS_PAHO_MQTT_VERSION +PAHO_MQTT_VERSION := $(patsubst "%",%,$(CONFIG_NETUTILS_PAHO_MQTT_VERSION)) +else +PAHO_MQTT_VERSION := 1.3.15 +endif + +PAHO_MQTT_ZIP = v$(PAHO_MQTT_VERSION).zip +PAHO_MQTT_UNPACK = paho_mqtt + +SRCDIR = $(APPDIR)/netutils/paho_mqtt/$(PAHO_MQTT_UNPACK)/src + +ifeq ($(CONFIG_LIB_MQTT5), y) + +# Check if paho_mqtt directory exists, if not download and extract +ifeq ($(wildcard $(PAHO_MQTT_UNPACK)/src),) +$(PAHO_MQTT_ZIP): + $(Q) echo "Downloading paho.mqtt.c-$(PAHO_MQTT_VERSION)" + $(Q) curl -L -o $(PAHO_MQTT_ZIP) $(PAHO_MQTT_URL)/$(PAHO_MQTT_ZIP) + +$(PAHO_MQTT_UNPACK): $(PAHO_MQTT_ZIP) + $(Q) echo "Unpacking: $(PAHO_MQTT_ZIP) -> $(PAHO_MQTT_UNPACK)" + $(Q) unzip -q $(PAHO_MQTT_ZIP) + $(Q) mv paho.mqtt.c-$(PAHO_MQTT_VERSION) $(PAHO_MQTT_UNPACK) + $(Q) rm -f $(PAHO_MQTT_ZIP) + $(Q) if [ -f paho_mqtt_01.patch ]; then \ + echo "Applying paho_mqtt_01 patch to $(PAHO_MQTT_UNPACK)"; \ + cd $(PAHO_MQTT_UNPACK) && patch -p1 --forward --ignore-whitespace < ../paho_mqtt_01.patch || true; \ + fi + $(Q) touch $(PAHO_MQTT_UNPACK) + +context:: $(PAHO_MQTT_UNPACK) + +distclean:: + $(call DELFILE, $(PAHO_MQTT_ZIP)) + $(call DELDIR, $(PAHO_MQTT_UNPACK)) +endif + +ifeq ($(CONFIG_OPENSSL_MBEDTLS_WRAPPER), y) +SKIP = $(SRCDIR)/SHA1.c +endif + +SKIP += $(SRCDIR)/MQTTClient.c +SKIP += $(SRCDIR)/MQTTVersion.c + +CSRCS = $(filter-out $(SKIP), $(wildcard $(SRCDIR)/*.c)) + +ifeq ($(CONFIG_UTILS_MQTT5),y) +CSRCS += $(SRCDIR)/samples/pubsub_opts.c +endif + +VARS = BUILD_TIMESTAMP PROJECT_VERSION PROJECT_VERSION_MAJOR +VARS += PROJECT_VERSION_MINOR PROJECT_VERSION_PATCH + +MQTT5_VERSION = $(SRCDIR)/VersionInfo.h + +SED_COMMANDS = $(foreach var,$(VARS),-e 's/@$(var)@/$($(var))/g') + +$(MQTT5_VERSION): $(SRCDIR)/VersionInfo.h.in + sed $(SED_COMMANDS) $< > $@ + +context:: $(MQTT5_VERSION) + +distclean:: + $(call DELFILE, $(MQTT5_VERSION)) + +ifeq ($(CONFIG_UTILS_MQTT5), y) +PROGNAME = mqtt_pub mqtt_sub +MAINSRC = $(SRCDIR)/samples/paho_c_pub.c $(SRCDIR)/samples/paho_c_sub.c +PRIORITY = $(CONFIG_UTILS_MQTT5_PRIORITY) +STACKSIZE = $(CONFIG_UTILS_MQTT5_STACKSIZE) +MODULE = $(CONFIG_UTILS_MQTT5) +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DmessageArrived=mqtt_pub_messageArrived +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DonDisconnect=mqtt_pub_onDisconnect +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DonConnectFailure5=mqtt_pub_onConnectFailure5 +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DonConnectFailure=mqtt_pub_onConnectFailure +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DonConnect5=mqtt_pub_onConnect5 +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -DonConnect=mqtt_pub_onConnect +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -Dmysleep=mqtt_pub_mysleep -DtoStop=mqtt_pub_toStop +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -Dopts=mqtt_pub_opts -Dmyconnect=mqtt_pub_myconnect +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -Dcfinish=mqtt_pub_cfinish +$(SRCDIR)/samples/paho_c_pub.c_CFLAGS += -Dtrace_callback=mqtt_pub_trace_callback +endif + +endif + +include $(APPDIR)/Application.mk diff --git a/netutils/paho_mqtt/paho_mqtt_01.patch b/netutils/paho_mqtt/paho_mqtt_01.patch new file mode 100644 index 000000000..67a1fccdf --- /dev/null +++ b/netutils/paho_mqtt/paho_mqtt_01.patch @@ -0,0 +1,314 @@ +From 36c02a3935382a32a7fc19d52c9d6ee602dec8d6 Mon Sep 17 00:00:00 2001 +From: zhangshuai39 +Date: Wed, 14 May 2025 11:58:02 +0800 +Subject: [PATCH] paho_mqtt: Fix mqtt compile warning + +Signed-off-by: zhangshuai39 +--- + src/MQTTAsync.c | 2 +- + src/MQTTPacket.h | 2 ++ + src/SHA1.c | 2 +- + src/Socket.c | 16 +++++++++++++++- + src/Socket.h | 1 + + src/Thread.c | 1 + + src/Thread.h | 2 +- + src/WebSocket.c | 1 - + src/WebSocket.h | 2 +- + src/samples/paho_c_pub.c | 29 ++++++++++++++++++----------- + src/samples/paho_c_sub.c | 4 +++- + 11 files changed, 45 insertions(+), 18 deletions(-) + +diff --git a/src/MQTTAsync.c b/src/MQTTAsync.c +index c548ae3..2487d69 100644 +--- a/src/MQTTAsync.c ++++ b/src/MQTTAsync.c +@@ -989,7 +989,7 @@ exit: + } + + +-int MQTTAsync_inCallback() ++int MQTTAsync_inCallback(void) + { + thread_id_type thread_id = Paho_thread_getid(); + return thread_id == sendThread_id || thread_id == receiveThread_id; +diff --git a/src/MQTTPacket.h b/src/MQTTPacket.h +index fd384ae..c833d0c 100644 +--- a/src/MQTTPacket.h ++++ b/src/MQTTPacket.h +@@ -28,7 +28,9 @@ + #include "LinkedList.h" + #include "Clients.h" + ++#ifndef bool + typedef unsigned int bool; ++#endif + typedef void* (*pf)(int, unsigned char, char*, size_t); + + #include "MQTTProperties.h" +diff --git a/src/SHA1.c b/src/SHA1.c +index a1b2c3d..e4f5g6h 100644 +--- a/src/SHA1.c ++++ b/src/SHA1.c +@@ -58,7 +58,7 @@ + # include + # define htobe32(x) OSSwapHostToBigInt32(x) + # define be32toh(x) OSSwapBigToHostInt32(x) +-#elif defined(__FreeBSD__) || defined(__NetBSD__) ++#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__NuttX__) + # include + #endif + #include +diff --git a/src/Socket.c b/src/Socket.c +index ff36657..0ea9009 100644 +--- a/src/Socket.c ++++ b/src/Socket.c +@@ -130,7 +130,7 @@ int Socket_error(char* aString, SOCKET sock) + } + + #if !defined(_WIN32) +-void SIGPIPE_ignore() ++void SIGPIPE_ignore(void) + { + #if defined(PAHO_IGNORE_WITH_SIGNAL) + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) +@@ -182,6 +182,7 @@ void Socket_outInitialize(void) + mod_s.saved.fds_write = NULL; + mod_s.saved.fds_read = NULL; + mod_s.saved.nfds = 0; ++ mod_s.poll_in_progress = 0; + #endif + FUNC_EXIT; + } +@@ -622,17 +623,24 @@ SOCKET Socket_getReadySocket(int more_work, int timeout, mutex_type mutex, int* + } + + /* Check pending write set for writeable sockets */ ++ mod_s.poll_in_progress = 1; + rc1 = poll(mod_s.saved.fds_write, mod_s.saved.nfds, 0); ++ mod_s.poll_in_progress = 0; + if (rc1 > 0 && Socket_continueWrites(&sock, mutex) == SOCKET_ERROR) + { + *rc = SOCKET_ERROR; ++ mod_s.poll_in_progress = 0; + goto exit; + } + + /* Prevent performance issue by unlocking the socket_mutex while waiting for a ready socket. */ ++ + Paho_thread_unlock_mutex(mutex); ++ mod_s.poll_in_progress = 1; + *rc = poll(mod_s.saved.fds_read, mod_s.saved.nfds, timeout_ms); ++ mod_s.poll_in_progress = 0; + Paho_thread_lock_mutex(mutex); ++ + if (*rc == SOCKET_ERROR) + { + Socket_error("poll", 0); +@@ -1029,6 +1037,12 @@ int Socket_close(SOCKET socket) + int rc = 0; + + FUNC_ENTRY; ++ ++ /* Waiting for poll to end */ ++ while (mod_s.poll_in_progress) { ++ usleep(1000); // 1ms ++ } ++ + Paho_thread_lock_mutex(socket_mutex); + Socket_close_only(socket); + Socket_abortWrite(socket); +diff --git a/src/Socket.h b/src/Socket.h +index 6e52ab8..8afbdb6 100644 +--- a/src/Socket.h ++++ b/src/Socket.h +@@ -125,6 +125,7 @@ typedef struct + unsigned int nfds; /**< no of file descriptors for poll */ + struct pollfd* fds_read; /**< poll read file descriptors */ + struct pollfd* fds_write; ++ volatile int poll_in_progress; /**< Indicates poll operation is in progress (poll branch only) */ + + struct { + int cur_fd; /**< index into the fds_saved array */ +diff --git a/src/Thread.c b/src/Thread.c +index f4d43fb..52f810a 100644 +--- a/src/Thread.c ++++ b/src/Thread.c +@@ -72,6 +72,7 @@ void Paho_thread_start(thread_fn fn, void* parameter) + #else + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ pthread_attr_setstacksize(&attr, CONFIG_UTILS_MQTT5_STACKSIZE); + if (pthread_create(&thread, &attr, fn, parameter) != 0) + thread = 0; + pthread_attr_destroy(&attr); +diff --git a/src/Thread.h b/src/Thread.h +index b0c823b..72dbc43 100644 +--- a/src/Thread.h ++++ b/src/Thread.h +@@ -76,7 +76,7 @@ LIBMQTT_API int Paho_thread_lock_mutex(mutex_type); + LIBMQTT_API int Paho_thread_unlock_mutex(mutex_type); + int Paho_thread_destroy_mutex(mutex_type); + +-LIBMQTT_API thread_id_type Paho_thread_getid(); ++LIBMQTT_API thread_id_type Paho_thread_getid(void); + + sem_type Thread_create_sem(int*); + int Thread_wait_sem(sem_type sem, int timeout); +diff --git a/src/WebSocket.c b/src/WebSocket.c +index 4338898..f941cda 100644 +--- a/src/WebSocket.c ++++ b/src/WebSocket.c +@@ -1136,7 +1136,6 @@ int WebSocket_receiveFrame(networkHandles *net, size_t *actual_len) + + if ( has_mask ) + { +- uint8_t mask[4]; + b = WebSocket_getRawSocketData(net, 4u, &len, &rcs); + if (rcs == SOCKET_ERROR) + { +diff --git a/src/WebSocket.h b/src/WebSocket.h +index 4e437d4..5343e6f 100644 +--- a/src/WebSocket.h ++++ b/src/WebSocket.h +@@ -60,7 +60,7 @@ int WebSocket_connect(networkHandles *net, int ssl, const char *uri); + /* obtain data from network socket */ + int WebSocket_getch(networkHandles *net, char* c); + char *WebSocket_getdata(networkHandles *net, size_t bytes, size_t* actual_len); +-size_t WebSocket_framePos(); ++size_t WebSocket_framePos(void); + void WebSocket_framePosSeekTo(size_t); + + /* send data out, in websocket format only if required */ +diff --git a/src/samples/paho_c_pub.c b/src/samples/paho_c_pub.c +index e891ef8..49392d8 100644 +--- a/src/samples/paho_c_pub.c ++++ b/src/samples/paho_c_pub.c +@@ -101,16 +101,12 @@ void onConnectFailure5(void* context, MQTTAsync_failureData5* response) + MQTTAsync_strerror(response->code), + MQTTReasonCode_toString(response->reasonCode)); + connected = -1; +- +- MQTTAsync client = (MQTTAsync)context; + } + + void onConnectFailure(void* context, MQTTAsync_failureData* response) + { + fprintf(stderr, "Connect failed, rc %s\n", response ? MQTTAsync_strerror(response->code) : "none"); + connected = -1; +- +- MQTTAsync client = (MQTTAsync)context; + } + + +@@ -140,6 +136,12 @@ void onConnect5(void* context, MQTTAsync_successData5* response) + } + } + ++ if (rc != 0) ++ { ++ fprintf(stderr, "Publish failed with code %d\n", rc); ++ toStop = 1; ++ } ++ + connected = 1; + } + +@@ -169,6 +171,12 @@ void onConnect(void* context, MQTTAsync_successData* response) + } + } + ++ if (rc != 0) ++ { ++ fprintf(stderr, "Publish failed with code %d\n", rc); ++ toStop = 1; ++ } ++ + connected = 1; + } + +@@ -219,7 +227,6 @@ void onPublish(void* context, MQTTAsync_successData* response) + + static int onSSLError(const char *str, size_t len, void *context) + { +- MQTTAsync client = (MQTTAsync)context; + return fprintf(stderr, "SSL error: %s\n", str); + } + +@@ -234,11 +241,11 @@ static unsigned int onPSKAuth(const char* hint, + int k, n; + + int rc = 0; +- struct pubsub_opts* opts = context; ++ struct pubsub_opts* local_opts = context; + + /* printf("Trying TLS-PSK auth with hint: %s\n", hint);*/ + +- if (opts->psk == NULL || opts->psk_identity == NULL) ++ if (local_opts->psk == NULL || local_opts->psk_identity == NULL) + { + /* printf("No PSK entered\n"); */ + goto exit; +@@ -246,7 +253,7 @@ static unsigned int onPSKAuth(const char* hint, + + /* psk should be array of bytes. This is a quick and dirty way to + * convert hex to bytes without input validation */ +- psk_len = (int)strlen(opts->psk) / 2; ++ psk_len = (int)strlen(local_opts->psk) / 2; + if (psk_len > max_psk_len) + { + fprintf(stderr, "PSK too long\n"); +@@ -254,11 +261,11 @@ static unsigned int onPSKAuth(const char* hint, + } + for (k=0, n=0; k < psk_len; k++, n += 2) + { +- sscanf(&opts->psk[n], "%2hhx", &psk[k]); ++ sscanf(&local_opts->psk[n], "%2hhx", &psk[k]); + } + + /* identity should be NULL terminated string */ +- strncpy(identity, opts->psk_identity, max_identity_len); ++ strncpy(identity, local_opts->psk_identity, max_identity_len); + if (identity[max_identity_len - 1] != '\0') + { + fprintf(stderr, "Identity too long\n"); +@@ -372,7 +379,6 @@ int main(int argc, char** argv) + char* url = NULL; + int url_allocated = 0; + int rc = 0; +- const char* version = NULL; + const char* program_name = "paho_c_pub"; + MQTTAsync_nameValue* infos = MQTTAsync_getVersionInfo(); + #if !defined(_WIN32) +@@ -506,6 +512,7 @@ int main(int argc, char** argv) + while (!disconnected) + mysleep(100); + ++ toStop = 0; + MQTTAsync_destroy(&client); + + if (url_allocated) +diff --git a/src/samples/paho_c_sub.c b/src/samples/paho_c_sub.c +index 85875c9..1fe18ce 100644 +--- a/src/samples/paho_c_sub.c ++++ b/src/samples/paho_c_sub.c +@@ -207,7 +207,6 @@ int main(int argc, char** argv) + MQTTAsync_SSLOptions ssl_opts = MQTTAsync_SSLOptions_initializer; + int rc = 0; + char* url = NULL; +- const char* version = NULL; + const char* program_name = "paho_c_sub"; + MQTTAsync_nameValue* infos = MQTTAsync_getVersionInfo(); + #if !defined(_WIN32) +@@ -343,6 +342,9 @@ int main(int argc, char** argv) + mysleep(100); + + exit: ++ finished = 0; ++ subscribed = 0; ++ disconnected = 0; + MQTTAsync_destroy(&client); + + return EXIT_SUCCESS; +-- +2.34.1 + diff --git a/netutils/ping/icmp_ping.c b/netutils/ping/icmp_ping.c index efbc8a61a..88f006b8f 100644 --- a/netutils/ping/icmp_ping.c +++ b/netutils/ping/icmp_ping.c @@ -45,6 +45,7 @@ #include #include +#include #include "netutils/icmp_ping.h" @@ -53,6 +54,7 @@ ****************************************************************************/ #define ICMP_IOBUFFER_SIZE(x) (sizeof(struct icmp_hdr_s) + (x)) +#define ICMP_SET_FILTER(t) (~(1U << (t))) /**************************************************************************** * Private Types @@ -64,7 +66,6 @@ struct ping_priv_s { struct sockaddr_in destaddr; struct sockaddr_in fromaddr; - struct icmp_hdr_s outhdr; struct pollfd recvfd; socklen_t addrlen; clock_t kickoff; @@ -74,6 +75,7 @@ struct ping_priv_s long elapsed; bool retry; int sockfd; + struct icmp_hdr_s outhdr; }; /**************************************************************************** @@ -91,6 +93,7 @@ static volatile bool g_exiting; * Private Functions ****************************************************************************/ +#ifdef CONFIG_ENABLE_ALL_SIGNALS /**************************************************************************** * Name: sigexit ****************************************************************************/ @@ -99,6 +102,7 @@ static void sigexit(int signo) { g_exiting = true; } +#endif /**************************************************************************** * Name: ping_newid @@ -179,6 +183,31 @@ static void icmp_callback(FAR struct ping_result_s *result, result->info->callback(result); } +/**************************************************************************** + * Name: icmp_checksum + ****************************************************************************/ + +static uint16_t icmp_checksum(FAR const void *buffer, size_t datalen) +{ + FAR const uint16_t *wptr = (FAR const uint16_t *)buffer; + size_t words = (sizeof(struct icmp_hdr_s) + datalen + 1) / 2; + uint32_t sum = 0; + size_t i; + + for (i = 0; i < words; i++) + { + sum += *wptr++; + if (sum & 0x80000000) + { + sum = (sum & 0XFFFF) + (sum >> 16); + } + } + + sum = (sum >> 16) + (sum & 0XFFFF); + sum += (sum >> 16); + return (uint16_t)(~sum); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -194,12 +223,18 @@ void icmp_ping(FAR const struct ping_info_s *info) FAR struct icmp_hdr_s *inhdr; FAR uint8_t *iobuffer; FAR uint8_t *ptr; + int recvlen; int ret; int ch; int i; +#ifdef CONFIG_NET_SOCKOPTS + int filter; +#endif g_exiting = false; +#ifdef CONFIG_ENABLE_ALL_SIGNALS signal(SIGINT, sigexit); +#endif /* Initialize result structure */ @@ -215,16 +250,17 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Allocate memory to hold private data and ping buffer */ - priv = malloc(sizeof(*priv) + result.outsize); + priv = malloc(sizeof(*priv) + sizeof(struct ipv4_hdr_s) + result.outsize); + if (priv == NULL) { icmp_callback(&result, ICMP_E_MEMORY, 0); return; } - iobuffer = (FAR uint8_t *)(priv + 1); + iobuffer = (FAR uint8_t *)(&priv->outhdr); - priv->sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + priv->sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (priv->sockfd < 0) { icmp_callback(&result, ICMP_E_SOCKET, errno); @@ -232,6 +268,33 @@ void icmp_ping(FAR const struct ping_info_s *info) return; } +#ifdef CONFIG_NET_BINDTODEVICE + if (info->devname) + { + ret = setsockopt(priv->sockfd, SOL_SOCKET, SO_BINDTODEVICE, + info->devname, strlen(info->devname)); + if (ret < 0) + { + icmp_callback(&result, ICMP_E_BINDDEV, errno); + free(priv); + return; + } + } +#endif + +#ifdef CONFIG_NET_SOCKOPTS + filter = ICMP_SET_FILTER(ICMP_ECHO_REPLY); + ret = setsockopt(priv->sockfd, IPPROTO_ICMP, ICMP_FILTER, + &filter, sizeof(filter)); + if (ret < 0) + { + icmp_callback(&result, ICMP_E_FILTER, errno); + close(priv->sockfd); + free(priv); + return; + } +#endif + priv->kickoff = clock(); memset(&priv->destaddr, 0, sizeof(struct sockaddr_in)); @@ -239,11 +302,6 @@ void icmp_ping(FAR const struct ping_info_s *info) priv->destaddr.sin_port = 0; priv->destaddr.sin_addr.s_addr = result.dest.s_addr; - memset(&priv->outhdr, 0, sizeof(struct icmp_hdr_s)); - priv->outhdr.type = ICMP_ECHO_REQUEST; - priv->outhdr.id = htons(result.id); - priv->outhdr.seqno = htons(result.seqno); - icmp_callback(&result, ICMP_I_BEGIN, 0); while (result.nrequests < info->count) @@ -253,9 +311,10 @@ void icmp_ping(FAR const struct ping_info_s *info) break; } - /* Copy the ICMP header into the I/O buffer */ - - memcpy(iobuffer, &priv->outhdr, sizeof(struct icmp_hdr_s)); + memset(&priv->outhdr, 0, sizeof(struct icmp_hdr_s)); + priv->outhdr.type = ICMP_ECHO_REQUEST; + priv->outhdr.id = htons(result.id); + priv->outhdr.seqno = htons(result.seqno); /* Add some easily verifiable payload data */ @@ -271,6 +330,10 @@ void icmp_ping(FAR const struct ping_info_s *info) } } + /* Calculate checksum after data padding */ + + priv->outhdr.icmpchksum = icmp_checksum(iobuffer, info->datalen); + priv->start = clock(); result.nrequests++; priv->nsent = sendto(priv->sockfd, iobuffer, result.outsize, 0, @@ -316,9 +379,10 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Get the ICMP response (ignoring the sender) */ + recvlen = sizeof(struct ipv4_hdr_s) + result.outsize; priv->addrlen = sizeof(struct sockaddr_in); priv->nrecvd = recvfrom(priv->sockfd, iobuffer, - result.outsize, 0, + recvlen, 0, (FAR struct sockaddr *)&priv->fromaddr, &priv->addrlen); if (priv->nrecvd < 0) @@ -332,8 +396,11 @@ void icmp_ping(FAR const struct ping_info_s *info) goto wait; } + /* Skip IP header, IP header length including options */ + priv->elapsed = TICK2USEC(clock() - priv->start); - inhdr = (FAR struct icmp_hdr_s *)iobuffer; + inhdr = (FAR struct icmp_hdr_s *) + (iobuffer + sizeof(struct ipv4_hdr_s)); if (inhdr->type == ICMP_ECHO_REPLY) { @@ -365,16 +432,17 @@ void icmp_ping(FAR const struct ping_info_s *info) /* Verify the payload data */ - if (priv->nrecvd != result.outsize) + if (priv->nrecvd != recvlen) { icmp_callback(&result, ICMP_W_RECVBIG, priv->nrecvd); verified = false; } else { - ptr = &iobuffer[sizeof(struct icmp_hdr_s)]; - ch = 0x20; + /* Data start offset: IP header + ICMP header */ + ptr = (FAR uint8_t *)(inhdr + 1); + ch = 0x20; for (i = 0; i < info->datalen; i++, ptr++) { if (*ptr != ch) diff --git a/netutils/ping/icmpv6_ping.c b/netutils/ping/icmpv6_ping.c index 74cd59ee9..c62c329d6 100644 --- a/netutils/ping/icmpv6_ping.c +++ b/netutils/ping/icmpv6_ping.c @@ -42,6 +42,7 @@ #endif #include +#include #include #include @@ -169,6 +170,7 @@ void icmp6_ping(FAR const struct ping6_info_s *info) struct ping6_result_s result; struct sockaddr_in6 destaddr; struct sockaddr_in6 fromaddr; + struct icmp6_filter filter; struct icmpv6_echo_request_s outhdr; FAR struct icmpv6_echo_reply_s *inhdr; struct pollfd recvfd; @@ -210,7 +212,7 @@ void icmp6_ping(FAR const struct ping6_info_s *info) return; } - sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMP6); + sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (sockfd < 0) { icmp6_callback(&result, ICMPv6_E_SOCKET, errno); @@ -218,6 +220,34 @@ void icmp6_ping(FAR const struct ping6_info_s *info) return; } +#ifdef CONFIG_NET_BINDTODEVICE + if (info->devname) + { + ret = setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + info->devname, strlen(info->devname)); + if (ret < 0) + { + icmp6_callback(&result, ICMPV6_E_BINDDEV, errno); + free(iobuffer); + return; + } + } +#endif + + memset(&filter, 0xff, sizeof(filter)); + + /* ICMPv6_ECHO_REPLY >> 5 = 4 */ + + filter.icmp6_filt[4] &= ~(1 << (ICMPv6_ECHO_REPLY & 31)); + if (setsockopt(sockfd, SOL_ICMPV6, ICMP6_FILTER, &filter, + sizeof(filter)) < 0) + { + icmp6_callback(&result, ICMPv6_E_SOCKET, errno); + close(sockfd); + free(iobuffer); + return; + } + kickoff = clock(); memset(&destaddr, 0, sizeof(struct sockaddr_in6)); diff --git a/netutils/pppd/ahdlc.h b/netutils/pppd/ahdlc.h index 8ed51ff5d..a3846f76a 100644 --- a/netutils/pppd/ahdlc.h +++ b/netutils/pppd/ahdlc.h @@ -68,8 +68,8 @@ void ahdlc_rx_ready(FAR struct ppp_context_s *ctx); uint8_t ahdlc_rx(FAR struct ppp_context_s *ctx, uint8_t); uint8_t ahdlc_tx(FAR struct ppp_context_s *ctx, uint16_t protocol, - FAR uint8_t *header, FAR uint8_t *buffer, uint16_t headerlen, - uint16_t datalen); + FAR uint8_t *header, FAR uint8_t *buffer, + uint16_t headerlen, uint16_t datalen); #undef EXTERN #ifdef __cplusplus diff --git a/netutils/pppd/pap.h b/netutils/pppd/pap.h index 402ef0f71..fa43c9827 100644 --- a/netutils/pppd/pap.h +++ b/netutils/pppd/pap.h @@ -56,6 +56,7 @@ ****************************************************************************/ /* PAP state machine flags */ + /* Client only */ #define PAP_TX_UP 0x01 diff --git a/netutils/pppd/ppp.h b/netutils/pppd/ppp.h index f436a1206..519b7f168 100644 --- a/netutils/pppd/ppp.h +++ b/netutils/pppd/ppp.h @@ -51,7 +51,7 @@ #include "netutils/chat.h" -#include "debug.h" +#include #include "ppp_conf.h" #include "ahdlc.h" #include "lcp.h" diff --git a/netutils/pppd/pppd.c b/netutils/pppd/pppd.c index 73efa27c9..849e3e874 100644 --- a/netutils/pppd/pppd.c +++ b/netutils/pppd/pppd.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/ptpd/CMakeLists.txt b/netutils/ptpd/CMakeLists.txt new file mode 100644 index 000000000..35c36b5b1 --- /dev/null +++ b/netutils/ptpd/CMakeLists.txt @@ -0,0 +1,23 @@ +# ############################################################################## +# apps/netutils/ptpd/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_NETUTILS_PTPD) + target_sources(apps PRIVATE ptpd.c) +endif() diff --git a/netutils/ptpd/Kconfig b/netutils/ptpd/Kconfig index 93cd288ca..63d756ad9 100644 --- a/netutils/ptpd/Kconfig +++ b/netutils/ptpd/Kconfig @@ -8,7 +8,7 @@ config NETUTILS_PTPD default n depends on NET_IPv4 depends on NET_IGMP - depends on NET_UDP + depends on NET_UDP || NET_PKT ---help--- Build a minimal implementation of IEEE-1588 precision time protocol. Uses system gettimeofday() and adjtime() calls to synchronize clock @@ -17,31 +17,6 @@ config NETUTILS_PTPD if NETUTILS_PTPD -config NETUTILS_PTPD_DEBUG - bool "Enable PTP debug messages" - default n - depends on DEBUG_INFO - ---help--- - Enable PTP debug messages even if CONFIG_DEBUG_NET_INFO is not enabled. - -config NETUTILS_PTPD_CLIENT - bool "Enable client support" - default y - ---help--- - Act as a PTP client, synchronizing the NuttX clock to a remote master - clock. - -config NETUTILS_PTPD_SERVER - bool "Enable server support" - default n - ---help--- - Act as a PTP server, providing NuttX clock time to other systems. - - Both server and client can be simultaneously enabled. NuttX will then - synchronize to a higher priority master clock, or act as a master - clock itself if it has the highest priority. - Refer to Best Master Clock algorithm in IEEE-1588 for details. - config NETUTILS_PTPD_STACKSIZE int "PTP daemon stack stack size" default DEFAULT_TASK_STACKSIZE @@ -58,8 +33,6 @@ config NETUTILS_PTPD_DOMAIN Set PTP domain to participate in. Default domain is 0, other domains can be used to isolate reference clocks from each other. -if NETUTILS_PTPD_SERVER - config NETUTILS_PTPD_PRIORITY1 int "PTP server priority1" default 128 @@ -161,10 +134,6 @@ config NETUTILS_PTPD_DELAYRESP_INTERVAL Default value 4 results in 16 second interval. -endif # NETUTILS_PTPD_SERVER - -if NETUTILS_PTPD_CLIENT - config NETUTILS_PTPD_TIMEOUT_MS int "PTP client timeout for changing clock source (ms)" default 60000 @@ -180,6 +149,14 @@ config NETUTILS_PTPD_SETTIME_THRESHOLD_MS time is reset with settimeofday() instead of changing the rate with adjtime(). +config NETUTILS_PTPD_ADJTIME_THRESHOLD_NS + int "PTP client threshold for using accumulated ppb to adjust system time (ns)" + default 500 + ---help--- + If difference between local and remote clock exceeds this threshold, + the PTP client can utilize current ppb instead of accumulated ppb to + accelerate system time adjustment. + config NETUTILS_PTPD_MULTICAST_TIMEOUT_MS int "PTP client timeout to rejoin multicast group (ms)" default 30000 @@ -198,15 +175,6 @@ config NETUTILS_PTPD_DRIFT_AVERAGE_S gives more stable estimate but reacts slower to crystal oscillator speed changes (such as caused by temperature changes). -config NETUTILS_PTPD_SEND_DELAYREQ - bool "PTP client enable delay requests" - default n - ---help--- - If enabled, sends delay request messages to measure the network delay - to server. If disabled, assumes zero delay. - -if NETUTILS_PTPD_SEND_DELAYREQ - config NETUTILS_PTPD_MAX_PATH_DELAY_NS int "PTP client maximum path delay (ns)" default 100000 @@ -221,8 +189,4 @@ config NETUTILS_PTPD_DELAYREQ_AVGCOUNT ---help--- Measured path delay is averaged over this many samples. -endif # NETUTILS_PTPD_SEND_DELAYREQ - -endif # NETUTILS_PTPD_CLIENT - endif # NETUTILS_PTPD diff --git a/netutils/ptpd/ptpd.c b/netutils/ptpd/ptpd.c index 309f78a07..49c608210 100644 --- a/netutils/ptpd/ptpd.c +++ b/netutils/ptpd/ptpd.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -39,24 +40,28 @@ #include #include #include -#include +#include #include #include #include +#include +#include #include #include #include #include #include +#include #include #include +#include "netutils/netlib.h" #include "ptpv2.h" /**************************************************************************** - * Private Data + * Private Types ****************************************************************************/ /* Carrier structure for querying PTPD status */ @@ -89,6 +94,10 @@ struct ptp_state_s int event_socket; int info_socket; + /* The ptp device file descriptor */ + + clockid_t clockid; + /* Our own identity as a clock source */ struct ptp_announce_s own_identity; @@ -151,7 +160,7 @@ struct ptp_state_s uint8_t raw[128]; } rxbuf; - uint8_t rxcmsg[CMSG_LEN(sizeof(struct timeval))]; + uint8_t rxcmsg[CMSG_LEN(sizeof(struct timespec))]; /* Buffered sync packet for two-step clock setting where server sends * the accurate timestamp in a separate follow-up message. @@ -159,49 +168,24 @@ struct ptp_state_s struct ptp_sync_s twostep_packet; struct timespec twostep_rxtime; + FAR const struct ptpd_config_s *config; }; -#ifdef CONFIG_NETUTILS_PTPD_SERVER -# define PTPD_POLL_INTERVAL CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC -#else -# define PTPD_POLL_INTERVAL CONFIG_NETUTILS_PTPD_TIMEOUT_MS -#endif - -/* PTP debug messages are enabled by either CONFIG_DEBUG_NET_INFO - * or separately by CONFIG_NETUTILS_PTPD_DEBUG. This simplifies - * debugging without having excessive amount of logging from net. - */ - -#ifdef CONFIG_NETUTILS_PTPD_DEBUG -# define ptpinfo _info -# define ptpwarn _warn -# define ptperr _err -#else -# define ptpinfo ninfo -# define ptpwarn nwarn -# define ptperr nerr -#endif - /**************************************************************************** * Private Functions ****************************************************************************/ /* Convert from timespec to PTP format */ -static void timespec_to_ptp_format(FAR struct timespec *ts, +static void timespec_to_ptp_format(FAR const struct timespec *ts, FAR uint8_t *timestamp) { /* IEEE 1588 uses 48 bits for seconds and 32 bits for nanoseconds, * both fields big-endian. */ -#ifdef CONFIG_SYSTEM_TIME64 timestamp[0] = (uint8_t)(ts->tv_sec >> 40); timestamp[1] = (uint8_t)(ts->tv_sec >> 32); -#else - timestamp[0] = 0; - timestamp[1] = 0; -#endif timestamp[2] = (uint8_t)(ts->tv_sec >> 24); timestamp[3] = (uint8_t)(ts->tv_sec >> 16); timestamp[4] = (uint8_t)(ts->tv_sec >> 8); @@ -240,20 +224,79 @@ static void ptp_format_to_timespec(FAR const uint8_t *timestamp, static bool is_better_clock(FAR const struct ptp_announce_s *a, FAR const struct ptp_announce_s *b) { - if (a->gm_priority1 < b->gm_priority1 /* Main priority field */ - || a->gm_quality[0] < b->gm_quality[0] /* Clock class */ - || a->gm_quality[1] < b->gm_quality[1] /* Clock accuracy */ - || a->gm_quality[2] < b->gm_quality[2] /* Clock variance high byte */ - || a->gm_quality[3] < b->gm_quality[3] /* Clock variance low byte */ - || a->gm_priority2 < b->gm_priority2 /* Sub priority field */ - || memcmp(a->gm_identity, b->gm_identity, sizeof(a->gm_identity)) < 0) + /* Main priority field */ + + if (a->gm_priority1 < b->gm_priority1) { return true; } - else + + if (a->gm_priority1 > b->gm_priority1) { return false; } + + /* Clock class */ + + if (a->gm_quality[0] < b->gm_quality[0]) + { + return true; + } + + if (a->gm_quality[0] > b->gm_quality[0]) + { + return false; + } + + /* Clock accuracy */ + + if (a->gm_quality[1] < b->gm_quality[1]) + { + return true; + } + + if (a->gm_quality[1] > b->gm_quality[1]) + { + return false; + } + + /* Clock variance high byte */ + + if (a->gm_quality[2] < b->gm_quality[2]) + { + return true; + } + + if (a->gm_quality[2] > b->gm_quality[2]) + { + return false; + } + + /* Clock variance low byte */ + + if (a->gm_quality[3] < b->gm_quality[3]) + { + return true; + } + + if (a->gm_quality[3] > b->gm_quality[3]) + { + return false; + } + + /* Sub priority field */ + + if (a->gm_priority2 < b->gm_priority2) + { + return true; + } + + if (a->gm_priority2 > b->gm_priority2) + { + return false; + } + + return memcmp(a->gm_identity, b->gm_identity, sizeof(a->gm_identity)) < 0; } static int64_t timespec_to_ms(FAR const struct timespec *ts) @@ -272,7 +315,6 @@ static int64_t timespec_delta_ns(FAR const struct timespec *ts1, delta_s = ts1->tv_sec - ts2->tv_sec; -#ifdef CONFIG_SYSTEM_TIME64 /* Conversion to nanoseconds could overflow if the system time is 64-bit */ if (delta_s >= INT64_MAX / NSEC_PER_SEC) @@ -283,7 +325,6 @@ static int64_t timespec_delta_ns(FAR const struct timespec *ts1, { return INT64_MIN; } -#endif return delta_s * NSEC_PER_SEC + (ts1->tv_nsec - ts2->tv_nsec); } @@ -333,16 +374,37 @@ static uint16_t ptp_get_sequence(FAR const struct ptp_header_s *hdr) return ((uint16_t)hdr->sequenceid[0] << 8) | hdr->sequenceid[1]; } -/* Get current system timestamp as a timespec - * TODO: Possibly add support for selecting different clock or using - * architecture-specific interface for clock access. - */ +static clockid_t ptp_open(FAR const char *clock) +{ + int fd; + + if (!strcmp(clock, "realtime")) + { + return CLOCK_REALTIME; + } + + fd = open(clock, O_RDWR | O_CLOEXEC); + if (fd < 0) + { + ptperr("Failed to open PTP clock device:%s, %d\n", clock, errno); + return fd; + } + + return (fd << CLOCK_SHIFT) | CLOCK_FD; +} + +static void ptp_close(clockid_t clockid) +{ + if (clockid > 0 && clockid != CLOCK_REALTIME) + { + close(clockid >> CLOCK_SHIFT); + } +} static int ptp_gettime(FAR struct ptp_state_s *state, FAR struct timespec *ts) { - UNUSED(state); - return clock_gettime(CLOCK_REALTIME, ts); + return clock_gettime(state->clockid, ts); } /* Change current system timestamp by jumping */ @@ -350,20 +412,33 @@ static int ptp_gettime(FAR struct ptp_state_s *state, static int ptp_settime(FAR struct ptp_state_s *state, FAR struct timespec *ts) { - UNUSED(state); - return clock_settime(CLOCK_REALTIME, ts); + return clock_settime(state->clockid, ts); } /* Smoothly adjust timestamp. */ -static int ptp_adjtime(FAR struct ptp_state_s *state, int64_t delta_ns) +static int ptp_adjtime(FAR struct ptp_state_s *state, int64_t delta_ns, + int64_t ppb) { - struct timeval delta; + if (state->clockid == CLOCK_REALTIME) + { + struct timeval delta; - delta.tv_sec = delta_ns / NSEC_PER_SEC; - delta_ns -= (int64_t)delta.tv_sec * NSEC_PER_SEC; - delta.tv_usec = delta_ns / NSEC_PER_USEC; - return adjtime(&delta, NULL); + delta.tv_sec = delta_ns / NSEC_PER_SEC; + delta_ns -= delta.tv_sec * NSEC_PER_SEC; + delta.tv_usec = delta_ns / NSEC_PER_USEC; + return adjtime(&delta, NULL); + } + else + { + struct timex buf; + + memset(&buf, 0, sizeof(buf)); + buf.freq = (long)(-ppb * 65536 / 1000); + buf.modes = ADJ_FREQUENCY; + + return clock_adjtime(state->clockid, &buf); + } } /* Get timestamp of latest received packet */ @@ -372,18 +447,22 @@ static int ptp_getrxtime(FAR struct ptp_state_s *state, FAR struct msghdr *rxhdr, FAR struct timespec *ts) { + FAR struct cmsghdr *cmsg; + /* Get hardware or kernel timestamp if available */ -#ifdef CONFIG_NET_TIMESTAMP - struct cmsghdr *cmsg; + if (!state->config->hardware_ts) + { + return ptp_gettime(state, ts); + } for_each_cmsghdr(cmsg, rxhdr) { if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SO_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + cmsg->cmsg_type == SO_TIMESTAMPNS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec))) { - TIMEVAL_TO_TIMESPEC((FAR struct timeval *)CMSG_DATA(cmsg), ts); + memcpy(ts, CMSG_DATA(cmsg), sizeof(*ts)); /* Sanity-check the value */ @@ -395,47 +474,168 @@ static int ptp_getrxtime(FAR struct ptp_state_s *state, } ptpwarn("CONFIG_NET_TIMESTAMP enabled but did not get packet timestamp\n"); -#endif + return ERROR; +} - /* Fall back to current timestamp */ +/* Unsubscribe multicast and destroy sockets */ - return ptp_gettime(state, ts); +static int ptp_destroy_state(FAR struct ptp_state_s *state) +{ + struct in_addr mcast_addr; + + ptp_close(state->clockid); + + mcast_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); + ipmsfilter(&state->interface_addr.sin_addr, + &mcast_addr, MCAST_EXCLUDE); + + if (state->tx_socket > 0) + { + close(state->tx_socket); + state->tx_socket = -1; + } + + if (state->event_socket > 0) + { + close(state->event_socket); + state->event_socket = -1; + } + + if (state->info_socket > 0) + { + close(state->info_socket); + state->info_socket = -1; + } + + return OK; } /* Initialize PTP client/server state and create sockets */ -static int ptp_initialize_state(FAR struct ptp_state_s *state, - FAR const char *interface) +static int ptp_initialize_state(FAR struct ptp_state_s *state) { int ret; + int arg = 1; struct ifreq req; - struct sockaddr_in bind_addr; -#ifdef CONFIG_NET_TIMESTAMP - int arg; -#endif + state->clockid = ptp_open(state->config->clock); + if (state->clockid < 0) + { + ptperr("Invalid clockid %d for ptp daemon\n", state->clockid); + return ERROR; + } /* Create sockets */ - state->tx_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (state->tx_socket < 0) + if (state->config->af == AF_PACKET) { - ptperr("Failed to create tx socket: %d\n", errno); - return ERROR; + struct sockaddr_ll addr; + + state->tx_socket = socket(AF_PACKET, SOCK_RAW, 0); + if (state->tx_socket < 0) + { + ptperr("Failed to create tx socket: %d\n", errno); + goto errout; + } + + state->event_socket = dup(state->tx_socket); + state->info_socket = -1; + + addr.sll_family = AF_PACKET; + addr.sll_ifindex = if_nametoindex(state->config->interface); + addr.sll_protocol = htons(ETHERTYPE_PTP); + ret = bind(state->tx_socket, (FAR struct sockaddr *)&addr, + sizeof(addr)); + if (ret < 0) + { + ptperr("ERROR: binding socket failed: %d\n", errno); + goto errout; + } + } + else if (state->config->af == AF_INET) + { + struct sockaddr_in bind_addr; + + state->tx_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (state->tx_socket < 0) + { + ptperr("Failed to create tx socket: %d\n", errno); + goto errout; + } + + state->event_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (state->event_socket < 0) + { + ptperr("Failed to create event socket: %d\n", errno); + goto errout; + } + + state->info_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (state->info_socket < 0) + { + ptperr("Failed to create info socket: %d\n", errno); + goto errout; + } + + /* Subscribe to PTP multicast address */ + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); + + ret = ipmsfilter(&state->interface_addr.sin_addr, + &bind_addr.sin_addr, MCAST_INCLUDE); + if (ret < 0) + { + ptperr("Failed to bind multicast address: %d\n", errno); + goto errout; + } + + /* Bind socket for events */ + + bind_addr.sin_port = HTONS(PTP_UDP_PORT_EVENT); + ret = bind(state->event_socket, (FAR struct sockaddr *)&bind_addr, + sizeof(bind_addr)); + if (ret < 0) + { + ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port); + goto errout; + } + + /* Bind socket for announcements */ + + bind_addr.sin_port = HTONS(PTP_UDP_PORT_INFO); + ret = bind(state->info_socket, (FAR struct sockaddr *)&bind_addr, + sizeof(bind_addr)); + if (ret < 0) + { + ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port); + goto errout; + } + + /* Bind TX socket to interface address (local addr cannot be + * multicast) + */ + + bind_addr.sin_addr = state->interface_addr.sin_addr; + ret = bind(state->tx_socket, (FAR struct sockaddr *)&bind_addr, + sizeof(bind_addr)); + if (ret < 0) + { + ptperr("Failed to bind tx to port %d\n", bind_addr.sin_port); + goto errout; + } } - state->event_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (state->event_socket < 0) + if (state->config->hardware_ts) { - ptperr("Failed to create event socket: %d\n", errno); - return ERROR; - } + ret = setsockopt(state->event_socket, SOL_SOCKET, SO_TIMESTAMPNS, + &arg, sizeof(arg)); - state->info_socket = socket(AF_INET, SOCK_DGRAM, 0); - if (state->info_socket < 0) - { - ptperr("Failed to create info socket: %d\n", errno); - return ERROR; + if (ret < 0) + { + ptperr("Failed to enable SO_TIMESTAMPNS: %s\n", strerror(errno)); + goto errout; + } } /* Get address information of the specified interface for binding socket @@ -443,13 +643,13 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state, */ memset(&req, 0, sizeof(req)); - strncpy(req.ifr_name, interface, sizeof(req.ifr_name)); + strlcpy(req.ifr_name, state->config->interface, sizeof(req.ifr_name)); if (ioctl(state->event_socket, SIOCGIFADDR, (unsigned long)&req) < 0) { ptperr("Failed to get IP address information for interface %s\n", - interface); - return ERROR; + state->config->interface); + goto errout; } state->interface_addr = *(FAR struct sockaddr_in *)&req.ifr_ifru.ifru_addr; @@ -461,8 +661,8 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state, if (ioctl(state->event_socket, SIOCGIFHWADDR, (unsigned long)&req) < 0) { ptperr("Failed to get HW address information for interface %s\n", - interface); - return ERROR; + state->config->interface); + goto errout; } state->own_identity.header.version = 2; @@ -488,99 +688,13 @@ static int ptp_initialize_state(FAR struct ptp_state_s *state, sizeof(state->own_identity.gm_identity)); state->own_identity.timesource = CONFIG_NETUTILS_PTPD_CLOCKSOURCE; - /* Subscribe to PTP multicast address */ - - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); - clock_gettime(CLOCK_MONOTONIC, &state->last_received_multicast); - ret = ipmsfilter(&state->interface_addr.sin_addr, - &bind_addr.sin_addr, MCAST_INCLUDE); - if (ret < 0) - { - ptperr("Failed to bind multicast address: %d\n", errno); - return ERROR; - } - - /* Bind socket for events */ - - bind_addr.sin_port = HTONS(PTP_UDP_PORT_EVENT); - ret = bind(state->event_socket, (FAR struct sockaddr *)&bind_addr, - sizeof(bind_addr)); - if (ret < 0) - { - ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port); - return ERROR; - } - -#ifdef CONFIG_NET_TIMESTAMP - arg = 1; - ret = setsockopt(state->event_socket, SOL_SOCKET, SO_TIMESTAMP, - &arg, sizeof(arg)); - - if (ret < 0) - { - ptperr("Failed to enable SO_TIMESTAMP: %s\n", strerror(errno)); - - /* PTPD can operate without, but with worse accuracy */ - } -#endif - - /* Bind socket for announcements */ - - bind_addr.sin_port = HTONS(PTP_UDP_PORT_INFO); - ret = bind(state->info_socket, (FAR struct sockaddr *)&bind_addr, - sizeof(bind_addr)); - if (ret < 0) - { - ptperr("Failed to bind to udp port %d\n", bind_addr.sin_port); - return ERROR; - } - - /* Bind TX socket to interface address (local addr cannot be multicast) */ - - bind_addr.sin_addr = state->interface_addr.sin_addr; - ret = bind(state->tx_socket, (FAR struct sockaddr *)&bind_addr, - sizeof(bind_addr)); - if (ret < 0) - { - ptperr("Failed to bind tx to port %d\n", bind_addr.sin_port); - return ERROR; - } - return OK; -} -/* Unsubscribe multicast and destroy sockets */ - -static int ptp_destroy_state(FAR struct ptp_state_s *state) -{ - struct in_addr mcast_addr; - - mcast_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); - ipmsfilter(&state->interface_addr.sin_addr, - &mcast_addr, MCAST_EXCLUDE); - - if (state->tx_socket > 0) - { - close(state->tx_socket); - state->tx_socket = -1; - } - - if (state->event_socket > 0) - { - close(state->event_socket); - state->event_socket = -1; - } - - if (state->info_socket > 0) - { - close(state->info_socket); - state->info_socket = -1; - } - - return OK; +errout: + ptp_destroy_state(state); + return ERROR; } /* Re-subscribe multicast address. @@ -622,6 +736,78 @@ static int ptp_check_multicast_status(FAR struct ptp_state_s *state) return OK; } +static int ptp_sendmsg(FAR struct ptp_state_s *state, FAR const void *buf, + size_t buflen, FAR const void *addr, + socklen_t addrlen, FAR struct timespec *sendts) +{ + int ret; + + if (state->config->af == AF_PACKET) + { + /* IEE802.1AS Multicast address for gptp */ + + const uint8_t ptp_multicast_mac[ETHER_ADDR_LEN] = + { + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e + }; + + char raw[sizeof(struct ether_header) + sizeof(struct ptp_announce_s)]; + FAR struct ether_header *header; + struct msghdr msg; + struct iovec iov; + + DEBUGASSERT(sizeof(struct ptp_announce_s) >= buflen); + + header = (FAR struct ether_header *)&raw; + memcpy(header->ether_dhost, ptp_multicast_mac, ETHER_ADDR_LEN); + netlib_getmacaddr(state->config->interface, header->ether_shost); + header->ether_type = ETHERTYPE_PTP; + memcpy(&raw[sizeof(*header)], buf, buflen); + buflen += sizeof(*header); + + iov.iov_base = raw; + iov.iov_len = buflen; + + msg.msg_name = (FAR void *)addr; + msg.msg_namelen = addrlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + + ret = sendmsg(state->tx_socket, &msg, 0); + if (ret < 0) + { + return ERROR; + } + + if (state->config->hardware_ts && sendts != NULL) + { + uint8_t rxcmsg[CMSG_LEN(sizeof(struct timespec))]; + + msg.msg_control = &rxcmsg; + msg.msg_controllen = CMSG_LEN(sizeof(struct timespec)); + ret = recvmsg(state->tx_socket, &msg, 0); + if (ret >= 0) + { + ptp_getrxtime(state, &msg, sendts); + } + } + } + else + { + ret = sendto(state->tx_socket, buf, buflen, 0, addr, addrlen); + } + + if (!state->config->hardware_ts && sendts != NULL) + { + ptp_gettime(state, sendts); + } + + return ret; +} + /* Send PTP server announcement packet */ static int ptp_send_announce(FAR struct ptp_state_s *state) @@ -644,12 +830,10 @@ static int ptp_send_announce(FAR struct ptp_state_s *state) ptp_gettime(state, &ts); timespec_to_ptp_format(&ts, msg.origintimestamp); - ret = sendto(state->tx_socket, &msg, sizeof(msg), 0, - (FAR struct sockaddr *)&addr, sizeof(addr)); - + ret = ptp_sendmsg(state, &msg, sizeof(msg), &addr, sizeof(addr), NULL); if (ret < 0) { - ptperr("sendto failed: %d", errno); + ptperr("ptp sendmsg failed: %d", errno); } else { @@ -664,17 +848,11 @@ static int ptp_send_announce(FAR struct ptp_state_s *state) static int ptp_send_sync(FAR struct ptp_state_s *state) { - struct msghdr txhdr; - struct iovec txiov; struct ptp_sync_s msg; struct sockaddr_in addr; struct timespec ts; - uint8_t controlbuf[64]; int ret; - memset(&txhdr, 0, sizeof(txhdr)); - memset(&txiov, 0, sizeof(txiov)); - addr.sin_family = AF_INET; addr.sin_addr.s_addr = HTONL(PTP_MULTICAST_ADDR); addr.sin_port = HTONS(PTP_UDP_PORT_EVENT); @@ -688,22 +866,13 @@ static int ptp_send_sync(FAR struct ptp_state_s *state) msg.header.flags[0] = PTP_FLAGS0_TWOSTEP; #endif - txhdr.msg_name = &addr; - txhdr.msg_namelen = sizeof(addr); - txhdr.msg_iov = &txiov; - txhdr.msg_iovlen = 1; - txhdr.msg_control = controlbuf; - txhdr.msg_controllen = sizeof(controlbuf); - txiov.iov_base = &msg; - txiov.iov_len = sizeof(msg); - /* Timestamp and send the sync message */ ptp_increment_sequence(&state->sync_seq, &msg.header); ptp_gettime(state, &ts); timespec_to_ptp_format(&ts, msg.origintimestamp); - ret = sendmsg(state->tx_socket, &txhdr, 0); + ret = ptp_sendmsg(state, &msg, sizeof(msg), &addr, sizeof(addr), &ts); if (ret < 0) { ptperr("sendmsg for sync message failed: %d\n", errno); @@ -711,22 +880,16 @@ static int ptp_send_sync(FAR struct ptp_state_s *state) } #ifdef CONFIG_NETUTILS_PTPD_TWOSTEP_SYNC - /* Get timestamp after send completes and send follow-up message - * - * TODO: Implement SO_TIMESTAMPING and use the actual tx timestamp here. - */ - ptp_gettime(state, &ts); timespec_to_ptp_format(&ts, msg.origintimestamp); msg.header.messagetype = PTP_MSGTYPE_FOLLOW_UP; msg.header.flags[0] = 0; addr.sin_port = HTONS(PTP_UDP_PORT_INFO); - ret = sendto(state->tx_socket, &msg, sizeof(msg), 0, - (FAR struct sockaddr *)&addr, sizeof(addr)); + ret = ptp_sendmsg(state, &msg, sizeof(msg), &addr, sizeof(addr), NULL); if (ret < 0) { - ptperr("sendto for follow-up message failed: %d\n", errno); + ptperr("ptp sendmsg for follow-up message failed: %d\n", errno); return ret; } @@ -761,18 +924,11 @@ static int ptp_send_delay_req(FAR struct ptp_state_s *state) ptp_gettime(state, &state->delayreq_time); timespec_to_ptp_format(&state->delayreq_time, req.origintimestamp); - ret = sendto(state->tx_socket, &req, sizeof(req), 0, - (FAR struct sockaddr *)&addr, sizeof(addr)); - - /* Get timestamp after send completes. - * TODO: Implement SO_TIMESTAMPING and use the actual tx timestamp here. - */ - - ptp_gettime(state, &state->delayreq_time); - + ret = ptp_sendmsg(state, &req, sizeof(req), + &addr, sizeof(addr), &state->delayreq_time); if (ret < 0) { - ptperr("sendto failed: %d", errno); + ptperr("ptp sendmsg failed: %d", errno); } else { @@ -788,12 +944,11 @@ static int ptp_send_delay_req(FAR struct ptp_state_s *state) static int ptp_periodic_send(FAR struct ptp_state_s *state) { -#ifdef CONFIG_NETUTILS_PTPD_SERVER /* If there is no better master clock on the network, * act as the reference source and send server packets. */ - if (!state->selected_source_valid) + if (!state->config->client_only && !state->selected_source_valid) { struct timespec time_now; struct timespec delta; @@ -801,7 +956,7 @@ static int ptp_periodic_send(FAR struct ptp_state_s *state) clock_gettime(CLOCK_MONOTONIC, &time_now); clock_timespec_subtract(&time_now, &state->last_transmitted_announce, &delta); - if (timespec_to_ms(&delta) + if (state->config->bmca && timespec_to_ms(&delta) > CONFIG_NETUTILS_PTPD_ANNOUNCE_INTERVAL_MSEC) { state->last_transmitted_announce = time_now; @@ -816,10 +971,9 @@ static int ptp_periodic_send(FAR struct ptp_state_s *state) ptp_send_sync(state); } } -#endif /* CONFIG_NETUTILS_PTPD_SERVER */ -#ifdef CONFIG_NETUTILS_PTPD_SEND_DELAYREQ - if (state->selected_source_valid && state->can_send_delayreq) + if (state->config->delay_e2e && state->selected_source_valid && + state->can_send_delayreq) { struct timespec time_now; struct timespec delta; @@ -833,7 +987,6 @@ static int ptp_periodic_send(FAR struct ptp_state_s *state) ptp_send_delay_req(state); } } -#endif return OK; } @@ -845,7 +998,7 @@ static int ptp_process_announce(FAR struct ptp_state_s *state, { clock_gettime(CLOCK_MONOTONIC, &state->last_received_announce); - if (is_better_clock(msg, &state->own_identity)) + if (state->conifg->bmca && is_better_clock(msg, &state->n_identity)) { if (!state->selected_source_valid || is_better_clock(msg, &state->selected_source)) @@ -877,11 +1030,11 @@ static int ptp_update_local_clock(FAR struct ptp_state_s *state, const int64_t adj_limit_ns = CONFIG_NETUTILS_PTPD_SETTIME_THRESHOLD_MS * (int64_t)NSEC_PER_MSEC; - ptpinfo("Local time: %lld.%09ld, remote time %lld.%09ld\n", - (long long)local_timestamp->tv_sec, - (long)local_timestamp->tv_nsec, - (long long)remote_timestamp->tv_sec, - (long)remote_timestamp->tv_nsec); + ptpinfo("Local time: %jd.%09ld, remote time %jd.%09ld\n", + (intmax_t)local_timestamp->tv_sec, + local_timestamp->tv_nsec, + (intmax_t)remote_timestamp->tv_sec, + remote_timestamp->tv_nsec); delta_ns = timespec_delta_ns(remote_timestamp, local_timestamp); delta_ns += state->path_delay_ns; @@ -909,8 +1062,8 @@ static int ptp_update_local_clock(FAR struct ptp_state_s *state, if (ret == OK) { - ptpinfo("Jumped to timestamp %lld.%09ld s\n", - (long long)new_time.tv_sec, (long)new_time.tv_nsec); + ptpinfo("Jumped to timestamp %jd.%09ld s\n", + (intmax_t)new_time.tv_sec, new_time.tv_nsec); } else { @@ -1016,7 +1169,14 @@ static int ptp_update_local_clock(FAR struct ptp_state_s *state, (long long)state->last_adjtime_ns, (long long)state->drift_ppb); - ret = ptp_adjtime(state, adjustment_ns); + if (absdelta_ns > CONFIG_NETUTILS_PTPD_ADJTIME_THRESHOLD_NS) + { + ret = ptp_adjtime(state, delta_ns, drift_ppb); + } + else + { + ret = ptp_adjtime(state, adjustment_ns, state->drift_ppb); + } if (ret != OK) { @@ -1041,7 +1201,8 @@ static int ptp_process_sync(FAR struct ptp_state_s *state, { struct timespec remote_time; - if (memcmp(msg->header.sourceidentity, + if (state->config->bmca && + memcmp(msg->header.sourceidentity, state->selected_source.header.sourceidentity, sizeof(msg->header.sourceidentity)) != 0) { @@ -1070,12 +1231,38 @@ static int ptp_process_sync(FAR struct ptp_state_s *state, return ptp_update_local_clock(state, &remote_time, &state->rxtime); } +static void ptp_add_correction_time(FAR const uint8_t *correction, + FAR struct timespec *ts) +{ + uint64_t correction_time = (((uint64_t)correction[0]) << 40) + | (((uint64_t)correction[1]) << 32) + | (((uint64_t)correction[2]) << 24) + | (((uint64_t)correction[3]) << 16) + | (((uint64_t)correction[4]) << 8) + | (((uint64_t)correction[5]) << 0); + + ptpinfo("correction before: %jd.%09ld\n", (intmax_t)ts->tv_sec, + ts->tv_nsec); + + ts->tv_sec += correction_time / NSEC_PER_SEC; + ts->tv_nsec += correction_time % NSEC_PER_SEC; + if (ts->tv_nsec >= NSEC_PER_SEC) + { + ts->tv_nsec -= NSEC_PER_SEC; + ts->tv_sec += 1; + } + + ptpinfo("correction after: %jd.%09ld\n", (intmax_t)ts->tv_sec, + ts->tv_nsec); +} + static int ptp_process_followup(FAR struct ptp_state_s *state, FAR struct ptp_follow_up_s *msg) { struct timespec remote_time; - if (memcmp(msg->header.sourceidentity, + if (state->config->bmca && + memcmp(msg->header.sourceidentity, state->twostep_packet.header.sourceidentity, sizeof(msg->header.sourceidentity)) != 0) { @@ -1097,6 +1284,13 @@ static int ptp_process_followup(FAR struct ptp_state_s *state, */ ptp_format_to_timespec(msg->origintimestamp, &remote_time); + + /* add correction time */ + + ptp_add_correction_time(msg->header.correction, &remote_time); + + /* done */ + return ptp_update_local_clock(state, &remote_time, &state->twostep_rxtime); } @@ -1131,12 +1325,10 @@ static int ptp_process_delay_req(FAR struct ptp_state_s *state, sizeof(resp.header.sequenceid)); resp.header.logmessageinterval = CONFIG_NETUTILS_PTPD_DELAYRESP_INTERVAL; - ret = sendto(state->tx_socket, &resp, sizeof(resp), 0, - (FAR struct sockaddr *)&addr, sizeof(addr)); - + ret = ptp_sendmsg(state, &resp, sizeof(resp), &addr, sizeof(addr), NULL); if (ret < 0) { - ptperr("sendto failed: %d", errno); + ptperr("ptp sendmsg failed: %d", errno); } else { @@ -1230,6 +1422,21 @@ static int ptp_process_delay_resp(FAR struct ptp_state_s *state, static int ptp_process_rx_packet(FAR struct ptp_state_s *state, ssize_t length) { + if (state->config->af == AF_PACKET) + { + /* Remove the header of ether message */ + + FAR struct ethhdr *header = (FAR struct ethhdr *)state->rxbuf.raw; + + if (htons(header->h_proto) != ETHERTYPE_PTP) + { + return -EINVAL; + } + + length -= sizeof(*header); + memmove(&state->rxbuf.raw, header + 1, length); + } + if (length < sizeof(struct ptp_header_s)) { ptpwarn("Ignoring invalid PTP packet, length only %d bytes\n", @@ -1247,8 +1454,7 @@ static int ptp_process_rx_packet(FAR struct ptp_state_s *state, clock_gettime(CLOCK_MONOTONIC, &state->last_received_multicast); switch (state->rxbuf.header.messagetype & PTP_MSGTYPE_MASK) - { -#ifdef CONFIG_NETUTILS_PTPD_CLIENT + { case PTP_MSGTYPE_ANNOUNCE: ptpinfo("Got announce packet, seq %ld\n", (long)ptp_get_sequence(&state->rxbuf.header)); @@ -1268,20 +1474,17 @@ static int ptp_process_rx_packet(FAR struct ptp_state_s *state, ptpinfo("Got delay-resp, seq %ld\n", (long)ptp_get_sequence(&state->rxbuf.header)); return ptp_process_delay_resp(state, &state->rxbuf.delay_resp); -#endif -#ifdef CONFIG_NETUTILS_PTPD_SERVER case PTP_MSGTYPE_DELAY_REQ: ptpinfo("Got delay req, seq %ld\n", (long)ptp_get_sequence(&state->rxbuf.header)); return ptp_process_delay_req(state, &state->rxbuf.delay_req); -#endif default: ptpinfo("Ignoring unknown PTP packet type: 0x%02x\n", state->rxbuf.header.messagetype); return OK; - } + } } /* Signal handler for status / stop requests */ @@ -1386,43 +1589,70 @@ static void ptp_process_statusreq(FAR struct ptp_state_s *state) state->status_req.dest = NULL; } -/* Main PTPD task */ +/**************************************************************************** + * Public Functions + ****************************************************************************/ -static int ptp_daemon(int argc, FAR char** argv) +/**************************************************************************** + * Name: ptpd_start + * + * Description: + * Start the PTP daemon and bind it to specified interface. + * + * Input Parameters: + * interface - Name of the network interface to bind to, e.g. "eth0" + * + * Returned Value: + * On success, the non-negative task ID of the PTP daemon is returned; + * On failure, a negated errno value is returned. + * + ****************************************************************************/ + +int ptpd_start(FAR const struct ptpd_config_s *config) { - FAR const char *interface = "eth0"; FAR struct ptp_state_s *state; struct pollfd pollfds[2]; struct msghdr rxhdr; struct iovec rxiov; + int timeout; + int idx = 1; int ret; memset(&rxhdr, 0, sizeof(rxhdr)); memset(&rxiov, 0, sizeof(rxiov)); state = calloc(1, sizeof(struct ptp_state_s)); - - if (argc > 1) + if (state == NULL) { - interface = argv[1]; + return -ENOMEM; } - if (ptp_initialize_state(state, interface) != OK) + state->config = config; + if (ptp_initialize_state(state) != OK) { ptperr("Failed to initialize PTP state, exiting\n"); + goto errout; + } - ptp_destroy_state(state); - free(state); - - return ERROR; + if (config->client_only) + { + timeout = CONFIG_NETUTILS_PTPD_TIMEOUT_MS; + } + else + { + timeout = CONFIG_NETUTILS_PTPD_SYNC_INTERVAL_MSEC; } ptp_setup_sighandlers(state); pollfds[0].events = POLLIN; pollfds[0].fd = state->event_socket; - pollfds[1].events = POLLIN; - pollfds[1].fd = state->info_socket; + if (state->info_socket > 0) + { + pollfds[1].events = POLLIN; + pollfds[1].fd = state->info_socket; + idx++; + } while (!state->stop) { @@ -1440,7 +1670,7 @@ static int ptp_daemon(int argc, FAR char** argv) pollfds[0].revents = 0; pollfds[1].revents = 0; - ret = poll(pollfds, 2, PTPD_POLL_INTERVAL); + ret = poll(pollfds, idx, timeout); if (pollfds[0].revents) { @@ -1481,58 +1711,13 @@ static int ptp_daemon(int argc, FAR char** argv) ptp_process_statusreq(state); } +errout: ptp_destroy_state(state); free(state); return 0; } -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: ptpd_start - * - * Description: - * Start the PTP daemon and bind it to specified interface. - * - * Input Parameters: - * interface - Name of the network interface to bind to, e.g. "eth0" - * - * Returned Value: - * On success, the non-negative task ID of the PTP daemon is returned; - * On failure, a negated errno value is returned. - * - ****************************************************************************/ - -int ptpd_start(FAR const char *interface) -{ - int pid; - FAR char *task_argv[] = - { - (FAR char *)interface, - NULL - }; - - pid = task_create("PTPD", CONFIG_NETUTILS_PTPD_SERVERPRIO, - CONFIG_NETUTILS_PTPD_STACKSIZE, ptp_daemon, task_argv); - - /* Use kill with signal 0 to check if the process is still alive - * after initialization. - */ - - usleep(USEC_PER_TICK); - if (kill(pid, 0) != OK) - { - return ERROR; - } - else - { - return pid; - } -} - /**************************************************************************** * Name: ptpd_status * @@ -1594,6 +1779,7 @@ int ptpd_status(int pid, FAR struct ptpd_status_s *status) ret = -errno; } + sem_destroy(&donesem); return ret; #endif /* CONFIG_BUILD_FLAT */ diff --git a/netutils/ptpd/ptpv2.h b/netutils/ptpd/ptpv2.h index 8b031567b..3f4525428 100644 --- a/netutils/ptpd/ptpv2.h +++ b/netutils/ptpd/ptpv2.h @@ -27,6 +27,8 @@ * Included Files ****************************************************************************/ +#include + #include /**************************************************************************** @@ -67,7 +69,7 @@ /* Common header for all message types */ -struct ptp_header_s +begin_packed_struct struct ptp_header_s { uint8_t messagetype; uint8_t version; @@ -82,11 +84,11 @@ struct ptp_header_s uint8_t sequenceid[2]; uint8_t controlfield; uint8_t logmessageinterval; -}; +} end_packed_struct; /* Announce a master clock */ -struct ptp_announce_s +begin_packed_struct struct ptp_announce_s { struct ptp_header_s header; uint8_t origintimestamp[10]; @@ -98,40 +100,40 @@ struct ptp_announce_s uint8_t gm_identity[8]; uint8_t stepsremoved[2]; uint8_t timesource; -}; +} end_packed_struct; /* Sync: transmit timestamp from master clock */ -struct ptp_sync_s +begin_packed_struct struct ptp_sync_s { struct ptp_header_s header; uint8_t origintimestamp[10]; -}; +} end_packed_struct; /* FollowUp: actual timestamp of when sync message was sent */ -struct ptp_follow_up_s +begin_packed_struct struct ptp_follow_up_s { struct ptp_header_s header; uint8_t origintimestamp[10]; -}; +} end_packed_struct; /* DelayReq: request delay measurement */ -struct ptp_delay_req_s +begin_packed_struct struct ptp_delay_req_s { struct ptp_header_s header; uint8_t origintimestamp[10]; -}; +} end_packed_struct; /* DelayResp: response to DelayReq */ -struct ptp_delay_resp_s +begin_packed_struct struct ptp_delay_resp_s { struct ptp_header_s header; uint8_t receivetimestamp[10]; uint8_t reqidentity[8]; uint8_t reqportindex[2]; -}; +} end_packed_struct; #endif /* __APPS_NETUTILS_PTPD_PTPV2_H */ diff --git a/netutils/rexec/rexec.c b/netutils/rexec/rexec.c index 01c8000bf..519790c4a 100644 --- a/netutils/rexec/rexec.c +++ b/netutils/rexec/rexec.c @@ -87,7 +87,7 @@ static int do_rexec(FAR struct rexec_arg_s *arg) char buffer[REXEC_BUFSIZE]; struct pollfd fds[2]; int sock; - int ret; + int ret = 0; sock = rexec_af(&arg->host, htons(arg->port), arg->user, arg->password, arg->command, diff --git a/netutils/rexecd/CMakeLists.txt b/netutils/rexecd/CMakeLists.txt index 0da40f5bd..4cde43028 100644 --- a/netutils/rexecd/CMakeLists.txt +++ b/netutils/rexecd/CMakeLists.txt @@ -21,5 +21,13 @@ # ############################################################################## if(CONFIG_NETUTILS_REXECD) - nuttx_add_application(NAME rexecd SRCS rexecd.c) + nuttx_add_application( + NAME + rexecd + SRCS + rexecd.c + STACKSIZE + ${CONFIG_NETUTILS_REXECD_STACKSIZE} + PRIORITY + ${CONFIG_NETUTILS_REXECD_PRIORITY}) endif() diff --git a/netutils/rexecd/rexecd.c b/netutils/rexecd/rexecd.c index 5c301c62a..2ae489b6d 100644 --- a/netutils/rexecd/rexecd.c +++ b/netutils/rexecd/rexecd.c @@ -25,9 +25,12 @@ ****************************************************************************/ #include +#include #include #include #include +#include +#include #include #include #include @@ -84,8 +87,8 @@ static FAR void *doit(pthread_addr_t pvarg) { char buf[REXECD_BUFSIZE]; struct pollfd fds[2]; - FAR FILE *fp; - int sock = (int)pvarg; + pid_t pid; + int sock = (long)pvarg; int ret; /* we need to read err_sock, user and passwd, but ignore them */ @@ -97,18 +100,18 @@ static FAR void *doit(pthread_addr_t pvarg) /* we need to read command */ getstr(sock, buf); - fp = popen(buf, "r+"); - if (!fp) - { - goto errout; - } memset(fds, 0, sizeof(fds)); - fds[0].fd = fileno(fp); fds[0].events = POLLIN; fds[1].fd = sock; fds[1].events = POLLIN; + fds[0].fd = dpopen(buf, O_RDWR, &pid); + if (fds[0].fd < 0) + { + goto errout; + } + while (1) { ret = poll(fds, 2, -1); @@ -119,7 +122,7 @@ static FAR void *doit(pthread_addr_t pvarg) if (fds[0].revents & POLLIN) { - ret = read(fileno(fp), buf, REXECD_BUFSIZE); + ret = read(fds[0].fd, buf, REXECD_BUFSIZE); if (ret <= 0) { break; @@ -140,7 +143,7 @@ static FAR void *doit(pthread_addr_t pvarg) break; } - ret = write(fileno(fp), buf, ret); + ret = write(fds[0].fd, buf, ret); if (ret < 0) { break; @@ -154,7 +157,7 @@ static FAR void *doit(pthread_addr_t pvarg) } } - pclose(fp); + dpclose(fds[0].fd, pid); errout: close(sock); return NULL; @@ -162,11 +165,13 @@ errout: static void usage(FAR const char *progname) { - fprintf(stderr, "Usage: %s [-4|-6|-r]\n", progname); + fprintf(stderr, "Usage: %s [-4|-6|-r] [-t]\n", progname); fprintf(stderr, "Remote Execution Daemon:\n" " -4, Specify address family to AF_INET(default)\n" " -6, Specify address family to AF_INET6\n" - " -r, Specify address family to AF_RPMSG\n"); + " -r, Specify address family to AF_RPMSG\n" + " -t, Serve each connection inline without spawning a" + " per-connection thread (saves heap, no concurrency)\n"); exit(EXIT_FAILURE); } @@ -175,6 +180,7 @@ int main(int argc, FAR char **argv) struct sockaddr_storage addr; pthread_attr_t attr; pthread_t tid; + bool threadless = false; int family; int option; int serv; @@ -182,7 +188,7 @@ int main(int argc, FAR char **argv) int ret; family = AF_INET; - while ((option = getopt(argc, argv, "46r")) != ERROR) + while ((option = getopt(argc, argv, "46rt")) != ERROR) { switch (option) { @@ -195,6 +201,9 @@ int main(int argc, FAR char **argv) case 'r': family = AF_RPMSG; break; + case 't': + threadless = true; + break; default: usage(argv[0]); } @@ -223,7 +232,7 @@ int main(int argc, FAR char **argv) case AF_RPMSG: ((FAR struct sockaddr_rpmsg *)&addr)->rp_family = AF_RPMSG; snprintf(((FAR struct sockaddr_rpmsg *)&addr)->rp_name, - RPMSG_SOCKET_NAME_SIZE, "%d", REXECD_PORT); + RPMSG_SOCKET_NAME_SIZE, "%d", htons(REXECD_PORT)); ret = sizeof(struct sockaddr_rpmsg); } @@ -239,22 +248,26 @@ int main(int argc, FAR char **argv) goto err_out; } - ret = pthread_attr_init(&attr); - if (ret != 0) + if (!threadless) { - goto err_out; - } + ret = pthread_attr_init(&attr); + if (ret != 0) + { + goto err_out; + } - ret = pthread_attr_setstacksize(&attr, CONFIG_NETUTILS_REXECD_STACKSIZE); - if (ret != 0) - { - goto attr_out; - } + ret = pthread_attr_setstacksize(&attr, + CONFIG_NETUTILS_REXECD_STACKSIZE); + if (ret != 0) + { + goto attr_out; + } - ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (ret != 0) - { - goto attr_out; + ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ret != 0) + { + goto attr_out; + } } while (1) @@ -273,15 +286,27 @@ int main(int argc, FAR char **argv) } } - ret = pthread_create(&tid, &attr, doit, (pthread_addr_t)sock); - if (ret < 0) + if (threadless) { - close(sock); + doit((pthread_addr_t)(long)sock); + } + else + { + ret = pthread_create(&tid, &attr, doit, + (pthread_addr_t)(long)sock); + if (ret < 0) + { + close(sock); + } } } attr_out: - pthread_attr_destroy(&attr); + if (!threadless) + { + pthread_attr_destroy(&attr); + } + err_out: syslog(LOG_ERR, "rexecd failed ret:%d errno:%d\n", ret, errno); close(serv); diff --git a/netutils/smtp/smtp.c b/netutils/smtp/smtp.c index 2a8d23346..e04b65bc0 100644 --- a/netutils/smtp/smtp.c +++ b/netutils/smtp/smtp.c @@ -3,7 +3,8 @@ * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2015, 2020 Gregory Nutt. All rights reserved. - * SPDX-FileCopyrightText: 2007, 2009, 2011, Gregory Nutt. All rights reserved. + * SPDX-FileCopyrightText: 2007, 2009, Gregory Nutt. All rights reserved. + * SPDX-FileCopyrightText: 2011, Gregory Nutt. All rights reserved. * SPDX-FileCopyrightText: 2004, Adam Dunkels. All rights reserved. * SPDX-FileContributor: Gregory Nutt * SPDX-FileContributor: Adam Dunkels @@ -116,7 +117,7 @@ static const char g_smtpto[] = "To: "; static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) { - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -133,7 +134,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -150,7 +151,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -167,7 +168,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -186,7 +187,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -202,7 +203,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } @@ -254,7 +255,7 @@ static inline int smtp_send_message(int sockfd, struct smtp_state *psmtp) return ERROR; } - if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) < 0) + if (recv(sockfd, psmtp->buffer, SMTP_INPUT_BUFFER_SIZE, 0) <= 0) { return ERROR; } diff --git a/netutils/telnetd/telnetd_daemon.c b/netutils/telnetd/telnetd_daemon.c index ed02b1569..c1512113c 100644 --- a/netutils/telnetd/telnetd_daemon.c +++ b/netutils/telnetd/telnetd_daemon.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include @@ -74,7 +74,7 @@ int telnetd_daemon(FAR const struct telnetd_config_s *config) #endif } addr; -#ifdef CONFIG_SCHED_HAVE_PARENT +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_ENABLE_ALL_SIGNALS) struct sigaction sa; sigset_t blockset; #endif @@ -85,7 +85,7 @@ int telnetd_daemon(FAR const struct telnetd_config_s *config) int optval; #endif -#ifdef CONFIG_SCHED_HAVE_PARENT +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_ENABLE_ALL_SIGNALS) /* Call sigaction with the SA_NOCLDWAIT flag so that we do not transform * children into "zombies" when they terminate: Child exit status will * not be retained. @@ -113,7 +113,7 @@ int telnetd_daemon(FAR const struct telnetd_config_s *config) nerr("ERROR: sigprocmask failed: %d\n", errno); goto errout; } -#endif /* CONFIG_SCHED_HAVE_PARENT */ +#endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_ENABLE_ALL_SIGNALS */ /* Create a new TCP socket to use to listen for connections */ diff --git a/netutils/tftpc/tftpc_get.c b/netutils/tftpc/tftpc_get.c index 36227e29a..84be9b223 100644 --- a/netutils/tftpc/tftpc_get.c +++ b/netutils/tftpc/tftpc_get.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include diff --git a/netutils/tftpc/tftpc_packets.c b/netutils/tftpc/tftpc_packets.c index 05d56083c..9f6af0c65 100644 --- a/netutils/tftpc/tftpc_packets.c +++ b/netutils/tftpc/tftpc_packets.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include diff --git a/netutils/tftpc/tftpc_put.c b/netutils/tftpc/tftpc_put.c index 3f3a9c849..456100bdb 100644 --- a/netutils/tftpc/tftpc_put.c +++ b/netutils/tftpc/tftpc_put.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include "netutils/tftp.h" diff --git a/netutils/thttpd/fdwatch.c b/netutils/thttpd/fdwatch.c index 105fea30f..dcbd782e5 100644 --- a/netutils/thttpd/fdwatch.c +++ b/netutils/thttpd/fdwatch.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include "config.h" diff --git a/netutils/thttpd/libhttpd.c b/netutils/thttpd/libhttpd.c index 2d6a558e6..8be1c6ec9 100644 --- a/netutils/thttpd/libhttpd.c +++ b/netutils/thttpd/libhttpd.c @@ -58,7 +58,7 @@ #include #include #include -#include +#include #include #include "netutils/thttpd.h" @@ -327,7 +327,7 @@ static void send_mime(httpd_conn *hc, int status, const char *title, (hc->range_end >= hc->range_start) && ((hc->range_end != length - 1) || (hc->range_start != 0)) && - (hc->range_if == (time_t) - 1 || hc->range_if == hc->sb.st_mtime)) + (hc->range_if == -1 || hc->range_if == hc->sb.st_mtime)) { partial_content = 1; status = 206; @@ -340,7 +340,7 @@ static void send_mime(httpd_conn *hc, int status, const char *title, } gettimeofday(&now, NULL); - if (mod == (time_t)0) + if (mod == 0) { mod = now.tv_sec; } @@ -510,7 +510,7 @@ static int send_err_file(httpd_conn *hc, int status, char *title, } send_mime(hc, status, title, "", extraheads, "text/html; charset=%s", - (off_t)-1, (time_t)0); + -1, 0); for (; ; ) { nread = fread(buf, 1, sizeof(buf) - 1, fp); @@ -2163,14 +2163,15 @@ FAR httpd_server *httpd_initialize(FAR httpd_sockaddr *sa) #else hs->hostname = httpd_strdup(httpd_ntoa(sa)); #endif - ninfo("hostname: %s\n", hs->hostname); - if (!hs->hostname) { nerr("ERROR: out of memory copying hostname\n"); + free_httpd_server(hs); return NULL; } + ninfo("hostname: %s\n", hs->hostname); + hs->cgi_count = 0; /* Initialize listen sockets */ @@ -2421,8 +2422,8 @@ int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc) hc->altdir[0] = '\0'; #endif hc->buflen = 0; - hc->if_modified_since = (time_t) - 1; - hc->range_if = (time_t)-1; + hc->if_modified_since = -1; + hc->range_if = -1; hc->contentlength = -1; hc->type = ""; #ifdef CONFIG_THTTPD_VHOST @@ -2926,7 +2927,7 @@ int httpd_parse_request(httpd_conn *hc) { cp = &buf[18]; hc->if_modified_since = tdate_parse(cp); - if (hc->if_modified_since == (time_t) - 1) + if (hc->if_modified_since == -1) { nerr("ERROR: unparsable time: %s\n", cp); } @@ -2973,7 +2974,7 @@ int httpd_parse_request(httpd_conn *hc) { cp = &buf[9]; hc->range_if = tdate_parse(cp); - if (hc->range_if == (time_t) - 1) + if (hc->range_if == -1) { nerr("ERROR: unparsable time: %s\n", cp); } @@ -3521,7 +3522,7 @@ int httpd_start_request(httpd_conn *hc, struct timeval *nowp) send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, hc->sb.st_mtime); } - else if (hc->if_modified_since != (time_t) - 1 && + else if (hc->if_modified_since != -1 && hc->if_modified_since >= hc->sb.st_mtime) { send_mime(hc, 304, err304title, hc->encodings, "", diff --git a/netutils/thttpd/tdate_parse.c b/netutils/thttpd/tdate_parse.c index 113059223..82b55846a 100644 --- a/netutils/thttpd/tdate_parse.c +++ b/netutils/thttpd/tdate_parse.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include "tdate_parse.h" @@ -348,7 +348,7 @@ time_t tdate_parse(char *str) #endif /* Day of week not yet supported by NuttX */ else { - return (time_t) - 1; + return -1; } if (tm.tm_year > 1900) diff --git a/netutils/thttpd/thttpd.c b/netutils/thttpd/thttpd.c index 22338665b..f7de45177 100644 --- a/netutils/thttpd/thttpd.c +++ b/netutils/thttpd/thttpd.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include diff --git a/netutils/thttpd/thttpd_alloc.c b/netutils/thttpd/thttpd_alloc.c index 7999f7823..6deade0be 100644 --- a/netutils/thttpd/thttpd_alloc.c +++ b/netutils/thttpd/thttpd_alloc.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "config.h" diff --git a/netutils/thttpd/thttpd_cgi.c b/netutils/thttpd/thttpd_cgi.c index 5c8914560..a0cacf03e 100644 --- a/netutils/thttpd/thttpd_cgi.c +++ b/netutils/thttpd/thttpd_cgi.c @@ -52,7 +52,7 @@ #include #include #include -#include +#include #include #include "netutils/thttpd.h" @@ -766,7 +766,7 @@ static int cgi_child(int argc, char **argv) /* Close all file descriptors EXCEPT for stdin, stdout, stderr and * hc->conn_fd. We'll keep stderr open for error reporting; stdin and * stdout will be closed later by dup2(). Keeping stdin and stdout open - * now prevents re-use of fd=0 and 1 by pipe(). + * now prevents reuse of fd=0 and 1 by pipe(). */ ninfo("Closing descriptors\n"); diff --git a/netutils/thttpd/timers.c b/netutils/thttpd/timers.c index a3bd89831..d2026bd1b 100644 --- a/netutils/thttpd/timers.c +++ b/netutils/thttpd/timers.c @@ -39,7 +39,7 @@ #include #include -#include +#include #include "thttpd_alloc.h" #include "timers.h" diff --git a/netutils/wakaama/CMakeLists.txt b/netutils/wakaama/CMakeLists.txt index df3c32c2c..6e5e4f094 100644 --- a/netutils/wakaama/CMakeLists.txt +++ b/netutils/wakaama/CMakeLists.txt @@ -78,6 +78,10 @@ if(CONFIG_NETUTILS_WAKAAMA) list(APPEND WAKAAMA_DEFINITIONS LWM2M_SUPPORT_SENML_JSON) endif() + if(CONFIG_WAKAAMA_TLV) + list(APPEND WAKAAMA_DEFINITIONS LWM2M_SUPPORT_TLV) + endif() + list(APPEND WAKAAMA_DEFINITIONS LWM2M_COAP_DEFAULT_BLOCK_SIZE=${CONFIG_WAKAAMA_COAP_DEFAULT_BLOCK_SIZE}) diff --git a/netutils/webclient/webclient.c b/netutils/webclient/webclient.c index 8784b68b5..61f56cb4f 100644 --- a/netutils/webclient/webclient.c +++ b/netutils/webclient/webclient.c @@ -48,7 +48,7 @@ #include #include -#include +#include #include #include @@ -857,7 +857,7 @@ static inline int wget_parsechunkheader(struct webclient_context *ctx, if (semicolon != NULL) { found_extension = true; - ninfo("Ignoring extentions in chunk header\n"); + ninfo("Ignoring extensions in chunk header\n"); *semicolon = 0; } } diff --git a/netutils/webserver/httpd.c b/netutils/webserver/httpd.c index 23f69a949..1cc8d58fc 100644 --- a/netutils/webserver/httpd.c +++ b/netutils/webserver/httpd.c @@ -52,7 +52,7 @@ #include #include #include -#include +#include #ifndef CONFIG_NETUTILS_HTTPD_SINGLECONNECT # include diff --git a/netutils/webserver/httpd.h b/netutils/webserver/httpd.h index 253adfcf1..e6486bb34 100644 --- a/netutils/webserver/httpd.h +++ b/netutils/webserver/httpd.h @@ -54,7 +54,9 @@ * Public Function Prototypes ****************************************************************************/ -/* 'file' must be allocated by caller and will be filled in by the function. */ +/* 'file' must be allocated by caller and will be filled in by the + * function. + */ #if defined(CONFIG_NETUTILS_HTTPD_SENDFILE) diff --git a/netutils/webserver/httpd_dirlist.c b/netutils/webserver/httpd_dirlist.c index bac1b2d77..b77d68e4e 100644 --- a/netutils/webserver/httpd_dirlist.c +++ b/netutils/webserver/httpd_dirlist.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -196,7 +196,11 @@ ssize_t httpd_dirlist(int outfd, FAR struct httpd_fs_file *file) } ret = asprintf(&path, "%s/%s", file->path, dent->d_name); - ASSERT(ret > 0 && path); + if (ret < 0 || path == NULL) + { + nerr("ERROR: asprintf failed\n"); + break; + } /* call stat() to obtain modified time and size */ diff --git a/netutils/webserver/httpd_mmap.c b/netutils/webserver/httpd_mmap.c index a4f47427d..b17ad54c4 100644 --- a/netutils/webserver/httpd_mmap.c +++ b/netutils/webserver/httpd_mmap.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "netutils/httpd.h" diff --git a/netutils/webserver/httpd_sendfile.c b/netutils/webserver/httpd_sendfile.c index 7ff8a5ccd..3a8286fe9 100644 --- a/netutils/webserver/httpd_sendfile.c +++ b/netutils/webserver/httpd_sendfile.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "netutils/httpd.h" diff --git a/nshlib/CMakeLists.txt b/nshlib/CMakeLists.txt index 86b1b06ae..866f8bb6c 100644 --- a/nshlib/CMakeLists.txt +++ b/nshlib/CMakeLists.txt @@ -42,6 +42,10 @@ if(CONFIG_NSH_LIBRARY) nsh_syscmds.c nsh_dbgcmds.c) + if(CONFIG_SCHED_USER_IDENTITY) + list(APPEND CSRCS nsh_identity.c) + endif() + list(APPEND CSRCS nsh_session.c) if(CONFIG_NSH_CONSOLE_LOGIN) @@ -97,6 +101,10 @@ if(CONFIG_NSH_LIBRARY) endif() endif() + if(CONFIG_NSH_DROPBEAR) + list(APPEND CSRCS nsh_dropbear.c) + endif() + if(NOT CONFIG_NSH_DISABLESCRIPT) list(APPEND CSRCS nsh_test.c) endif() diff --git a/nshlib/Kconfig b/nshlib/Kconfig index 7fd49bbc7..fbbb08d3b 100644 --- a/nshlib/Kconfig +++ b/nshlib/Kconfig @@ -9,7 +9,7 @@ config NSH_LIBRARY bool "NSH Library" default n select NETUTILS_NETLIB if NET - select BOARDCTL if (!NSH_DISABLE_MKRD && !DISABLE_MOUNTPOINT) || NSH_ARCHINIT + select BOARDCTL if (!NSH_DISABLE_MKRD && !DISABLE_MOUNTPOINT) select BOARDCTL_MKRD if !NSH_DISABLE_MKRD && !DISABLE_MOUNTPOINT ---help--- Build the NSH support library. This is used, for example, by @@ -69,6 +69,24 @@ config NSH_PROMPT_STRING Provide the shell prompt string with size limit NSH_PROMPT_MAX. default is "nsh> ". +config NSH_PROMPT_STRING_ROOT + string "Prompt string for effective root (euid=0)" + default "" + depends on SCHED_USER_IDENTITY + ---help--- + If non-empty, NSH uses this prompt when the effective UID is zero. + If empty, the prompt from NSH_PROMPT_STRING (or ENV/HOSTNAME) is used. + Set explicitly for multi-user shells (for example, "nsh# "). + +config NSH_PROMPT_STRING_USER + string "Prompt string for non-root effective UID" + default "" + depends on SCHED_USER_IDENTITY + ---help--- + If non-empty, NSH uses this prompt when the effective UID is non-zero. + If empty, the prompt from NSH_PROMPT_STRING (or ENV/HOSTNAME) is used. + Set explicitly for multi-user shells (for example, "nsh$ "). + config NSH_PROMPT_MAX int "Maximum Size of Prompt String" default NAME_MAX @@ -341,6 +359,31 @@ config NSH_DISABLE_CD bool "Disable cd" default DEFAULT_SMALL +config NSH_DISABLE_CHMOD + bool "Disable chmod" + default DEFAULT_SMALL + depends on FS_PERMISSION + +config NSH_DISABLE_CHOWN + bool "Disable chown" + default DEFAULT_SMALL + depends on FS_PERMISSION + +config NSH_DISABLE_SU + bool "Disable su" + default DEFAULT_SMALL + depends on SCHED_USER_IDENTITY + +config NSH_DISABLE_ID + bool "Disable id" + default DEFAULT_SMALL + depends on SCHED_USER_IDENTITY + +config NSH_DISABLE_WHOAMI + bool "Disable whoami" + default DEFAULT_SMALL + depends on SCHED_USER_IDENTITY + config NSH_DISABLE_CP bool "Disable cp" default DEFAULT_SMALL @@ -370,6 +413,10 @@ config NSH_DISABLE_DMESG bool "Disable dmesg" default DEFAULT_SMALL +config NSH_DISABLE_DU + bool "Disable du" + default DEFAULT_SMALL + config NSH_DISABLE_ECHO bool "Disable echo" default DEFAULT_SMALL @@ -413,7 +460,7 @@ config NSH_DISABLE_HELP config NSH_DISABLE_ERROR_PRINT bool "Disable NSH Error Printing" - default DEFAULT_SMALL + default n config NSH_DISABLE_HEXDUMP bool "Disable hexdump" @@ -423,6 +470,10 @@ config NSH_DISABLE_IFCONFIG bool "Disable ifconfig" default DEFAULT_SMALL || !FS_PROCFS || FS_PROCFS_EXCLUDE_NET +config NSH_DISABLE_VCONFIG + bool "Disable vconfig" + default DEFAULT_SMALL || !FS_PROCFS || FS_PROCFS_EXCLUDE_NET + config NSH_DISABLE_IFUPDOWN bool "Disable ifup/down" default DEFAULT_SMALL || !FS_PROCFS || FS_PROCFS_EXCLUDE_NET @@ -1039,6 +1090,7 @@ config NSH_USBKBD bool "Use USB keyboard input" default n depends on NSH_CONSOLE && USBHOST_HIDKBD && !NSH_USBCONSOLE + depends on INPUT_KEYBOARD_BYTESTREAM ---help--- Normally NSH uses the same device for stdin, stdout, and stderr. By default, that device is /dev/console. If this option is selected, @@ -1047,6 +1099,9 @@ config NSH_USBKBD interface) and the data from the keyboard will drive NSH. NSH output (stdout and stderr) will still go to /dev/console. + NSH reads the keyboard as a stream of characters, which is what + INPUT_KEYBOARD_BYTESTREAM makes a keyboard device deliver. + if NSH_USBKBD config NSH_USBKBD_DEVNAME @@ -1112,16 +1167,6 @@ config NSH_USBDEV_TRACEINTERRUPTS endif # NSH_USBDEV_TRACE endmenu # USB Device Trace Support -config NSH_ARCHINIT - bool "Have architecture-specific initialization" - default n - select BOARDCTL - ---help--- - Set if your board provides architecture specific initialization - via the board-interface function boardctl(). The boardctl() - function will be called early in NSH initialization to allow - board logic to do such things as configure MMC/SD slots. - menu "Networking Configuration" depends on NET @@ -1164,6 +1209,28 @@ config NSH_DISABLE_TELNETSTART endmenu # Telnet Configuration +menu "Dropbear Configuration" + +config NSH_DROPBEAR + bool "Start Dropbear SSH server" + default n + depends on NETUTILS_DROPBEAR + depends on NSH_BUILTIN_APPS + depends on !NSH_DISABLEBG + select NSH_LOGIN + ---help--- + If NSH_DROPBEAR is set to 'y', then NSH starts the Dropbear SSH + server automatically. + +config NSH_DISABLE_DROPBEARSTART + bool "Disable to start Dropbear" + default n + depends on NSH_DROPBEAR + ---help--- + Determines if NSH starts Dropbear automatically. + +endmenu # Dropbear Configuration + config NSH_LOGIN bool default n @@ -1171,32 +1238,42 @@ config NSH_LOGIN config NSH_CONSOLE_LOGIN bool "Console Login" default n + depends on FSUTILS_PASSWD + depends on !ETC_ROMFS || BOARD_ETC_ROMFS_PASSWD_ENABLE select NSH_LOGIN ---help--- If defined, then the console user will be required to provide a - username and password to start the NSH shell. + username and password to start the NSH shell. Requires + CONFIG_FSUTILS_PASSWD (PBKDF2-HMAC-SHA256) so credentials are + verified against the encrypted password file. + + When CONFIG_ETC_ROMFS is enabled, also enable + CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE so /etc/passwd is generated + at build time (or provide the passwd file by other means and leave + ETC_ROMFS disabled). config NSH_TELNET_LOGIN bool "Telnet Login" default n - depends on NSH_TELNET + depends on NSH_TELNET && FSUTILS_PASSWD + depends on !ETC_ROMFS || BOARD_ETC_ROMFS_PASSWD_ENABLE select NSH_LOGIN ---help--- If defined, then the Telnet user will be required to provide a - username and password to start the NSH shell. + username and password to start the NSH shell. Requires + CONFIG_FSUTILS_PASSWD (PBKDF2-HMAC-SHA256) so credentials are + verified against the encrypted password file. + + When CONFIG_ETC_ROMFS is enabled, also enable + CONFIG_BOARD_ETC_ROMFS_PASSWD_ENABLE so /etc/passwd is generated + at build time (or provide the passwd file by other means and leave + ETC_ROMFS disabled). if NSH_LOGIN choice prompt "Verification method" - default NSH_LOGIN_PASSWD if FSUTILS_PASSWD - default NSH_LOGIN_FIXED if !FSUTILS_PASSWD - -config NSH_LOGIN_FIXED - bool "Fixed username/password" - ---help--- - Verify user credentials by matching to fixed username and password - strings + default NSH_LOGIN_PASSWD config NSH_LOGIN_PLATFORM bool "Platform username/password" @@ -1228,17 +1305,10 @@ endchoice # Verification method config NSH_LOGIN_USERNAME string "Login username" - default "admin" + default "root" depends on !NSH_LOGIN_PASSWD ---help--- - Login user name. Default: "admin" - -config NSH_LOGIN_PASSWORD - string "Login password" - default "Administrator" - depends on !NSH_LOGIN_PASSWD - ---help--- - Login password: Default: "Administrator" + Login user name. Default: "root" config NSH_LOGIN_FAILDELAY int "Login failure delay" @@ -1255,6 +1325,19 @@ config NSH_LOGIN_FAILCOUNT ---help--- Number of login retry attempts. +config NSH_LOGIN_SETUID + bool "Set user identity after successful login" + default y + depends on SCHED_USER_IDENTITY + ---help--- + After a successful NSH login, look up the authenticated user name + in the passwd database and call setuid()/setgid() so that the shell + session runs with that user's credentials. + + When CONFIG_LIBC_PASSWD_FILE is enabled, any user listed in the + passwd file may be selected. Otherwise only the built-in "root" + account is supported. + config NSH_PLATFORM_CHALLENGE bool "Platform challenge" default n diff --git a/nshlib/Makefile b/nshlib/Makefile index 26dbbd0de..4aba9e3a8 100644 --- a/nshlib/Makefile +++ b/nshlib/Makefile @@ -28,6 +28,10 @@ CSRCS = nsh_init.c nsh_parse.c nsh_console.c nsh_script.c nsh_system.c CSRCS += nsh_command.c nsh_fscmds.c nsh_proccmds.c nsh_mmcmds.c CSRCS += nsh_timcmds.c nsh_envcmds.c nsh_syscmds.c nsh_dbgcmds.c nsh_prompt.c +ifeq ($(CONFIG_SCHED_USER_IDENTITY),y) +CSRCS += nsh_identity.c +endif + CSRCS += nsh_session.c ifeq ($(CONFIG_NSH_CONSOLE_LOGIN),y) CSRCS += nsh_login.c @@ -82,6 +86,10 @@ CSRCS += nsh_telnetlogin.c endif endif +ifeq ($(CONFIG_NSH_DROPBEAR),y) +CSRCS += nsh_dropbear.c +endif + ifneq ($(CONFIG_NSH_DISABLESCRIPT),y) CSRCS += nsh_test.c endif diff --git a/nshlib/nsh.h b/nshlib/nsh.h index 8d037f517..a7ceb7c95 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -266,8 +266,7 @@ * If CONFIG_NSH_TELNET_LOGIN is defined, then these additional * options may be specified: * - * CONFIG_NSH_LOGIN_USERNAME - Login user name. Default: "admin" - * CONFIG_NSH_LOGIN_PASSWORD - Login password: Default: "Administrator" + * CONFIG_NSH_LOGIN_USERNAME - Login user name. Default: "root" * CONFIG_NSH_LOGIN_FAILCOUNT - Number of login retry attempts. * Default 3. */ @@ -275,11 +274,7 @@ #ifdef CONFIG_NSH_TELNET_LOGIN # ifndef CONFIG_NSH_LOGIN_USERNAME -# define CONFIG_NSH_LOGIN_USERNAME "admin" -# endif - -# ifndef CONFIG_NSH_LOGIN_PASSWORD -# define CONFIG_NSH_LOGIN_PASSWORD "nuttx" +# define CONFIG_NSH_LOGIN_USERNAME "root" # endif # ifndef CONFIG_NSH_LOGIN_FAILCOUNT @@ -416,12 +411,13 @@ #define NSH_HAVE_IOBUFFER 1 -/* The I/O buffer is needed for the ls, cp, and ps commands. It is also - * needed if the platform supplied MOTD is configured. +/* The I/O buffer is needed for the ls, cp, du, and ps commands. It is + * also needed if the platform supplied MOTD is configured. */ #if defined(CONFIG_NSH_DISABLE_LS) && defined(CONFIG_NSH_DISABLE_CP) && \ - defined(CONFIG_NSH_DISABLE_PS) && !defined(CONFIG_NSH_PLATFORM_MOTD) && \ + defined(CONFIG_NSH_DISABLE_DU) && defined(CONFIG_NSH_DISABLE_PS) && \ + !defined(CONFIG_NSH_PLATFORM_MOTD) && \ defined(CONFIG_DISABLE_ENVIRON) # undef NSH_HAVE_IOBUFFER #endif @@ -655,6 +651,7 @@ struct nsh_parser_s #endif bool np_redir_out; /* true: Output from the last command was re-directed */ bool np_redir_in; /* true: Input from the last command was re-directed */ + bool np_redir_err; /* true: Error from the last command was re-directed */ bool np_fail; /* true: The last command failed */ pid_t np_lastpid; /* Pid of the last command executed */ #ifdef NSH_HAVE_VARS @@ -749,6 +746,7 @@ extern const char g_userprompt[]; extern const char g_passwordprompt[]; extern const char g_loginsuccess[]; extern const char g_badcredentials[]; +extern const char g_badidentity[]; extern const char g_loginfailure[]; #endif extern const char g_fmtsyntax[]; @@ -806,13 +804,6 @@ int nsh_loginscript(FAR struct nsh_vtbl_s *vtbl); * available: */ -/* Architecture-specific initialization depends on boardctl(BOARDIOC_INIT) */ - -#if defined(CONFIG_NSH_ARCHINIT) && !defined(CONFIG_BOARDCTL) -# warning CONFIG_NSH_ARCHINIT is set, but CONFIG_BOARDCTL is not -# undef CONFIG_NSH_ARCHINIT -#endif - /* The mkrd command depends on boardctl(BOARDIOC_MKRD) */ #if !defined(CONFIG_BOARDCTL) || !defined(CONFIG_BOARDCTL_MKRD) @@ -849,6 +840,10 @@ int nsh_login(FAR struct console_stdio_s *pstate); int nsh_telnetlogin(FAR struct console_stdio_s *pstate); #endif +#if defined(CONFIG_NSH_DROPBEAR) && !defined(CONFIG_NSH_DISABLE_DROPBEARSTART) +int nsh_dropbearstart(void); +#endif + /* Application interface */ int nsh_command(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char *argv[]); @@ -969,6 +964,24 @@ int cmd_irqinfo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #ifndef CONFIG_NSH_DISABLE_CAT int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD) + int cmd_chmod(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +#endif +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN) + int cmd_chown(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +#endif +#ifdef CONFIG_SCHED_USER_IDENTITY + int nsh_setuser_identity(FAR const char *username); +# ifndef CONFIG_NSH_DISABLE_SU + int cmd_su(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_ID + int cmd_id(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +# endif +# ifndef CONFIG_NSH_DISABLE_WHOAMI + int cmd_whoami(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +# endif +#endif #ifndef CONFIG_NSH_DISABLE_CP int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif @@ -984,6 +997,9 @@ int cmd_irqinfo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #ifndef CONFIG_NSH_DISABLE_LS int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif +#ifndef CONFIG_NSH_DISABLE_DU + int cmd_du(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +#endif #if defined(CONFIG_SYSLOG_DEVPATH) && !defined(CONFIG_NSH_DISABLE_DMESG) int cmd_dmesg(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif @@ -1093,6 +1109,9 @@ int cmd_irqinfo(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); # ifndef CONFIG_NSH_DISABLE_IFCONFIG int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); # endif +# ifndef CONFIG_NSH_DISABLE_VCONFIG + int cmd_vconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); +# endif # ifndef CONFIG_NSH_DISABLE_IFUPDOWN int cmd_ifup(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); int cmd_ifdown(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); @@ -1176,6 +1195,7 @@ int cmd_switchboot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); int cmd_unset(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif +#ifndef CONFIG_DISABLE_ALL_SIGNALS #ifndef CONFIG_NSH_DISABLE_KILL int cmd_kill(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif @@ -1188,6 +1208,7 @@ int cmd_switchboot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #ifndef CONFIG_NSH_DISABLE_USLEEP int cmd_usleep(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); #endif +#endif /* !CONFIG_DISABLE_ALL_SIGNALS */ #ifndef CONFIG_NSH_DISABLE_UPTIME int cmd_uptime(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv); diff --git a/nshlib/nsh_altconsole.c b/nshlib/nsh_altconsole.c index 7423fedf7..bb9fc30f2 100644 --- a/nshlib/nsh_altconsole.c +++ b/nshlib/nsh_altconsole.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "nsh.h" diff --git a/nshlib/nsh_builtin.c b/nshlib/nsh_builtin.c index 196220f41..76f542f8f 100644 --- a/nshlib/nsh_builtin.c +++ b/nshlib/nsh_builtin.c @@ -74,10 +74,14 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, FAR char **argv, FAR const struct nsh_param_s *param) { -#if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) +#if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) && \ + defined(CONFIG_ENABLE_ALL_SIGNALS) struct sigaction act; struct sigaction old; -#endif +#endif /* !CONFIG_NSH_DISABLEBG && CONFIG_SCHED_CHILD_STATUS && + * CONFIG_ENABLE_ALL_SIGNALS + */ + int ret = OK; /* Lock the scheduler in an attempt to prevent the application from @@ -86,7 +90,8 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, sched_lock(); -#if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) +#if !defined(CONFIG_NSH_DISABLEBG) && \ + defined(CONFIG_SCHED_CHILD_STATUS) && defined(CONFIG_ENABLE_ALL_SIGNALS) /* Ignore the child status if run the application on background. */ if (vtbl->np.np_bg == true) @@ -98,7 +103,9 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, sigaction(SIGCHLD, &act, &old); } -#endif /* CONFIG_NSH_DISABLEBG */ +#endif /* !CONFIG_NSH_DISABLEBG && !CONFIG_SCHED_CHILD_STATUS && + * CONFIG_ENABLE_ALL_SIGNALS + */ /* Try to find and execute the command within the list of builtin * applications. @@ -230,7 +237,8 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, #if !defined(CONFIG_SCHED_WAITPID) || !defined(CONFIG_NSH_DISABLEBG) { -#if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) +#if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) && \ + defined(CONFIG_ENABLE_ALL_SIGNALS) /* Restore the old actions */ @@ -241,7 +249,10 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, sigaction(SIGCHLD, &old, NULL); } -# endif +# endif /* !CONFIG_NSH_DISABLEBG && CONFIG_SCHED_CHILD_STATUS && \ + * CONFIG_ENABLE_ALL_SIGNALS + */ + struct sched_param sched; sched_getparam(ret, &sched); nsh_output(vtbl, "%s [%d:%d]\n", cmd, ret, sched.sched_priority); diff --git a/nshlib/nsh_codeccmd.c b/nshlib/nsh_codeccmd.c index 91b3953ff..0d677669f 100644 --- a/nshlib/nsh_codeccmd.c +++ b/nshlib/nsh_codeccmd.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #if defined(CONFIG_NSH_DISABLE_URLENCODE) && defined(CONFIG_NSH_DISABLE_URLDECODE) # undef CONFIG_CODECS_URLCODE diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index 3911e0844..098abaa76 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -168,6 +168,14 @@ static const struct cmdmap_s g_cmdmap[] = CMD_MAP("cd", cmd_cd, 1, 2, "[|-|~|..]"), #endif +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD) + CMD_MAP("chmod", cmd_chmod, 3, 3, " "), +#endif + +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN) + CMD_MAP("chown", cmd_chown, 3, 3, "[][:] "), +#endif + #ifndef CONFIG_NSH_DISABLE_CP CMD_MAP("cp", cmd_cp, 3, 4, "[-r] "), #endif @@ -248,6 +256,10 @@ static const struct cmdmap_s g_cmdmap[] = CMD_MAP("free", cmd_free, 1, 1, NULL), #endif +#if defined(CONFIG_SCHED_USER_IDENTITY) && !defined(CONFIG_NSH_DISABLE_ID) + CMD_MAP("id", cmd_id, 1, 1, NULL), +#endif + #ifdef CONFIG_DEBUG_MM # ifndef CONFIG_NSH_DISABLE_MEMDUMP CMD_MAP("memdump", cmd_memdump, @@ -286,6 +298,10 @@ static const struct cmdmap_s g_cmdmap[] = "[dr|gw|gateway ] [netmask |prefixlen ] " "[dns ] [hw ]"), # endif +# if defined(CONFIG_NET_VLAN) && !defined(CONFIG_NSH_DISABLE_VCONFIG) + CMD_MAP("vconfig", cmd_vconfig, 3, 5, + "[add iface-name vlan-id [pcp]]|[rem vlan-name]"), +# endif # ifndef CONFIG_NSH_DISABLE_IFUPDOWN CMD_MAP("ifdown", cmd_ifdown, 2, 2, ""), CMD_MAP("ifup", cmd_ifup, 2, 2, ""), @@ -300,14 +316,6 @@ static const struct cmdmap_s g_cmdmap[] = CMD_MAP("irqinfo", cmd_irqinfo, 1, 1, NULL), #endif -#ifndef CONFIG_NSH_DISABLE_KILL - CMD_MAP("kill", cmd_kill, 2, 3, "[-] "), -#endif - -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PKILL) - CMD_MAP("pkill", cmd_pkill, 2, 3, "[-] "), -#endif - #ifndef CONFIG_DISABLE_MOUNTPOINT # if defined(CONFIG_DEV_LOOP) && !defined(CONFIG_NSH_DISABLE_LOSETUP) CMD_MAP("losetup", cmd_losetup, 3, 6, @@ -326,9 +334,15 @@ static const struct cmdmap_s g_cmdmap[] = #ifndef CONFIG_DISABLE_MOUNTPOINT # if defined(CONFIG_MTD_LOOP) && !defined(CONFIG_NSH_DISABLE_LOMTD) +# ifndef CONFIG_MTD_CONFIG_NONE + CMD_MAP("lomtd", cmd_lomtd, 3, 10, + "[-d ] | [[-o ] [-e ] " + "[-b ] [-c ] ]]"), +# else CMD_MAP("lomtd", cmd_lomtd, 3, 9, "[-d ] | [[-o ] [-e ] " "[-b ] ]]"), +# endif # endif #endif @@ -340,6 +354,11 @@ static const struct cmdmap_s g_cmdmap[] = CMD_MAP("ls", cmd_ls, 1, 5, "[-lRsh] "), #endif +#ifndef CONFIG_NSH_DISABLE_DU + CMD_MAP("du", cmd_du, 1, 7, + "[-h] [-s] [-a] [-d N] ..."), +#endif + #if defined(CONFIG_MODULE) && !defined(CONFIG_NSH_DISABLE_MODCMDS) # if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) CMD_MAP("lsmod", cmd_lsmod, 1, 1, NULL), @@ -535,7 +554,7 @@ static const struct cmdmap_s g_cmdmap[] = #if defined(CONFIG_RPTUN) && !defined(CONFIG_NSH_DISABLE_RPTUN) CMD_MAP("rptun", cmd_rptun, 2, 7, - " " + " " " [value|times length ack sleep]"), #endif @@ -561,6 +580,10 @@ static const struct cmdmap_s g_cmdmap[] = #endif #endif /* CONFIG_NSH_DISABLE_SET */ +#if defined(CONFIG_SCHED_USER_IDENTITY) && !defined(CONFIG_NSH_DISABLE_SU) + CMD_MAP("su", cmd_su, 1, 2, "[]"), +#endif + #ifndef CONFIG_NSH_DISABLE_SHUTDOWN #if defined(CONFIG_BOARDCTL_POWEROFF) && defined(CONFIG_BOARDCTL_RESET) CMD_MAP("shutdown", cmd_shutdown, 1, 2, "[--reboot]"), @@ -571,16 +594,30 @@ static const struct cmdmap_s g_cmdmap[] = #endif #endif +#ifndef CONFIG_DISABLE_ALL_SIGNALS +#ifndef CONFIG_NSH_DISABLE_KILL + CMD_MAP("kill", cmd_kill, 2, 3, "[-] "), +#endif + +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PKILL) + CMD_MAP("pkill", cmd_pkill, 2, 3, "[-] "), +#endif + #ifndef CONFIG_NSH_DISABLE_SLEEP CMD_MAP("sleep", cmd_sleep, 2, 2, ""), #endif +#ifndef CONFIG_NSH_DISABLE_USLEEP + CMD_MAP("usleep", cmd_usleep, 2, 2, ""), +#endif +#endif + #if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_SOURCE) CMD_MAP("source", cmd_source, 2, 2, ""), #endif #if defined(CONFIG_BOARDCTL_SWITCH_BOOT) && !defined(CONFIG_NSH_DISABLE_SWITCHBOOT) - CMD_MAP("swtichboot", cmd_switchboot, 2, 2, ""), + CMD_MAP("switchboot", cmd_switchboot, 2, 2, ""), #endif #if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_TEST) @@ -611,6 +648,10 @@ static const struct cmdmap_s g_cmdmap[] = # endif #endif +#if defined(CONFIG_SCHED_USER_IDENTITY) && !defined(CONFIG_NSH_DISABLE_WHOAMI) + CMD_MAP("whoami", cmd_whoami, 1, 1, NULL), +#endif + #ifndef CONFIG_NSH_DISABLE_UNAME # ifdef CONFIG_NET CMD_MAP("uname", cmd_uname, 1, 7, "[-a | -imnoprsv]"), @@ -621,7 +662,7 @@ static const struct cmdmap_s g_cmdmap[] = #if !defined(CONFIG_DISABLE_MOUNTPOINT) # ifndef CONFIG_NSH_DISABLE_UMOUNT - CMD_MAP("umount", cmd_umount, 2, 2, ""), + CMD_MAP("umount", cmd_umount, 2, 3, "[-f] "), # endif #endif @@ -653,10 +694,6 @@ static const struct cmdmap_s g_cmdmap[] = # endif #endif -#ifndef CONFIG_NSH_DISABLE_USLEEP - CMD_MAP("usleep", cmd_usleep, 2, 2, ""), -#endif - #ifndef CONFIG_NSH_DISABLE_WATCH CMD_MAP("watch", cmd_watch, 2, 6, "[-n] interval [-c] count "), diff --git a/nshlib/nsh_console.c b/nshlib/nsh_console.c index 2d8c315ad..0e4284222 100644 --- a/nshlib/nsh_console.c +++ b/nshlib/nsh_console.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include "nsh.h" #include "nsh_console.h" @@ -76,7 +76,7 @@ static int nsh_erroroutput(FAR struct nsh_vtbl_s *vtbl, #endif static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl); static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in, - int fd_out, FAR uint8_t *save); + int fd_out, int fd_err, FAR uint8_t *save); static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl, @@ -131,18 +131,19 @@ static ssize_t nsh_consolewrite(FAR struct nsh_vtbl_s *vtbl, FAR const void *buffer, size_t nbytes) { FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; - ssize_t ret; - /* Write the data to the output stream */ + /* Write the data to the output stream. + * + * Errors are reported to the caller via the negative return value and + * errno; do NOT _err() here. OUTFD may itself be wired to the syslog + * backend (e.g. nsh_catfile dumping /dev/log on dmesg), in which case + * logging on write failure would produce new bytes, get picked up by + * the next read(), and lock the shell into an infinite loop at the + * highest priority — independent of which errno (EPIPE/EIO/ENOSPC/...) + * the underlying device returns. + */ - ret = write(OUTFD(pstate), buffer, nbytes); - if (ret < 0) - { - _err("ERROR: [%d] Failed to send buffer: %d\n", - OUTFD(pstate), errno); - } - - return ret; + return write(OUTFD(pstate), buffer, nbytes); } /**************************************************************************** @@ -332,7 +333,7 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) ****************************************************************************/ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in, - int fd_out, FAR uint8_t *save) + int fd_out, int fd_err, FAR uint8_t *save) { FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; @@ -354,6 +355,7 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in, OUTFD(pstate) = fd_out; INFD(pstate) = fd_in; + ERRFD(pstate) = fd_err; } /**************************************************************************** diff --git a/nshlib/nsh_console.h b/nshlib/nsh_console.h index 0b94a2b6b..268d03379 100644 --- a/nshlib/nsh_console.h +++ b/nshlib/nsh_console.h @@ -43,15 +43,15 @@ /* Method access macros */ -#define nsh_clone(v) (v)->clone(v) -#define nsh_release(v) (v)->release(v) -#define nsh_write(v,b,n) (v)->write(v,b,n) -#define nsh_read(v,b,n) (v)->read(v,b,n) -#define nsh_ioctl(v,c,a) (v)->ioctl(v,c,a) -#define nsh_linebuffer(v) (v)->linebuffer(v) -#define nsh_redirect(v,fi,fo,s) (v)->redirect(v,fi,fo,s) -#define nsh_undirect(v,s) (v)->undirect(v,s) -#define nsh_exit(v,s) (v)->exit(v,s) +#define nsh_clone(v) (v)->clone(v) +#define nsh_release(v) (v)->release(v) +#define nsh_write(v,b,n) (v)->write(v,b,n) +#define nsh_read(v,b,n) (v)->read(v,b,n) +#define nsh_ioctl(v,c,a) (v)->ioctl(v,c,a) +#define nsh_linebuffer(v) (v)->linebuffer(v) +#define nsh_redirect(v,fi,fo,fe,s) (v)->redirect(v,fi,fo,fe,s) +#define nsh_undirect(v,s) (v)->undirect(v,s) +#define nsh_exit(v,s) (v)->exit(v,s) #ifdef CONFIG_CPP_HAVE_VARARGS # define nsh_error(v, ...) (v)->error(v, ##__VA_ARGS__) @@ -127,7 +127,7 @@ struct nsh_vtbl_s printf_like(2, 3); FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl); void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd_in, int fd_out, - FAR uint8_t *save); + int fd_err, FAR uint8_t *save); void (*undirect)(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); void (*exit)(FAR struct nsh_vtbl_s *vtbl, int status) noreturn_function; diff --git a/nshlib/nsh_dropbear.c b/nshlib/nsh_dropbear.c new file mode 100644 index 000000000..9e7753f11 --- /dev/null +++ b/nshlib/nsh_dropbear.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * apps/nshlib/nsh_dropbear.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "nsh.h" +#include "nsh_console.h" + +#ifdef CONFIG_NSH_DROPBEAR + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_dropbearstart + * + * Description: + * nsh_dropbearstart() starts the Dropbear SSH server. This function + * returns immediately after the daemon has been started. + * + * Returned Values: + * Zero is returned if Dropbear was started. A negated errno value will be + * returned on failure. + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_DROPBEARSTART +int nsh_dropbearstart(void) +{ + FAR struct console_stdio_s *pstate = nsh_newconsole(false); + char cmdline[] = CONFIG_NETUTILS_DROPBEAR_PROGNAME " &"; + int ret; + + DEBUGASSERT(pstate != NULL); + + ninfo("Starting the Dropbear SSH server\n"); + + ret = nsh_parse(&pstate->cn_vtbl, cmdline); + if (ret < 0) + { + nerr("ERROR: Failed to start Dropbear: %d\n", ret); + } + + nsh_release(&pstate->cn_vtbl); + return ret; +} +#endif + +#endif /* CONFIG_NSH_DROPBEAR */ diff --git a/nshlib/nsh_envcmds.c b/nshlib/nsh_envcmds.c index d2378bb38..52be0e96c 100644 --- a/nshlib/nsh_envcmds.c +++ b/nshlib/nsh_envcmds.c @@ -257,17 +257,38 @@ int cmd_cd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) else if (strcmp(path, "-") == 0) { alloc = strdup(nsh_getwd(g_oldpwd)); + if (alloc == NULL) + { + nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]); + ret = ERROR; + goto errout; + } + path = alloc; } #endif else if (strcmp(path, "..") == 0) { alloc = strdup(nsh_getcwd(vtbl)); + if (alloc == NULL) + { + nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]); + ret = ERROR; + goto errout; + } + path = dirname(alloc); } else { fullpath = nsh_getfullpath(vtbl, path); + if (fullpath == NULL) + { + nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]); + ret = ERROR; + goto errout; + } + path = fullpath; } @@ -288,6 +309,7 @@ int cmd_cd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) /* Free any memory that was allocated */ +errout: if (alloc) { free(alloc); @@ -368,19 +390,18 @@ do_echo: str_escape(argv[0]); } - nsh_output(vtbl, "%s", argv[0]); + if (argc > 1) + { + nsh_output(vtbl, "%s ", argv[0]); + } + else + { + nsh_output(vtbl, newline ? "%s\n" : "%s", argv[0]); + break; + } --argc; ++argv; - if (argc > 0) - { - nsh_output(vtbl, " "); - } - } - - if (newline) - { - nsh_output(vtbl, "\n"); } return OK; diff --git a/nshlib/nsh_fileapps.c b/nshlib/nsh_fileapps.c index 54efd6de2..66f41e36f 100644 --- a/nshlib/nsh_fileapps.c +++ b/nshlib/nsh_fileapps.c @@ -177,6 +177,39 @@ int nsh_fileapp(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, } } #endif + + /* Handle redirection of error output */ + + if (param->file_err) + { + /* 2> file: Redirect stderr to a file */ + + ret = posix_spawn_file_actions_addopen(&file_actions, 2, + param->file_err, + param->oflags_err, + 0644); + if (ret != 0) + { + nsh_error(vtbl, g_fmtcmdfailed, cmd, + "posix_spawn_file_actions_addopen", + NSH_ERRNO); + goto errout_with_attrs; + } + } + else if (param->fd_err != -1) + { + /* 2>&1: Redirect stderr to stdout */ + + ret = posix_spawn_file_actions_adddup2(&file_actions, + param->fd_err, 2); + if (ret != 0) + { + nsh_error(vtbl, g_fmtcmdfailed, cmd, + "posix_spawn_file_actions_adddup2", + NSH_ERRNO); + goto errout_with_attrs; + } + } } #ifdef CONFIG_BUILTIN diff --git a/nshlib/nsh_fscmds.c b/nshlib/nsh_fscmds.c index 1c705befe..0a6fd945f 100644 --- a/nshlib/nsh_fscmds.c +++ b/nshlib/nsh_fscmds.c @@ -37,13 +37,21 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include + +#ifdef CONFIG_LIBC_PASSWD_FILE +# include +#endif +#ifdef CONFIG_LIBC_GROUP_FILE +# include +#endif #include "nsh.h" @@ -340,6 +348,44 @@ static inline int ls_specialdir(FAR const char *dir) return (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0); } + +#ifdef CONFIG_SCHED_USER_IDENTITY +/**************************************************************************** + * Name: nsh_ls_printowner + ****************************************************************************/ + +static void nsh_ls_printowner(FAR struct nsh_vtbl_s *vtbl, uid_t uid, + gid_t gid) +{ +#ifdef CONFIG_LIBC_PASSWD_FILE + FAR struct passwd *pwd; + + pwd = getpwuid(uid); + if (pwd != NULL && pwd->pw_name != NULL) + { + nsh_output(vtbl, "%8s", pwd->pw_name); + } + else +#endif + { + nsh_output(vtbl, "%8d", (int)uid); + } + +#ifdef CONFIG_LIBC_GROUP_FILE + FAR struct group *grp; + + grp = getgrgid(gid); + if (grp != NULL && grp->gr_name != NULL) + { + nsh_output(vtbl, "%8s", grp->gr_name); + } + else +#endif + { + nsh_output(vtbl, "%8d", (int)gid); + } +} +#endif /* CONFIG_SCHED_USER_IDENTITY */ #endif /**************************************************************************** @@ -511,35 +557,62 @@ static int ls_handler(FAR struct nsh_vtbl_s *vtbl, FAR const char *dirpath, #ifdef CONFIG_SCHED_USER_IDENTITY if ((lsflags & LSFLAGS_UID_GID) != 0) { - nsh_output(vtbl, "%8d", buf.st_uid); - nsh_output(vtbl, "%8d", buf.st_gid); + nsh_ls_printowner(vtbl, buf.st_uid, buf.st_gid); } #endif if ((lsflags & LSFLAGS_SIZE) != 0) { -#ifdef CONFIG_HAVE_FLOAT if (lsflags & LSFLAGS_HUMANREADBLE && buf.st_size >= KB) { + uint32_t integer_part; + uint32_t decimal_part; + uint32_t unit; + char suffix; + + /* Determine the appropriate unit and suffix */ + if (buf.st_size >= GB) { - nsh_output(vtbl, "%11.1fG", (float)buf.st_size / GB); + unit = GB; + suffix = 'G'; } else if (buf.st_size >= MB) { - nsh_output(vtbl, "%11.1fM", (float)buf.st_size / MB); + unit = MB; + suffix = 'M'; } else { - nsh_output(vtbl, "%11.1fK", (float)buf.st_size / KB); + unit = KB; + suffix = 'K'; } + + /* Use integer arithmetic to avoid floating point */ + + integer_part = buf.st_size / unit; + decimal_part = ((buf.st_size % unit) * 10) / unit; + nsh_output(vtbl, "%10" PRIu32 ".%" PRIu32 "%c", + integer_part, decimal_part, suffix); } else -#endif { nsh_output(vtbl, "%12" PRIdOFF, buf.st_size); } } + + /* Display modification time in long format */ + + if ((lsflags & LSFLAGS_LONG) != 0 && buf.st_mtime != 0) + { + struct tm tm; + char timebuf[20]; + time_t mtime = (time_t)buf.st_mtime; + + gmtime_r(&mtime, &tm); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M", &tm); + nsh_output(vtbl, " %s", timebuf); + } } /* Then provide the filename that is common to normal and verbose output */ @@ -680,13 +753,14 @@ static int fdinfo_callback(FAR struct nsh_vtbl_s *vtbl, if (ret < 0) { nsh_error(vtbl, g_fmtcmdfailed, "fdinfo", "asprintf", NSH_ERRNO); + return ret; } nsh_output(vtbl, "\npid:%s", entryp->d_name); ret = nsh_catfile(vtbl, "fdinfo", filepath); if (ret < 0) { - nsh_error(vtbl, g_fmtcmdfailed, "fdinfo", "nsh_catfaile", NSH_ERRNO); + nsh_error(vtbl, g_fmtcmdfailed, "fdinfo", "nsh_catfile", NSH_ERRNO); } free(filepath); @@ -798,6 +872,10 @@ int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) if (argc == 1) { char *buf = malloc(BUFSIZ); + if (buf == NULL) + { + return -ENOMEM; + } /* Dump from input */ @@ -828,6 +906,129 @@ int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } #endif +/**************************************************************************** + * Name: cmd_chmod + * + * Description: + * chmod + * + * Only numeric (octal) modes are supported. + * + ****************************************************************************/ + +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHMOD) +int cmd_chmod(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + FAR char *fullpath; + FAR char *endptr; + long mode; + int ret = ERROR; + + UNUSED(argc); + + mode = strtol(argv[1], &endptr, 8); + if (endptr == argv[1] || *endptr != '\0' || mode < 0 || mode > 0777) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + fullpath = nsh_getfullpath(vtbl, argv[2]); + if (fullpath != NULL) + { + ret = chmod(fullpath, (mode_t)mode); + if (ret < 0) + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "chmod", NSH_ERRNO); + } + + nsh_freefullpath(fullpath); + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: cmd_chown + * + * Description: + * chown [:] + * chown []: + * + * Only numeric uid/gid forms are supported. Empty uid or gid fields + * leave that side unchanged. + * + ****************************************************************************/ + +#if defined(CONFIG_FS_PERMISSION) && !defined(CONFIG_NSH_DISABLE_CHOWN) +int cmd_chown(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + FAR const char *spec = argv[1]; + FAR char *endptr; + FAR char *fullpath; + long value; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; + int ret = ERROR; + + UNUSED(argc); + + value = strtol(spec, &endptr, 10); + if (endptr != spec) + { + if (value < 0) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + uid = (uid_t)value; + } + + if (*endptr == ':') + { + FAR const char *gidstr = endptr + 1; + if (*gidstr != '\0') + { + value = strtol(gidstr, &endptr, 10); + if (*endptr != '\0' || value < 0) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + gid = (gid_t)value; + } + } + else if (*endptr != '\0') + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + if (uid == (uid_t)-1 && gid == (gid_t)-1) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + fullpath = nsh_getfullpath(vtbl, argv[2]); + if (fullpath != NULL) + { + ret = lchown(fullpath, uid, gid); + if (ret < 0) + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "chown", NSH_ERRNO); + } + + nsh_freefullpath(fullpath); + } + + return ret; +} +#endif + /**************************************************************************** * Name: cmd_dmesg ****************************************************************************/ @@ -950,8 +1151,8 @@ errout: * Name: cmd_losetup ****************************************************************************/ -#ifndef CONFIG_DISABLE_MOUNTPOINT -# if defined(CONFIG_DEV_LOOP) && !defined(CONFIG_NSH_DISABLE_LOSETUP) +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_DEV_LOOP) && \ + !defined(CONFIG_NSH_DISABLE_LOSETUP) int cmd_losetup(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *loopdev = NULL; @@ -1105,14 +1306,13 @@ errout_with_paths: return ret; } #endif -#endif /**************************************************************************** * Name: cmd_losmart ****************************************************************************/ -#ifndef CONFIG_DISABLE_MOUNTPOINT -# if defined(CONFIG_SMART_DEV_LOOP) && !defined(CONFIG_NSH_DISABLE_LOSMART) +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_SMART_DEV_LOOP) && \ + !defined(CONFIG_NSH_DISABLE_LOSMART) int cmd_losmart(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *loopdev = NULL; @@ -1277,14 +1477,13 @@ errout_with_paths: return ret; } #endif -#endif /**************************************************************************** * Name: cmd_lomtd ****************************************************************************/ -#ifndef CONFIG_DISABLE_MOUNTPOINT -# if defined(CONFIG_MTD_LOOP) && !defined(CONFIG_NSH_DISABLE_LOMTD) +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_MTD_LOOP) && \ + !defined(CONFIG_NSH_DISABLE_LOMTD) int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *loopdev = NULL; @@ -1295,6 +1494,9 @@ int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) int sectsize = -1; off_t offset = 0; bool badarg = false; +# ifndef CONFIG_MTD_CONFIG_NONE + int configdata = 0; +# endif int ret = ERROR; int option; int fd; @@ -1302,14 +1504,17 @@ int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) /* Get the lomtd options: Two forms are supported: * * lomtd -d - * lomtd [-o ] [-e erasesize] [-b sectsize] + * lomtd [-o ] [-e erasesize] [-b sectsize] [-c configdata] * * * NOTE that the -o and -r options are accepted with the -d option, but * will be ignored. */ - +# ifndef CONFIG_MTD_CONFIG_NONE + while ((option = getopt(argc, argv, "d:o:e:b:c:")) != ERROR) +# else while ((option = getopt(argc, argv, "d:o:e:b:")) != ERROR) +# endif { switch (option) { @@ -1330,6 +1535,12 @@ int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) sectsize = atoi(optarg); break; +# ifndef CONFIG_MTD_CONFIG_NONE + case 'c': + configdata = atoi(optarg); + break; +# endif + case '?': default: nsh_error(vtbl, g_fmtarginvalid, argv[0]); @@ -1405,11 +1616,14 @@ int cmd_lomtd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { /* Set up the loop device */ - setup.devname = loopdev; /* The loop block device to be created */ - setup.filename = filepath; /* The file or character device to use */ - setup.sectsize = sectsize; /* The sector size to use with the block device */ - setup.erasesize = erasesize; /* The sector size to use with the block device */ - setup.offset = offset; /* An offset that may be applied to the device */ + setup.devname = loopdev; /* The loop block device to be created */ + setup.filename = filepath; /* The file or character device to use */ + setup.sectsize = sectsize; /* The sector size to use with the block device */ + setup.erasesize = erasesize; /* The sector size to use with the block device */ + setup.offset = offset; /* An offset that may be applied to the device */ +# ifndef CONFIG_MTD_CONFIG_NONE + setup.configdata = configdata; /* Is a loop mtdconfig device */ +# endif ret = ioctl(fd, MTD_LOOPIOC_SETUP, (unsigned long)((uintptr_t)&setup)); @@ -1440,7 +1654,6 @@ errout_with_paths: return ret; } -# endif #endif /**************************************************************************** @@ -1549,11 +1762,10 @@ int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) lsflags |= LSFLAGS_SIZE; break; -#ifdef CONFIG_HAVE_FLOAT case 'h': lsflags |= LSFLAGS_HUMANREADBLE; break; -#endif + case '?': default: nsh_error(vtbl, g_fmtarginvalid, argv[0]); @@ -1643,12 +1855,244 @@ int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } #endif +/**************************************************************************** + * Name: du_print + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_DU +#define DU_FLAG_HUMANREADABLE (1 << 0) /* -h: human readable sizes */ +#define DU_FLAG_ALL (1 << 1) /* -a: list all files, not only dirs */ + +static void du_print(FAR struct nsh_vtbl_s *vtbl, FAR const char *path, + off_t bytes, unsigned int flags) +{ + off_t kblocks = (bytes + 1023) / 1024; + + if ((flags & DU_FLAG_HUMANREADABLE) != 0) + { + off_t unit; + char suffix; + + if (bytes >= GB) + { + unit = GB; + suffix = 'G'; + } + else if (bytes >= MB) + { + unit = MB; + suffix = 'M'; + } + else if (bytes >= KB) + { + unit = KB; + suffix = 'K'; + } + else + { + nsh_output(vtbl, "%" PRIdOFF "B\t%s\n", bytes, path); + return; + } + + /* Use integer arithmetic to avoid floating point */ + + nsh_output(vtbl, "%" PRIdOFF ".%" PRIdOFF "%c\t%s\n", + bytes / unit, (bytes % unit) * 10 / unit, suffix, path); + } + else + { + nsh_output(vtbl, "%" PRIdOFF "\t%s\n", kblocks, path); + } +} + +static off_t du_recursive(FAR struct nsh_vtbl_s *vtbl, FAR const char *path, + unsigned int flags, int printlimit, int depth) +{ + FAR struct dirent *entry; + FAR char *child; + struct stat st; + off_t total; + DIR *dp; + + if (lstat(path, &st) < 0) + { + nsh_error(vtbl, g_fmtcmdfailed, "du", "stat", NSH_ERRNO); + return 0; + } + + /* st_size not st_blocks: units differ (NuttX FS=st_blksize, hostfs=512) */ + + total = st.st_size; + + /* A file argument (depth 0) is always shown; nested files need -a. */ + + if (!S_ISDIR(st.st_mode)) + { + if (depth == 0 || + ((flags & DU_FLAG_ALL) != 0 && depth <= printlimit)) + { + du_print(vtbl, path, total, flags); + } + + return total; + } + + dp = opendir(path); + if (dp == NULL) + { + nsh_error(vtbl, g_fmtcmdfailed, "du", "opendir", NSH_ERRNO); + } + else + { + while ((entry = readdir(dp)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + { + continue; + } + + child = nsh_getdirpath(vtbl, path, entry->d_name); + if (child == NULL) + { + nsh_error(vtbl, g_fmtcmdfailed, "du", "nsh_getdirpath", + NSH_ERRNO); + continue; + } + + total += du_recursive(vtbl, child, flags, printlimit, depth + 1); + free(child); + } + + closedir(dp); + } + + /* Print this directory's cumulative total unless suppressed by -s/-d. */ + + if (depth <= printlimit) + { + du_print(vtbl, path, total, flags); + } + + return total; +} +#endif + +/**************************************************************************** + * Name: cmd_du + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_DU +int cmd_du(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + unsigned int flags = 0; + int printlimit = INT_MAX; + bool summary = false; + bool badarg = false; + int option; + int i; + int ret = OK; + + /* Get the du options */ + + while ((option = getopt(argc, argv, "hsad:")) != ERROR) + { + switch (option) + { + case 'h': + flags |= DU_FLAG_HUMANREADABLE; + break; + + case 's': + summary = true; + break; + + case 'a': + flags |= DU_FLAG_ALL; + break; + + case 'd': + printlimit = atoi(optarg); + break; + + case '?': + default: + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + badarg = true; + break; + } + } + + /* If a bad argument was encountered, + * then return without processing the command + */ + + if (badarg) + { + return ERROR; + } + + /* -s reports only each argument's own total (depth 0); -d N overrides -s + * when both are given. -s also suppresses the -a file listing. + */ + + if (summary) + { + if (printlimit == INT_MAX) + { + printlimit = 0; + } + + flags &= ~DU_FLAG_ALL; + } + + /* Walk each path argument (default: current directory). */ + + if (optind >= argc) + { +#ifndef CONFIG_DISABLE_ENVIRON + FAR char *fullpath = nsh_getfullpath(vtbl, nsh_getcwd(vtbl)); + + if (fullpath == NULL) + { + nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]); + return ERROR; + } + + du_recursive(vtbl, fullpath, flags, printlimit, 0); + nsh_freefullpath(fullpath); +#else + nsh_error(vtbl, g_fmtargrequired, argv[0]); + return ERROR; +#endif + } + else + { + for (i = optind; i < argc; i++) + { + FAR char *fullpath = nsh_getfullpath(vtbl, argv[i]); + + if (fullpath == NULL) + { + nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]); + ret = ERROR; + continue; + } + + du_recursive(vtbl, fullpath, flags, printlimit, 0); + nsh_freefullpath(fullpath); + } + } + + return ret; +} +#endif + /**************************************************************************** * Name: cmd_mkdir ****************************************************************************/ -#ifdef NSH_HAVE_DIROPTS -#ifndef CONFIG_NSH_DISABLE_MKDIR +#if defined(NSH_HAVE_DIROPTS) && !defined(CONFIG_NSH_DISABLE_MKDIR) int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *fullpath = NULL; @@ -1708,14 +2152,13 @@ int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_mkfatfs ****************************************************************************/ -#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FSUTILS_MKFATFS) -#ifndef CONFIG_NSH_DISABLE_MKFATFS +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FSUTILS_MKFATFS) && \ + !defined(CONFIG_NSH_DISABLE_MKFATFS) int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { struct fat_format_s fmt = FAT_FORMAT_INITIALIZER; @@ -1811,7 +2254,6 @@ int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_mkfifo @@ -1948,8 +2390,7 @@ errout_with_fmt: ****************************************************************************/ #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_SMARTFS) && \ - defined(CONFIG_FSUTILS_MKSMARTFS) -#ifndef CONFIG_NSH_DISABLE_MKSMARTFS + defined(CONFIG_FSUTILS_MKSMARTFS) && !defined(CONFIG_NSH_DISABLE_MKSMARTFS) int cmd_mksmartfs(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *fullpath = NULL; @@ -2034,14 +2475,12 @@ int cmd_mksmartfs(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_mv ****************************************************************************/ -#ifdef NSH_HAVE_DIROPTS -#ifndef CONFIG_NSH_DISABLE_MV +#if defined(NSH_HAVE_DIROPTS) && !defined(CONFIG_NSH_DISABLE_MV) int cmd_mv(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { UNUSED(argc); @@ -2084,7 +2523,6 @@ errout_with_oldpath: return ret; } #endif -#endif /**************************************************************************** * Name: cmd_readlink @@ -2127,9 +2565,7 @@ int cmd_readlink(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) * Name: cmd_rm ****************************************************************************/ -#ifdef NSH_HAVE_DIROPTS -#ifndef CONFIG_NSH_DISABLE_RM - +#if defined(NSH_HAVE_DIROPTS) && !defined(CONFIG_NSH_DISABLE_RM) static int unlink_recursive(FAR char *path, FAR struct stat *stat) { struct dirent *d; @@ -2259,14 +2695,12 @@ int cmd_rm(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_rmdir ****************************************************************************/ -#ifdef NSH_HAVE_DIROPTS -#ifndef CONFIG_NSH_DISABLE_RMDIR +#if defined(NSH_HAVE_DIROPTS) && !defined(CONFIG_NSH_DISABLE_RMDIR) int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { UNUSED(argc); @@ -2288,7 +2722,6 @@ int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_source @@ -2417,8 +2850,7 @@ errout: * Name: cmd_truncate ****************************************************************************/ -#ifndef CONFIG_DISABLE_MOUNTPOINT -#ifndef CONFIG_NSH_DISABLE_TRUNCATE +#if !defined(CONFIG_DISABLE_MOUNTPOINT) && !defined(CONFIG_NSH_DISABLE_TRUNCATE) int cmd_truncate(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { UNUSED(argc); @@ -2522,7 +2954,6 @@ int cmd_truncate(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ret; } #endif -#endif /**************************************************************************** * Name: cmd_fdinfo diff --git a/nshlib/nsh_fsutils.c b/nshlib/nsh_fsutils.c index 878c1d18e..7044d8d54 100644 --- a/nshlib/nsh_fsutils.c +++ b/nshlib/nsh_fsutils.c @@ -228,8 +228,14 @@ int nsh_catfile(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, NSH_ERRNO_OF(errcode)); } + /* Jump straight to cleanup. On a broken stdout (e.g. + * closed PTY master) we must not keep reading: the + * outer loop would otherwise drain /dev/log forever + * when cat is dumping it on dmesg. + */ + ret = ERROR; - break; + goto errout; } else { @@ -255,6 +261,7 @@ int nsh_catfile(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, /* Close the input file and return the result */ +errout: close(fd); free(buffer); return ret; diff --git a/nshlib/nsh_identity.c b/nshlib/nsh_identity.c new file mode 100644 index 000000000..6d9e6160f --- /dev/null +++ b/nshlib/nsh_identity.c @@ -0,0 +1,651 @@ +/**************************************************************************** + * apps/nshlib/nsh_identity.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_NSH_CLE +# include "system/cle.h" +#else +# include "system/readline.h" +#endif + +#ifdef CONFIG_NSH_LOGIN_PASSWD +# include "fsutils/passwd.h" +#endif + +#include "nshlib/nshlib.h" +#include "nsh.h" +#include "nsh_console.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_is_privileged + * + * Description: + * Return true if the caller has root privileges (effective UID or GID is + * zero). NuttX does not support supplementary groups; membership in the + * root group is indicated by a primary GID of zero. + * + ****************************************************************************/ + +static bool nsh_is_privileged(void) +{ + return geteuid() == 0 || getegid() == 0; +} + +#ifdef CONFIG_LIBC_PASSWD_FILE + +/**************************************************************************** + * Name: nsh_lookup_user + * + * Description: + * Look up a passwd entry by name using re-entrant libc helpers. + * + ****************************************************************************/ + +static int nsh_lookup_user(FAR const char *username, uid_t *uid, gid_t *gid) +{ + struct passwd pwd; + struct passwd *result; + char buf[CONFIG_LIBC_PASSWD_LINESIZE]; + int ret; + + ret = getpwnam_r(username, &pwd, buf, sizeof(buf), &result); + if (ret != 0) + { + return -ret; + } + + if (result == NULL) + { + return -ENOENT; + } + + *uid = result->pw_uid; + *gid = result->pw_gid; + return OK; +} +#endif /* CONFIG_LIBC_PASSWD_FILE */ + +#ifdef CONFIG_NSH_LOGIN + +/**************************************************************************** + * Name: nsh_read_password + ****************************************************************************/ + +static int nsh_read_password(FAR struct nsh_vtbl_s *vtbl, + FAR char *password, size_t buflen) +{ + FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; + struct termios cfg; + int ret; + + write(OUTFD(pstate), g_passwordprompt, strlen(g_passwordprompt)); + + if (isatty(INFD(pstate))) + { + if (tcgetattr(INFD(pstate), &cfg) == 0) + { + cfg.c_lflag &= ~ECHO; + tcsetattr(INFD(pstate), TCSANOW, &cfg); + } + } + + password[0] = '\0'; + pstate->cn_line[0] = '\0'; + +#ifdef CONFIG_NSH_CLE + ret = cle_fd(pstate->cn_line, "", LINE_MAX, + INFD(pstate), OUTFD(pstate)); +#else + ret = readline_fd(pstate->cn_line, LINE_MAX, INFD(pstate), -1); +#endif + + if (isatty(INFD(pstate))) + { + if (tcgetattr(INFD(pstate), &cfg) == 0) + { + cfg.c_lflag |= ECHO; + tcsetattr(INFD(pstate), TCSANOW, &cfg); + } + } + + if (ret > 0) + { + strlcpy(password, pstate->cn_line, buflen); + write(OUTFD(pstate), "\n", 1); + return OK; + } + + return ERROR; +} + +/**************************************************************************** + * Name: nsh_verify_credentials + ****************************************************************************/ + +static bool nsh_verify_credentials(FAR const char *username, + FAR const char *password) +{ +#ifdef CONFIG_NSH_LOGIN_PASSWD + return PASSWORD_VERIFY_MATCH(passwd_verify(username, password)); +#elif defined(CONFIG_NSH_LOGIN_PLATFORM) + return PASSWORD_VERIFY_MATCH(platform_user_verify(username, password)); +#else + UNUSED(username); + UNUSED(password); + return false; +#endif +} +#endif /* CONFIG_NSH_LOGIN */ + +/**************************************************************************** + * Name: nsh_switch_credentials + * + * Description: + * Switch the session to the given UID/GID. NSH starts with real UID/GID + * zero; file permission checks use the effective identity. When the real + * UID is still zero, only the effective UID/GID are changed so that a + * later 'su' can regain root via seteuid(0) after password verification. + * + ****************************************************************************/ + +static int nsh_switch_credentials(uid_t uid, gid_t gid) +{ + if (getuid() == 0) + { + if (geteuid() != 0 || getegid() != 0) + { + if (seteuid(0) != 0 || setegid(0) != 0) + { + return -errno; + } + } + + if (seteuid(uid) != 0 || setegid(gid) != 0) + { + return -errno; + } + + return OK; + } + + if (setuid(uid) != 0 || setgid(gid) != 0) + { + return -errno; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nsh_setuser_identity + * + * Description: + * Look up 'username' in the passwd database and set the calling task's + * session identity. When NSH still has real UID zero, only the effective + * UID/GID are updated so that 'su' can switch users later. + * + * Input Parameters: + * username - Login name to assume + * + * Returned Value: + * OK on success; negated errno on failure. + * + ****************************************************************************/ + +int nsh_setuser_identity(FAR const char *username) +{ +#ifdef CONFIG_LIBC_PASSWD_FILE + FAR struct passwd *pwd; + uid_t uid; + gid_t gid; + + /* getpwnam() uses libc static storage; NSH is single-threaded here. */ + + pwd = getpwnam(username); + if (pwd == NULL) + { + return -ENOENT; + } + + uid = pwd->pw_uid; + gid = pwd->pw_gid; +#else + uid_t uid; + gid_t gid; + + if (strcmp(username, "root") != 0) + { + return -ENOENT; + } + + uid = 0; + gid = 0; +#endif + + return nsh_switch_credentials(uid, gid); +} + +/**************************************************************************** + * Name: cmd_su + * + * Description: + * su [username] + * + * Switch the NSH session to the credentials of 'username'. Callers with + * root privileges (effective UID or GID zero) may switch to any user + * without a password. Other users may switch to their own identity + * without a password, or to another user after entering that user's + * password. + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_SU +int cmd_su(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + char username_buf[48]; + FAR const char *username; + uid_t target_uid; + gid_t target_gid; + int ret; + bool need_password = false; + + if (argc > 2) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + if (argc == 1) + { + username = "root"; + } + else + { + /* argv[1] points into cn_line; copy it before readline reuses that + * buffer for the password prompt. + */ + + strlcpy(username_buf, argv[1], sizeof(username_buf)); + username = username_buf; + } + +#ifdef CONFIG_LIBC_PASSWD_FILE + ret = nsh_lookup_user(username, &target_uid, &target_gid); + if (ret == -ENOENT) + { + nsh_error(vtbl, "su: Unknown user '%s'\n", username); + return ERROR; + } + else if (ret < 0) + { + nsh_error(vtbl, "su: Permission denied\n"); + return ERROR; + } +#else + if (strcmp(username, "root") != 0) + { + nsh_error(vtbl, "su: Unknown user '%s'\n", username); + return ERROR; + } + + target_uid = 0; + target_gid = 0; +#endif + + if (!nsh_is_privileged()) + { +#ifdef CONFIG_LIBC_PASSWD_FILE + if (target_uid != geteuid()) + { + need_password = true; + } +#else + need_password = true; +#endif + } + +#ifdef CONFIG_NSH_LOGIN + if (need_password) + { + char password[LINE_MAX]; + + ret = nsh_read_password(vtbl, password, sizeof(password)); + if (ret < 0) + { + return ERROR; + } + + if (!nsh_verify_credentials(username, password)) + { + nsh_error(vtbl, "su: Authentication failure\n"); + return ERROR; + } + } +#else + if (need_password) + { + nsh_error(vtbl, "su: Permission denied\n"); + return ERROR; + } +#endif + + ret = nsh_switch_credentials(target_uid, target_gid); + if (ret < 0) + { + nsh_error(vtbl, "su: Permission denied\n"); + return ERROR; + } + + nsh_update_prompt(); + return OK; +} +#endif + +#ifndef CONFIG_NSH_DISABLE_ID + +/**************************************************************************** + * Name: nsh_id_format_uid + * + * Description: + * Format a UID token in Linux id(1) style, e.g. "uid=0(root)". + * + ****************************************************************************/ + +static void nsh_id_format_uid(FAR char *buf, size_t buflen, + FAR const char *tag, uid_t uid) +{ +#ifdef CONFIG_LIBC_PASSWD_FILE + FAR struct passwd *pwd = getpwuid(uid); + + if (pwd != NULL && pwd->pw_name != NULL) + { + snprintf(buf, buflen, "%s=%d(%s)", tag, uid, pwd->pw_name); + return; + } +#endif + + if (uid == 0) + { + snprintf(buf, buflen, "%s=%d(root)", tag, uid); + } + else + { + snprintf(buf, buflen, "%s=%d", tag, uid); + } +} + +/**************************************************************************** + * Name: nsh_id_format_gid + * + * Description: + * Format a GID token in Linux id(1) style, e.g. "gid=0(root)". + * + ****************************************************************************/ + +static void nsh_id_format_gid(FAR char *buf, size_t buflen, + FAR const char *tag, gid_t gid) +{ +#ifdef CONFIG_LIBC_GROUP_FILE + FAR struct group *grp = getgrgid(gid); + + if (grp != NULL && grp->gr_name != NULL) + { + snprintf(buf, buflen, "%s=%d(%s)", tag, gid, grp->gr_name); + return; + } +#endif + + if (gid == 0) + { + snprintf(buf, buflen, "%s=%d(root)", tag, gid); + } + else + { + snprintf(buf, buflen, "%s=%d", tag, gid); + } +} + +static void nsh_id_append(FAR char *line, size_t linelen, + FAR const char *token) +{ + size_t len = strlen(line); + + if (len > 0) + { + strlcat(line, " ", linelen); + } + + strlcat(line, token, linelen); +} + +#ifdef CONFIG_LIBC_PASSWD_FILE +/**************************************************************************** + * Name: nsh_id_format_group_value + * + * Description: + * Format a bare group value for a groups= list, e.g. "0(root)". + * + ****************************************************************************/ + +static void nsh_id_format_group_value(FAR char *buf, size_t buflen, + gid_t gid) +{ +#ifdef CONFIG_LIBC_GROUP_FILE + FAR struct group *grp = getgrgid(gid); + + if (grp != NULL && grp->gr_name != NULL) + { + snprintf(buf, buflen, "%d(%s)", gid, grp->gr_name); + return; + } +#endif + + if (gid == 0) + { + snprintf(buf, buflen, "0(root)"); + } + else + { + snprintf(buf, buflen, "%d", gid); + } +} + +/**************************************************************************** + * Name: nsh_id_append_groups + * + * Description: + * Append a Linux-style groups= list to the id output line. + * + ****************************************************************************/ + +static void nsh_id_append_groups(FAR char *line, size_t linelen) +{ + gid_t grouplist[8]; + char token[48]; + int ngroups; + int i; + + ngroups = getgroups(sizeof(grouplist) / sizeof(grouplist[0]), grouplist); + if (ngroups <= 0) + { + return; + } + + strlcat(line, " groups=", linelen); + + for (i = 0; i < ngroups; i++) + { + if (i > 0) + { + strlcat(line, ",", linelen); + } + + nsh_id_format_group_value(token, sizeof(token), grouplist[i]); + strlcat(line, token, linelen); + } +} +#endif /* CONFIG_LIBC_PASSWD_FILE */ + +/**************************************************************************** + * Name: cmd_id + * + * Description: + * Print real, effective, and saved-set UID/GID for the current session in + * Linux id(1) style with optional user and group names. + * + ****************************************************************************/ + +int cmd_id(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + uid_t ruid; + uid_t euid; + uid_t suid; + gid_t rgid; + gid_t egid; + gid_t sgid; + char line[256]; + char token[48]; + + if (argc != 1) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + if (getresuid(&ruid, &euid, &suid) != 0) + { + nsh_error(vtbl, "id: getresuid() failed: %d\n", errno); + return ERROR; + } + + if (getresgid(&rgid, &egid, &sgid) != 0) + { + nsh_error(vtbl, "id: getresgid() failed: %d\n", errno); + return ERROR; + } + + line[0] = '\0'; + + nsh_id_format_uid(token, sizeof(token), "uid", ruid); + nsh_id_append(line, sizeof(line), token); + + if (euid != ruid) + { + nsh_id_format_uid(token, sizeof(token), "euid", euid); + nsh_id_append(line, sizeof(line), token); + } + + if (suid != ruid && suid != euid) + { + nsh_id_format_uid(token, sizeof(token), "suid", suid); + nsh_id_append(line, sizeof(line), token); + } + + nsh_id_format_gid(token, sizeof(token), "gid", rgid); + nsh_id_append(line, sizeof(line), token); + + if (egid != rgid) + { + nsh_id_format_gid(token, sizeof(token), "egid", egid); + nsh_id_append(line, sizeof(line), token); + } + + if (sgid != rgid && sgid != egid) + { + nsh_id_format_gid(token, sizeof(token), "sgid", sgid); + nsh_id_append(line, sizeof(line), token); + } + +#ifdef CONFIG_LIBC_PASSWD_FILE + nsh_id_append_groups(line, sizeof(line)); +#endif + nsh_output(vtbl, "%s\n", line); + return OK; +} +#endif /* CONFIG_NSH_DISABLE_ID */ + +/**************************************************************************** + * Name: cmd_whoami + * + * Description: + * Print the user name for the effective UID. + * + ****************************************************************************/ + +#ifndef CONFIG_NSH_DISABLE_WHOAMI +int cmd_whoami(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + uid_t euid; + + if (argc != 1) + { + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + + euid = geteuid(); + +#ifdef CONFIG_LIBC_PASSWD_FILE + FAR struct passwd *pwd; + + pwd = getpwuid(euid); + if (pwd != NULL && pwd->pw_name != NULL) + { + nsh_output(vtbl, "%s\n", pwd->pw_name); + return OK; + } +#endif + + if (euid == 0) + { + nsh_output(vtbl, "root\n"); + } + else + { + nsh_output(vtbl, "%d\n", euid); + } + + return OK; +} +#endif diff --git a/nshlib/nsh_init.c b/nshlib/nsh_init.c index 4846ff15b..49c1488e6 100644 --- a/nshlib/nsh_init.c +++ b/nshlib/nsh_init.c @@ -143,12 +143,6 @@ void nsh_initialize(void) boardctl(BOARDIOC_APP_SYMTAB, (uintptr_t)&symdesc); #endif -#ifdef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); -#endif - #if defined(CONFIG_ETC_ROMFS) && !defined(CONFIG_NSH_DISABLESCRIPT) pstate = nsh_newconsole(false); @@ -163,12 +157,6 @@ void nsh_initialize(void) netinit_bringup(); #endif -#if defined(CONFIG_NSH_ARCHINIT) && defined(CONFIG_BOARDCTL_FINALINIT) - /* Perform architecture-specific final-initialization (if configured) */ - - boardctl(BOARDIOC_FINALINIT, 0); -#endif - #if defined(CONFIG_ETC_ROMFS) && !defined(CONFIG_NSH_DISABLESCRIPT) /* Execute the start-up script */ @@ -187,4 +175,16 @@ void nsh_initialize(void) nsh_telnetstart(AF_UNSPEC); #endif + +#if defined(CONFIG_NSH_DROPBEAR) && \ + !defined(CONFIG_NSH_DISABLE_DROPBEARSTART) && \ + !defined(CONFIG_NETINIT_NETLOCAL) + /* If Dropbear is selected as an SSH front-end, then start the daemon + * UNLESS network initialization is deferred via CONFIG_NETINIT_NETLOCAL. + * In that case, Dropbear must be started manually with the dropbear + * command after the network has been initialized. + */ + + nsh_dropbearstart(); +#endif } diff --git a/nshlib/nsh_login.c b/nshlib/nsh_login.c index cde17f55c..228a66397 100644 --- a/nshlib/nsh_login.c +++ b/nshlib/nsh_login.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "fsutils/passwd.h" #ifdef CONFIG_NSH_CLE @@ -242,14 +243,21 @@ int nsh_login(FAR struct console_stdio_s *pstate) #endif if (PASSWORD_VERIFY_MATCH(ret)) -#elif defined(CONFIG_NSH_LOGIN_FIXED) - if (strcmp(password, CONFIG_NSH_LOGIN_PASSWORD) == 0 && - strcmp(username, CONFIG_NSH_LOGIN_USERNAME) == 0) #else # error No user verification method selected #endif { write(OUTFD(pstate), g_loginsuccess, strlen(g_loginsuccess)); + +#if defined(CONFIG_NSH_LOGIN_SETUID) && defined(CONFIG_SCHED_USER_IDENTITY) + if (nsh_setuser_identity(username) < 0) + { + write(OUTFD(pstate), g_badidentity, strlen(g_badidentity)); + return -1; + } + + nsh_update_prompt(); +#endif return OK; } else diff --git a/nshlib/nsh_mntcmds.c b/nshlib/nsh_mntcmds.c index 62579d8b7..a58d6ef9c 100644 --- a/nshlib/nsh_mntcmds.c +++ b/nshlib/nsh_mntcmds.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -363,16 +363,37 @@ int cmd_nfsmount(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #if !defined(CONFIG_DISABLE_MOUNTPOINT) && !defined(CONFIG_NSH_DISABLE_UMOUNT) int cmd_umount(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { - UNUSED(argc); - - FAR char *fullpath = nsh_getfullpath(vtbl, argv[1]); + unsigned int flags = 0; + FAR char *fullpath; int ret = ERROR; + int option; + while ((option = getopt(argc, argv, "f")) != ERROR) + { + switch (option) + { + case 'f': + flags |= MNT_FORCE; + break; + + default: + nsh_error(vtbl, g_fmtarginvalid, argv[0]); + return ERROR; + } + } + + if (optind >= argc) + { + nsh_error(vtbl, g_fmtargrequired, argv[0]); + return ret; + } + + fullpath = nsh_getfullpath(vtbl, argv[optind]); if (fullpath) { /* Perform the umount */ - ret = umount(fullpath); + ret = umount2(fullpath, flags); if (ret < 0) { nsh_error(vtbl, g_fmtcmdfailed, argv[0], "umount", NSH_ERRNO); diff --git a/nshlib/nsh_netcmds.c b/nshlib/nsh_netcmds.c index 791e1c922..8e25c69a2 100644 --- a/nshlib/nsh_netcmds.c +++ b/nshlib/nsh_netcmds.c @@ -45,7 +45,7 @@ #include /* Needed for basename */ #include #include -#include +#include #include #if defined(CONFIG_LIBC_NETDB) && !defined(CONFIG_NSH_DISABLE_NSLOOKUP) @@ -142,6 +142,15 @@ struct tftpc_args_s }; #endif +#ifdef CONFIG_NET_ARP +typedef enum +{ + ARP_DEFAULT, /* Did not set arp configure in the command */ + ARP_ENABLE, /* Clean the NOARP flag for the interface to enable the arp learning function */ + ARP_DISABLE /* Set the NOARP flag for the interface to disable the arp learning function */ +} arp_cfg_e; +#endif + typedef int (*nsh_netdev_callback_t)(FAR struct nsh_vtbl_s *vtbl, FAR char *devname); @@ -575,6 +584,9 @@ int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #endif bool missingarg = true; bool badarg = false; +#ifdef CONFIG_NET_ARP + arp_cfg_e arpflag = ARP_DEFAULT; +#endif #ifdef HAVE_HWADDR mac_addr_t macaddr; #endif @@ -742,6 +754,20 @@ int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) badarg = true; } } +#ifdef CONFIG_NET_ARP + else if (!strcmp(tmp, "arp")) + { + /* Enable arp function on interface */ + + arpflag = ARP_ENABLE; + } + else if (!strcmp(tmp, "-arp")) + { + /* Disable arp function on interface */ + + arpflag = ARP_DISABLE; + } +#endif else if (hostip == NULL && i <= 4) { /* Let first non-option be host ip, to support inet/inet6 @@ -1044,6 +1070,17 @@ int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } #endif +#ifdef CONFIG_NET_ARP + if (arpflag == ARP_ENABLE) + { + netlib_ifarp(ifname); + } + else if (arpflag == ARP_DISABLE) + { + netlib_ifnoarp(ifname); + } +#endif + #if !defined(CONFIG_NET_IPv4) && !defined(CONFIG_NET_IPv6) UNUSED(hostip); UNUSED(mask); @@ -1060,6 +1097,60 @@ int cmd_ifconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } #endif +/**************************************************************************** + * Name: cmd_vconfig + ****************************************************************************/ + +#if defined(CONFIG_NET_VLAN) && !defined(CONFIG_NSH_DISABLE_VCONFIG) +int cmd_vconfig(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) +{ + DEBUGASSERT(argc >= 2); + + if (!strcmp(argv[1], "add")) + { + int prio = 0; + + if (argc != 4 && argc != 5) + { + nsh_error(vtbl, g_fmtargrequired, argv[0]); + return ERROR; + } + + if (argc == 5) + { + prio = atoi(argv[4]); + } + + if (netlib_add_vlan(argv[2], atoi(argv[3]), prio) < 0) + { + perror("Failed to add VLAN"); + return ERROR; + } + } + else if (!strcmp(argv[1], "rem") || !strcmp(argv[1], "del")) + { + if (argc != 3) + { + nsh_error(vtbl, g_fmtargrequired, argv[0]); + return ERROR; + } + + if (netlib_del_vlan(argv[2]) < 0) + { + perror("Failed to remove VLAN"); + return ERROR; + } + } + else + { + nsh_error(vtbl, g_fmtarginvalid, argv[1]); + return ERROR; + } + + return OK; +} +#endif + /**************************************************************************** * Name: cmd_nslookup ****************************************************************************/ @@ -1219,7 +1310,7 @@ int cmd_arp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) FAR uint8_t *ptr; if (ifname != NULL && - strcmp(ifname, (FAR char *)arptab[i].arp_dev) != 0) + strcmp(ifname, arptab[i].arp_dev) != 0) { continue; } @@ -1464,6 +1555,12 @@ int cmd_wget(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) if (localfile == NULL) { allocfile = strdup(url); + if (allocfile == NULL) + { + fmt = g_fmtcmdoutofmemory; + goto errout; + } + localfile = basename(allocfile); } diff --git a/nshlib/nsh_parse.c b/nshlib/nsh_parse.c index 9b7b371af..9de45b887 100644 --- a/nshlib/nsh_parse.c +++ b/nshlib/nsh_parse.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include @@ -264,6 +264,12 @@ static const char g_redirect_out2[] = ">>"; static const size_t g_redirect_out2_len = sizeof(g_redirect_out2) - 1; static const char g_redirect_in1[] = "<"; static const size_t g_redirect_in1_len = sizeof(g_redirect_in1) - 1; +static const char g_redirect_err1[] = "2>"; +static const size_t g_redirect_err1_len = sizeof(g_redirect_err1) - 1; +static const char g_redirect_err2[] = "2>>"; +static const size_t g_redirect_err2_len = sizeof(g_redirect_err2) - 1; +static const char g_redirect_err3[] = "2>&1"; +static const size_t g_redirect_err3_len = sizeof(g_redirect_err3) - 1; #ifdef CONFIG_NSH_PIPELINE static const char g_pipeline1[] = "|"; static const size_t g_pipeline1_len = sizeof(g_pipeline1) - 1; @@ -310,6 +316,7 @@ const char g_userprompt[] = "login: "; const char g_passwordprompt[] = "password: "; const char g_loginsuccess[] = "\nUser Logged-in!\n"; const char g_badcredentials[] = "\nInvalid username or password\n"; +const char g_badidentity[] = "\nUnknown user identity\n"; const char g_loginfailure[] = "Login failed!\n"; #endif @@ -503,6 +510,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, FAR const struct nsh_param_s *param) { int fd_out = STDOUT_FILENO; + int fd_err = STDERR_FILENO; int fd_in = STDIN_FILENO; int ret; @@ -703,11 +711,36 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, } } - /* Handle redirection of stdin/stdout file descriptor */ + /* Redirected error output? */ - if (vtbl->np.np_redir_out || vtbl->np.np_redir_in) + if (vtbl->np.np_redir_err) { - nsh_redirect(vtbl, fd_in, fd_out, save); + if (param->file_err) + { + /* 2> file: Open the redirection file for stderr */ + + fd_err = open(param->file_err, param->oflags_err, 0666); + if (fd_err < 0) + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", + NSH_ERRNO); + return nsh_saveresult(vtbl, true); + } + } + else + { + /* 2>&1: redirect stderr to current stdout fd */ + + fd_err = fd_out; + } + } + + /* Handle redirection of stdin/stdout/stderr file descriptor */ + + if (vtbl->np.np_redir_out || vtbl->np.np_redir_in || + vtbl->np.np_redir_err) + { + nsh_redirect(vtbl, fd_in, fd_out, fd_err, save); } /* Then execute the command in "foreground" -- i.e., while the user @@ -723,9 +756,12 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, * file descriptor. */ - if (vtbl->np.np_redir_out || vtbl->np.np_redir_in) + if (vtbl->np.np_redir_out || vtbl->np.np_redir_in || + vtbl->np.np_redir_err) { nsh_undirect(vtbl, save); + fd_out = -1; + fd_in = -1; } } @@ -883,10 +919,13 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, { .fd_in = -1, .fd_out = -1, + .fd_err = -1, .oflags_in = 0, .oflags_out = O_WRONLY | O_CREAT | O_TRUNC, + .oflags_err = O_WRONLY | O_CREAT | O_TRUNC, .file_in = NULL, - .file_out = NULL + .file_out = NULL, + .file_err = NULL }; FAR char *tmpfile; @@ -2445,10 +2484,13 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) { .fd_in = -1, .fd_out = -1, + .fd_err = -1, .oflags_in = 0, .oflags_out = 0, + .oflags_err = 0, .file_in = NULL, - .file_out = NULL + .file_out = NULL, + .file_err = NULL }; #ifdef CONFIG_NSH_PIPELINE @@ -2467,6 +2509,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) int ret; bool redirect_out_save = false; bool redirect_in_save = false; + bool redirect_err_save = false; #ifdef CONFIG_NSH_PIPELINE bool bg_save = false; #endif @@ -2490,6 +2533,7 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) vtbl->np.np_redir_out = false; vtbl->np.np_redir_in = false; + vtbl->np.np_redir_err = false; /* Parse out the command at the beginning of the line */ @@ -2690,6 +2734,60 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) param.oflags_in = O_RDONLY; param.file_in = nsh_getfullpath(vtbl, arg); } + else if (!strncmp(argv[argc], g_redirect_err3, g_redirect_err3_len)) + { + redirect_err_save = vtbl->np.np_redir_err; + vtbl->np.np_redir_err = true; + param.fd_err = STDOUT_FILENO; + } + else if (!strncmp(argv[argc], g_redirect_err2, g_redirect_err2_len)) + { + FAR char *arg; + if (argv[argc][g_redirect_err2_len]) + { + arg = &argv[argc][g_redirect_err2_len]; + } + else + { + arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar); + } + + if (!arg) + { + nsh_error(vtbl, g_fmtarginvalid, cmd); + ret = ERROR; + goto dynlist_free; + } + + redirect_err_save = vtbl->np.np_redir_err; + vtbl->np.np_redir_err = true; + param.oflags_err = O_WRONLY | O_CREAT | O_APPEND; + param.file_err = nsh_getfullpath(vtbl, arg); + } + else if (!strncmp(argv[argc], g_redirect_err1, g_redirect_err1_len)) + { + FAR char *arg; + if (argv[argc][g_redirect_err1_len]) + { + arg = &argv[argc][g_redirect_err1_len]; + } + else + { + arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar); + } + + if (!arg) + { + nsh_error(vtbl, g_fmtarginvalid, cmd); + ret = ERROR; + goto dynlist_free; + } + + redirect_err_save = vtbl->np.np_redir_err; + vtbl->np.np_redir_err = true; + param.oflags_err = O_WRONLY | O_CREAT | O_TRUNC; + param.file_err = nsh_getfullpath(vtbl, arg); + } #ifdef CONFIG_NSH_PIPELINE else if (!strncmp(argv[argc], g_pipeline1, g_pipeline1_len)) { @@ -2846,6 +2944,18 @@ dynlist_free: } #endif + /* Free the redirected error file path and restore state */ + + if (param.file_err) + { + nsh_freefullpath((char *)param.file_err); + } + + if (vtbl->np.np_redir_err) + { + vtbl->np.np_redir_err = redirect_err_save; + } + NSH_ALIASLIST_FREE(vtbl, &alist); NSH_MEMLIST_FREE(&memlist); #ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP diff --git a/nshlib/nsh_passwdcmds.c b/nshlib/nsh_passwdcmds.c index 5c7f86ed2..f37cbaf2a 100644 --- a/nshlib/nsh_passwdcmds.c +++ b/nshlib/nsh_passwdcmds.c @@ -53,8 +53,19 @@ int cmd_useradd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) ret = passwd_adduser(argv[1], argv[2]); if (ret < 0) { - nsh_error(vtbl, g_fmtcmdfailed, argv[0], "passwd_adduser", - NSH_ERRNO_OF(-ret)); + if (ret == -EINVAL) + { + nsh_error(vtbl, + "%s: password does not meet security policy " + "(min 8 chars, upper, lower, digit, special)\n", + argv[0]); + } + else + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "passwd_adduser", + NSH_ERRNO_OF(-ret)); + } + return ERROR; } @@ -99,8 +110,19 @@ int cmd_passwd(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) ret = passwd_update(argv[1], argv[2]); if (ret < 0) { - nsh_error(vtbl, g_fmtcmdfailed, argv[0], "passwd_update", - NSH_ERRNO_OF(-ret)); + if (ret == -EINVAL) + { + nsh_error(vtbl, + "%s: password does not meet security policy " + "(min 8 chars, upper, lower, digit, special)\n", + argv[0]); + } + else + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "passwd_update", + NSH_ERRNO_OF(-ret)); + } + return ERROR; } diff --git a/nshlib/nsh_proccmds.c b/nshlib/nsh_proccmds.c index fe9810564..8be9fe2d2 100644 --- a/nshlib/nsh_proccmds.c +++ b/nshlib/nsh_proccmds.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -873,6 +875,7 @@ static int top_cmpcpuload(FAR const void *item1, FAR const void *item2) } } +#ifdef CONFIG_ENABLE_ALL_SIGNALS /**************************************************************************** * Name: top_exit ****************************************************************************/ @@ -881,6 +884,108 @@ static void top_exit(int signo, FAR siginfo_t *siginfo, FAR void *context) { *(FAR bool *)siginfo->si_user = true; } +#endif /* CONFIG_ENABLE_ALL_SIGNALS */ + +/**************************************************************************** + * Name: top_state_is_running + ****************************************************************************/ + +static bool top_state_is_running(FAR const char *state) +{ + return strncmp(state, "Running", 7) == 0 || + strncmp(state, "Ready", 5) == 0 || + strncmp(state, "Pending", 7) == 0; +} + +/**************************************************************************** + * Name: top_summary + * + * Description: + * Print a Linux-top-like summary header: uptime, task counts, CPU + * busy/idle and memory usage. Lines end with an erase-to-eol so the + * screen can be refreshed in place. + * + ****************************************************************************/ + +static void top_summary(FAR struct nsh_vtbl_s *vtbl, + FAR struct nsh_topstatus_s *topstatus) +{ + struct sysinfo info; + unsigned long uptime; + unsigned long busy; + size_t running = 0; + size_t i; + + for (i = 0; i < topstatus->index; i++) + { + if (top_state_is_running(topstatus->status[i]->td_state)) + { + running++; + } + } + + sysinfo(&info); + uptime = info.uptime; + + if (uptime >= 86400) + { + nsh_output(vtbl, VT100_STR_CLEAREOL + "top - up %lud %02lu:%02lu:%02lu\n", + uptime / 86400, (uptime % 86400) / 3600, + (uptime % 3600) / 60, uptime % 60); + } + else + { + nsh_output(vtbl, VT100_STR_CLEAREOL "top - up %02lu:%02lu:%02lu\n", + uptime / 3600, (uptime % 3600) / 60, uptime % 60); + } + + nsh_output(vtbl, VT100_STR_CLEAREOL + "Tasks: %zu total, %zu running, %zu sleeping\n", + topstatus->index, running, topstatus->index - running); + + busy = (info.loads[0] * 1000) >> SI_LOAD_SHIFT; + nsh_output(vtbl, VT100_STR_CLEAREOL + "%%Cpu(s): %2lu.%lu busy, %2lu.%lu idle\n", + busy / 10, busy % 10, + (1000 - busy) / 10, (1000 - busy) % 10); + + nsh_output(vtbl, VT100_STR_CLEAREOL + "Mem : %8lu total, %8lu used, %8lu free\n", + info.totalram, info.totalram - info.freeram, info.freeram); + + nsh_output(vtbl, VT100_STR_CLEAREOL "\n"); +} + +/**************************************************************************** + * Name: top_wait_key + * + * Description: + * Sleep for the update interval, returning true if the user asked to + * quit (q, ESC or Ctrl-C read from stdin). + * + ****************************************************************************/ + +static bool top_wait_key(int delay) +{ + struct pollfd fds; + + fds.fd = STDIN_FILENO; + fds.events = POLLIN; + + if (poll(&fds, 1, delay * 1000) > 0) + { + char c; + + if (read(STDIN_FILENO, &c, 1) == 1 && + (c == 'q' || c == 0x1b || c == 0x03)) + { + return true; + } + } + + return false; +} #endif @@ -993,7 +1098,8 @@ int cmd_pidof(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) * Name: cmd_kill ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLE_KILL +#if !defined(CONFIG_NSH_DISABLE_KILL) && \ + !defined(CONFIG_DISABLE_ALL_SIGNALS) int cmd_kill(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR char *ptr; @@ -1096,7 +1202,8 @@ invalid_arg: * Name: cmd_pkill ****************************************************************************/ -#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PKILL) +#if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_NSH_DISABLE_PKILL) && \ + !defined(CONFIG_DISABLE_ALL_SIGNALS) int cmd_pkill(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { FAR const char *name; @@ -1197,7 +1304,8 @@ invalid_arg: * Name: cmd_sleep ****************************************************************************/ -#ifndef CONFIG_NSH_DISABLE_SLEEP +#if !defined(CONFIG_NSH_DISABLE_SLEEP) && \ + !defined(CONFIG_DISABLE_ALL_SIGNALS) int cmd_sleep(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { UNUSED(argc); @@ -1220,8 +1328,8 @@ int cmd_sleep(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) /**************************************************************************** * Name: cmd_usleep ****************************************************************************/ - -#ifndef CONFIG_NSH_DISABLE_USLEEP +#if !defined(CONFIG_NSH_DISABLE_USLEEP) && \ + !defined(CONFIG_DISABLE_ALL_SIGNALS) int cmd_usleep(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { UNUSED(argc); @@ -1357,7 +1465,9 @@ int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) FAR char *pidlist = NULL; size_t num = SIZE_MAX; size_t i; +#ifdef CONFIG_ENABLE_ALL_SIGNALS struct sigaction act; +#endif bool quit = false; int delay = 3; int ret = 0; @@ -1393,6 +1503,7 @@ int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } } +#ifdef CONFIG_ENABLE_ALL_SIGNALS act.sa_user = &quit; act.sa_sigaction = top_exit; sigemptyset(&act.sa_mask); @@ -1402,17 +1513,19 @@ int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) nsh_error(vtbl, g_fmtcmdfailed, "top", "sigaction", NSH_ERRNO); return ERROR; } +#endif if (vtbl->isctty) { tc = nsh_ioctl(vtbl, TIOCSCTTY, getpid()); } + nsh_output(vtbl, VT100_STR_CLEARSCREEN); + while (!quit) { topstatus.index = 0; - nsh_output(vtbl, "\033[2J\033[1;1H"); - ps_title(vtbl, topstatus.heap); + nsh_output(vtbl, VT100_STR_CURSORHOME); if (pidlist) { @@ -1446,17 +1559,29 @@ int cmd_top(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) qsort(topstatus.status, topstatus.index, sizeof(topstatus.status[0]), top_cmpcpuload); + top_summary(vtbl, &topstatus); + + nsh_output(vtbl, VT100_STR_CLEAREOL VT100_STR_BOLD); + ps_title(vtbl, topstatus.heap); + nsh_output(vtbl, VT100_STR_MODESOFF); + for (i = 0; i < MIN(topstatus.index, num); i++) { + nsh_output(vtbl, VT100_STR_CLEAREOL); ps_output(vtbl, topstatus.heap, topstatus.status[i]); } if (vtbl->isctty && tc == 0) { - nsh_output(vtbl, "use Ctrl+c' to quit\n"); + nsh_output(vtbl, VT100_STR_CLEAREOL "use 'q' or Ctrl+c to quit\n"); } - sleep(delay); + nsh_output(vtbl, VT100_STR_CLEAREOS); + + if (top_wait_key(delay)) + { + break; + } } if (topstatus.status != NULL) diff --git a/nshlib/nsh_prompt.c b/nshlib/nsh_prompt.c index 4dfe24c3c..04f110a83 100644 --- a/nshlib/nsh_prompt.c +++ b/nshlib/nsh_prompt.c @@ -30,6 +30,10 @@ #include #include +#ifdef CONFIG_SCHED_USER_IDENTITY +# include +#endif + #include "nsh.h" /**************************************************************************** @@ -59,6 +63,10 @@ static char g_nshprompt[CONFIG_NSH_PROMPT_MAX] = CONFIG_NSH_PROMPT_STRING; * - non-empty NSH_PROMPT_STRING * - non-empty HOSTNAME and suffix * + * When SCHED_USER_IDENTITY is enabled and NSH_PROMPT_STRING_ROOT or + * NSH_PROMPT_STRING_USER are non-empty, the prompt for the current + * effective UID replaces the value from the sources above. + * * Note that suffix has higher priority when used to help clearly separate * prompts from command line inputs. * @@ -90,6 +98,22 @@ void nsh_update_prompt(void) gethostname(g_nshprompt, NSH_PROMPT_SIZE); strcat(g_nshprompt, CONFIG_NSH_PROMPT_SUFFIX); } + +#ifdef CONFIG_SCHED_USER_IDENTITY + if (geteuid() == 0) + { + if (CONFIG_NSH_PROMPT_STRING_ROOT[0] != '\0') + { + strlcpy(g_nshprompt, CONFIG_NSH_PROMPT_STRING_ROOT, + CONFIG_NSH_PROMPT_MAX); + } + } + else if (CONFIG_NSH_PROMPT_STRING_USER[0] != '\0') + { + strlcpy(g_nshprompt, CONFIG_NSH_PROMPT_STRING_USER, + CONFIG_NSH_PROMPT_MAX); + } +#endif } /**************************************************************************** diff --git a/nshlib/nsh_script.c b/nshlib/nsh_script.c index a0db4d6d0..20114efc0 100644 --- a/nshlib/nsh_script.c +++ b/nshlib/nsh_script.c @@ -61,10 +61,11 @@ static int nsh_script_redirect(FAR struct nsh_vtbl_s *vtbl, if (CONFIG_NSH_SCRIPT_REDIRECT_PATH[0]) { - fd = open(CONFIG_NSH_SCRIPT_REDIRECT_PATH, 0666); + fd = open(CONFIG_NSH_SCRIPT_REDIRECT_PATH, + O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd > 0) { - nsh_redirect(vtbl, 0, fd, save); + nsh_redirect(vtbl, 0, fd, fd, save); } } @@ -120,7 +121,7 @@ int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const FAR char *cmd, /* Open the file containing the script */ - vtbl->np.np_fd = open(fullpath, O_RDOK | O_CLOEXEC); + vtbl->np.np_fd = open(fullpath, O_RDONLY | O_CLOEXEC); if (vtbl->np.np_fd < 0) { if (log) diff --git a/nshlib/nsh_session.c b/nshlib/nsh_session.c index 500c8d6f1..acacb0f54 100644 --- a/nshlib/nsh_session.c +++ b/nshlib/nsh_session.c @@ -211,17 +211,24 @@ int nsh_session(FAR struct console_stdio_s *pstate, ret = cle_fd(pstate->cn_line, nsh_prompt(), LINE_MAX, INFD(pstate), OUTFD(pstate)); - if (ret < 0) + if (ret == EOF) { dprintf(ERRFD(pstate), g_fmtcmdfailed, "nsh_session", "cle", NSH_ERRNO_OF(-ret)); - continue; + ret = EXIT_SUCCESS; + break; } #else /* Display the prompt string */ write(OUTFD(pstate), nsh_prompt(), strlen(nsh_prompt())); +#if defined(CONFIG_READLINE_TABCOMPLETION) || defined(CONFIG_READLINE_EDIT) + /* Set the prompt so readline can redraw it for editing features */ + + readline_prompt(nsh_prompt()); +#endif + /* readline() normally returns the number of characters read, but * will return EOF on end of file or if an error occurs. EOF * will cause the session to terminate. diff --git a/nshlib/nsh_syscmds.c b/nshlib/nsh_syscmds.c index 3541156ef..15ca82c46 100644 --- a/nshlib/nsh_syscmds.c +++ b/nshlib/nsh_syscmds.c @@ -405,6 +405,7 @@ int cmd_switchboot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) int cmd_boot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { struct boardioc_boot_info_s info; + FAR char *fullpath = NULL; memset(&info, 0, sizeof(info)); @@ -421,7 +422,8 @@ int cmd_boot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) /* Go through */ case 1: - info.path = argv[1]; + fullpath = nsh_getfullpath(vtbl, argv[1]); + info.path = fullpath; /* Go through */ @@ -439,6 +441,7 @@ int cmd_boot(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) */ nsh_error(vtbl, g_fmtcmdfailed, argv[0], "boardctl", NSH_ERRNO); + nsh_freefullpath(fullpath); return ERROR; } #endif @@ -591,6 +594,12 @@ static int cmd_rpmsg_once(FAR struct nsh_vtbl_s *vtbl, cmd = RPMSGIOC_PING; val = (unsigned long)&ping; } +#endif +#ifdef CONFIG_RPMSG_TEST + else if (strcmp(argv[1], "test") == 0) + { + cmd = RPMSGIOC_TEST; + } #endif else if (rpmsg_cb && rpmsg_cb(&cmd, &val, argv) == OK) { @@ -642,7 +651,7 @@ static int cmd_rpmsg_recursive(FAR struct nsh_vtbl_s *vtbl, static int cmd_rpmsg_help(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) { - nsh_output(vtbl, "%s \n", argv[0]); + nsh_output(vtbl, "%s \n", argv[0]); #ifdef CONFIG_RPMSG_PING nsh_output(vtbl, "%s ping " "\n\n", argv[0]); diff --git a/nshlib/nsh_telnetd.c b/nshlib/nsh_telnetd.c index 62f87e479..83fc36ca6 100644 --- a/nshlib/nsh_telnetd.c +++ b/nshlib/nsh_telnetd.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include diff --git a/nshlib/nsh_telnetlogin.c b/nshlib/nsh_telnetlogin.c index 417ffda68..8007b2684 100644 --- a/nshlib/nsh_telnetlogin.c +++ b/nshlib/nsh_telnetlogin.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "fsutils/passwd.h" @@ -247,14 +248,21 @@ int nsh_telnetlogin(FAR struct console_stdio_s *pstate) if (PASSWORD_VERIFY_MATCH(platform_user_verify(username, password))) # endif -#elif defined(CONFIG_NSH_LOGIN_FIXED) - if (strcmp(password, CONFIG_NSH_LOGIN_PASSWORD) == 0 && - strcmp(username, CONFIG_NSH_LOGIN_USERNAME) == 0) #else # error No user verification method selected #endif { write(OUTFD(pstate), g_loginsuccess, strlen(g_loginsuccess)); + +#if defined(CONFIG_NSH_LOGIN_SETUID) && defined(CONFIG_SCHED_USER_IDENTITY) + if (nsh_setuser_identity(username) < 0) + { + write(OUTFD(pstate), g_badidentity, strlen(g_badidentity)); + return -1; + } + + nsh_update_prompt(); +#endif return OK; } else diff --git a/nshlib/nsh_timcmds.c b/nshlib/nsh_timcmds.c index a0952acc1..9a812d461 100644 --- a/nshlib/nsh_timcmds.c +++ b/nshlib/nsh_timcmds.c @@ -26,6 +26,7 @@ #include +#include #include #include #include @@ -120,7 +121,7 @@ static inline int date_showtime(FAR struct nsh_vtbl_s *vtbl, if (utc) { - if (gmtime_r((FAR const time_t *)&ts.tv_sec, &tm) == NULL) + if (gmtime_r(&ts.tv_sec, &tm) == NULL) { nsh_error(vtbl, g_fmtcmdfailed, name, "gmtime_r", NSH_ERRNO); return ERROR; @@ -128,7 +129,7 @@ static inline int date_showtime(FAR struct nsh_vtbl_s *vtbl, } else { - if (localtime_r((FAR const time_t *)&ts.tv_sec, &tm) == NULL) + if (localtime_r(&ts.tv_sec, &tm) == NULL) { nsh_error(vtbl, g_fmtcmdfailed, name, "localtime_r", NSH_ERRNO); return ERROR; @@ -265,6 +266,10 @@ static inline int date_settime(FAR struct nsh_vtbl_s *vtbl, tm.tm_year = (int)result - 1900; + /* Information about daylight saving not available -> let TZ handle it */ + + tm.tm_isdst = -1; + /* Convert this to the right form, then set the timer */ ts.tv_sec = utc ? timegm(&tm): mktime(&tm); @@ -350,8 +355,8 @@ int cmd_time(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) } diff.tv_nsec = end.tv_nsec - start.tv_nsec; - nsh_output(vtbl, "\n%lu.%04lu sec\n", (unsigned long)diff.tv_sec, - (unsigned long)diff.tv_nsec / 100000); + nsh_output(vtbl, "\n%jd.%04ld sec\n", (intmax_t)diff.tv_sec, + diff.tv_nsec / 100000); } } @@ -485,7 +490,7 @@ int cmd_timedatectl(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) return ERROR; } - if (localtime_r((FAR const time_t *)&ts.tv_sec, &tm) == NULL) + if (localtime_r(&ts.tv_sec, &tm) == NULL) { nsh_error(vtbl, g_fmtcmdfailed, argv[0], "localtime_r", NSH_ERRNO); return ERROR; @@ -504,7 +509,7 @@ int cmd_timedatectl(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) tm.tm_gmtoff); nsh_output(vtbl, " Local time: %s %s\n", timbuf, tm.tm_zone); - if (gmtime_r((FAR const time_t *)&ts.tv_sec, &tm) == NULL) + if (gmtime_r(&ts.tv_sec, &tm) == NULL) { nsh_error(vtbl, g_fmtcmdfailed, argv[0], "gmtime_r", NSH_ERRNO); return ERROR; diff --git a/nshlib/nsh_usbconsole.c b/nshlib/nsh_usbconsole.c index cc7f97005..74f8a4795 100644 --- a/nshlib/nsh_usbconsole.c +++ b/nshlib/nsh_usbconsole.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #ifdef CONFIG_CDCACM # include diff --git a/nshlib/nsh_usbtrace.c b/nshlib/nsh_usbtrace.c index df05d45a3..f57a81ac6 100644 --- a/nshlib/nsh_usbtrace.c +++ b/nshlib/nsh_usbtrace.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include diff --git a/nshlib/nsh_vars.c b/nshlib/nsh_vars.c index f940d64a3..401d90670 100644 --- a/nshlib/nsh_vars.c +++ b/nshlib/nsh_vars.c @@ -257,6 +257,11 @@ int nsh_setvar(FAR struct nsh_vtbl_s *vtbl, FAR const char *name, if (pstate->varp != NULL) { + if (varlen > INT_MAX - pstate->varsz) + { + return -ENOMEM; + } + newsize = pstate->varsz + varlen; newvarp = (FAR char *)realloc(pstate->varp, newsize); if (newvarp == NULL) diff --git a/platform/mikroe-stm32f4/mikroe_configdata.c b/platform/mikroe-stm32f4/mikroe_configdata.c index b6d9bb6fc..ab786c625 100644 --- a/platform/mikroe-stm32f4/mikroe_configdata.c +++ b/platform/mikroe-stm32f4/mikroe_configdata.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -86,7 +86,7 @@ int platform_setconfig(enum config_data_e id, int instance, /* Try to open the /dev/config device file */ - if ((fd = open("/dev/config", O_RDOK)) == -1) + if ((fd = open("/dev/config", O_RDONLY)) == -1) { /* Error opening the config device */ @@ -209,7 +209,7 @@ int platform_getconfig(enum config_data_e id, int instance, /* Try to open the /dev/config device file */ - if ((fd = open("/dev/config", O_RDOK)) == -1) + if ((fd = open("/dev/config", O_RDONLY)) == -1) { /* Error opening the config device */ @@ -233,7 +233,7 @@ int platform_getconfig(enum config_data_e id, int instance, case CONFIGDATA_TSCALIBRATION: #ifdef CONFIG_MIKROE_STM32F4_CONFIGDATA_FS - /* Load config data fram a file on the filesystem. Try to open + /* Load config data from a file on the filesystem. Try to open * the file. */ diff --git a/system/adb/Kconfig b/system/adb/Kconfig index 06d7f940a..a97ac5a9f 100644 --- a/system/adb/Kconfig +++ b/system/adb/Kconfig @@ -95,7 +95,7 @@ config ADBD_FRAME_MAX config ADBD_TCP_SERVER bool "Network socket transport support" - depends on NET_TCP + depends on NET_TCP_NO_STACK || NET_TCPBACKLOG default n ---help--- Run adb daemon on network socket. @@ -174,6 +174,7 @@ config ADBD_SHELL_SERVICE depends on SYSTEM_NSH depends on LIBC_EXECFUNCS depends on PSEUDOTERM + depends on SCHED_CHILD_STATUS default n ---help--- Enable "adb shell" feature. @@ -199,19 +200,12 @@ config ADBD_SOCKET_SERVICE ---help--- Enable "adb forward|reverse" feature. -config ADBD_BOARD_INIT - bool "Board initialization" - depends on BOARDCTL - default n - ---help--- - Setup board before running adb daemon. - config ADBD_USB_BOARDCTL bool "USB Board Control" depends on BOARDCTL depends on ADBD_USB_SERVER select BOARDCTL_USBDEVCTRL - default ADBD_BOARD_INIT + default y ---help--- Connect usbdev before running adb daemon. diff --git a/system/adb/adb_main.c b/system/adb/adb_main.c index 3895001d5..8703337c6 100644 --- a/system/adb/adb_main.c +++ b/system/adb/adb_main.c @@ -32,8 +32,7 @@ #include #include -#if defined(CONFIG_ADBD_BOARD_INIT) || defined (CONFIG_BOARDCTL_RESET) || \ - defined(CONFIG_ADBD_USB_BOARDCTL) +#if defined (CONFIG_BOARDCTL_RESET) || defined(CONFIG_ADBD_USB_BOARDCTL) # include #endif @@ -120,10 +119,6 @@ int main(int argc, FAR char **argv) int ret; #endif -#ifdef CONFIG_ADBD_BOARD_INIT - boardctl(BOARDIOC_INIT, 0); -#endif /* CONFIG_ADBD_BOARD_INIT */ - #ifdef CONFIG_ADBD_USB_BOARDCTL /* Setup USBADB device */ diff --git a/system/adcscope/adcscope_main.c b/system/adcscope/adcscope_main.c index 3413c6b52..991a756ca 100644 --- a/system/adcscope/adcscope_main.c +++ b/system/adcscope/adcscope_main.c @@ -382,16 +382,10 @@ int main(int argc, FAR char *argv[]) pthread_t thread; int ret; -#ifndef CONFIG_NSH_ARCHINIT - /* Perform architecture-specific initialization (if configured) */ - - boardctl(BOARDIOC_INIT, 0); - -# ifdef CONFIG_BOARDCTL_FINALINIT +#ifdef CONFIG_BOARDCTL_FINALINIT /* Perform architecture-specific final-initialization (if configured) */ boardctl(BOARDIOC_FINALINIT, 0); -# endif #endif #ifdef CONFIG_SYSTEM_ADCSCOPE_CDCACM diff --git a/system/cdcacm/sercon_main.c b/system/cdcacm/sercon_main.c index 9b8ab41d8..c03ce2d18 100644 --- a/system/cdcacm/sercon_main.c +++ b/system/cdcacm/sercon_main.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/system/cdcacm/serdis_main.c b/system/cdcacm/serdis_main.c index 834ae186b..56e5fbfe0 100644 --- a/system/cdcacm/serdis_main.c +++ b/system/cdcacm/serdis_main.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/system/cfgdata/Kconfig b/system/cfgdata/Kconfig index 23eb9ebce..64f51d40e 100644 --- a/system/cfgdata/Kconfig +++ b/system/cfgdata/Kconfig @@ -6,7 +6,7 @@ config SYSTEM_CFGDATA tristate "Cfgdata Command" default n - depends on MTD_CONFIG + depends on !MTD_CONFIG_NONE ---help--- Enable support for the CFGDATA tool. diff --git a/system/cle/cle.c b/system/cle/cle.c index cf11f7097..bfa01c11f 100644 --- a/system/cle/cle.c +++ b/system/cle/cle.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -316,7 +316,7 @@ static int cle_getch(FAR struct cle_s *priv) if (nread == 0 || errcode != EINTR) { cledbg("ERROR: read from stdin failed: %d\n", errcode); - return -EIO; + return EOF; } } } @@ -372,13 +372,13 @@ static void cle_setcursor(FAR struct cle_s *priv, int16_t column) int len; int off; - /* Sub prompt offset from real postion to get correct offset to execute */ + /* Sub prompt offset from real position to get correct offset to execute */ off = column - (priv->realpos - priv->coloffs); cleinfo("column=%d offset=%d\n", column, off); - /* If cursor not move, retrun directly */ + /* If cursor not move, return directly */ if (off == 0) { @@ -703,9 +703,9 @@ static int cle_editloop(FAR struct cle_s *priv) for (; ; ) { ch = cle_getch(priv); - if (ch < 0) + if (ch == EOF) { - return -EIO; + return EOF; } else if (state != 0) { diff --git a/system/composite/CMakeLists.txt b/system/composite/CMakeLists.txt index 9ca2f72ca..d88246363 100644 --- a/system/composite/CMakeLists.txt +++ b/system/composite/CMakeLists.txt @@ -21,5 +21,6 @@ # ############################################################################## if(CONFIG_SYSTEM_COMPOSITE) - nuttx_add_application(NAME composite SRCS composite_main.c) + nuttx_add_application(NAME conn SRCS composite_main.c) + nuttx_add_application(NAME disconn) endif() diff --git a/system/composite/composite_main.c b/system/composite/composite_main.c index 9dc4399e8..f6dc54bff 100644 --- a/system/composite/composite_main.c +++ b/system/composite/composite_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/system/coredump/coredump.c b/system/coredump/coredump.c index 6a742f479..2787ee3d0 100644 --- a/system/coredump/coredump.c +++ b/system/coredump/coredump.c @@ -264,9 +264,9 @@ static void coredump_restore(FAR char *savepath, size_t maxfile) /* 'date -d @$(printf "%d" 0x6720C67E)' restore utc to date */ ret = snprintf(dumppath, sizeof(dumppath), - "%s/%.16s-%llx"COREDUMP_FILE_SUFFIX, + "%s/%.16s-%jx"COREDUMP_FILE_SUFFIX, savepath, info.name.version, - (unsigned long long)info.time.tv_sec); + (intmax_t)info.time.tv_sec); while (ret--) { diff --git a/system/cu/cu.h b/system/cu/cu.h deleted file mode 100644 index be9b31fb3..000000000 --- a/system/cu/cu.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** - * apps/system/cu/cu.h - * - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-FileCopyrightText: 2014 sysmocom - s.f.m.c. GmbH. All rights reserved. - * SPDX-FileContributor: Harald Welte - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -#ifndef __APPS_SYSTEM_CU_CU_H -#define __APPS_SYSTEM_CU_CU_H - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Configuration ************************************************************/ - -#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE -# define CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE "/dev/ttyS0" -#endif - -#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD -# define CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD 115200 -#endif - -/**************************************************************************** - * Public Types - ****************************************************************************/ - -/* All terminal state data is packaged in a single structure to minimize - * name conflicts with other global symbols -- a poor man's name space. - */ - -struct cu_globals_s -{ - int devfd; /* I/O data to serial port */ - int stdfd; /* I/O data to standard console */ - int escape; /* Escape char */ - struct termios devtio; /* Original serial port setting */ - struct termios stdtio; /* Original standard console setting */ - pthread_t listener; /* Terminal listener thread */ - bool force_exit; /* Force exit */ -}; - -/**************************************************************************** - * Public Data - ****************************************************************************/ - -/**************************************************************************** - * Public Function Prototypes - ****************************************************************************/ - -#endif /* __APPS_SYSTEM_CU_CU_H */ diff --git a/system/cu/cu_main.c b/system/cu/cu_main.c index 20cd101b5..f0767ddc3 100644 --- a/system/cu/cu_main.c +++ b/system/cu/cu_main.c @@ -52,11 +52,27 @@ #include #include #include -#include +#include +#include +#include +#include +#include #include "system/readline.h" -#include "cu.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE +# define CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE "/dev/ttyS0" +#endif + +#ifndef CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD +# define CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD 115200 +#endif #ifdef CONFIG_SYSTEM_CUTERM_DISABLE_ERROR_PRINT # define cu_error(...) @@ -75,12 +91,25 @@ enum parity_mode PARITY_ODD, }; +/* All terminal state data is packaged in a single structure to minimize + * name conflicts with other global symbols -- a poor man's name space. + */ + +struct cu_globals_s +{ + int devfd; /* I/O data to serial port */ + int stdfd; /* I/O data to standard console */ + int escape; /* Escape char */ + struct termios devtio; /* Original serial port setting */ + struct termios stdtio; /* Original standard console setting */ + pthread_t listener; /* Terminal listener thread */ + bool force_exit; /* Force exit */ +}; + /**************************************************************************** * Private Data ****************************************************************************/ -static struct cu_globals_s g_cu; - /**************************************************************************** * Public Data ****************************************************************************/ @@ -103,16 +132,16 @@ static FAR void *cu_listener(FAR void *parameter) for (; ; ) { + char buf[CONFIG_LINE_MAX]; int rc; - char ch; - rc = read(cu->devfd, &ch, 1); + rc = read(cu->devfd, buf, sizeof(buf)); if (rc <= 0) { break; } - rc = write(STDOUT_FILENO, &ch, 1); + rc = write(STDOUT_FILENO, buf, rc); if (rc <= 0) { break; @@ -124,10 +153,13 @@ static FAR void *cu_listener(FAR void *parameter) return NULL; } -static void sigint(int sig) +#ifdef CONFIG_ENABLE_ALL_SIGNALS +static void cu_exit(int signo, FAR siginfo_t *siginfo, FAR void *context) { - g_cu.force_exit = true; + FAR struct cu_globals_s *cu = siginfo->si_user; + cu->force_exit = true; } +#endif #ifdef CONFIG_SERIAL_TERMIOS static int set_termios(FAR struct cu_globals_s *cu, int rate, @@ -275,9 +307,11 @@ static int cu_cmd(FAR struct cu_globals_s *cu, char bcmd) int main(int argc, FAR char *argv[]) { pthread_attr_t attr; +#ifdef CONFIG_ENABLE_ALL_SIGNALS struct sigaction sa; +#endif + struct cu_globals_s cu; FAR const char *devname = CONFIG_SYSTEM_CUTERM_DEFAULT_DEVICE; - FAR struct cu_globals_s *cu = &g_cu; #ifdef CONFIG_SERIAL_TERMIOS int baudrate = CONFIG_SYSTEM_CUTERM_DEFAULT_BAUD; enum parity_mode parity = PARITY_NONE; @@ -289,18 +323,27 @@ int main(int argc, FAR char *argv[]) int start_of_line = 1; int exitval = EXIT_FAILURE; bool badarg = false; + bool escaping = false; /* Initialize global data */ - memset(cu, 0, sizeof(*cu)); - cu->escape = '~'; - + memset(&cu, 0, sizeof(cu)); + cu.escape = '~'; +#ifdef CONFIG_ENABLE_ALL_SIGNALS /* Install signal handlers */ memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigint; - sigaction(SIGINT, &sa, NULL); + sa.sa_user = &cu; + sa.sa_sigaction = cu_exit; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, NULL) < 0) + { + cu_error("cu_main: ERROR during setup cu_exit sigaction(): %d\n", + errno); + return EXIT_FAILURE; + } +#endif optind = 0; /* Global that needs to be reset in FLAT mode */ while ((option = getopt(argc, argv, "l:s:ceE:fho?")) != ERROR) { @@ -333,7 +376,7 @@ int main(int argc, FAR char *argv[]) break; case 'E': - cu->escape = atoi(optarg); + cu.escape = atoi(optarg); break; case 'h': @@ -356,8 +399,8 @@ int main(int argc, FAR char *argv[]) /* Open the serial device for reading and writing */ - cu->devfd = open(devname, O_RDWR); - if (cu->devfd < 0) + cu.devfd = open(devname, O_RDWR); + if (cu.devfd < 0) { cu_error("cu_main: ERROR: Failed to open %s for writing: %d\n", devname, errno); @@ -366,9 +409,9 @@ int main(int argc, FAR char *argv[]) /* Remember serial device termios attributes */ - if (isatty(cu->devfd)) + if (isatty(cu.devfd)) { - ret = tcgetattr(cu->devfd, &cu->devtio); + ret = tcgetattr(cu.devfd, &cu.devtio); if (ret) { cu_error("cu_main: ERROR during tcgetattr(): %d\n", errno); @@ -382,30 +425,30 @@ int main(int argc, FAR char *argv[]) if (isatty(STDERR_FILENO)) { - cu->stdfd = STDERR_FILENO; + cu.stdfd = STDERR_FILENO; } else if (isatty(STDOUT_FILENO)) { - cu->stdfd = STDOUT_FILENO; + cu.stdfd = STDOUT_FILENO; } else if (isatty(STDIN_FILENO)) { - cu->stdfd = STDIN_FILENO; + cu.stdfd = STDIN_FILENO; } else { - cu->stdfd = -1; + cu.stdfd = -1; } - if (cu->stdfd >= 0) + if (cu.stdfd >= 0) { - tcgetattr(cu->stdfd, &cu->stdtio); + tcgetattr(cu.stdfd, &cu.stdtio); } #ifdef CONFIG_SERIAL_TERMIOS - if (set_termios(cu, baudrate, parity, rtscts, nocrlf) != 0) + if (set_termios(&cu, baudrate, parity, rtscts, nocrlf) != 0) #else - if (set_termios(cu, nocrlf) != 0) + if (set_termios(&cu, nocrlf) != 0) #endif { goto errout_with_devfd_retrieve; @@ -424,7 +467,7 @@ int main(int argc, FAR char *argv[]) attr.priority = CONFIG_SYSTEM_CUTERM_PRIORITY; - ret = pthread_create(&cu->listener, &attr, cu_listener, cu); + ret = pthread_create(&cu.listener, &attr, cu_listener, &cu); pthread_attr_destroy(&attr); if (ret != 0) { @@ -434,69 +477,94 @@ int main(int argc, FAR char *argv[]) /* Send messages and get responses -- forever */ - while (!cu->force_exit) + while (!cu.force_exit) { - char ch; + char buf[CONFIG_LINE_MAX]; + ssize_t nwrite = 0; + ssize_t nread; + ssize_t i; - if (read(STDIN_FILENO, &ch, 1) <= 0) + /* Read multiple characters at once (blocking) */ + + nread = read(STDIN_FILENO, buf, sizeof(buf)); + if (nread <= 0) { continue; } - if (start_of_line == 1 && ch == cu->escape) + /* Process each character */ + + for (i = 0; i < nread; i++) { - /* We've seen and escape (~) character, echo it to local - * terminal and read the next char from serial - */ + char ch = buf[i]; - write(STDOUT_FILENO, &ch, 1); + /* Check if we're waiting for an escape command character */ - if (read(STDIN_FILENO, &ch, 1) <= 0) + if (escaping) { + /* We got the escape character in previous read, + * now process the command character + */ + + escaping = false; + if (cu_cmd(&cu, ch) == 1) + { + cu.force_exit = true; + nread = i; + break; + } + } + + if (start_of_line == 1 && ch == cu.escape) + { + /* Normal character */ + + if (i > nwrite) + { + write(cu.devfd, &buf[nwrite], i - nwrite); + } + + nwrite = i + 1; + + /* We've seen and escape (~) character, echo it to local + * terminal and read the next char from serial + */ + + write(STDOUT_FILENO, &ch, 1); + start_of_line = 0; + escaping = true; continue; } - if (ch == cu->escape) - { - /* Escaping a tilde: handle like normal char */ + /* Determine if we are now at the start of a new line or not */ - write(cu->devfd, &ch, 1); - continue; + if (ch == '\n' || ch == '\r') + { + start_of_line = 1; } else { - if (cu_cmd(cu, ch) == 1) - { - break; - } + start_of_line = 0; } } /* Normal character */ - write(cu->devfd, &ch, 1); - - /* Determine if we are now at the start of a new line or not */ - - if (ch == '\n' || ch == '\r') + if (nread > nwrite) { - start_of_line = 1; - } - else - { - start_of_line = 0; + write(cu.devfd, &buf[nwrite], nread - nwrite); } } - pthread_cancel(cu->listener); + pthread_cancel(cu.listener); exitval = EXIT_SUCCESS; /* Error exits */ errout_with_devfd_retrieve: - retrieve_termios(cu); + retrieve_termios(&cu); errout_with_devfd: - close(cu->devfd); + close(cu.devfd); errout_with_devinit: return exitval; } diff --git a/system/curl/CMakeLists.txt b/system/curl/CMakeLists.txt new file mode 100644 index 000000000..ff48d44a1 --- /dev/null +++ b/system/curl/CMakeLists.txt @@ -0,0 +1,33 @@ +# ############################################################################## +# apps/system/curl/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_CURL) + nuttx_add_application( + NAME + ${CONFIG_SYSTEM_CURL_PROGNAME} + SRCS + curl_main.c + STACKSIZE + ${CONFIG_SYSTEM_CURL_STACKSIZE} + PRIORITY + ${CONFIG_SYSTEM_CURL_PRIORITY}) +endif() diff --git a/system/curl/Kconfig b/system/curl/Kconfig new file mode 100644 index 000000000..ca2963aff --- /dev/null +++ b/system/curl/Kconfig @@ -0,0 +1,42 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_CURL + tristate "curl HTTP command" + default n + depends on NETUTILS_WEBCLIENT + ---help--- + Enable the "curl" command: a small command-line HTTP client + supporting GET and POST (and other methods via -X), custom + headers (-H), a raw request body (-d, including -d @file) and + multipart/form-data file uploads (-F name=@file), built on top + of the netutils webclient library. HTTP only (no HTTPS). + +if SYSTEM_CURL + +config SYSTEM_CURL_PROGNAME + string "Program name" + default "curl" + ---help--- + This is the name of the program that will be used when the + NSH ELF program is installed. + +config SYSTEM_CURL_PRIORITY + int "curl task priority" + default 100 + +config SYSTEM_CURL_STACKSIZE + int "curl stack size" + default DEFAULT_TASK_STACKSIZE + +config SYSTEM_CURL_BUFFERSIZE + int "curl transfer buffer size" + default 512 + ---help--- + Size of the buffer used to hold the request/response headers and + to stream body data. It must be large enough to hold the whole + request header line and the whole response header. + +endif diff --git a/system/curl/Make.defs b/system/curl/Make.defs new file mode 100644 index 000000000..4003825c7 --- /dev/null +++ b/system/curl/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/system/curl/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_CURL),) +CONFIGURED_APPS += $(APPDIR)/system/curl +endif diff --git a/system/curl/Makefile b/system/curl/Makefile new file mode 100644 index 000000000..70469e218 --- /dev/null +++ b/system/curl/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/system/curl/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# curl built-in application info + +PROGNAME = $(CONFIG_SYSTEM_CURL_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_CURL_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_CURL_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_CURL) + +# curl command + +MAINSRC = curl_main.c + +include $(APPDIR)/Application.mk diff --git a/system/curl/curl_main.c b/system/curl/curl_main.c new file mode 100644 index 000000000..11837c625 --- /dev/null +++ b/system/curl/curl_main.c @@ -0,0 +1,860 @@ +/**************************************************************************** + * apps/system/curl/curl_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netutils/webclient.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_SYSTEM_CURL_BUFFERSIZE +# define CONFIG_SYSTEM_CURL_BUFFERSIZE 512 +#endif + +/* Limits for user-provided -H headers and -F form fields. */ + +#define CURL_MAX_HEADERS 12 +#define CURL_MAX_FORMS 8 + +/* Each form field expands to at most 3 body chunks (part header, content, + * trailing CRLF), plus one final closing-boundary chunk. + */ + +#define CURL_MAX_CHUNKS (CURL_MAX_FORMS * 3 + 1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* A single piece of the request body. It is either a block of memory + * (fd < 0, data in "mem") or the contents of an open file (fd >= 0). + */ + +struct curl_chunk_s +{ + int fd; /* >= 0: read from this file; < 0: use "mem" */ + FAR char *mem; /* Heap buffer owned by this chunk (freed on cleanup) */ + size_t len; /* Total number of bytes in this chunk */ + size_t off; /* Bytes already consumed */ +}; + +/* The full request body, streamed chunk by chunk. */ + +struct curl_body_s +{ + struct curl_chunk_s chunks[CURL_MAX_CHUNKS]; + int nchunks; + int cur; +}; + +/* Destination for the response body. */ + +struct curl_sink_s +{ + int fd; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: curl_guess_mime + * + * Description: + * Very small extension-to-MIME map for the common cases. Anything else + * defaults to application/octet-stream, exactly like curl's fallback. + * + ****************************************************************************/ + +static FAR const char *curl_guess_mime(FAR const char *name) +{ + FAR const char *dot = strrchr(name, '.'); + + if (dot != NULL) + { + if (strcasecmp(dot, ".png") == 0) + { + return "image/png"; + } + + if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) + { + return "image/jpeg"; + } + + if (strcasecmp(dot, ".gif") == 0) + { + return "image/gif"; + } + + if (strcasecmp(dot, ".json") == 0) + { + return "application/json"; + } + + if (strcasecmp(dot, ".txt") == 0) + { + return "text/plain"; + } + } + + return "application/octet-stream"; +} + +/**************************************************************************** + * Name: curl_add_mem_chunk + * + * Description: + * Append a memory chunk. Takes ownership of "mem" (heap allocated). + * + ****************************************************************************/ + +static int curl_add_mem_chunk(FAR struct curl_body_s *body, FAR char *mem, + size_t len) +{ + FAR struct curl_chunk_s *c; + + if (body->nchunks >= CURL_MAX_CHUNKS) + { + return -ENOSPC; + } + + c = &body->chunks[body->nchunks++]; + c->fd = -1; + c->mem = mem; + c->len = len; + c->off = 0; + return OK; +} + +/**************************************************************************** + * Name: curl_add_file_chunk + * + * Description: + * Append a file chunk. Takes ownership of the open descriptor "fd". + * + ****************************************************************************/ + +static int curl_add_file_chunk(FAR struct curl_body_s *body, int fd, + size_t len) +{ + FAR struct curl_chunk_s *c; + + if (body->nchunks >= CURL_MAX_CHUNKS) + { + return -ENOSPC; + } + + c = &body->chunks[body->nchunks++]; + c->fd = fd; + c->mem = NULL; + c->len = len; + c->off = 0; + return OK; +} + +/**************************************************************************** + * Name: curl_add_str_chunk + * + * Description: + * Convenience wrapper: duplicate a string and append it as a memory + * chunk. + * + ****************************************************************************/ + +static int curl_add_str_chunk(FAR struct curl_body_s *body, + FAR const char *str, size_t len) +{ + FAR char *dup = strdup(str); + int ret; + + if (dup == NULL) + { + return -ENOMEM; + } + + ret = curl_add_mem_chunk(body, dup, len); + if (ret < 0) + { + free(dup); + } + + return ret; +} + +/**************************************************************************** + * Name: curl_body_cleanup + ****************************************************************************/ + +static void curl_body_cleanup(FAR struct curl_body_s *body) +{ + int i; + + for (i = 0; i < body->nchunks; i++) + { + FAR struct curl_chunk_s *c = &body->chunks[i]; + + if (c->fd >= 0) + { + close(c->fd); + } + + free(c->mem); + } + + body->nchunks = 0; +} + +/**************************************************************************** + * Name: curl_body_total + ****************************************************************************/ + +static size_t curl_body_total(FAR const struct curl_body_s *body) +{ + size_t total = 0; + int i; + + for (i = 0; i < body->nchunks; i++) + { + total += body->chunks[i].len; + } + + return total; +} + +/**************************************************************************** + * Name: curl_body_callback + * + * Description: + * webclient request-body provider. Fills "buffer" with up to *sizep bytes + * pulled sequentially from the chunk list. Called repeatedly by webclient + * until exactly bodylen bytes have been produced. + * + ****************************************************************************/ + +static int curl_body_callback(FAR void *buffer, FAR size_t *sizep, + FAR const void * FAR *datap, size_t reqsize, + FAR void *arg) +{ + FAR struct curl_body_s *body = (FAR struct curl_body_s *)arg; + FAR char *out = (FAR char *)buffer; + size_t cap = *sizep; + size_t done = 0; + + (void)reqsize; + (void)datap; + + while (done < cap && body->cur < body->nchunks) + { + FAR struct curl_chunk_s *c = &body->chunks[body->cur]; + size_t remain = c->len - c->off; + size_t want; + + if (remain == 0) + { + body->cur++; + continue; + } + + want = cap - done; + if (want > remain) + { + want = remain; + } + + if (c->fd < 0) + { + memcpy(out + done, c->mem + c->off, want); + c->off += want; + done += want; + } + else + { + ssize_t n = read(c->fd, out + done, want); + if (n < 0) + { + return -errno; + } + + if (n == 0) + { + /* Short file: the pre-computed Content-Length can no longer + * be honoured, so fail rather than send a truncated body. + */ + + return -EIO; + } + + c->off += n; + done += n; + } + } + + *sizep = done; + return OK; +} + +/**************************************************************************** + * Name: curl_sink_callback + * + * Description: + * webclient response-body consumer. Writes each chunk to the destination + * file descriptor (stdout or the -o file). + * + ****************************************************************************/ + +static int curl_sink_callback(FAR char **buffer, int offset, int datend, + FAR int *buflen, FAR void *arg) +{ + FAR struct curl_sink_s *sink = (FAR struct curl_sink_s *)arg; + int len = datend - offset; + + (void)buflen; + + while (len > 0) + { + ssize_t n = write(sink->fd, *buffer + offset, len); + if (n < 0) + { + return -errno; + } + + offset += n; + len -= n; + } + + return OK; +} + +/**************************************************************************** + * Name: curl_header_callback + ****************************************************************************/ + +static int curl_header_callback(FAR const char *line, bool truncated, + FAR void *arg) +{ + (void)truncated; + (void)arg; + + fprintf(stderr, "< %s\n", line); + return OK; +} + +/**************************************************************************** + * Name: curl_add_form + * + * Description: + * Parse a single -F "name=value" or -F "name=@path[;type=mime]" spec and + * append the corresponding multipart chunks to the body. + * + ****************************************************************************/ + +static int curl_add_form(FAR struct curl_body_s *body, + FAR const char *boundary, FAR const char *spec) +{ + char hdr[256]; + FAR const char *eq; + FAR const char *value; + FAR char name[64]; + size_t namelen; + int len; + int ret; + + eq = strchr(spec, '='); + if (eq == NULL) + { + fprintf(stderr, "curl: bad -F spec '%s' (want name=value)\n", spec); + return -EINVAL; + } + + namelen = eq - spec; + if (namelen >= sizeof(name)) + { + return -E2BIG; + } + + memcpy(name, spec, namelen); + name[namelen] = '\0'; + value = eq + 1; + + if (value[0] == '@') + { + /* File upload: name=@path[;type=mime] */ + + FAR const char *path = value + 1; + FAR const char *mime; + FAR char *typesep; + char pathbuf[CONFIG_WEBCLIENT_MAXFILENAME]; + struct stat st; + int fd; + + strlcpy(pathbuf, path, sizeof(pathbuf)); + typesep = strchr(pathbuf, ';'); + if (typesep != NULL && strncmp(typesep, ";type=", 6) == 0) + { + *typesep = '\0'; + mime = typesep + 6; + } + else + { + mime = curl_guess_mime(pathbuf); + } + + fd = open(pathbuf, O_RDONLY | O_CLOEXEC); + if (fd < 0) + { + fprintf(stderr, "curl: cannot open '%s': %d\n", pathbuf, errno); + return -errno; + } + + if (fstat(fd, &st) < 0) + { + fprintf(stderr, "curl: stat '%s' failed: %d\n", pathbuf, errno); + close(fd); + return -errno; + } + + len = snprintf(hdr, sizeof(hdr), + "--%s\r\n" + "Content-Disposition: form-data; name=\"%s\"; " + "filename=\"%s\"\r\n" + "Content-Type: %s\r\n\r\n", + boundary, name, basename(pathbuf), mime); + if (len < 0 || len >= sizeof(hdr)) + { + close(fd); + return -E2BIG; + } + + ret = curl_add_str_chunk(body, hdr, len); + if (ret < 0) + { + close(fd); + return ret; + } + + ret = curl_add_file_chunk(body, fd, st.st_size); + if (ret < 0) + { + close(fd); + return ret; + } + } + else + { + /* Plain text field: name=value */ + + len = snprintf(hdr, sizeof(hdr), + "--%s\r\n" + "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", + boundary, name); + if (len < 0 || len >= sizeof(hdr)) + { + return -E2BIG; + } + + ret = curl_add_str_chunk(body, hdr, len); + if (ret < 0) + { + return ret; + } + + ret = curl_add_str_chunk(body, value, strlen(value)); + if (ret < 0) + { + return ret; + } + } + + /* Trailing CRLF that terminates this part's content. */ + + return curl_add_str_chunk(body, "\r\n", 2); +} + +/**************************************************************************** + * Name: curl_usage + ****************************************************************************/ + +static void curl_usage(FAR const char *progname) +{ + fprintf(stderr, + "Usage: %s [options] \n" + " -X HTTP method (default GET, or POST with -d/-F).\n" + " -d Request body. Use -d @file to read from a file.\n" + " --data-binary Like -d but never interprets @-less data.\n" + " -F multipart/form-data field. Use name=@file to\n" + " upload a file. Repeatable. Implies POST.\n" + " -H
Add a request header. Repeatable.\n" + " -o, --output Write response body to instead of stdout.\n" + " -v Verbose: print response headers to stderr.\n" + " -h Show this help.\n" + "\n" + "Only http:// URLs are supported (no HTTPS). A scheme-less URL such\n" + "as host:port/path is treated as http://host:port/path.\n", + progname); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *headers[CURL_MAX_HEADERS]; + FAR const char *forms[CURL_MAX_FORMS]; + FAR const char *method = NULL; + FAR const char *data = NULL; + FAR const char *rawurl = NULL; + FAR const char *outfile = NULL; + struct webclient_context ctx; + struct curl_body_s body; + struct curl_sink_s sink; + char boundary[48]; + + /* Sized so the compiler can prove the Content-Type snprintf never + * truncates: the fixed prefix (including its NUL) plus the largest + * possible boundary string. + */ + + char ctype[sizeof("Content-Type: multipart/form-data; boundary=") + + sizeof(boundary)]; + char urlbuf[CONFIG_WEBCLIENT_MAXHOSTNAME + CONFIG_WEBCLIENT_MAXFILENAME + + 16]; + FAR const char *url; + FAR char *buffer = NULL; + unsigned int nheaders = 0; + unsigned int nforms = 0; + bool have_ctype = false; + bool verbose = false; + int ret; + int opt; + unsigned int i; + + struct option long_options[] = + { + {"data", required_argument, NULL, 'd'}, + {"data-binary", required_argument, NULL, 'B'}, + {"form", required_argument, NULL, 'F'}, + {"header", required_argument, NULL, 'H'}, + {"request", required_argument, NULL, 'X'}, + {"output", required_argument, NULL, 'o'}, + {"verbose", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + body.nchunks = 0; + body.cur = 0; + + while ((opt = getopt_long(argc, argv, "X:d:F:H:o:vh", + long_options, NULL)) != ERROR) + { + switch (opt) + { + case 'X': + method = optarg; + break; + + case 'd': + case 'B': + data = optarg; + break; + + case 'F': + if (nforms >= CURL_MAX_FORMS) + { + fprintf(stderr, "curl: too many -F fields (max %d)\n", + CURL_MAX_FORMS); + return EXIT_FAILURE; + } + + forms[nforms++] = optarg; + break; + + case 'H': + if (nheaders >= CURL_MAX_HEADERS - 1) + { + fprintf(stderr, "curl: too many -H headers (max %d)\n", + CURL_MAX_HEADERS - 1); + return EXIT_FAILURE; + } + + if (strncasecmp(optarg, "Content-Type:", 13) == 0) + { + have_ctype = true; + } + + headers[nheaders++] = optarg; + break; + + case 'o': + outfile = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'h': + curl_usage(argv[0]); + return EXIT_SUCCESS; + + case '?': + default: + curl_usage(argv[0]); + return EXIT_FAILURE; + } + } + + if (optind >= argc) + { + fprintf(stderr, "curl: missing URL\n"); + curl_usage(argv[0]); + return EXIT_FAILURE; + } + + rawurl = argv[optind]; + + if (data != NULL && nforms > 0) + { + fprintf(stderr, "curl: -d and -F cannot be combined\n"); + return EXIT_FAILURE; + } + + /* Prepend a default http:// scheme when the URL has none. */ + + if (strstr(rawurl, "://") == NULL) + { + snprintf(urlbuf, sizeof(urlbuf), "http://%s", rawurl); + url = urlbuf; + } + else + { + url = rawurl; + } + + /* Default method: POST when sending a body, GET otherwise. */ + + if (method == NULL) + { + method = (data != NULL || nforms > 0) ? "POST" : "GET"; + } + + /* Build the request body. */ + + if (nforms > 0) + { + /* multipart/form-data: derive a boundary and add the Content-Type + * header ourselves. + */ + + snprintf(boundary, sizeof(boundary), "----NuttXCurl%08x%08x", + getpid(), nforms); + + for (i = 0; i < nforms; i++) + { + ret = curl_add_form(&body, boundary, forms[i]); + if (ret < 0) + { + goto errout_body; + } + } + + /* Closing boundary. */ + + ret = curl_add_str_chunk(&body, "--", 2); + if (ret >= 0) + { + ret = curl_add_str_chunk(&body, boundary, strlen(boundary)); + } + + if (ret >= 0) + { + ret = curl_add_str_chunk(&body, "--\r\n", 4); + } + + if (ret < 0) + { + goto errout_body; + } + + if (nheaders < CURL_MAX_HEADERS && !have_ctype) + { + snprintf(ctype, sizeof(ctype), + "Content-Type: multipart/form-data; boundary=%s", + boundary); + headers[nheaders++] = ctype; + have_ctype = true; + } + } + else if (data != NULL && data[0] == '@') + { + /* -d @file : stream the file as the raw request body. */ + + struct stat st; + int fd = open(data + 1, O_RDONLY | O_CLOEXEC); + + if (fd < 0) + { + fprintf(stderr, "curl: cannot open '%s': %d\n", data + 1, errno); + ret = -errno; + goto errout_body; + } + + if (fstat(fd, &st) < 0) + { + fprintf(stderr, "curl: stat '%s' failed: %d\n", data + 1, errno); + ret = -errno; + close(fd); + goto errout_body; + } + + ret = curl_add_file_chunk(&body, fd, st.st_size); + if (ret < 0) + { + close(fd); + goto errout_body; + } + } + else if (data != NULL) + { + /* -d 'literal' : the string itself is the body. */ + + ret = curl_add_str_chunk(&body, data, strlen(data)); + if (ret < 0) + { + goto errout_body; + } + } + + /* curl adds a form Content-Type by default for -d without an explicit + * one. JSON callers are expected to pass -H "Content-Type: ...". + */ + + if (data != NULL && !have_ctype && nheaders < CURL_MAX_HEADERS) + { + headers[nheaders++] = + "Content-Type: application/x-www-form-urlencoded"; + } + + buffer = malloc(CONFIG_SYSTEM_CURL_BUFFERSIZE); + if (buffer == NULL) + { + fprintf(stderr, "curl: out of memory\n"); + ret = -ENOMEM; + goto errout_body; + } + + /* Open the destination for the response body. */ + + if (outfile != NULL) + { + sink.fd = open(outfile, + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + if (sink.fd < 0) + { + fprintf(stderr, "curl: cannot open '%s': %d\n", outfile, errno); + ret = -errno; + goto errout_buffer; + } + } + else + { + sink.fd = STDOUT_FILENO; + } + + /* Assemble and run the request. */ + + webclient_set_defaults(&ctx); + ctx.method = method; + ctx.url = url; + ctx.buffer = buffer; + ctx.buflen = CONFIG_SYSTEM_CURL_BUFFERSIZE; + ctx.sink_callback = curl_sink_callback; + ctx.sink_callback_arg = &sink; + + if (nheaders > 0) + { + ctx.headers = headers; + ctx.nheaders = nheaders; + } + + if (verbose) + { + ctx.header_callback = curl_header_callback; + + fprintf(stderr, "> %s %s\n", method, url); + for (i = 0; i < nheaders; i++) + { + fprintf(stderr, "> %s\n", headers[i]); + } + } + + if (body.nchunks > 0) + { + ctx.body_callback = curl_body_callback; + ctx.body_callback_arg = &body; + ctx.bodylen = curl_body_total(&body); + } + + ret = webclient_perform(&ctx); + if (ret < 0) + { + fprintf(stderr, "curl: request failed: %d\n", ret); + } + else + { + fprintf(stderr, "curl: HTTP %u\n", ctx.http_status); + } + + if (sink.fd != STDOUT_FILENO) + { + close(sink.fd); + } + +errout_buffer: + free(buffer); + +errout_body: + curl_body_cleanup(&body); + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/system/dd/dd_main.c b/system/dd/dd_main.c index f08041836..be9aba342 100644 --- a/system/dd/dd_main.c +++ b/system/dd/dd_main.c @@ -32,7 +32,7 @@ #include #ifdef __NuttX__ -#include +#include #endif #include #include @@ -62,10 +62,18 @@ #define CONFIG_SYSTEM_DD_PROGNAME "dd" #endif #if !defined(__NuttX__) -#define FAR -#define NSEC_PER_USEC 1000 -#define USEC_PER_SEC 1000000 -#define NSEC_PER_SEC 1000000000 +# ifndef FAR +# define FAR +# endif +# ifndef NSEC_PER_USEC +# define NSEC_PER_USEC 1000 +# endif +# ifndef USEC_PER_SEC +# define USEC_PER_SEC 1000000 +# endif +# ifndef NSEC_PER_SEC +# define NSEC_PER_SEC 1000000000 +# endif #endif #define g_dd CONFIG_SYSTEM_DD_PROGNAME @@ -348,7 +356,7 @@ int main(int argc, FAR char **argv) } else if (strncmp(argv[i], "verify", 6) == 0) { - dd.oflags |= O_RDONLY; + dd.oflags = (dd.oflags & ~O_ACCMODE) | O_RDWR; } else if (strncmp(argv[i], "conv=", 5) == 0) { @@ -401,7 +409,8 @@ int main(int argc, FAR char **argv) /* If verify enabled, infile and outfile are mandatory */ - if ((dd.oflags & O_RDONLY) && (infile == NULL || outfile == NULL)) + if ((dd.oflags & O_ACCMODE) == O_RDWR && + (infile == NULL || outfile == NULL)) { fprintf(stderr, "%s: invalid parameters: %s\n", g_dd, strerror(EINVAL)); @@ -500,8 +509,8 @@ int main(int argc, FAR char **argv) #ifdef CONFIG_SYSTEM_DD_STATS clock_gettime(CLOCK_MONOTONIC, &ts1); - elapsed = (((uint64_t)ts1.tv_sec * NSEC_PER_SEC) + ts1.tv_nsec); - elapsed -= (((uint64_t)ts0.tv_sec * NSEC_PER_SEC) + ts0.tv_nsec); + elapsed = (ts1.tv_sec * NSEC_PER_SEC) + ts1.tv_nsec; + elapsed -= (ts0.tv_sec * NSEC_PER_SEC) + ts0.tv_nsec; elapsed /= NSEC_PER_USEC; /* usec */ fprintf(stderr, "%" PRIu64 " bytes (%" PRIu32 " blocks) copied, %u usec, ", @@ -511,7 +520,7 @@ int main(int argc, FAR char **argv) / ((double)elapsed / USEC_PER_SEC))); #endif - if (ret == 0 && (dd.oflags & O_RDONLY) != 0) + if (ret == 0 && (dd.oflags & O_ACCMODE) == O_RDWR) { ret = dd_verify(&dd); } diff --git a/system/dhcp6c/renew6_main.c b/system/dhcp6c/renew6_main.c index d0f44382b..7149af1f2 100644 --- a/system/dhcp6c/renew6_main.c +++ b/system/dhcp6c/renew6_main.c @@ -25,7 +25,7 @@ ****************************************************************************/ #include -#include +#include #include #include diff --git a/system/fastboot/Kconfig b/system/fastboot/Kconfig index f5b14cd02..0842bcb0d 100644 --- a/system/fastboot/Kconfig +++ b/system/fastboot/Kconfig @@ -45,4 +45,11 @@ config SYSTEM_FASTBOOTD_SHELL e.g. fastboot oem shell ps e.g. fastboot oem shell "mkrd -m 10 -s 512 640" +config SYSTEM_FASTBOOTD_NET_INIT + bool "Network initialization" + default n + select NETUTILS_NETINIT + ---help--- + This option enables/disables all network initialization in fastboot server. + endif # SYSTEM_FASTBOOTD diff --git a/system/fastboot/fastboot.c b/system/fastboot/fastboot.c index b20be6b7d..8c7448e3e 100644 --- a/system/fastboot/fastboot.c +++ b/system/fastboot/fastboot.c @@ -52,6 +52,10 @@ #include #include +#ifdef CONFIG_SYSTEM_FASTBOOTD_NET_INIT +# include "netutils/netinit.h" +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -222,6 +226,10 @@ static void fastboot_filedump(FAR struct fastboot_ctx_s *ctx, static void fastboot_shell(FAR struct fastboot_ctx_s *ctx, FAR const char *arg); #endif +#ifdef CONFIG_BOARDCTL_SWITCH_BOOT +static void fastboot_switchboot(FAR struct fastboot_ctx_s *context, + FAR const char *arg); +#endif /* USB transport */ @@ -268,6 +276,9 @@ static const struct fastboot_cmd_s g_oem_cmd[] = #ifdef CONFIG_SYSTEM_FASTBOOTD_SHELL { "shell", fastboot_shell }, #endif +#ifdef CONFIG_BOARDCTL_SWITCH_BOOT + { "switchboot", fastboot_switchboot }, +#endif }; #ifdef CONFIG_BOARD_MEMORY_RANGE @@ -341,14 +352,14 @@ static int fastboot_write(int fd, FAR const void *buf, size_t len) static void fastboot_ack(FAR struct fastboot_ctx_s *ctx, FAR const char *code, FAR const char *reason) { - char response[FASTBOOT_MSG_LEN]; + char response[FASTBOOT_MSG_LEN + 4]; if (reason == NULL) { reason = ""; } - snprintf(response, FASTBOOT_MSG_LEN, "%s%s", code, reason); + snprintf(response, FASTBOOT_MSG_LEN + 4, "%s%s", code, reason); ctx->ops->write(ctx, response, strlen(response)); } @@ -860,6 +871,7 @@ static void fastboot_filedump(FAR struct fastboot_ctx_s *ctx, FAR const char *arg) { struct stat sb; + size_t len; int ret; if (!arg) @@ -868,14 +880,30 @@ static void fastboot_filedump(FAR struct fastboot_ctx_s *ctx, return; } - ret = sscanf(arg, "%s %" PRIdOFF " %zu", ctx->upload_param.u.file.path, - &ctx->upload_param.u.file.offset, &ctx->upload_param.size); - if (ret != 1 && ret != 3) + arg += strspn(arg, " \t\r\n"); + len = strcspn(arg, " \t\r\n"); + if (len == 0) { fastboot_fail(ctx, "Failed to parse arguments"); return; } - else if (ret == 1) + + if (len >= sizeof(ctx->upload_param.u.file.path)) + { + len = sizeof(ctx->upload_param.u.file.path) - 1; + } + + memcpy(ctx->upload_param.u.file.path, arg, len); + ctx->upload_param.u.file.path[len] = '\0'; + + ret = sscanf(arg + len, "%" PRIdOFF " %zu", + &ctx->upload_param.u.file.offset, &ctx->upload_param.size); + if (ret != EOF && ret != 0 && ret != 2) + { + fastboot_fail(ctx, "Failed to parse arguments"); + return; + } + else if (ret != 2) { ret = stat(ctx->upload_param.u.file.path, &sb); if (ret < 0) @@ -926,6 +954,21 @@ static void fastboot_shell(FAR struct fastboot_ctx_s *ctx, } #endif +#ifdef CONFIG_BOARDCTL_SWITCH_BOOT +static void fastboot_switchboot(FAR struct fastboot_ctx_s *context, + FAR const char *arg) +{ + if (!arg) + { + fastboot_fail(context, "Invalid argument"); + return; + } + + boardctl(BOARDIOC_SWITCH_BOOT, (uintptr_t)&arg[0]); + fastboot_okay(context, ""); +} +#endif + static void fastboot_upload(FAR struct fastboot_ctx_s *ctx, FAR const char *arg) { @@ -987,11 +1030,12 @@ static void fastboot_oem(FAR struct fastboot_ctx_s *ctx, FAR const char *arg) } } -static void fastboot_command_loop(FAR struct fastboot_ctx_s *ctx, - size_t nctx) +static int fastboot_command_loop(FAR struct fastboot_ctx_s *ctx, + size_t nctx) { FAR struct fastboot_ctx_s *c; struct epoll_event ev[nctx]; + int ret = OK; int epfd; int n; @@ -999,14 +1043,15 @@ static void fastboot_command_loop(FAR struct fastboot_ctx_s *ctx, if (epfd < 0) { fb_err("open epoll failed %d", errno); - return; + return epfd; } for (c = ctx, n = nctx; n-- > 0; c++) { ev[n].events = EPOLLIN; ev[n].data.ptr = c; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, c->tran_fd[0], &ev[n]) < 0) + ret = epoll_ctl(epfd, EPOLL_CTL_ADD, c->tran_fd[0], &ev[n]); + if (ret < 0) { fb_err("err add poll %d", c->tran_fd[0]); goto epoll_close; @@ -1015,7 +1060,8 @@ static void fastboot_command_loop(FAR struct fastboot_ctx_s *ctx, if (ctx->left > 0) { - if (epoll_wait(epfd, ev, nitems(ev), ctx->left) <= 0) + ret = epoll_wait(epfd, ev, nitems(ev), ctx->left); + if (ret <= 0) { goto epoll_close; } @@ -1070,6 +1116,7 @@ epoll_close: } close(epfd); + return ret; } static void fastboot_publish(FAR struct fastboot_ctx_s *ctx, @@ -1259,8 +1306,15 @@ static int fastboot_tcp_initialize(FAR struct fastboot_ctx_s *ctx) { struct sockaddr_in addr; - ctx->tran_fd[0] = socket(AF_INET, SOCK_STREAM, - SOCK_CLOEXEC | SOCK_NONBLOCK); +#ifdef CONFIG_SYSTEM_FASTBOOTD_NET_INIT + /* Bring up the network */ + + netinit_bringup(); +#endif + + ctx->tran_fd[0] = socket(AF_INET, + SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + 0); if (ctx->tran_fd[0] < 0) { fb_err("create socket failed %d", errno); @@ -1424,7 +1478,7 @@ static int fastboot_tcp_write(FAR struct fastboot_ctx_s *ctx, static int fastboot_context_initialize(FAR struct fastboot_ctx_s *ctx, size_t nctx) { - int ret; + int ret = ERROR; for (; nctx-- > 0; ctx++) { @@ -1455,7 +1509,7 @@ static int fastboot_context_initialize(FAR struct fastboot_ctx_s *ctx, } } - return 0; + return ret; } static void fastboot_context_deinit(FAR struct fastboot_ctx_s *ctx, @@ -1475,11 +1529,30 @@ static void fastboot_context_deinit(FAR struct fastboot_ctx_s *ctx, * Public Functions ****************************************************************************/ -int main(int argc, FAR char **argv) +int fastboot_handler(uint64_t timeout) { struct fastboot_ctx_s context[nitems(g_tran_ops)]; int ret; + context[0].left = timeout; + ret = fastboot_context_initialize(context, nitems(context)); + if (ret < 0) + { + return ret; + } + + fastboot_create_publish(context, nitems(context)); + ret = fastboot_command_loop(context, nitems(context)); + fastboot_free_publish(context, nitems(context)); + fastboot_context_deinit(context, nitems(context)); + + return ret; +} + +int main(int argc, FAR char **argv) +{ + uint64_t timeout = 0; + if (argc > 1) { if (strcmp(argv[1], "-h") == 0) @@ -1490,22 +1563,11 @@ int main(int argc, FAR char **argv) return 0; } - if (sscanf(argv[1], "%" SCNu64 , &context[0].left) != 1) + if (sscanf(argv[1], "%" SCNu64 , &timeout) != 1) { return -EINVAL; } } - ret = fastboot_context_initialize(context, nitems(context)); - if (ret < 0) - { - return ret; - } - - fastboot_create_publish(context, nitems(context)); - fastboot_command_loop(context, nitems(context)); - fastboot_free_publish(context, nitems(context)); - fastboot_context_deinit(context, nitems(context)); - - return ret; + return fastboot_handler(timeout); } diff --git a/system/fdt/Kconfig b/system/fdt/Kconfig index 0d3af5e54..1d9c7f235 100644 --- a/system/fdt/Kconfig +++ b/system/fdt/Kconfig @@ -12,7 +12,7 @@ config SYSTEM_FDT if LIBC_FDT config SYSTEM_FDTDUMP - bool "system fdtdump command" + tristate "system fdtdump command" default n select SYSTEM_FDT ---help--- @@ -35,7 +35,7 @@ config SYSTEM_FDTDUMP_PRIORITY endif # SYSTEM_FDTDUMP config SYSTEM_FDTGET - bool "system fdtget command" + tristate "system fdtget command" default n select SYSTEM_FDT ---help--- @@ -58,7 +58,7 @@ config SYSTEM_FDTGET_PRIORITY endif # SYSTEM_FDTGET config SYSTEM_FDTPUT - bool "system fdtput command" + tristate "system fdtput command" default n select SYSTEM_FDT ---help--- @@ -81,7 +81,7 @@ config SYSTEM_FDTPUT_PRIORITY endif # SYSTEM_FDTPUT config SYSTEM_FDTOVERLAY - bool "system fdtoverlay command" + tristate "system fdtoverlay command" default n select SYSTEM_FDT ---help--- diff --git a/system/fdt/Makefile b/system/fdt/Makefile index 58329c89a..6b4284697 100644 --- a/system/fdt/Makefile +++ b/system/fdt/Makefile @@ -26,29 +26,33 @@ include $(APPDIR)/Make.defs CSRCS = util.c -ifeq ($(CONFIG_SYSTEM_FDTDUMP),y) +ifneq ($(CONFIG_SYSTEM_FDTDUMP),) MAINSRC += fdtdump.c + MODULE += $(CONFIG_SYSTEM_FDTDUMP) PROGNAME += fdtdump STACKSIZE += $(CONFIG_SYSTEM_FDTDUMP_STACKSIZE) PRIORITY += $(CONFIG_SYSTEM_FDTDUMP_PRIORITY) endif -ifeq ($(CONFIG_SYSTEM_FDTGET),y) +ifneq ($(CONFIG_SYSTEM_FDTGET),) MAINSRC += fdtget.c + MODULE += $(CONFIG_SYSTEM_FDTGET) PROGNAME += fdtget STACKSIZE += $(CONFIG_SYSTEM_FDTGET_STACKSIZE) PRIORITY += $(CONFIG_SYSTEM_FDTGET_PRIORITY) endif -ifeq ($(CONFIG_SYSTEM_FDTPUT),y) +ifneq ($(CONFIG_SYSTEM_FDTPUT),) MAINSRC += fdtput.c + MODULE += $(CONFIG_SYSTEM_FDTPUT) PROGNAME += fdtput STACKSIZE += $(CONFIG_SYSTEM_FDTPUT_STACKSIZE) PRIORITY += $(CONFIG_SYSTEM_FDTPUT_PRIORITY) endif -ifeq ($(CONFIG_SYSTEM_FDTOVERLAY),y) +ifneq ($(CONFIG_SYSTEM_FDTOVERLAY),) MAINSRC += fdtoverlay.c + MODULE += $(CONFIG_SYSTEM_FDTOVERLAY) PROGNAME += fdtoverlay STACKSIZE += $(CONFIG_SYSTEM_FDTOVERLAY_STACKSIZE) PRIORITY += $(CONFIG_SYSTEM_FDTOVERLAY_PRIORITY) diff --git a/system/i2c/i2c_main.c b/system/i2c/i2c_main.c index 7abe9bdd1..80e7974d2 100644 --- a/system/i2c/i2c_main.c +++ b/system/i2c/i2c_main.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include "i2ctool.h" @@ -165,7 +165,7 @@ static int i2ccmd_help(FAR struct i2ctool_s *i2ctool, int argc, "o Arguments are \"sticky\". For example, once " "the I2C address is\n"); i2ctool_printf(i2ctool, - " specified, that address will be re-used until " + " specified, that address will be reused until " "it is changed.\n"); i2ctool_printf(i2ctool, "\nWARNING:\n"); i2ctool_printf(i2ctool, diff --git a/system/iptables/Kconfig b/system/iptables/Kconfig index 171b52ed0..a98f00771 100644 --- a/system/iptables/Kconfig +++ b/system/iptables/Kconfig @@ -29,4 +29,9 @@ config SYSTEM_IPTABLES_STACKSIZE int "iptables & ip6tables stack size" default DEFAULT_TASK_STACKSIZE +config SYSTEM_IPTABLES_LOCK_FILE_PATH + string "File lock path to prevent concurrent overwrite" + default "/tmp/iptables.lock" + depends on FS_LOCK_BUCKET_SIZE > 0 + endif diff --git a/system/iptables/iptables.c b/system/iptables/iptables.c index 7072ed968..367519a34 100644 --- a/system/iptables/iptables.c +++ b/system/iptables/iptables.c @@ -26,6 +26,7 @@ #include +#include #include #include @@ -33,17 +34,62 @@ #include "iptables.h" #include "netutils/netlib.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if CONFIG_FS_LOCK_BUCKET_SIZE == 0 +# define iptables_lock() 0 +# define iptables_unlock(fd) (void)(fd) +#endif + /**************************************************************************** * Private Types ****************************************************************************/ typedef CODE int - (*iptables_command_func_t)(FAR const struct iptables_args_s *args); +(*iptables_command_func_t)(FAR const struct iptables_args_s *args); /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: iptables_lock / iptables_unlock + * + * Description: + * Lock and unlock iptables to prevent concurrent overwrite. + * + ****************************************************************************/ + +#if CONFIG_FS_LOCK_BUCKET_SIZE > 0 +static int iptables_lock(void) +{ + struct flock lock; + int lockfd; + + lockfd = open(CONFIG_SYSTEM_IPTABLES_LOCK_FILE_PATH, O_CREAT | O_RDWR, + 0666); + if (lockfd < 0) + { + return lockfd; + } + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + fcntl(lockfd, F_SETLKW, &lock); + + return lockfd; +} + +static int iptables_unlock(int lockfd) +{ + return close(lockfd); +} +#endif + /**************************************************************************** * Name: iptables_addr2str * @@ -485,6 +531,7 @@ static int iptables_apply(FAR const struct iptables_args_s *args, int main(int argc, FAR char *argv[]) { struct iptables_args_s args; + int lock; int ret = iptables_parse(&args, argc, argv); if (ret < 0 || args.cmd == COMMAND_INVALID) @@ -493,6 +540,13 @@ int main(int argc, FAR char *argv[]) return ret; } + lock = iptables_lock(); + if (lock < 0) + { + printf("Failed to lock iptables!\n"); + return lock; + } + #ifdef CONFIG_NET_IPFILTER if (args.table == NULL || strcmp(args.table, XT_TABLE_NAME_FILTER) == 0) { @@ -506,7 +560,7 @@ int main(int argc, FAR char *argv[]) else #endif #ifdef CONFIG_NET_NAT - if (strcmp(args.table, TABLE_NAME_NAT) == 0) + if (args.table != NULL && strcmp(args.table, TABLE_NAME_NAT) == 0) { ret = iptables_apply(&args, iptables_nat_command); if (ret < 0) @@ -520,5 +574,7 @@ int main(int argc, FAR char *argv[]) printf("Unknown table: %s\n", args.table); } + iptables_unlock(lock); + return ret; } diff --git a/system/iptables/iptables_utils.c b/system/iptables/iptables_utils.c index 9428e0fea..86c1492de 100644 --- a/system/iptables/iptables_utils.c +++ b/system/iptables/iptables_utils.c @@ -546,7 +546,7 @@ int iptables_parse_ip(FAR const char *str, FAR void *addr, FAR void *mask, return -EINVAL; } - *mask4 <<= 32 - prefixlen; + *mask4 >>= 32 - prefixlen; } #endif diff --git a/system/irtest/Kconfig b/system/irtest/Kconfig index 34743bb87..e77bea6aa 100644 --- a/system/irtest/Kconfig +++ b/system/irtest/Kconfig @@ -32,7 +32,7 @@ config SYSTEM_IRTEST_MAX_NIRDEV default 2 config SYSTEM_IRTEST_MAX_SIRDATA - int "The Maximum size of sending data in unsigned int" + int "The Maximum size of sending data in nwords (4 bytes)" default 64 endif diff --git a/system/irtest/cmd.cxx b/system/irtest/cmd.cxx index d61527df7..c8673c9b1 100644 --- a/system/irtest/cmd.cxx +++ b/system/irtest/cmd.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "enum.hpp" @@ -226,7 +227,8 @@ CMD1(close_device, size_t, index) CMD1(write_data, size_t, index) { - unsigned int data[CONFIG_SYSTEM_IRTEST_MAX_SIRDATA]; + uint32_t data[CONFIG_SYSTEM_IRTEST_MAX_SIRDATA]; + uint32_t tmp; if (index >= CONFIG_SYSTEM_IRTEST_MAX_NIRDEV) { @@ -236,7 +238,7 @@ CMD1(write_data, size_t, index) int size = 0; for (; size < CONFIG_SYSTEM_IRTEST_MAX_SIRDATA; size++) { - unsigned int tmp = get_next_arg < unsigned int > (); + tmp = get_next_arg < uint32_t > (); if (tmp == 0) { break; @@ -245,19 +247,30 @@ CMD1(write_data, size_t, index) data[size] = tmp; } + /* Avoid silently truncating long write_data commands. */ + + if (size == CONFIG_SYSTEM_IRTEST_MAX_SIRDATA) + { + tmp = get_next_arg < uint32_t > (); + if (tmp != 0) + { + return -E2BIG; + } + } + /* lirc require the odd length */ if (size % 2 == 0) { int result = write(g_irdevs[index], data, - sizeof(unsigned int) * (size - 1)); + sizeof(uint32_t) * (size - 1)); usleep(data[size - 1]); return result; } else { return write(g_irdevs[index], data, - sizeof(unsigned int) * size); + sizeof(uint32_t) * size); } } @@ -268,20 +281,20 @@ CMD2(read_data, size_t, index, size_t, size) return ERROR; } - unsigned int data[size]; + uint32_t data[size]; int result = read(g_irdevs[index], data, sizeof(data)); if (result > 0) { - result /= sizeof(unsigned int); + result /= sizeof(uint32_t); for (int i = 0; i < result; i++) { if (i + 1 == result) { - printf("%d\n", data[i]); + printf("%" PRIu32 "\n", data[i]); } else { - printf("%d, ", data[i]); + printf("%" PRIu32 ", ", data[i]); } } } diff --git a/system/irtest/main.cxx b/system/irtest/main.cxx index d8534aae6..a50976666 100644 --- a/system/irtest/main.cxx +++ b/system/irtest/main.cxx @@ -24,11 +24,17 @@ * Included Files ****************************************************************************/ +#include + #include #include "system/readline.h" #include "cmd.hpp" +#ifndef CONFIG_LINE_MAX +# define CONFIG_LINE_MAX 512 +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -48,7 +54,7 @@ extern "C" { int main(int argc, char *argv[]) { - char cmdline[512]; + char cmdline[CONFIG_LINE_MAX]; init_device(); diff --git a/system/kbd/CMakeLists.txt b/system/kbd/CMakeLists.txt new file mode 100644 index 000000000..8ee13e65b --- /dev/null +++ b/system/kbd/CMakeLists.txt @@ -0,0 +1,35 @@ +# ############################################################################## +# apps/system/kbd/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_KBD) + nuttx_add_application( + NAME + ${CONFIG_SYSTEM_KBD_PROGNAME} + PRIORITY + ${CONFIG_SYSTEM_KBD_PRIORITY} + STACKSIZE + ${CONFIG_SYSTEM_KBD_STACKSIZE} + MODULE + ${CONFIG_SYSTEM_KBD} + SRCS + kbd_main.c) +endif() diff --git a/system/kbd/Kconfig b/system/kbd/Kconfig new file mode 100644 index 000000000..d1030ab09 --- /dev/null +++ b/system/kbd/Kconfig @@ -0,0 +1,38 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_KBD + tristate "Keyboard dump" + default n + depends on INPUT_KEYBOARD + ---help--- + Dump what a keyboard reports. This reads any keyboard registered + with keyboard_register(), whatever the hardware behind it is, so it + is the tool to reach for when bringing up a new keyboard. + + Replaces the hidkbd and keyboard examples, which did the same thing + for one kind of keyboard each. + +if SYSTEM_KBD + +config SYSTEM_KBD_PROGNAME + string "Program name" + default "kbd" + +config SYSTEM_KBD_PRIORITY + int "Task priority" + default 100 + +config SYSTEM_KBD_STACKSIZE + int "Task stack size" + default DEFAULT_TASK_STACKSIZE + +config SYSTEM_KBD_DEVPATH + string "Keyboard device path" + default "/dev/kbd0" + ---help--- + The keyboard to read when none is named on the command line. + +endif diff --git a/system/kbd/Make.defs b/system/kbd/Make.defs new file mode 100644 index 000000000..f80780936 --- /dev/null +++ b/system/kbd/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/system/kbd/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_KBD),) +CONFIGURED_APPS += $(APPDIR)/system/kbd +endif diff --git a/system/kbd/Makefile b/system/kbd/Makefile new file mode 100644 index 000000000..97c5dad19 --- /dev/null +++ b/system/kbd/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/system/kbd/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = $(CONFIG_SYSTEM_KBD_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_KBD_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_KBD_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_KBD) + +MAINSRC = kbd_main.c + +include $(APPDIR)/Application.mk diff --git a/system/kbd/kbd_main.c b/system/kbd/kbd_main.c new file mode 100644 index 000000000..20851077b --- /dev/null +++ b/system/kbd/kbd_main.c @@ -0,0 +1,476 @@ +/**************************************************************************** + * apps/system/kbd/kbd_main.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* Dump what a keyboard reports. + * + * Every keyboard registered with keyboard_register() is read the same way, + * whatever the hardware behind it is: a USB HID keyboard, a matrix, the + * simulator, virtio. So this works with all of them, and it is the place + * to look when bringing up a new one. + * + * The device delivers struct keyboard_event_s events unless the kernel was + * built with INPUT_KEYBOARD_BYTESTREAM, in which case it delivers the byte + * stream that the keyboard codec defines. This follows that setting rather + * than having a switch of its own. + * + * With -i it goes the other way and injects into a uinput keyboard, either + * what it reads from its own stdin or every key of another keyboard. So an + * application can be driven from a serial console with no keyboard hardware + * at all, and a real keyboard and an injected one can drive it at the same + * time, which neither can do on its own since an application opens a single + * device. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM +# include +# include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define KBD_READ_SIZE 64 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: kbd_typename + ****************************************************************************/ + +static FAR const char *kbd_typename(uint32_t type) +{ + switch (type) + { + case KEYBOARD_PRESS: + return "press"; + + case KEYBOARD_RELEASE: + return "release"; + + case KEYBOARD_SPECPRESS: + return "specpress"; + + case KEYBOARD_SPECREL: + return "specrel"; + + default: + return "unknown"; + } +} + +/**************************************************************************** + * Name: kbd_show + * + * Description: + * Print one key. A normal key carries a character, a special key carries + * a value from enum kbd_keycode_e, and the type is what says which. + * + ****************************************************************************/ + +static void kbd_show(uint32_t code, uint32_t type) +{ + if (type == KEYBOARD_PRESS || type == KEYBOARD_RELEASE) + { + printf("%-9s code %3" PRIu32 " '%c'\n", kbd_typename(type), code, + isprint(code) ? (int)code : '.'); + } + else + { + printf("%-9s keycode %" PRIu32 "\n", kbd_typename(type), code); + } + + fflush(stdout); +} + +/**************************************************************************** + * Name: kbd_dump + * + * Description: + * Print everything in one read. + * + * Returned Value: + * The number of keys printed. + * + ****************************************************************************/ + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM +static size_t kbd_dump(FAR char *buffer, size_t nbytes) +{ + struct lib_meminstream_s stream; + struct kbd_getstate_s state; + uint8_t ch; + size_t nkeys = 0; + int ret; + + lib_meminstream(&stream, buffer, nbytes); + memset(&state, 0, sizeof(state)); + + for (; ; ) + { + ret = kbd_decode(&stream.common, &state, &ch); + if (ret == KBD_ERROR) + { + break; + } + + kbd_show(ch, ret); + nkeys++; + } + + return nkeys; +} +#else +static size_t kbd_dump(FAR char *buffer, size_t nbytes) +{ + FAR struct keyboard_event_s *key = (FAR struct keyboard_event_s *)buffer; + size_t nkeys = 0; + + if ((nbytes % sizeof(struct keyboard_event_s)) != 0) + { + fprintf(stderr, "kbd: short read of %zu bytes, ignoring\n", nbytes); + return 0; + } + + while (nbytes >= sizeof(struct keyboard_event_s)) + { + kbd_show(key->code, key->type); + + nbytes -= sizeof(struct keyboard_event_s); + key++; + nkeys++; + } + + return nkeys; +} +#endif + +/**************************************************************************** + * Name: kbd_inject + * + * Description: + * Turn each byte read from stdin into a key press and release on the + * given device. The device has to be a uinput keyboard: a real one has + * nothing to inject into. + * + * Note that this always writes struct keyboard_event_s. Injection goes + * through the lower half write method, which is not affected by + * INPUT_KEYBOARD_BYTESTREAM. + * + * Returned Value: + * The number of keys injected. + * + ****************************************************************************/ + +static bool kbd_emit(int fd, uint32_t code, uint32_t type) +{ + struct keyboard_event_s key; + + key.code = code; + key.type = type; + + if (write(fd, &key, sizeof(key)) != (ssize_t)sizeof(key)) + { + fprintf(stderr, "kbd: inject failed: %d\n", errno); + return false; + } + + return true; +} + +static size_t kbd_inject(int fd) +{ + unsigned char buf[KBD_READ_SIZE]; + uint32_t code; + ssize_t nread; + ssize_t i; + size_t nkeys = 0; + + for (; ; ) + { + nread = read(STDIN_FILENO, buf, sizeof(buf)); + if (nread <= 0) + { + break; + } + + for (i = 0; i < nread; i++) + { + /* A terminal sends a carriage return where a keyboard would + * send a line feed. + */ + + code = (buf[i] == '\r') ? '\n' : buf[i]; + + if (!kbd_emit(fd, code, KEYBOARD_PRESS)) + { + return nkeys; + } + + kbd_emit(fd, code, KEYBOARD_RELEASE); + nkeys++; + } + } + + return nkeys; +} + +/**************************************************************************** + * Name: kbd_forward + * + * Description: + * Copy every key from one keyboard onto another. Pointing an + * application at the destination lets a real keyboard and an injected one + * drive it at the same time, which neither can do on its own since an + * application opens a single device. + * + * Returned Value: + * The number of keys forwarded. + * + ****************************************************************************/ + +static size_t kbd_forward(int fd, int srcfd) +{ + char buf[KBD_READ_SIZE]; + ssize_t nread; + size_t nkeys = 0; +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + struct lib_meminstream_s stream; + struct kbd_getstate_s state; + uint8_t ch; + int ret; +#else + FAR struct keyboard_event_s *key; +#endif + + for (; ; ) + { + nread = read(srcfd, buf, sizeof(buf)); + if (nread <= 0) + { + break; + } + +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + + /* The source speaks the byte stream and the destination only takes + * events, so this decodes rather than copies. The decoder returns + * the event type directly. + */ + + memset(&state, 0, sizeof(state)); + lib_meminstream(&stream, buf, nread); + + for (; ; ) + { + ret = kbd_decode(&stream.common, &state, &ch); + if (ret == KBD_ERROR) + { + break; + } + + if (!kbd_emit(fd, ch, ret)) + { + return nkeys; + } + + nkeys++; + } +#else + key = (FAR struct keyboard_event_s *)buf; + + while (nread >= (ssize_t)sizeof(struct keyboard_event_s)) + { + if (!kbd_emit(fd, key->code, key->type)) + { + return nkeys; + } + + nread -= sizeof(struct keyboard_event_s); + key++; + nkeys++; + } +#endif + } + + return nkeys; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR const char *devpath = CONFIG_SYSTEM_KBD_DEVPATH; + FAR const char *srcpath = NULL; + char buffer[KBD_READ_SIZE]; + bool inject = false; + size_t nkeys = 0; + size_t limit = 0; + ssize_t nbytes; + int argi = 1; + int srcfd; + int fd; + + if (argi < argc && strcmp(argv[argi], "-i") == 0) + { + inject = true; + argi++; + } + + if (argi < argc) + { + devpath = argv[argi++]; + } + + if (argi < argc) + { + /* When injecting, the extra argument is the keyboard to forward from + * rather than a count: there is nothing to count. + */ + + if (inject) + { + srcpath = argv[argi]; + } + else + { + limit = strtoul(argv[argi], NULL, 10); + } + } + + /* Wait for the device. A USB keyboard appears when it is plugged in, so + * this is the normal way to start rather than an error path. + */ + + for (; ; ) + { + fd = open(devpath, (inject ? O_WRONLY : O_RDONLY) | O_CLOEXEC); + if (fd >= 0) + { + break; + } + + printf("kbd: waiting for %s: %d\n", devpath, errno); + fflush(stdout); + sleep(3); + } + + if (inject) + { + if (srcpath == NULL) + { + printf("kbd: injecting into %s, type to send, Ctrl-D to stop\n", + devpath); + fflush(stdout); + + nkeys = kbd_inject(fd); + } + else + { + srcfd = open(srcpath, O_RDONLY | O_CLOEXEC); + if (srcfd < 0) + { + fprintf(stderr, "kbd: open %s failed: %d\n", srcpath, errno); + close(fd); + return EXIT_FAILURE; + } + + printf("kbd: forwarding %s into %s\n", srcpath, devpath); + fflush(stdout); + + nkeys = kbd_forward(fd, srcfd); + close(srcfd); + } + + goto done; + } + + printf("kbd: reading %s, %s\n", devpath, +#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM + "byte stream" +#else + "keyboard events" +#endif + ); + fflush(stdout); + + for (; ; ) + { + nbytes = read(fd, buffer, sizeof(buffer)); + if (nbytes < 0) + { + if (errno == EINTR) + { + continue; + } + + fprintf(stderr, "kbd: read %s failed: %d\n", devpath, errno); + break; + } + else if (nbytes == 0) + { + /* The keyboard is gone */ + + break; + } + + nkeys += kbd_dump(buffer, nbytes); + + if (limit > 0 && nkeys >= limit) + { + break; + } + } + +done: + close(fd); + printf("kbd: %zu keys\n", nkeys); + return EXIT_SUCCESS; +} diff --git a/system/libffi/.gitignore b/system/libffi/.gitignore new file mode 100644 index 000000000..26d13e2cd --- /dev/null +++ b/system/libffi/.gitignore @@ -0,0 +1,4 @@ +/*.zip +/build/ +/install/ +/libffi/ diff --git a/system/libffi/Kconfig b/system/libffi/Kconfig new file mode 100644 index 000000000..0b11516ef --- /dev/null +++ b/system/libffi/Kconfig @@ -0,0 +1,24 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config LIB_LIBFFI + bool "libffi" + default n + ---help--- + Enable the libffi Foreign Function Interface library. libffi + provides a portable way to call C functions with arguments + determined at runtime. It is required by CPython's _ctypes module. + +if LIB_LIBFFI + +config LIB_LIBFFI_VERSION + string "libffi commit hash" + default "9760868682cc9a33008761f158d86481d56738aa" + ---help--- + Git commit hash of the libffi repository to download and build. + The source is fetched from https://github.com/libffi/libffi at + the specified commit. + +endif # LIB_LIBFFI diff --git a/system/libffi/Make.defs b/system/libffi/Make.defs new file mode 100644 index 000000000..5949ff04f --- /dev/null +++ b/system/libffi/Make.defs @@ -0,0 +1,28 @@ +############################################################################ +# apps/system/libffi/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_LIB_LIBFFI),) +CONFIGURED_APPS += $(APPDIR)/system/libffi + +EXTRA_LIBPATHS += -L$(APPDIR)/system/libffi/install/lib +EXTRA_LIBS += -lffi +endif diff --git a/system/libffi/Makefile b/system/libffi/Makefile new file mode 100644 index 000000000..c2b4810ed --- /dev/null +++ b/system/libffi/Makefile @@ -0,0 +1,135 @@ +############################################################################ +# apps/system/libffi/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +LIBFFI_URL = https://github.com/libffi/libffi/archive +LIBFFI_COMMIT = $(patsubst "%",%,$(strip $(CONFIG_LIB_LIBFFI_VERSION))) +LIBFFI_ZIP = libffi-$(LIBFFI_COMMIT).zip +LIBFFI_SRC = libffi +LIBFFI_BUILDDIR = $(CURDIR)/build +LIBFFI_INSTALLDIR = $(CURDIR)/install +APPS_INCDIR = $(APPDIR)$(DELIM)include + +UNPACK ?= unzip -q -o + +# --------------------------------------------------------------------------- +# Derive the autotools --host triplet from the NuttX configuration. +# +# libffi's configure.host matches on patterns such as: +# riscv*-*-* arm*-*-* aarch64*-*-* xtensa*-*-* +# mips*-*-* i?86-*-* x86_64-*-* +# +# We map CONFIG_ARCH (and, for RISC-V, the word-size selectors) to the +# CPU part that libffi expects, then append "-nuttx-elf" as vendor-os. +# --------------------------------------------------------------------------- + +ifeq ($(CONFIG_ARCH),risc-v) + ifeq ($(CONFIG_ARCH_RV64),y) + LIBFFI_CPU = riscv64 + else + LIBFFI_CPU = riscv32 + endif +else ifeq ($(CONFIG_ARCH),arm64) + LIBFFI_CPU = aarch64 +else ifeq ($(CONFIG_ARCH),arm) + LIBFFI_CPU = arm +else ifeq ($(CONFIG_ARCH),xtensa) + LIBFFI_CPU = xtensa +else ifeq ($(CONFIG_ARCH),mips) + LIBFFI_CPU = mips +else ifeq ($(CONFIG_ARCH),x86_64) + LIBFFI_CPU = x86_64 +else ifeq ($(CONFIG_ARCH),x86) + LIBFFI_CPU = i686 +else + LIBFFI_CPU = $(subst -,,$(CONFIG_ARCH)) +endif + +LIBFFI_HOST = $(LIBFFI_CPU)-nuttx-elf + +# --------------------------------------------------------------------------- +# CFLAGS for the libffi build itself. +# +# We start from the NuttX-provided CFLAGS (which already carry -march, +# -mabi, -isystem paths, -D__NuttX__, etc.) and add a few flags that the +# library needs. We deliberately strip -nostdlib because autoconf link +# checks need the default startfiles/libs to succeed. +# --------------------------------------------------------------------------- + +LIBFFI_CFLAGS = $(filter-out -nostdlib -Werror -Werror=% -Wno-cpp,$(CFLAGS)) +LIBFFI_CFLAGS += -ffunction-sections -fdata-sections -O2 + +# --------------------------------------------------------------------------- +# Download, autoreconf, configure, build, install +# --------------------------------------------------------------------------- + +ifeq ($(wildcard $(LIBFFI_SRC)/.git),) + +$(LIBFFI_ZIP): + @echo "Downloading: $(LIBFFI_URL)/$(LIBFFI_COMMIT).zip" + $(Q) curl -L $(LIBFFI_URL)/$(LIBFFI_COMMIT).zip -o $(LIBFFI_ZIP) + +$(LIBFFI_SRC): $(LIBFFI_ZIP) + @echo "Unpacking: $(LIBFFI_ZIP) -> $(LIBFFI_SRC)" + $(Q) $(UNPACK) $(LIBFFI_ZIP) + $(Q) mv libffi-$(LIBFFI_COMMIT) $(LIBFFI_SRC) + +endif + +$(LIBFFI_SRC)/configure: $(LIBFFI_SRC) + @echo "Generating libffi configure script (autoreconf)" + $(Q) (cd $(LIBFFI_SRC) && autoreconf -v -i) + +$(LIBFFI_INSTALLDIR)/lib/libffi.a: $(LIBFFI_SRC)/configure + $(Q) mkdir -p $(LIBFFI_BUILDDIR) + $(Q) ( \ + cd $(LIBFFI_BUILDDIR) && \ + $(CURDIR)/$(LIBFFI_SRC)/configure \ + --host=$(LIBFFI_HOST) \ + --prefix=$(LIBFFI_INSTALLDIR) \ + --disable-shared \ + --enable-static \ + --disable-docs \ + --disable-exec-static-tramp \ + --disable-multi-os-directory \ + CC="$(CC)" \ + CFLAGS="$(LIBFFI_CFLAGS)" \ + AR="$(CROSSDEV)ar" \ + RANLIB="$(CROSSDEV)ranlib" \ + ) + $(MAKE) -C $(LIBFFI_BUILDDIR) -j$(shell nproc) + $(MAKE) -C $(LIBFFI_BUILDDIR) install + +context:: $(LIBFFI_INSTALLDIR)/lib/libffi.a + $(Q) mkdir -p $(APPS_INCDIR) + $(Q) cp $(LIBFFI_INSTALLDIR)/include/*.h $(APPS_INCDIR) + +distclean:: + $(call DELDIR, $(LIBFFI_BUILDDIR)) + $(call DELDIR, $(LIBFFI_INSTALLDIR)) + $(call DELDIR, $(LIBFFI_SRC)) + $(call DELFILE, $(LIBFFI_ZIP)) + $(call DELFILE, $(APPS_INCDIR)/ffi.h) + $(call DELFILE, $(APPS_INCDIR)/ffitarget.h) + +include $(APPDIR)/Application.mk diff --git a/system/libuv/0001-libuv-port-for-nuttx.patch b/system/libuv/0001-libuv-port-for-nuttx.patch index 0c2eb18dd..2364110e6 100644 --- a/system/libuv/0001-libuv-port-for-nuttx.patch +++ b/system/libuv/0001-libuv-port-for-nuttx.patch @@ -424,7 +424,7 @@ index 00000000..298bd2f8 + return 0; + } + -+ /* Make sure the memory is initiallized to zero using calloc() */ ++ /* Make sure the memory is initialized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (*addresses == NULL) { + freeifaddrs(addrs); @@ -3972,3 +3972,15 @@ diff --color -ur libuv_back/src/unix/thread.c libuv/src/unix/thread.c # include # define uv__cpu_set_t cpu_set_t #elif defined(__FreeBSD__) +diff --git a/src/unix/nuttx.c b/src/unix/nuttx.c +index d0e110a..e1c962e 100644 +--- a/src/unix/nuttx.c ++++ b/src/unix/nuttx.c +@@ -222,7 +222,7 @@ ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { + return UV_ENOTSUP; + } + +-ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags) { ++ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + return UV_ENOTSUP; + } diff --git a/system/libuv/Kconfig b/system/libuv/Kconfig index 6b2caf7ef..b969cda90 100644 --- a/system/libuv/Kconfig +++ b/system/libuv/Kconfig @@ -62,10 +62,10 @@ config LIBUV_UTILS_NONE bool "none" config LIBUV_UTILS_TEST - bool "uv_run_tests" + tristate "uv_run_tests" config LIBUV_UTILS_BENCHMARK - bool "uv_run_benchmarks" + tristate "uv_run_benchmarks" endchoice diff --git a/system/libuv/Makefile b/system/libuv/Makefile index b8d4e0d8b..dec76cc1a 100644 --- a/system/libuv/Makefile +++ b/system/libuv/Makefile @@ -129,13 +129,14 @@ ifneq ($(CONFIG_NET_UDP),) CSRCS += udp.c endif -ifeq ($(findstring y,$(CONFIG_LIBUV_UTILS_TEST)$(CONFIG_LIBUV_UTILS_BENCHMARK)), y) +ifneq ($(CONFIG_LIBUV_UTILS_TEST)$(CONFIG_LIBUV_UTILS_BENCHMARK),) PRIORITY = $(CONFIG_LIBUV_UTILS_PRIORITY) STACKSIZE = $(CONFIG_LIBUV_UTILS_STACKSIZE) endif ifneq ($(CONFIG_LIBUV_UTILS_TEST),) +MODULE = $(CONFIG_LIBUV_UTILS_TEST) PROGNAME = uv_run_tests MAINSRC = run-tests.c @@ -148,6 +149,7 @@ endif ifneq ($(CONFIG_LIBUV_UTILS_BENCHMARK),) +MODULE = $(CONFIG_LIBUV_UTILS_BENCHMARK) PROGNAME = uv_run_benchmarks MAINSRC = run-benchmarks.c diff --git a/system/lsan/lsan_main.c b/system/lsan/lsan_main.c index d0c8a06ea..f4a1f8579 100644 --- a/system/lsan/lsan_main.c +++ b/system/lsan/lsan_main.c @@ -26,6 +26,7 @@ #include #include +#include /**************************************************************************** * Public Functions diff --git a/system/lzf/Kconfig b/system/lzf/Kconfig index dad84b9a2..897945169 100644 --- a/system/lzf/Kconfig +++ b/system/lzf/Kconfig @@ -8,7 +8,7 @@ config SYSTEM_LZF default n depends on LIBC_LZF ---help--- - Enable theLZF compression tool + Enable the LZF compression tool if SYSTEM_LZF diff --git a/system/microros/.gitignore b/system/microros/.gitignore new file mode 100644 index 000000000..878b0e80a --- /dev/null +++ b/system/microros/.gitignore @@ -0,0 +1,13 @@ +# Build artifacts +/build/ +/install/ +/micro_ros_dev/ +/micro_ros_src/ + +# Generated files +/libmicroros.a +/toolchain.cmake +/colcon.meta + +# Archives +*.tar.gz diff --git a/system/microros/CMakeLists.txt b/system/microros/CMakeLists.txt new file mode 100644 index 000000000..1c3d89576 --- /dev/null +++ b/system/microros/CMakeLists.txt @@ -0,0 +1,67 @@ +# ############################################################################## +# apps/system/microros/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_MICROROS) + set(MICROROS_DIR ${CMAKE_CURRENT_LIST_DIR}) + set(MICROROS_LIB_DIR ${MICROROS_DIR}/micro_ros_lib) + set(MICROROS_DISTRO ${CONFIG_MICROROS_DISTRO}) + + include(ExternalProject) + + ExternalProject_Add( + microros_build + SOURCE_DIR ${MICROROS_LIB_DIR} + CONFIGURE_COMMAND "" + BUILD_COMMAND + ${CMAKE_COMMAND} -E env CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} + AR=${CMAKE_AR} CFLAGS=${CMAKE_C_FLAGS} CXXFLAGS=${CMAKE_CXX_FLAGS} make -C + ${MICROROS_DIR} libmicroros.a + INSTALL_COMMAND "" + BUILD_IN_SOURCE TRUE + BUILD_BYPRODUCTS ${MICROROS_DIR}/libmicroros.a) + + nuttx_add_library(microros STATIC IMPORTED GLOBAL) + set_target_properties( + microros PROPERTIES IMPORTED_LOCATION ${MICROROS_DIR}/libmicroros.a + INTERFACE_INCLUDE_DIRECTORIES ${MICROROS_DIR}/include) + add_dependencies(microros microros_build) + + # Transport glue compiled into the apps tree + set(MICROROS_TRANSPORT_SRCS ${MICROROS_DIR}/transport/microros_transport.c) + + if(CONFIG_MICROROS_TRANSPORT_UDP) + list(APPEND MICROROS_TRANSPORT_SRCS + ${MICROROS_DIR}/transport/microros_transport_udp.c) + endif() + + if(CONFIG_MICROROS_TRANSPORT_SERIAL) + list(APPEND MICROROS_TRANSPORT_SRCS + ${MICROROS_DIR}/transport/microros_transport_serial.c) + endif() + + nuttx_add_library(microros_transport STATIC) + target_sources(microros_transport PRIVATE ${MICROROS_TRANSPORT_SRCS}) + target_include_directories(microros_transport + PRIVATE ${MICROROS_DIR}/transport) + target_include_directories(microros_transport PUBLIC ${MICROROS_DIR}/include) + add_dependencies(microros_transport microros_build) +endif() diff --git a/system/microros/Kconfig b/system/microros/Kconfig new file mode 100644 index 000000000..d1d7d562a --- /dev/null +++ b/system/microros/Kconfig @@ -0,0 +1,102 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +menuconfig SYSTEM_MICROROS + bool "micro-ROS Integration" + default n + depends on NET_UDP || SERIAL_TERMIOS + ---help--- + Enable micro-ROS integration for NuttX RTOS. Provides a ROS 2 + client library for embedded and real-time systems. + +if SYSTEM_MICROROS + +config MICROROS_DISTRO + string "ROS 2 Distribution" + default "jazzy" + ---help--- + Specifies the ROS 2 distribution branch (e.g., humble, jazzy, kilted). + This determines which versions of micro-ROS packages are fetched. + +choice + prompt "Transport Method" + default MICROROS_TRANSPORT_UDP + +config MICROROS_TRANSPORT_UDP + bool "UDP Transport" + ---help--- + Use UDP socket for communication with micro-ROS agent. + +config MICROROS_TRANSPORT_SERIAL + bool "Serial Transport" + depends on SERIAL_TERMIOS + ---help--- + Use serial (UART/USB-CDC) for communication with micro-ROS agent. + +endchoice + +if MICROROS_TRANSPORT_UDP + +config MICROROS_AGENT_IP + string "Agent IP Address" + default "10.42.0.214" + ---help--- + IP address of the micro-ROS agent. + +config MICROROS_AGENT_PORT + int "Agent Port" + default 8888 + ---help--- + UDP port of the micro-ROS agent. + +endif + +if MICROROS_TRANSPORT_SERIAL + +config MICROROS_SERIAL_DEVICE + string "Serial Device Path" + default "/dev/ttyS0" + ---help--- + Serial device to use for transport (e.g., /dev/ttyS0, /dev/ttyACM0). + +config MICROROS_SERIAL_BAUD + int "Serial Baud Rate" + default 115200 + ---help--- + Baud rate for serial communication. + +endif + +config MICROROS_MAX_NODES + int "Maximum Nodes" + default 1 + ---help--- + Maximum number of ROS nodes that can be created. + +config MICROROS_MAX_PUBLISHERS + int "Maximum Publishers" + default 5 + ---help--- + Maximum number of publishers per node. + +config MICROROS_MAX_SUBSCRIPTIONS + int "Maximum Subscriptions" + default 5 + ---help--- + Maximum number of subscriptions per node. + +config MICROROS_MAX_SERVICES + int "Maximum Services" + default 1 + ---help--- + Maximum number of services per node. + +config MICROROS_MAX_CLIENTS + int "Maximum Clients" + default 1 + ---help--- + Maximum number of service clients per node. + +endif diff --git a/system/microros/Make.defs b/system/microros/Make.defs new file mode 100644 index 000000000..beb8a0c3d --- /dev/null +++ b/system/microros/Make.defs @@ -0,0 +1,32 @@ +############################################################################ +# apps/system/microros/Make.defs +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_MICROROS),) +CONFIGURED_APPS += $(APPDIR)/system/microros + +MICROROS_INC_DIRS := $(APPDIR)/system/microros/include \ + $(wildcard $(APPDIR)/system/microros/include/*) +CFLAGS += $(foreach d,$(MICROROS_INC_DIRS),${INCDIR_PREFIX}$(d)) +CXXFLAGS += $(foreach d,$(MICROROS_INC_DIRS),${INCDIR_PREFIX}$(d)) + +EXTRA_LIBS += $(APPDIR)/system/microros/libmicroros.a +endif diff --git a/system/microros/Makefile b/system/microros/Makefile new file mode 100644 index 000000000..18067408f --- /dev/null +++ b/system/microros/Makefile @@ -0,0 +1,258 @@ +############################################################################ +# apps/system/microros/Makefile +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +MICROROS_DIR := $(APPDIR)/system/microros +MICROROS_LIB_DIR := $(MICROROS_DIR)/micro_ros_lib +MICROROS_DISTRO := $(patsubst "%",%,$(CONFIG_MICROROS_DISTRO)) +MICROROS_AR := $(firstword $(AR)) + +MAX_NODES := $(CONFIG_MICROROS_MAX_NODES) +MAX_PUB := $(CONFIG_MICROROS_MAX_PUBLISHERS) +MAX_SUB := $(CONFIG_MICROROS_MAX_SUBSCRIPTIONS) +MAX_SVC := $(CONFIG_MICROROS_MAX_SERVICES) +MAX_CLIENTS := $(CONFIG_MICROROS_MAX_CLIENTS) + +# Escape for sed: forward slashes and strip quotes +ESCAPED_CC := $(subst /,\/,$(CC)) +ESCAPED_CXX := $(subst /,\/,$(CXX)) +ESCAPED_TOPDIR := $(subst /,\/,$(TOPDIR)) +ESCAPED_APPDIR := $(subst /,\/,$(APPDIR)) +ESCAPED_LIBDIR := $(subst /,\/,$(MICROROS_LIB_DIR)) +ESCAPED_CFLAGS := $(subst /,\/,$(subst ",,$(CFLAGS))) +ESCAPED_CXXFLAGS := $(subst /,\/,$(subst ",,$(CXXFLAGS))) + +MENUDESC = "micro-ROS Library" + +# Transport sources compiled into the user-side app library + +CSRCS += transport/microros_transport.c + +ifeq ($(CONFIG_MICROROS_TRANSPORT_UDP),y) +CSRCS += transport/microros_transport_udp.c +endif + +ifeq ($(CONFIG_MICROROS_TRANSPORT_SERIAL),y) +CSRCS += transport/microros_transport_serial.c +endif + +# Architecture for CMake toolchain +ifeq ($(CONFIG_ARCH_ARM),y) +MICROROS_ARCH := arm +else ifeq ($(CONFIG_ARCH_ARM64),y) +MICROROS_ARCH := arm64 +else ifeq ($(CONFIG_ARCH_RISCV),y) +MICROROS_ARCH := riscv +else ifeq ($(CONFIG_ARCH_XTENSA),y) +MICROROS_ARCH := xtensa +else +MICROROS_ARCH := generic +endif + +# Wire libmicroros.a into the build when the context target fires +context:: $(MICROROS_DIR)/libmicroros.a + +# --------------------------------------------------------------------------- +# Step 1 – Build the ament/colcon dev tools (host-side) +# --------------------------------------------------------------------------- + +$(MICROROS_LIB_DIR)/micro_ros_dev/install: + rm -rf $(MICROROS_LIB_DIR)/micro_ros_dev + mkdir -p $(MICROROS_LIB_DIR)/micro_ros_dev/src + cd $(MICROROS_LIB_DIR)/micro_ros_dev && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ament/ament_cmake src/ament_cmake && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ament/ament_lint src/ament_lint && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ament/ament_package src/ament_package && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ament/googletest src/googletest && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/ament_cmake_ros src/ament_cmake_ros && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ament/ament_index src/ament_index && \ + touch src/ament_cmake_ros/rmw_test_fixture_implementation/COLCON_IGNORE 2>/dev/null || true && \ + touch src/ament_cmake_ros/rmw_test_fixture/COLCON_IGNORE 2>/dev/null || true && \ + colcon build --cmake-args -DBUILD_TESTING=OFF + +# --------------------------------------------------------------------------- +# Step 2 – Clone all micro-ROS source packages +# --------------------------------------------------------------------------- + +$(MICROROS_LIB_DIR)/micro_ros_src/src: $(MICROROS_LIB_DIR)/micro_ros_dev/install + rm -rf $(MICROROS_LIB_DIR)/micro_ros_src + mkdir -p $(MICROROS_LIB_DIR)/micro_ros_src/src + cd $(MICROROS_LIB_DIR)/micro_ros_src && \ + git clone -b ros2 --depth 1 \ + https://github.com/eProsima/micro-CDR src/micro-CDR && \ + git clone -b ros2 --depth 1 \ + https://github.com/eProsima/Micro-XRCE-DDS-Client src/Micro-XRCE-DDS-Client && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/rmw-microxrcedds src/rmw_microxrcedds && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/rcl src/rcl && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rclc src/rclc && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/rcutils src/rcutils && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rosidl src/rosidl && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rosidl_defaults src/rosidl_defaults && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rosidl_dynamic_typesupport src/rosidl_dynamic_typesupport && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rosidl_core src/rosidl_core && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rmw src/rmw && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rmw_implementation src/rmw_implementation && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rcl_interfaces src/rcl_interfaces && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/common_interfaces src/common_interfaces && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/rcl_logging src/rcl_logging && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/micro_ros_msgs src/micro_ros_msgs && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/rosidl_typesupport src/rosidl_typesupport && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/rosidl_typesupport_microxrcedds src/rosidl_typesupport_microxrcedds && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/micro-ROS/micro_ros_utilities src/micro_ros_utilities && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/unique_identifier_msgs src/unique_identifier_msgs && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/test_interface_files src/test_interface_files && \ + git clone -b $(MICROROS_DISTRO) --depth 1 \ + https://github.com/ros2/ros2_tracing src/ros2_tracing && \ + touch src/ros2_tracing/test_tracetools/COLCON_IGNORE && \ + touch src/ros2_tracing/lttngpy/COLCON_IGNORE && \ + touch src/rmw/rmw_security_common/COLCON_IGNORE && \ + touch src/rcl_logging/rcl_logging_spdlog/COLCON_IGNORE && \ + touch src/rclc/rclc_examples/COLCON_IGNORE && \ + touch src/rcl/rcl_yaml_param_parser/COLCON_IGNORE && \ + touch src/rosidl/rosidl_typesupport_introspection_cpp/COLCON_IGNORE && \ + touch src/rcl_interfaces/test_msgs/COLCON_IGNORE && \ + touch src/common_interfaces/actionlib_msgs/COLCON_IGNORE && \ + touch src/common_interfaces/std_srvs/COLCON_IGNORE && \ + patch -p1 --forward --ignore-whitespace \ + -d src < $(MICROROS_LIB_DIR)/patches/0001-dir-typedef-nuttx-guard.patch || true && \ + patch -p1 --forward --ignore-whitespace \ + -d src < $(MICROROS_LIB_DIR)/patches/0002-rcutils-process-nuttx-compat.patch || true && \ + patch -p1 --forward --ignore-whitespace \ + -d src < $(MICROROS_LIB_DIR)/patches/0003-rcutils-strcasecmp-include-strings-h.patch || true + +# --------------------------------------------------------------------------- +# Step 3 – Generate toolchain.cmake from template +# --------------------------------------------------------------------------- + +$(MICROROS_LIB_DIR)/toolchain.cmake: $(MICROROS_LIB_DIR)/toolchain.cmake.in + sed \ + -e 's/@CMAKE_C_COMPILER@/$(ESCAPED_CC)/g' \ + -e 's/@CMAKE_CXX_COMPILER@/$(ESCAPED_CXX)/g' \ + -e 's/@ARCH_C_FLAGS@/$(ESCAPED_CFLAGS)/g' \ + -e 's/@ARCH_CPP_FLAGS@/$(ESCAPED_CXXFLAGS)/g' \ + -e 's/@NUTTX_TOPDIR@/$(ESCAPED_TOPDIR)/g' \ + -e 's/@NUTTX_APPDIR@/$(ESCAPED_APPDIR)/g' \ + -e 's/@INCLUDE_OVR_DIR@/$(ESCAPED_LIBDIR)/g' \ + -e 's/@CMAKE_SYSTEM_PROCESSOR@/$(MICROROS_ARCH)/g' \ + $< > $@ + +# --------------------------------------------------------------------------- +# Step 4 – Generate colcon.meta from template +# --------------------------------------------------------------------------- + +$(MICROROS_LIB_DIR)/colcon.meta: $(MICROROS_LIB_DIR)/colcon.meta.in + sed \ + -e 's/@MAX_NODES@/$(MAX_NODES)/g' \ + -e 's/@MAX_PUBLISHERS@/$(MAX_PUB)/g' \ + -e 's/@MAX_SUBSCRIPTIONS@/$(MAX_SUB)/g' \ + -e 's/@MAX_SERVICES@/$(MAX_SVC)/g' \ + -e 's/@MAX_CLIENTS@/$(MAX_CLIENTS)/g' \ + $< > $@ + +# --------------------------------------------------------------------------- +# Step 5 – Cross-build micro-ROS packages with colcon +# --------------------------------------------------------------------------- + +$(MICROROS_LIB_DIR)/micro_ros_src/install: \ + $(MICROROS_LIB_DIR)/toolchain.cmake \ + $(MICROROS_LIB_DIR)/colcon.meta \ + $(MICROROS_LIB_DIR)/micro_ros_src/src + cd $(MICROROS_LIB_DIR)/micro_ros_src && \ + unset AMENT_PREFIX_PATH && \ + . ../micro_ros_dev/install/local_setup.sh && \ + colcon build \ + --merge-install \ + "--packages-ignore-regex=(.*_cpp$$|.*_py$$|test_rmw_implementation|rclc_examples)" \ + --metas $(MICROROS_LIB_DIR)/colcon.meta \ + --cmake-args \ + --no-warn-unused-cli \ + -DCMAKE_POSITION_INDEPENDENT_CODE=OFF \ + -DTHIRDPARTY=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_TESTING=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + "-DCMAKE_TOOLCHAIN_FILE=$(MICROROS_LIB_DIR)/toolchain.cmake" \ + -DCMAKE_VERBOSE_MAKEFILE=OFF + +# --------------------------------------------------------------------------- +# Step 6 – Merge all .a files into a single libmicroros.a +# --------------------------------------------------------------------------- + +$(MICROROS_DIR)/libmicroros.a: $(MICROROS_LIB_DIR)/micro_ros_src/install + mkdir -p $(MICROROS_LIB_DIR)/libmicroros_obj + cd $(MICROROS_LIB_DIR)/libmicroros_obj && \ + for file in $$(find $(MICROROS_LIB_DIR)/micro_ros_src/install/lib/ -name '*.a'); do \ + folder=$$(basename $$file .a); \ + mkdir -p $$folder && cd $$folder; \ + $(MICROROS_AR) x $$file; \ + for f in *; do \ + [ -f "$$f" ] && mv "$$f" "../$${folder}-$${f}" 2>/dev/null || true; \ + done; \ + cd ..; \ + rm -rf $$folder; \ + done && \ + $(MICROROS_AR) rc libmicroros.a $$(find . -maxdepth 1 -type f ! -name 'libmicroros.a') && \ + cp libmicroros.a $(MICROROS_DIR)/ + cd $(MICROROS_LIB_DIR) && rm -rf libmicroros_obj + cp -R $(MICROROS_LIB_DIR)/micro_ros_src/install/include $(MICROROS_DIR)/include + +# --------------------------------------------------------------------------- +# Cleanup targets +# --------------------------------------------------------------------------- + +clean:: + $(call DELFILE,$(MICROROS_DIR)/libmicroros.a) + $(call DELDIR,$(MICROROS_DIR)/include) + $(call DELFILE,$(MICROROS_LIB_DIR)/toolchain.cmake) + $(call DELFILE,$(MICROROS_LIB_DIR)/colcon.meta) + +distclean:: clean + $(call DELDIR,$(MICROROS_LIB_DIR)/micro_ros_src) + $(call DELDIR,$(MICROROS_LIB_DIR)/micro_ros_dev) + +include $(APPDIR)/Application.mk diff --git a/system/microros/micro_ros_lib/.keep b/system/microros/micro_ros_lib/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/system/microros/micro_ros_lib/colcon.meta.in b/system/microros/micro_ros_lib/colcon.meta.in new file mode 100644 index 000000000..00468545b --- /dev/null +++ b/system/microros/micro_ros_lib/colcon.meta.in @@ -0,0 +1,52 @@ +{ + "names": { + "microxrcedds_client": { + "cmake-args": [ + "-DUCLIENT_PIC=OFF", + "-DUCLIENT_PROFILE_DISCOVERY=OFF", + "-DUCLIENT_PROFILE_UDP=OFF", + "-DUCLIENT_PROFILE_TCP=OFF", + "-DUCLIENT_PROFILE_SERIAL=OFF", + "-DUCLIENT_PROFILE_CUSTOM_TRANSPORT=ON", + "-DUCLIENT_PROFILE_STREAM_FRAMING=ON", + "-DUCLIENT_EXTERNAL_SERIAL=ON" + ] + }, + "rmw_microxrcedds": { + "cmake-args": [ + "-DRMW_UXRCE_TRANSPORT=custom", + "-DRMW_UXRCE_MAX_NODES=@MAX_NODES@", + "-DRMW_UXRCE_MAX_PUBLISHERS=@MAX_PUBLISHERS@", + "-DRMW_UXRCE_MAX_SUBSCRIPTIONS=@MAX_SUBSCRIPTIONS@", + "-DRMW_UXRCE_MAX_SERVICES=@MAX_SERVICES@", + "-DRMW_UXRCE_MAX_CLIENTS=@MAX_CLIENTS@", + "-DRMW_UXRCE_MAX_HISTORY=4" + ] + }, + "rcl": { + "cmake-args": [ + "-DRCL_MICROROS=ON" + ] + }, + "rcutils": { + "cmake-args": [ + "-DRCUTILS_NO_FILESYSTEM=OFF", + "-DRCUTILS_NO_THREAD_SUPPORT=ON", + "-DRCUTILS_NO_64_ATOMIC=ON", + "-DRCUTILS_AVOID_DYNAMIC_ALLOCATION=ON", + "-DENABLE_TESTING=OFF" + ] + }, + "rosidl_typesupport": { + "cmake-args": [ + "-DROSIDL_TYPESUPPORT_SINGLE_TYPESUPPORT=ON" + ] + }, + "tracetools": { + "cmake-args": [ + "-DTRACETOOLS_DISABLED=ON", + "-DTRACETOOLS_STATUS_CHECKING_TOOL=OFF" + ] + } + } +} diff --git a/system/microros/micro_ros_lib/include_override/assert.h b/system/microros/micro_ros_lib/include_override/assert.h new file mode 100644 index 000000000..1874aa304 --- /dev/null +++ b/system/microros/micro_ros_lib/include_override/assert.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * apps/system/microros/micro_ros_lib/include_override/assert.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __MICROROS_INCLUDE_OVERRIDE_ASSERT_H +#define __MICROROS_INCLUDE_OVERRIDE_ASSERT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/* Pull the system assert.h, then redefine assert to a no-op when NDEBUG is + * set. Avoids an assert redefinition conflict in rosidl typesupport. + */ + +#include_next + +#ifdef NDEBUG +# undef assert +# define assert(x) ((void)(0)) +#endif + +#endif /* __MICROROS_INCLUDE_OVERRIDE_ASSERT_H */ diff --git a/system/microros/micro_ros_lib/toolchain.cmake.in b/system/microros/micro_ros_lib/toolchain.cmake.in new file mode 100644 index 000000000..ebfa15eea --- /dev/null +++ b/system/microros/micro_ros_lib/toolchain.cmake.in @@ -0,0 +1,29 @@ +include(CMakeForceCompiler) + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR @CMAKE_SYSTEM_PROCESSOR@) +set(CMAKE_CROSSCOMPILING 1) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(PLATFORM_NAME "nuttx") + +set(CMAKE_C_COMPILER @CMAKE_C_COMPILER@) +set(CMAKE_CXX_COMPILER @CMAKE_CXX_COMPILER@) + +set(CMAKE_C_FLAGS_INIT "-std=c11 @ARCH_C_FLAGS@ -D__NuttX__" CACHE STRING "" FORCE) +set(CMAKE_CXX_FLAGS_INIT "-std=c++14 @ARCH_CPP_FLAGS@ -fno-rtti -D__NuttX__" CACHE STRING "" FORCE) + +set(NUTTX_TOPDIR @NUTTX_TOPDIR@) +set(NUTTX_APPDIR @NUTTX_APPDIR@) +set(INCLUDE_OVR_DIR @INCLUDE_OVR_DIR@) + +include_directories(SYSTEM + ${NUTTX_TOPDIR}/include + ${NUTTX_TOPDIR}/include/cxx + ${NUTTX_APPDIR}/include +) + +include_directories(AFTER SYSTEM + ${INCLUDE_OVR_DIR}/include_override +) + +set(__BIG_ENDIAN__ 0) diff --git a/system/microros/patches/0001-dir-typedef-nuttx-guard.patch b/system/microros/patches/0001-dir-typedef-nuttx-guard.patch new file mode 100644 index 000000000..75e4f91d9 --- /dev/null +++ b/system/microros/patches/0001-dir-typedef-nuttx-guard.patch @@ -0,0 +1,31 @@ +From: micro-ROS NuttX Port +Subject: [PATCH] rcutils: guard bare DIR typedef to avoid conflict with NuttX dirent.h + +NuttX defines DIR as a struct type in dirent.h. The bare + typedef int DIR; +in rcutils/src/filesystem.c causes a redefinition error when the +NuttX system headers are on the include path. Guard it so it is +only emitted when DIR has not already been defined. + +--- a/rcutils/src/filesystem.c ++++ b/rcutils/src/filesystem.c +@@ -1,6 +1,8 @@ + // Copyright 2017 Open Source Robotics Foundation, Inc. + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + + #if !defined _WIN32 && !defined __QNXTO__ ++#ifndef DIR + typedef int DIR; ++#endif + #endif diff --git a/system/microros/patches/0002-rcutils-process-nuttx-compat.patch b/system/microros/patches/0002-rcutils-process-nuttx-compat.patch new file mode 100644 index 000000000..ca8b6c56a --- /dev/null +++ b/system/microros/patches/0002-rcutils-process-nuttx-compat.patch @@ -0,0 +1,20 @@ +From: micro-ROS NuttX Port +Subject: [PATCH] rcutils: skip program_invocation_name on NuttX + +When cross-compiling for NuttX on a Linux host the compiler still +defines __linux__, causing rcutils_get_executable_name() to reference +program_invocation_name which is a glibc extension absent from the +NuttX C library. Guard the Linux branch with !defined(__NuttX__) so +the "embedded OS" fallback (empty string) is taken instead. + +--- a/rcutils/src/process.c ++++ b/rcutils/src/process.c +@@ -61,7 +61,7 @@ int rcutils_get_pid() + #if defined __APPLE__ || defined __FreeBSD__ || (defined __ANDROID__ && __ANDROID_API__ >= 21) + const char * appname = getprogname(); + #elif defined __GNUC__ && !defined(__QNXNTO__) && !defined(__OHOS__) +- #if defined __linux__ || defined __linux || defined __gnu_linux__ || defined linux ++ #if (defined __linux__ || defined __linux || defined __gnu_linux__ || defined linux) && !defined(__NuttX__) + const char * appname = program_invocation_name; + #else + // Some embedded OS compile with __GNUC__ but are not quite conformant with GNU-specific extensions. diff --git a/system/microros/patches/0003-rcutils-strcasecmp-include-strings-h.patch b/system/microros/patches/0003-rcutils-strcasecmp-include-strings-h.patch new file mode 100644 index 000000000..7392f2828 --- /dev/null +++ b/system/microros/patches/0003-rcutils-strcasecmp-include-strings-h.patch @@ -0,0 +1,19 @@ +From: micro-ROS NuttX Port +Subject: [PATCH] rcutils: include for strcasecmp on NuttX + +POSIX specifies strcasecmp/strncasecmp in , not . +NuttX follows the standard strictly so omitting this header causes an +"implicit declaration" error. Guard the include with __NuttX__ so it +only applies where needed and does not affect Linux or other platforms +that already expose strcasecmp through . + +--- a/rcutils/src/strcasecmp.c ++++ b/rcutils/src/strcasecmp.c +@@ -17,6 +17,9 @@ extern "C" + #include + #include ++#ifdef __NuttX__ ++#include ++#endif + + #include "rcutils/strcasecmp.h" diff --git a/system/microros/transport/microros_transport.c b/system/microros/transport/microros_transport.c new file mode 100644 index 000000000..e9fdc44fa --- /dev/null +++ b/system/microros/transport/microros_transport.c @@ -0,0 +1,62 @@ +/**************************************************************************** + * apps/system/microros/transport/microros_transport.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include + +#include + +#include "microros_transport.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int microros_transport_init(void) +{ +#if defined(CONFIG_MICROROS_TRANSPORT_UDP) + rmw_uros_set_custom_transport(false, + NULL, + microros_udp_open, + microros_udp_close, + microros_udp_write, + microros_udp_read); + return 0; +#elif defined(CONFIG_MICROROS_TRANSPORT_SERIAL) + rmw_uros_set_custom_transport(true, + NULL, + microros_serial_open, + microros_serial_close, + microros_serial_write, + microros_serial_read); + return 0; +#else + return -ENOSYS; +#endif +} diff --git a/system/microros/transport/microros_transport.h b/system/microros/transport/microros_transport.h new file mode 100644 index 000000000..4483b8ffc --- /dev/null +++ b/system/microros/transport/microros_transport.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * apps/system/microros/transport/microros_transport.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __APPS_SYSTEM_MICROROS_TRANSPORT_MICROROS_TRANSPORT_H +#define __APPS_SYSTEM_MICROROS_TRANSPORT_MICROROS_TRANSPORT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Backend callbacks. Exposed for testing only. */ + +bool microros_udp_open(struct uxrCustomTransport *transport); +bool microros_udp_close(struct uxrCustomTransport *transport); +size_t microros_udp_write(struct uxrCustomTransport *transport, + const uint8_t *buf, size_t len, uint8_t *err); +size_t microros_udp_read(struct uxrCustomTransport *transport, + uint8_t *buf, size_t len, + int timeout_ms, uint8_t *err); + +bool microros_serial_open(struct uxrCustomTransport *transport); +bool microros_serial_close(struct uxrCustomTransport *transport); +size_t microros_serial_write(struct uxrCustomTransport *transport, + const uint8_t *buf, size_t len, uint8_t *err); +size_t microros_serial_read(struct uxrCustomTransport *transport, + uint8_t *buf, size_t len, + int timeout_ms, uint8_t *err); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_SYSTEM_MICROROS_TRANSPORT_MICROROS_TRANSPORT_H */ diff --git a/system/microros/transport/microros_transport_serial.c b/system/microros/transport/microros_transport_serial.c new file mode 100644 index 000000000..6a9c7badc --- /dev/null +++ b/system/microros/transport/microros_transport_serial.c @@ -0,0 +1,216 @@ +/**************************************************************************** + * apps/system/microros/transport/microros_transport_serial.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "microros_transport.h" + +/**************************************************************************** + * Private Helpers + ****************************************************************************/ + +static inline int serial_fd(struct uxrCustomTransport *transport) +{ + return (int)(intptr_t)transport->args - 1; +} + +static inline void serial_set_fd(struct uxrCustomTransport *transport, + int fd) +{ + transport->args = (void *)(intptr_t)(fd + 1); +} + +static int set_baud(struct termios *tio, int baud) +{ + speed_t s; + + switch (baud) + { + case 9600: + s = B9600; + break; + + case 19200: + s = B19200; + break; + + case 38400: + s = B38400; + break; + + case 57600: + s = B57600; + break; + + case 115200: + s = B115200; + break; + + case 230400: + s = B230400; + break; + + case 460800: + s = B460800; + break; + + case 921600: + s = B921600; + break; + + default: + return -1; + } + + cfsetispeed(tio, s); + cfsetospeed(tio, s); + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +bool microros_serial_open(struct uxrCustomTransport *transport) +{ + struct termios tio; + int fd; + + fd = open(CONFIG_MICROROS_SERIAL_DEVICE, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (fd < 0) + { + return false; + } + + if (tcgetattr(fd, &tio) < 0) + { + close(fd); + return false; + } + + cfmakeraw(&tio); + tio.c_cflag |= (CLOCAL | CREAD); + tio.c_cflag &= ~CRTSCTS; + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 0; + + if (set_baud(&tio, CONFIG_MICROROS_SERIAL_BAUD) < 0) + { + close(fd); + return false; + } + + if (tcsetattr(fd, TCSANOW, &tio) < 0) + { + close(fd); + return false; + } + + tcflush(fd, TCIOFLUSH); + serial_set_fd(transport, fd); + return true; +} + +bool microros_serial_close(struct uxrCustomTransport *transport) +{ + int fd = serial_fd(transport); + + if (fd >= 0) + { + close(fd); + serial_set_fd(transport, -1); + } + + return true; +} + +size_t microros_serial_write(struct uxrCustomTransport *transport, + const uint8_t *buf, size_t len, uint8_t *err) +{ + int fd = serial_fd(transport); + ssize_t n; + + if (fd < 0) + { + *err = 1; + return 0; + } + + n = write(fd, buf, len); + if (n < 0) + { + *err = 1; + return 0; + } + + return n; +} + +size_t microros_serial_read(struct uxrCustomTransport *transport, + uint8_t *buf, size_t len, + int timeout_ms, uint8_t *err) +{ + int fd = serial_fd(transport); + struct pollfd pfd; + ssize_t n; + int r; + + if (fd < 0) + { + *err = 1; + return 0; + } + + pfd.fd = fd; + pfd.events = POLLIN; + + r = poll(&pfd, 1, timeout_ms); + if (r < 0) + { + *err = 1; + return 0; + } + + if (r == 0) + { + return 0; + } + + n = read(fd, buf, len); + if (n < 0) + { + *err = 1; + return 0; + } + + return n; +} diff --git a/system/microros/transport/microros_transport_udp.c b/system/microros/transport/microros_transport_udp.c new file mode 100644 index 000000000..6b048a480 --- /dev/null +++ b/system/microros/transport/microros_transport_udp.c @@ -0,0 +1,162 @@ +/**************************************************************************** + * apps/system/microros/transport/microros_transport_udp.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "microros_transport.h" + +/**************************************************************************** + * Private Helpers + ****************************************************************************/ + +static inline int udp_fd(struct uxrCustomTransport *transport) +{ + return (int)(intptr_t)transport->args - 1; +} + +static inline void udp_set_fd(struct uxrCustomTransport *transport, int fd) +{ + transport->args = (void *)(intptr_t)(fd + 1); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +bool microros_udp_open(struct uxrCustomTransport *transport) +{ + struct sockaddr_in addr; + int fd; + + fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + { + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(CONFIG_MICROROS_AGENT_PORT); + + if (inet_pton(AF_INET, CONFIG_MICROROS_AGENT_IP, &addr.sin_addr) != 1) + { + close(fd); + return false; + } + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + close(fd); + return false; + } + + udp_set_fd(transport, fd); + return true; +} + +bool microros_udp_close(struct uxrCustomTransport *transport) +{ + int fd = udp_fd(transport); + + if (fd >= 0) + { + close(fd); + udp_set_fd(transport, -1); + } + + return true; +} + +size_t microros_udp_write(struct uxrCustomTransport *transport, + const uint8_t *buf, size_t len, uint8_t *err) +{ + int fd = udp_fd(transport); + ssize_t n; + + if (fd < 0) + { + *err = 1; + return 0; + } + + n = send(fd, buf, len, 0); + if (n < 0) + { + *err = 1; + return 0; + } + + return n; +} + +size_t microros_udp_read(struct uxrCustomTransport *transport, + uint8_t *buf, size_t len, + int timeout_ms, uint8_t *err) +{ + int fd = udp_fd(transport); + struct pollfd pfd; + ssize_t n; + int r; + + if (fd < 0) + { + *err = 1; + return 0; + } + + pfd.fd = fd; + pfd.events = POLLIN; + + r = poll(&pfd, 1, timeout_ms); + if (r < 0) + { + *err = 1; + return 0; + } + + if (r == 0) + { + return 0; + } + + n = recv(fd, buf, len, 0); + if (n < 0) + { + *err = 1; + return 0; + } + + return n; +} diff --git a/system/nxcamera/nxcamera.c b/system/nxcamera/nxcamera.c index d2070e966..0f74b418a 100644 --- a/system/nxcamera/nxcamera.c +++ b/system/nxcamera/nxcamera.c @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -52,6 +52,10 @@ #ifdef CONFIG_LIBYUV # include + +# define CONVERT_TO_ARGB ConvertToARGB +# define CONVERT_FROM_I420 ConvertFromI420 +# define CONVERT_TO_I420 ConvertToI420 #endif /**************************************************************************** @@ -91,38 +95,38 @@ static int show_image(FAR struct nxcamera_s *pcam, FAR v4l2_buffer_t *buf) #ifdef CONFIG_LIBYUV if (pcam->display_vinfo.fmt == FB_FMT_RGB32) { - return ConvertToARGB(pcam->bufs[buf->index], - pcam->buf_sizes[buf->index], - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - 0, - 0, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - 0, - pcam->fmt.fmt.pix.pixelformat); + return CONVERT_TO_ARGB(pcam->bufs[buf->index], + pcam->buf_sizes[buf->index], + pcam->display_pinfo.fbmem, + pcam->display_pinfo.stride, + 0, + 0, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + 0, + pcam->fmt.fmt.pix.pixelformat); } else if (pcam->display_vinfo.fmt == FB_FMT_RGB16_565) { if (pcam->fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { - return ConvertFromI420(pcam->bufs[buf->index], - pcam->fmt.fmt.pix.width, - &pcam->bufs[buf->index][ - pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &pcam->bufs[buf->index][ - pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - V4L2_PIX_FMT_RGB565); + return CONVERT_FROM_I420(pcam->bufs[buf->index], + pcam->fmt.fmt.pix.width, + &pcam->bufs[buf->index][ + pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height], + pcam->fmt.fmt.pix.width / 2, + &pcam->bufs[buf->index][ + pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height * 5 / 4], + pcam->fmt.fmt.pix.width / 2, + pcam->display_pinfo.fbmem, + pcam->display_pinfo.stride, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + V4L2_PIX_FMT_RGB565); } else { @@ -134,42 +138,42 @@ static int show_image(FAR struct nxcamera_s *pcam, FAR v4l2_buffer_t *buf) return -ENOMEM; } - ret = ConvertToI420(pcam->bufs[buf->index], - pcam->buf_sizes[buf->index], - dst, - pcam->fmt.fmt.pix.width, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - 0, - 0, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - 0, - pcam->fmt.fmt.pix.pixelformat); + ret = CONVERT_TO_I420(pcam->bufs[buf->index], + pcam->buf_sizes[buf->index], + dst, + pcam->fmt.fmt.pix.width, + &dst[pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height], + pcam->fmt.fmt.pix.width / 2, + &dst[pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height * 5 / 4], + pcam->fmt.fmt.pix.width / 2, + 0, + 0, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + 0, + pcam->fmt.fmt.pix.pixelformat); if (ret < 0) { goto err; } - ret = ConvertFromI420(dst, - pcam->fmt.fmt.pix.width, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height], - pcam->fmt.fmt.pix.width / 2, - &dst[pcam->fmt.fmt.pix.width * - pcam->fmt.fmt.pix.height * 5 / 4], - pcam->fmt.fmt.pix.width / 2, - pcam->display_pinfo.fbmem, - pcam->display_pinfo.stride, - pcam->fmt.fmt.pix.width, - pcam->fmt.fmt.pix.height, - V4L2_PIX_FMT_RGB565); + ret = CONVERT_FROM_I420(dst, + pcam->fmt.fmt.pix.width, + &dst[pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height], + pcam->fmt.fmt.pix.width / 2, + &dst[pcam->fmt.fmt.pix.width * + pcam->fmt.fmt.pix.height * 5 / 4], + pcam->fmt.fmt.pix.width / 2, + pcam->display_pinfo.fbmem, + pcam->display_pinfo.stride, + pcam->fmt.fmt.pix.width, + pcam->fmt.fmt.pix.height, + V4L2_PIX_FMT_RGB565); if (ret < 0) { goto err; @@ -571,7 +575,7 @@ int nxcamera_setfile(FAR struct nxcamera_s *pcam, FAR const char *pfile, /* Try to open the file */ - temp_fd = open(pfile, O_CREAT | O_RDWR); + temp_fd = open(pfile, O_CREAT | O_RDWR, 0666); if (temp_fd == -1) { /* Error opening the file */ @@ -672,7 +676,7 @@ int nxcamera_stream(FAR struct nxcamera_s *pcam, int i; struct v4l2_buffer buf; struct v4l2_requestbuffers req; - struct v4l2_streamparm parm; + struct v4l2_streamparm param; DEBUGASSERT(pcam != NULL); @@ -716,11 +720,11 @@ int nxcamera_stream(FAR struct nxcamera_s *pcam, return ret; } - memset(&parm, 0, sizeof(parm)); - parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - parm.parm.capture.timeperframe.denominator = framerate; - parm.parm.capture.timeperframe.numerator = 1; - ret = ioctl(pcam->capture_fd, VIDIOC_S_PARM, (uintptr_t)&parm); + memset(¶m, 0, sizeof(param)); + param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + param.parm.capture.timeperframe.denominator = framerate; + param.parm.capture.timeperframe.numerator = 1; + ret = ioctl(pcam->capture_fd, VIDIOC_S_PARM, (uintptr_t)¶m); if (ret < 0) { ret = -errno; diff --git a/system/nxdiag/CMakeLists.txt b/system/nxdiag/CMakeLists.txt new file mode 100644 index 000000000..ec0cd116c --- /dev/null +++ b/system/nxdiag/CMakeLists.txt @@ -0,0 +1,102 @@ +# ############################################################################## +# apps/system/nxdiag/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_NXDIAG) + + find_program(PYTHON3 python3) + if(NOT PYTHON3) + message(FATAL_ERROR "python3 not found in PATH (required for nxdiag)") + endif() + + set(NXDIAG_TOOLSDIR ${NUTTX_DIR}/tools) + set(NXDIAG_SYSINFO ${CMAKE_CURRENT_SOURCE_DIR}/sysinfo.h) + + # Build host_info_dump.py argument list + + set(NXDIAG_FLAGS ${NUTTX_DIR}) + + if(CONFIG_SYSTEM_NXDIAG_CONF) + list(APPEND NXDIAG_FLAGS --config) + endif() + + if(CONFIG_SYSTEM_NXDIAG_COMP_FLAGS) + list(APPEND NXDIAG_FLAGS --flags "${CMAKE_C_FLAGS}" "${CMAKE_CXX_FLAGS}" + "${CMAKE_EXE_LINKER_FLAGS}") + endif() + + if(CONFIG_SYSTEM_NXDIAG_HOST_PACKAGES) + list(APPEND NXDIAG_FLAGS --packages) + endif() + + if(CONFIG_SYSTEM_NXDIAG_HOST_MODULES) + list(APPEND NXDIAG_FLAGS --modules) + endif() + + if(CONFIG_SYSTEM_NXDIAG_HOST_PATH) + list(APPEND NXDIAG_FLAGS --path) + endif() + + if(CONFIG_SYSTEM_NXDIAG_VERBOSE) + list(APPEND NXDIAG_FLAGS --verbose) + endif() + + set(NXDIAG_PY_FLAGS "") + + # Vendor-specific checks + + if(CONFIG_SYSTEM_NXDIAG_ESPRESSIF) + list(APPEND NXDIAG_FLAGS --target_info) + list(APPEND NXDIAG_PY_FLAGS -B) + endif() + + # Generate sysinfo.h + + add_custom_command( + OUTPUT ${NXDIAG_SYSINFO} + COMMAND ${PYTHON3} ${NXDIAG_PY_FLAGS} ${NXDIAG_TOOLSDIR}/host_info_dump.py + ${NXDIAG_FLAGS} -b ${CMAKE_BINARY_DIR} > ${NXDIAG_SYSINFO} + DEPENDS ${NXDIAG_TOOLSDIR}/host_info_dump.py + COMMENT "Generating nxdiag sysinfo.h" + VERBATIM) + + add_custom_target(nxdiag_sysinfo_gen DEPENDS ${NXDIAG_SYSINFO}) + + nuttx_add_application( + NAME + nxdiag + SRCS + nxdiag.c + PRIORITY + ${CONFIG_SYSTEM_NXDIAG_PRIORITY} + STACKSIZE + ${CONFIG_SYSTEM_NXDIAG_STACKSIZE} + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS + nxdiag_sysinfo_gen) + + set_property( + DIRECTORY ${CMAKE_SOURCE_DIR} + APPEND + PROPERTY ADDITIONAL_CLEAN_FILES ${NXDIAG_SYSINFO}) + +endif() diff --git a/system/nxinit/CMakeLists.txt b/system/nxinit/CMakeLists.txt new file mode 100644 index 000000000..5aef892e9 --- /dev/null +++ b/system/nxinit/CMakeLists.txt @@ -0,0 +1,39 @@ +# ############################################################################## +# apps/system/nxinit/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_SYSTEM_NXINIT) + + set(CSRCS init.c action.c builtin.c import.c parser.c service.c) + + nuttx_add_application( + MODULE + ${CONFIG_SYSTEM_NXINIT} + NAME + ${CONFIG_SYSTEM_NXINIT_PROGNAME} + STACKSIZE + ${CONFIG_SYSTEM_NXINIT_STACKSIZE} + PRIORITY + ${CONFIG_SYSTEM_NXINIT_PRIORITY} + SRCS + ${CSRCS}) + +endif() diff --git a/system/nxinit/Kconfig b/system/nxinit/Kconfig new file mode 100644 index 000000000..e159e90b7 --- /dev/null +++ b/system/nxinit/Kconfig @@ -0,0 +1,129 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_NXINIT + tristate "NuttX Init" + default n + depends on EXPERIMENTAL + depends on LIBC_EXECFUNCS + depends on SCHED_CHILD_STATUS + ---help--- + Enable NxInit(NuttX Init) component for system initialization. + The script of NxInit(init.rc) is compatible with the Android Init Language syntax. + +if SYSTEM_NXINIT + +comment "NXInit Basic" + +config SYSTEM_NXINIT_PRIORITY + int "Thread priority" + default 100 + +config SYSTEM_NXINIT_STACKSIZE + int "Stack size" + default DEFAULT_TASK_STACKSIZE + +config SYSTEM_NXINIT_PROGNAME + string "Program name" + default "init" + +comment "NXInit Run Control(RC)" + +config SYSTEM_NXINIT_RC_FILE_PATH + string "Path to RC file" + default "/etc/init.d/init.rc" + ---help--- + Path to the init.rc file to use for NXInit's boot logic. + +config SYSTEM_NXINIT_RC_LINE_MAX + int "Max line length of RC file" + default 128 + ---help--- + Maximum line length of RC file. + More details: https://android.googlesource.com/platform/system/core/+/master/init/README.md + +comment "NXInit Action" + +config SYSTEM_NXINIT_ACTION_CMD_ARGS_MAX + int "Max number of command arguments" + default 8 + ---help--- + Maximum number of command arguments. + Form: + ``` + on + + + + ... + ``` + +config SYSTEM_NXINIT_ACTION_WARN_SLOW + int "Slow command warning timeout" + default 50 + depends on SYSTEM_NXINIT_WARN + ---help--- + Warning if command took more than `SYSTEM_NXINIT_ACTION_WARN_SLOW` ms. + +config SYSTEM_NXINIT_ACTION_MANAGER_EVENT_MAX + int "Max number of action manager events" + default 32 + ---help--- + Maximum number of action manager events. + ``` + struct action_manager_s + { + ... + FAR char *events[CONFIG_SYSTEM_NXINIT_ACTION_MANAGER_EVENT_MAX]; + ... + }; + ``` +config SYSTEM_NXINIT_FINALINIT + bool "Enable NXInit final event" + default n + ---help--- + Enable support to run the "final" event. Currently only "boot", + and "init" event are enabled by default, where "netinit" and + "final" are optional. + +comment "NXInit Service" + +config SYSTEM_NXINIT_SERVICE_ARGS_MAX + int "Max number of service arguments" + default 8 + range 3 64 + ---help--- + Maximum number of service arguments, + including "name", "pathname" and key word "service"(at least 3). Form: + ``` + service [ ]* +