├── .github ├── get-deps-mingw.ps1 ├── get-deps-msvc.ps1 └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── TODO.txt ├── cmake └── FindSDL2.cmake ├── doc ├── interrupts.md ├── keypress.txt ├── levels.md ├── monsters.txt ├── references.md ├── resource31.md ├── resources.md ├── script.md ├── style.md └── viewport.md ├── dos ├── README.md ├── dragon └── dragon.asm ├── img ├── choose_party.png ├── dw.png ├── encounter.png └── opening.png ├── script ├── encounter.scr ├── init.cpp ├── script0.scr ├── script01.scr ├── script03.scr ├── script06.scr ├── script11.scr ├── script12.scr ├── script13.scr ├── script15.scr ├── script18.scr ├── script19.scr ├── script22.scr └── script71.scr └── src ├── CMakeLists.txt ├── Makefile ├── README.md ├── fe ├── CMakeLists.txt ├── Makefile ├── main.c ├── vga_dos.c ├── vga_null.c ├── vga_sdl.c └── vga_xlib.c ├── lib ├── CMakeLists.txt ├── Makefile ├── bufio.c ├── bufio.h ├── compress.c ├── compress.h ├── engine.c ├── engine.h ├── log.c ├── log.h ├── mouse.c ├── mouse.h ├── offsets.c ├── offsets.h ├── player.c ├── player.h ├── resource.c ├── resource.h ├── state.c ├── state.h ├── tables.c ├── tables.h ├── timers.c ├── timers.h ├── ui.c ├── ui.h ├── utils.c ├── utils.h ├── vga.c └── vga.h ├── tests ├── CMakeLists.txt ├── test_compress.cpp ├── test_compress.h ├── test_opendw.cpp ├── test_vga.cpp └── test_vga.h └── tools ├── CMakeLists.txt ├── Makefile ├── disasm.cpp ├── kbhit.cpp ├── le16bit.cpp ├── monster_info.cpp ├── mouse.cpp ├── ppm.cpp ├── resextract.cpp ├── script_ops.cpp └── strextract.cpp /.github/get-deps-mingw.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | $ProgressPreference = "SilentlyContinue" 3 | 4 | $sdlVersion = "2.0.16" 5 | 6 | $sdlArchive = "SDL2-devel-$sdlVersion-mingw.tar.gz" 7 | 8 | $sdlUrl = "https://www.libsdl.org/release/$sdlArchive" 9 | 10 | "Downloading $sdlUrl" | Write-Host 11 | curl.exe -sSLfO $sdlUrl 12 | if ($LASTEXITCODE) { "Download failed" | Write-Host: exit 1 } 13 | 14 | $target = Join-Path $pwd.Drive.Root "opt/local/x86_64-w64-mingw32" 15 | mkdir $target 16 | 17 | tar xf $sdlArchive 18 | gci ".\SDL2-$sdlVersion\x86_64-w64-mingw32" | cp -dest $target -recurse -force 19 | mv ".\SDL2-$sdlVersion" ".\SDL2" 20 | 21 | -------------------------------------------------------------------------------- /.github/get-deps-msvc.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | $ProgressPreference = "SilentlyContinue" 3 | 4 | $sdlVersion = "2.0.18" 5 | 6 | $sdlArchive = "SDL2-devel-$sdlVersion-VC.zip" 7 | 8 | $sdlUrl = "https://www.libsdl.org/release/$sdlArchive" 9 | 10 | "Downloading $sdlUrl" | Write-Host 11 | curl.exe -sSLfO $sdlUrl 12 | if ($LASTEXITCODE) { "Download failed" | Write-Host: exit 1 } 13 | 14 | $env:GITHUB_PATH 15 | $myLocation = Get-Location 16 | 17 | $target = Join-Path $myLocation "deps" 18 | mkdir $target 19 | 20 | Expand-Archive -Path $sdlArchive -DestinationPath $target 21 | 22 | # Remove version string from path to help CMake 23 | $extractTarget = Join-Path $target "SDL2-$sdlVersion" 24 | $newTarget = Join-Path $target "sdl2" 25 | Move-Item -Path $extractTarget -Destination $newTarget 26 | 27 | Get-ChildItem -Path $target -Recurse -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request, release] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | name: ${{ matrix.config.name }} 12 | runs-on: ${{ matrix.config.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | config: 17 | - { 18 | name: "Windows Latest MSVC", 19 | os: "windows-latest", 20 | build_type: "Release", 21 | cc: "cl", 22 | cxx: "cl", 23 | generators: "Visual Studio 17 2022", 24 | archiver: "7z -tzip a", 25 | artifact: "windows_msvc.zip" 26 | } 27 | - { 28 | name: "Ubuntu Latest GCC", 29 | os: "ubuntu-latest", 30 | build_type: "Release", 31 | cc: "gcc", 32 | cxx: "g++", 33 | generators: "Unix Makefiles", 34 | archiver: "tar czf", 35 | artifact: "ubuntu_gcc.tar.gz" 36 | } 37 | - { 38 | name: "MacOS Latest Clang", 39 | os: "macos-latest", 40 | build_type: "Release", 41 | cc: "clang", 42 | cxx: "clang++", 43 | generators: "Unix Makefiles", 44 | archiver: "tar czf", 45 | artifact: "macos_clang.tar.gz" 46 | } 47 | 48 | steps: 49 | - uses: actions/checkout@v3 50 | 51 | - name: Print env 52 | run: | 53 | echo github.event.action: ${{ github.event.action }} 54 | echo github.event_name: ${{ github.event_name }} 55 | 56 | - name: Install dependencies on Windows 57 | if: startsWith(matrix.config.os, 'windows') 58 | run: | 59 | .\.github\get-deps-msvc.ps1 60 | cmake --version 61 | 62 | - name: Install dependencies on Ubuntu 63 | if: startsWith(matrix.config.name, 'Ubuntu') 64 | run: | 65 | sudo apt update 66 | sudo apt install -y libsdl2-dev 67 | cmake --version 68 | gcc --version 69 | 70 | - name: Install dependencies on MacOS 71 | if: startsWith(matrix.config.os, 'macos') 72 | run: | 73 | brew install sdl2 74 | cmake --version 75 | clang --version 76 | 77 | - name: Configure CMake 78 | shell: bash 79 | run: | 80 | mkdir build 81 | mkdir instdir 82 | cmake -S . -B . \ 83 | -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ 84 | -G "${{ matrix.config.generators }}" \ 85 | -DENABLE_TOOLS=ON \ 86 | -DCMAKE_INSTALL_PREFIX:PATH=instdir 87 | 88 | - name: Build 89 | shell: bash 90 | run: cmake --build . --config ${{ matrix.config.build_type }} 91 | 92 | - name: Test 93 | shell: bash 94 | # Execute tests defined by the CMake configuration. 95 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html 96 | # for more detail 97 | run: ctest -C $BUILD_TYPE 98 | 99 | - name: Install & Strip 100 | shell: bash 101 | run: cmake --install . --strip 102 | 103 | - name: Pack 104 | shell: bash 105 | working-directory: instdir 106 | run: | 107 | ls -laR 108 | ${{ matrix.config.archiver }} ../${{ matrix.config.artifact }} . 109 | 110 | - name: Upload 111 | uses: actions/upload-artifact@v4 112 | with: 113 | path: ./${{ matrix.config.artifact }} 114 | name: ${{ matrix.config.artifact }} 115 | 116 | - name: Upload release asset 117 | if: github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'created') 118 | uses: softprops/action-gh-release@v1 119 | env: 120 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 121 | with: 122 | files: ./${{ matrix.config.artifact }} 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Dragon Wars specific files. 55 | sdldragon 56 | xdragon 57 | ndragon 58 | data1 59 | data2 60 | dragon.com 61 | *.swp 62 | 63 | cmake-build-* 64 | build-*/ 65 | build/ 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | project(opendw) 3 | 4 | set(PACKAGE opendw) 5 | set(VERSION 0.02) 6 | 7 | enable_testing() 8 | 9 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) 10 | 11 | # Currently the only supported output is SDL2. 12 | find_package(SDL2 REQUIRED) 13 | message (SDL2_FOUND=${SDL2_FOUND}) 14 | message (SDL2_INC_DIRS=${SDL2_INCLUDE_DIRS}) 15 | message (SDL2_INC_DIR=${SDL2_INCLUDE_DIR}) 16 | message (SDL2_LIBS=${SDL2_LIBRARIES}) 17 | message (SDL2_LIB=${SDL2_LIBRARY}) 18 | #find_package(X11) 19 | 20 | add_subdirectory(src) 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Devin Smith 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Launcher for CMake 2 | 3 | export CMAKE_GENERATOR = Ninja 4 | export CMAKE_COLOR_DIAGNOSTICS = ON 5 | 6 | all: debug release 7 | 8 | clean: 9 | rm -r build_cmake 10 | 11 | clean_cache: 12 | rm -rf build_cmake/*/CMake* 13 | 14 | debug: 15 | mkdir -p build_cmake/debug 16 | cd build_cmake/debug && cmake -DCMAKE_BUILD_TYPE=Debug ../.. 17 | cd build_cmake/debug && cmake --build . 18 | 19 | release: 20 | mkdir -p build_cmake/release 21 | cd build_cmake/release && cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../.. 22 | cd build_cmake/release && cmake --build . 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenDW 2 | A game engine for Interplay's 1989/1990 Dragon Wars game. 3 | Original data files are required. 4 | This game was originally written in 16 bit x86 assembly language running in 5 | real mode. OpenDW is an attempt to port this game to modern environments using 6 | the C language. 7 | 8 | Original game engine by [Rebecca Ann Heineman](https://www.burgerbecky.com/). 9 | 10 | This game can be purchased at [GOG](https://www.gog.com/game/dragon_wars). 11 | 12 | # Prerequisites 13 | - Executable (DRAGON.COM). 14 | - Original data files (DATA1 and DATA2). 15 | - SDL2. 16 | - *Optionally* [Check](https://libcheck.github.io/check/) (building unit tests only). 17 | 18 | Under Linux/Unix operating systems, this engine expects that the original game 19 | files should be all lowercase (e.g. DRAGON.COM -> dragon.com). The files 20 | dragon.com, data1, and data2 should be in the same directory with the build. 21 | 22 | # Building 23 | 24 | Install dependencies first: 25 | 26 | VoidLinux 27 | 28 | ``` 29 | sudo xbps-install -S SDL2-devel 30 | ``` 31 | 32 | Debian 33 | 34 | ``` 35 | sudo apt install libsdl2-dev 36 | ``` 37 | 38 | Then use CMake to build: 39 | 40 | ``` 41 | mkdir build 42 | cd build 43 | cmake .. 44 | make 45 | ``` 46 | 47 | The `sdldragon` binary will be in build/src/fe/sdldragon 48 | 49 | Other flags can be passed to CMake: 50 | 51 | * `ENABLE_TESTS=ON/OFF` toggles building unit tests (Requires Check). OFF by default. 52 | * `ENABLE_TOOLS=ON/OFF` toggles building some extra tools for extracting resources. OFF by default. 53 | 54 | # Screenshots 55 | 56 | ![Title screen](img/dw.png) 57 | ![Party](img/choose_party.png) 58 | ![Opening](img/opening.png) 59 | ![Encounter](img/encounter.png) 60 | 61 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | op_51 extracts data from ES segment, but it's not clear where ES is setup 2 | 3 | More drawing routines (sub_CF8) 4 | There is still some UI glitches with the viewport (e.g. water displaying instead of land). 5 | 6 | Run script (Section 3, 1706 -> 0x1E1) 7 | op_7C is the start of a random encounter (I think) but we don't match up with dragon.com for some reason. 8 | -- Check that op_30 is correctly populating variables. 9 | -------------------------------------------------------------------------------- /cmake/FindSDL2.cmake: -------------------------------------------------------------------------------- 1 | find_path(SDL2_INCLUDE_DIR SDL.h PATH_SUFFIXES SDL2 PATHS ${CMAKE_SOURCE_DIR}/deps/sdl2/include) 2 | set(SDL2_INCLUDE_DIRS ${SDL2_INCLUDE_DIR}) 3 | 4 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 5 | set(VC_LIB_PATH_SUFFIX lib/x64) 6 | else() 7 | set(VC_LIB_PATH_SUFFIX lib/x86) 8 | endif() 9 | 10 | FIND_LIBRARY(SDL2_LIBRARY NAMES SDL2 PATH_SUFFIXES lib PATHS ${CMAKE_SOURCE_DIR}/deps/sdl2/${VC_LIB_PATH_SUFFIX}) 11 | IF(SDL2_LIBRARY) 12 | SET(SDL2_LIBRARIES ${SDL2_LIBRARIES} ${SDL2_LIBRARY}) 13 | ENDIF(SDL2_LIBRARY) 14 | 15 | IF(WIN32) 16 | # MinGW needs an additional library, mwindows 17 | # It's total link flags should look like -lmingw32 -lSDL2TTFmain -lSDL2TTF -lmwindows 18 | # (Actually on second look, I think it only needs one of the m* libraries.) 19 | IF(MINGW) 20 | SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") 21 | ENDIF(MINGW) 22 | FIND_LIBRARY(SDL2_MAIN_LIBRARY NAMES SDL2main PATH_SUFFIXES lib PATHS ${CMAKE_SOURCE_DIR}/deps/sdl2/${VC_LIB_PATH_SUFFIX}) 23 | SET(SDL2_LIBRARIES ${MINGW32_LIBRARY} ${SDL2_MAIN_LIBRARY} ${SDL2_LIBRARIES}) 24 | ENDIF(WIN32) 25 | 26 | INCLUDE(FindPackageHandleStandardArgs) 27 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARIES SDL2_INCLUDE_DIRS) 28 | MARK_AS_ADVANCED(SDL2_LIBRARY SDL2_MAIN_LIBRARY SDL2_INCLUDE_DIR) 29 | -------------------------------------------------------------------------------- /doc/interrupts.md: -------------------------------------------------------------------------------- 1 | # Interrupt handling in Dragon Wars 2 | 3 | There are typically no "threads" in 16 bit DOS programs, but DOS does 4 | provide interrupts that can be hooked (like a signal) that are invoked on 5 | certain conditions. These are also called interrupt service routines (ISRs). 6 | These are the interrupts handled by Dragon Wars. 7 | 8 | ## DOS Interrupts 9 | ### Critical Error handler: 0x0616 (INT 0x24) 10 | 11 | Appears to abort the program (dragon.com) on error, but it's unused 12 | by dosbox emulation (as far as I can tell). 13 | 14 | ## Hardware Interrupts 15 | ### System timer handler (title screen) 0x5C7B (INT 0x08) 16 | 17 | Invoked on hardware PIT interrupt (0x08). Responsible for playing 18 | opening music during the title screen. 19 | 20 | ## Software Interrupts 21 | ### Tick handler: 0x4B10 (INT 0x1C) 22 | 23 | First invokes existing tick handler then stores values into 24 | timer variables. 25 | -------------------------------------------------------------------------------- /doc/keypress.txt: -------------------------------------------------------------------------------- 1 | In game, pressing 'q' should quit the game. 2 | 3 | op_89 4 | 0x4984 (engine.c:3417) 5 | cpu.ax = 0x18D1 6 | op_58 // loads key response script (section F) also key lookup data? 7 | op_06 // set loop counter (9 ?) 8 | op_12 (bx = 0x41) (store_game_state_data) (key press code) 9 | op_0D 10 | op_3D 11 | op_44 12 | // Loops through all characters (loop ends at 2) 13 | op_49 14 | op_0D // load 15 | op_3D // operate 16 | op_44 // check 17 | op_49 18 | op_0D 19 | op_3D 20 | 21 | op_2B 22 | op_00 // set mask 23 | op_0D // load ? (jump address) ? 24 | op_56 25 | op_01 // clear mask 26 | op_54 // pop's word from 56? 27 | op_74 28 | 29 | 30 | Encounter: (op_7C) 31 | 32 | op_30 33 | op_7C (0x1E1) 34 | op_01 // clear mask (operate on 8 bit values) 35 | op_66 (1 argument) (load game_state and check for 0) set zero flag. 36 | op_46 (is argument signed) 37 | op_10 ( 38 | -------------------------------------------------------------------------------- /doc/levels.md: -------------------------------------------------------------------------------- 1 | Levels are a variable number of bytes. 2 | 3 | Examples: 4 | 5 | Purgatory (0x71) 6 | ================ 7 | 8 | First 4 bytes of level file: 9 | 10 | 0x22, 0x22, 0x1c, 0x64, these are loaded into gamestate [0x21 - 0x24] 11 | 12 | Next bytes are variable, read until hitting > 0x80: 13 | 14 | These are the resources we need for this level (& 0x7F += 0x6E): 15 | 16 | 0x00, 0x01, 0x02, 0x06, 0x09, 0x05, 0x7, 0x93 17 | ============================================= 18 | 0x6E, 0x6F, 0x70 19 | 20 | Next bytes are set in pairs: 21 | 22 | 0x00, 0xd1 23 | 0x05, 0x81 24 | 0x00, 0xe1 25 | 0x80, 0x91 26 | 27 | # Cached resources 28 | 29 | Next bytes are loaded individually but are a maximum of 4 byte chunks 30 | 31 | | Resources | Resource Size | 32 | |------------|------------------| 33 | | 0 - 3 | 0x1, 0x82 | 34 | | 4 - 7 | 0x06, 0x82 | 35 | | 8 - 11 | 0x04, 0x03, 0x87 | 36 | 37 | Now offset to the title of the level: 38 | 39 | 0x16CF 40 | -------------------------------------------------------------------------------- /doc/monsters.txt: -------------------------------------------------------------------------------- 1 | Monster graphics / sprites are stored in the DATA1 file. 2 | The following resources contain the different monster sprites 3 | 4 | 168 (A8) - Wolf (wild dog?) 5 | 196 (C4) - Spider / Rock Spiders 6 | 200 (C8) - Innocent Man 7 | 210 (D2) - Pikeman 8 | 222 (DE) - Fanatic / Loon 9 | -------------------------------------------------------------------------------- /doc/references.md: -------------------------------------------------------------------------------- 1 | # Reference Material 2 | 3 | Burger Becky on animation and compression techniques used for 4 | Bards Tale / Dragon Wars: 5 | 6 | https://youtu.be/Uf-JvoZxnso?t=2873 (about 30 minutes) 7 | -------------------------------------------------------------------------------- /doc/resource31.md: -------------------------------------------------------------------------------- 1 | # Resource 31 data 2 | 3 | 0000-0001: (0x1F9) 16 bit offset to ? 4 | 5 | 6 | 0002-0003: (0x1AD) 16 bit offset to another lookup table 7 | 01AE-01AF: (0x1C4) 16 bit offset to another lookup table. 8 | 9 | 01C4: 0x07 - ? 10 | 11 | -- Copied to gamestate[0x29+] 12 | 01C5->: 0x00 - 13 | 14 | 0x1F9: 0x16 - ? (multiped with something) 15 | # 1, 2, 5, 11, 22, 44, 88, 176, 352, ... 16 | 17 | 0x1F9 + shift 18 | 19 | 20 | 0x4FE -> gets copied to script 0x3D6 21 | 0x4FE: 0x05, 0x16, 0x03, 0x01, 0x07, 0x00, 0x01, 0x00, 0x01, 22 | 0x30, 0x84, 0x1D, 0x28 23 | 24 | -------------------------------------------------------------------------------- /doc/resources.md: -------------------------------------------------------------------------------- 1 | # Game resources 2 | 3 | Resource data in data1. It is unknown how data2 is used at this point. 4 | 5 | | Num | Size | Compressed | Description | 6 | |------|------|------------|-------------------------------------| 7 | | 0 | 1148 | N | Initial game script | 8 | | 7 | 5632 | N | Character data. | 9 | | 29 | | Y | Title screen (320x200) | 10 | | 31 | 2177 | Y | String data for monsters? | 11 | | 71 | 5846 | Y | Purgatory level | 12 | | 110 |11860 | Y | Castle wall (viewport) | 13 | | 111 | 7050 | Y | Sky portion (viewport) | 14 | | 112 | 5054 | Y | Red clay road portion (viewport) | 15 | | 116 | 936 | Y | Water puddle (viewport) | 16 | | 261 |12452 | Y | Scream (PCM audio) | 17 | 18 | There are also resources in the game executable: 19 | 20 | | Num | Size | Description | 21 | |------|------|-------------------------------------| 22 | | 0 | 2560 | Bottom bricks (msg window) | 23 | | 1 | 128 | Lower left bricks (msg) | 24 | | 2 | 128 | Lower right bricks (msg) | 25 | | 3 | 1280 | Top bricks (msg) | 26 | | 4 | 576 | Right character border | 27 | | 5 | 1536 | Character banner (Dragon Wars) | 28 | | 6 | 1152 | Left green pillar | 29 | | 7 | 64 | Left brick pillar connector | 30 | | 8 | 64 | Right brick pillar connector | 31 | | 9 | 2880 | Right green pillar | 32 | | 10 | 364 | North arrow | 33 | | 11 | 364 | East arrow | 34 | | 12 | 364 | South arrow | 35 | | 13 | 364 | West arrow | 36 | 37 | 38 | # Resource 7 (Character data) 39 | 40 | The first 3584 (7 * 512) are individual player records. 41 | The next 256 bytes are gamestate data 42 | The final 0x700 bytes are loaded into D760. 43 | 44 | -------------------------------------------------------------------------------- /doc/script.md: -------------------------------------------------------------------------------- 1 | # Game script and virtual CPU 2 | 3 | Much of the Dragon Wars game is implemented by various game scripts that execute on a virtual CPU. This 4 | virtual CPU, can run in both 8 bit or 16 bit mode. The virtual CPU has at most 256 op codes. This virtual CPU 5 | is implemented inside engine.c. Op codes take a variable number of arguments and writing a disassembler is 6 | difficult because the word/byte mode may switch before a jump is taken. 7 | 8 | Not much is known about the CPU but it may be somewhat similar to the 65C816 processor considering the game 9 | was originally authored on the Apple II GS. 10 | 11 | The initial game script is stored in the DATA1 file, in the very first section (section 0). 12 | 13 | Known scripts: 14 | DATA1: Section 0 (initial game script) 1148 bytes. 15 | Section 0 off: 1103, 45 bytes 16 | Section 0 off: 237 ? 17 | Section 0 off: 246, ? (Display's player name) 18 | 19 | Section 0 off: 1137, 11 bytes 20 | 21 | Section 71, offset: 3735 22 | Section 71, offset: 5764 23 | 24 | Section 22, offset 68 25 | 26 | Section 71, 5764 27 | Section 71, 3689 28 | 29 | Section 3, 1706 (encounter) 30 | Section 71, 0xe97 (Purgatory game start) 31 | 32 | Section ?, 395 33 | 34 | 35 | 36 | The following op codes are documented. Arguments are designated with number and type (B = byte, W = word) 37 | 38 | | OP | Arguments | Description | 39 | |----|-----------|----------------------------------------------| 40 | | 00 | None | Switches to 16 bit mode | 41 | | 01 | None | Switches to 8 bit mode | 42 | | 02 | -- | Unknown | 43 | | 03 | None | Pops stack, resets resource | 44 | | 04 | None | Pushes resource index on stack | 45 | | .. | .. | .. | 46 | | 09 | 1 (B|W) | Loads word\_3AE2 with argument | 47 | | 10 | 2 B | Load gamestate (arg1), offset (arg2) word value | 48 | | .. | .. | .. | 49 | | 46 | None | Jump if signed | 50 | | .. | .. | .. | 51 | | 58 | 1 B | Load gamestate, check and set zero/sign flag | 52 | | 66 | 1 B | Load gamestate, check and set zero/sign flag | 53 | -------------------------------------------------------------------------------- /doc/style.md: -------------------------------------------------------------------------------- 1 | # OpenDW Coding style 2 | 3 | The coding style of OpenDW is not ideal. In fact it's more or less a rough 4 | translation of 16 bit x86 assembly to C. Variables are just memory locations 5 | and it's not always easy to to determine the scope of a variable. IDA helps 6 | with this to some extent but even IDA could not fully disassemble this game. 7 | 8 | There is some hope that with further reverse engineering of the game that 9 | the coding style can be improved but for now the goal is to get through a 10 | rough translation first. 11 | 12 | -------------------------------------------------------------------------------- /doc/viewport.md: -------------------------------------------------------------------------------- 1 | Viewport drawing 2 | ================ 3 | 4 | 1. The sky tile is drawn first 5 | 6 | 2. Then components of the ground (this is drawn in 9 sprites) 7 | 8 | 3. Then other components (there are 24 possible sprites that can be overlaid) 9 | 10 | 11 | The components of the ground and other components are defined by data\_5A56 12 | which is re-loaded based on x,y position and direction. 13 | 14 | When drawing ground components we ignore the lower 4 bits of 5A56, 15 | 16 | When drawing other components we ignore the lower 4 bits only if the 17 | value is greater than 0x80. 18 | 19 | This allows bytes in 5A56 to serve as a dual purpose. 20 | 21 | -------------------------------------------------------------------------------- /dos/README.md: -------------------------------------------------------------------------------- 1 | DOS Version of Dragon Wars 2 | ========================== 3 | 4 | This is a DOS version of Dragon Wars that we hope to be byte for byte 5 | compatible with the Interplay version. It uses MASM 5.0 and a makefile for 6 | generation of the executable. 7 | 8 | To generate, use DOS (or dosbox) with an installation of MASM 5.0 9 | 10 | ``` 11 | MAKE DRAGON 12 | DRAGON.COM 13 | ``` 14 | 15 | A `DRAGON.EXE` is also generated, but it does not work. This is because MASM 16 | 5.0 cannot generate COM files directly and `EXE2BIN` is used to convert the 17 | EXE to a COM file. 18 | 19 | -------------------------------------------------------------------------------- /dos/dragon: -------------------------------------------------------------------------------- 1 | # Simple makefile for Interplay's DragonWars 2 | 3 | DRAGON.OBJ: DRAGON.ASM 4 | MASM DRAGON; 5 | 6 | DRAGON.EXE: DRAGON.OBJ 7 | LINK DRAGON; 8 | 9 | DRAGON.COM: DRAGON.EXE 10 | EXE2BIN DRAGON.EXE DRAGON.COM 11 | -------------------------------------------------------------------------------- /img/choose_party.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devinsmith/opendw/ba76991f1e029f3d3ccf727c2887dd6f5a77f2a2/img/choose_party.png -------------------------------------------------------------------------------- /img/dw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devinsmith/opendw/ba76991f1e029f3d3ccf727c2887dd6f5a77f2a2/img/dw.png -------------------------------------------------------------------------------- /img/encounter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devinsmith/opendw/ba76991f1e029f3d3ccf727c2887dd6f5a77f2a2/img/encounter.png -------------------------------------------------------------------------------- /img/opening.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devinsmith/opendw/ba76991f1e029f3d3ccf727c2887dd6f5a77f2a2/img/opening.png -------------------------------------------------------------------------------- /script/encounter.scr: -------------------------------------------------------------------------------- 1 | 000000: 09 FF 52 8E 0E 09 04 52 8E 0E 09 05 52 8E 0E 09 ..R....R....R... 2 | 000010: 06 52 8E 0E 09 07 52 8E 0E 09 08 52 8E 0E 09 09 .R....R....R.... 3 | 000020: 52 8E 0E 09 01 58 01 02 00 42 96 0E 5B 5A 9D 19 R....X...B..[Z.. 4 | 000030: 99 45 3C 11 9B 19 99 58 13 06 00 5A 62 24 01 42 .E<....X...Zb$.B 5 | 000040: 3C 11 58 08 18 00 0E 00 5A 74 05 20 23 90 78 F4 <.X.....Zt. #.x. 6 | 000050: D2 68 9B 52 08 82 81 40 2A 70 66 93 04 8A 9D C3 .h.R...@*pf..... 7 | 000060: 61 31 98 0F 8E 28 84 66 63 ED B0 DD F4 0C 8C 60 a1...(.fc......` 8 | 000070: CD 26 0D 85 33 44 2F 38 66 93 05 22 88 DF 10 CD .&..3D/8f..".... 9 | 000080: 31 7D 10 90 D9 2A 22 43 77 D0 26 74 CD 13 14 33 1}...*"Cw.&t...3 10 | 000090: 4A A4 12 C4 C2 29 8A 15 49 86 C0 40 9D 04 B9 45 J....)..I..@...E 11 | 0000a0: 71 0F 9B 04 B9 78 F2 A4 13 C4 C6 2B DA 13 C5 ED q....x.....+.... 12 | 0000b0: 0F A1 48 A0 53 7C 6E 1B 3E 33 48 44 16 3D C8 09 ..H.S|n.>3HD.=.. 13 | 0000c0: 55 53 05 4E 9B C3 64 8A 61 6A 90 99 84 66 0C 92 US.N..d.aj...f.. 14 | 0000d0: 26 31 F6 0D 85 33 47 61 9A 4C E0 88 25 34 A0 68 &1...3Ga.L..%4.h 15 | 0000e0: 8A 66 0C AC A6 86 69 8B E8 84 86 69 30 52 28 8D .f....i....i0R(. 16 | 0000f0: F1 FF 00 88 74 0A 48 1E 70 78 F1 5E 1B BE 81 39 ....t.H.px.^...9 17 | 18 | word_3AE2 = 0xFF; 19 | call(0xE8E); 20 | 21 | 22 | sub_E8E() 23 | { 24 | op_58(01, 0x0002) // loads tag item, and offset, now we switch to a new script. 25 | } 26 | 27 | tag_item 1: offset2 28 | 29 | op_58(0x03, 0x0000) 30 | 31 | Section 3 script (5390 bytes) 32 | 000000: 12 28 04 55 12 53 9A 35 11 36 06 06 0B 0A 13 11 .(.U.S.5.6...... 33 | 000010: 49 0C 00 19 1F 20 53 F1 04 53 6B 01 53 D2 01 58 I.... S..Sk.S..X 34 | 000020: 12 1A 00 74 16 00 28 98 76 58 12 00 00 53 0E 01 ...t..(.vX...S.. 35 | 000030: 78 08 A1 03 0A 38 EF 40 58 12 97 00 42 28 00 52 x....8.@X...B(.R 36 | 37 | 38 | 39 | 00: op_12(0x28); -- Set's gamestate arg1 with word_3AE2 low byte. (0xFF) 40 | 02: op_04(); 41 | 03: op_55(); // Extract stack byte or word. 42 | 04: op_12(0x53); -- Set's gamestate arg1 with word_3AE2 low byte. (resource_index) 43 | 06: op_9A(0x35); -- Set gamestate (arg1) with 0xFF 44 | 08: op_11(0x36); -- Set gamestate (arg1) with 0x00 ? 45 | 0A: op_06(0x06); -- Set 3AE4 as arg1 (set loop?) 46 | // do 47 | { 48 | 0C: op_0B(0x0A); -- Set's 3AE2 with game_state byte (hmm) 49 | 0E: op_13(0x11); -- 50 | 11: LOOP(0x000C) // While 51 | 52 | 53 | // 5D 4D 99 45 F5 00 23 41 5A 54 | func_237() 55 | { 56 | get_character_data(0x4D); // move to word_3AE2 57 | op_99() // do tests 58 | jz(0x00F5) // 59 | increment_memory(0x41) 60 | } 61 | 62 | // 5D 4D 99 44 7B 04 30 56 4E 99 5A 63 | func_1137() 64 | 65 | { 66 | get_character_data(0x4D); 67 | op_99(); // test 68 | if (!= 0) { 69 | // jmp to 0x47B 70 | } 71 | op_30(0x56); 72 | op_4E? 73 | op_99(); 74 | } 75 | -------------------------------------------------------------------------------- /script/init.cpp: -------------------------------------------------------------------------------- 1 | // 0000: 01 7b f2 9d 33 46 0c 15 c0 53 2a 03 2 | set_byte_mode(); 3 | 4 | // f2 9d 33 46 0c 15 c0 5 | read_header_bytes(); // Sets title as "Interplay" 6 | call 0x032A 7 | 8 | // 032A: 09 07 86 12 98 1a 9 | // 0330: 43 01 00 11 96 11 41 01 10 | set_word3AE2_arg(0x07); // load word_3AE2 with 7 11 | load_word3AE2_resource(word_3AE2); // store resource index into word_3AE2 12 | op_12(0x98); // set game state 0x98 with word_3AE2 (resource index) 13 | op_1A(0x43, 0x01); // set game_state 0x43 with 0x01 14 | set_word_mode(); 15 | op_11(0x96); // set game_state 0x96-0x97 (word mode) with 0x0000. 16 | op_11(0x41); // set game_state 0x41-0x42 (word mode) with 0x0000 17 | set_byte_mode(); 18 | 19 | 20 | // Initialize characters single byte at a time in loop of 255. 21 | // 0338: 06 FF 0F 96 17 41 49 3A 22 | // 0340: 03 23 | int counter = 0xFF; // typically stored in word_3AE4 24 | do { 25 | do { 26 | // 0x33A 27 | 28 | // Transfer point is through word_3AE2 29 | op_0F(0x96); // loads word_3AE2 30 | store_data_into_resource(0x41) // stores word_3AE2 31 | counter--; 32 | } while(counter > 0); 33 | 34 | // 0341: 23 42 23 97 0a 42 3e 0e 41 3a 03 0a 56 99 44 35 | increment_memory(0x42); 36 | increment_memory(0x97); 37 | load_word3AE2_gamestate(0x42); 38 | } while (word_3AE2 < 0xE); 39 | 40 | // 034C: 0a 56 99 44 41 | // 0350: 53 03 85 0a 5a 99 44 5a 03 85 40 96 41 64 03 40 42 | load_word3AE2_gamestate(0x56); 43 | op_99(word3AE2); // test it. 44 | if (word3AE2 != 0) { 45 | op_85(word3AE2); 46 | } 47 | 48 | load_word3AE2_gamestate(0x56); 49 | op_99(word3AE2); // test it. 50 | if (word3AE2 != 0) { 51 | op_85(word3AE2); 52 | } 53 | 54 | // 0x035A 55 | for (int i = 0; i < 0xFF; i++) { 56 | op_40(0x96) // tests against word_3AE4 57 | jc(0x0364) 58 | op_40(0x99) 59 | jc(0x0368) 60 | // 0x364 61 | 62 | op_0F(0x96); // loads word_3AE2 63 | op_13(0x00); // set game_state(0 + word_3AE4) with word_3AE2 64 | } 65 | 66 | // Load gamestate with 0xFF 67 | op_9A(0x5B); 68 | op_9A(0x57); 69 | op_9A(0x5A); 70 | 71 | op_05(0x98); // load word_3AE4 with gs[0x98] 72 | op_93(); // push word_3AE4 (byte) onto stack 73 | op_03(); // pop byte, reload scripts. 74 | set_word_mode(); 75 | 76 | counter = 0xff; 77 | 78 | -------------------------------------------------------------------------------- /script/script01.scr: -------------------------------------------------------------------------------- 1 | Disassembling script01.bin (208 bytes) 2 | 0x0000: word_3AE2 = 0xff 3 | 0x0002: load_resource res: 0x03, offset: 0x0000 4 | 0x0006: jnc 0x001f 5 | 0x0009: refresh_viewport 6 | 0x000A: set loop = 0x03 7 | 0x000C: call 0x0025 8 | 0x000F: call 0x0025 9 | 0x0012: word_3AE2 = gamestate[0x26] 10 | 0x0014: word_3AE2 &= 0xc0 11 | 0x0016: cmp word_3EA2, 0xc0 12 | 0x0018: jnz 0x0020 13 | 0x001B: loop 0x000f 14 | 0x001E: stc 15 | 0x001F: retf 16 | 0x0020: op_6C 17 | 0x0021: gamestate[0x40] = 0 18 | 0x0023: stc 19 | 0x0024: retf 20 | 0x0025: word_3AE2 = gamestate[0x03] 21 | 0x0027: inc word_3AE2 22 | 0x0028: word_3AE2 &= 0x03 23 | 0x002A: gamestate[0x03] = word_3AE2 24 | 0x002C: refresh_viewport 25 | 0x002D: ret 26 | 0x002E: op_6D 27 | 28 | # Main game loop spans 0x2F - 0x85 29 | # Starting a new game? 30 | 0x002F: refresh_viewport 31 | 0x0030: load_world 32 | 0x0031: op_48 0xda 33 | 0x0033: jnz 0x003a 34 | 35 | # Check for dead. 36 | 0x0036: load_resource res: 0x16, offset: 0x0000 37 | 38 | 0x003A: op_48 0x3d 39 | 0x003C: jz 0x002f 40 | 0x003F: op_91 41 | 0x0040: word_3AE4 = gamestate[0x03] 42 | 0x0042: inc byte [word_3AE4] 43 | 0x0043: op_9D 0x05, 0x23 44 | 0x0046: jnz 0x004f 45 | 0x0049: test game_state[0xC2] 46 | 0x004B: jnz 0x004f 47 | 0x004E: word_3AE4 = 0 48 | 0x004F: gamestate[0xbe] = word_3AE4 49 | 0x0051: wait_event 0x4038, 'J', 0x0079, "LEFT", 0x0079, 'L', 0x007d, "RIGHT", 0x007d, 'K', 0x00b0, "ENTER", 50 | 0x00b0, "UP", 0x00b0, "DOWN", 0x007b, 'I', 0x0088, "#[1-7]", 0x00c2, '?', 0x002e, "Unknown", 0x00c9, 0xff 51 | 52 | # TURNING 53 | # ----------------------------------------------- 54 | # game_state[0x03] will hold direction 55 | # 56 | 57 | # Turn left (we turn right 3 times) 58 | 0x0079: inc game_state[0x03] 59 | # Turn down (we turn right 2 times) 60 | 0x007B: inc game_state[0x03] 61 | # Turn right (once) 62 | 0x007D: inc game_state[0x03] 63 | 0x007F: word_3AE2 = gamestate[0x03] 64 | 0x0081: word_3AE2 &= 0x03 65 | 0x0083: gamestate[0x03] = word_3AE2 66 | 0x0085: jmp 0x002f 67 | 68 | 69 | # Move forward 70 | 0x0088: word_3AE2 = gamestate[0x26] 71 | 0x008A: word_3AE2 &= 0xc0 72 | 0x008C: test word_3AE2 73 | 0x008D: jnz 0x00a8 74 | 0x0090: op_90 0x01 75 | 0x0092: op_6C 76 | 0x0093: gamestate[0x40] = 0 77 | 0x0095: refresh_viewport 78 | 0x0096: word_3AE2 = gamestate[0x24] 79 | 0x0098: test word_3AE2 80 | 0x0099: jz 0x0030 81 | 0x009C: op_4D 82 | 0x009D: test word_3AE2 83 | 0x009E: jnz 0x0030 84 | 0x00A1: load_resource res: 0x01, offset: 0x0000 85 | 0x00A5: jmp 0x002f 86 | 0x00A8: op_90 0x03 87 | 0x00AA: op_9B 0x01, 0x40 88 | 0x00AD: jmp 0x0030 89 | 90 | # Enter/UP 91 | 0x00B0: word_3AE2 = gamestate[0x26] 92 | 0x00B2: word_3AE2 &= 0xc0 93 | 0x00B4: test word_3AE2 94 | 0x00B5: jz 0x0090 95 | 0x00B8: op_2A 96 | 0x00B9: test word_3AE2 97 | 0x00BA: js 0x00a8 98 | 0x00BD: op_90 0x02 99 | 0x00BF: jmp 0x0092 100 | 101 | # Mini map 102 | 0x00C2: load_resource res: 0x0d, offset: 0x0000 103 | 0x00C6: jmp 0x0031 104 | 105 | # Other keys? 106 | 0x00C9: load_resource res: 0x0f, offset: 0x0000 107 | 0x00CD: jmp 0x0031 108 | -------------------------------------------------------------------------------- /script/script11.scr: -------------------------------------------------------------------------------- 1 | Disassembling script11.bin (877 bytes) 2 | 0x0000: jmp 0x0012 3 | 0x0003: jmp 0x003f 4 | 0x0006: jmp 0x0066 5 | 0x0009: jmp 0x007b 6 | 0x000C: jmp 0x00a2 7 | 0x000F: jmp 0x0215 8 | 0x0012: load_resource res: 0x0b, offset: 0x003f 9 | 0x0016: gamestate[0x41] = 0xFF 10 | 0x0018: var al = game_state[6] 11 | for (game_state[6] < game_state[0x1F]; game_state[6]++) { 12 | call 0x001E 13 | } 14 | game_state[6] = al 15 | 0x001B: test game_state[0x41] 16 | 0x001D: retf 17 | 0x001E: test game_state[0x41] 18 | 0x0020: jz 0x003e 19 | 0x0023: word_3AE4 = gamestate[0xD9] 20 | 0x0025: push_byte(word_3AE4 & 0xFF) 21 | 0x0026: data_res = resource_get(pop_byte()) 22 | 0x0027: .word 23 | 0x0028: word_3AE2 = gamestate[0xD7] 24 | 0x002A: op_64 25 | 0x002B: .byte 26 | 0x002C: jnz 0x003e 27 | 0x002F: gamestate[0x41] = 0 28 | 0x0031: write_character_name 29 | 0x0032: set_msg $(" gets the ") 30 | 0x003A: op_7E 0x09 31 | 0x003C: op_AE 32 | 0x003D: write_number 33 | 0x003E: ret 34 | 0x003F: push (word|byte) 35 | 0x0040: test word_3AE2 36 | 0x0041: .word 37 | 0x0042: js 0x0059 38 | 0x0045: set loop = 0x06 39 | 0x0047: gamestate[0x58] = 0 40 | gamestate[0x59] = 0 41 | 0x0049: var offset = gamestate[0x58] 42 | offset += gamestate[0x59] << 8 43 | offset += word_3AE4 44 | word_3AE2 = resource_idx(gamestate[0x5A])->bytes[offset] 45 | word_3AE2 += resource_idx(gamestate[0x5A])->bytes[offset + 1] << 8 46 | 0x004B: gamestate[0x58] = word_3AE2 47 | 0x004D: pop byte 48 | 0x004E: shl byte [word_3AE4], 1 49 | 0x004F: var offset = gamestate[0x58] 50 | offset += gamestate[0x59] << 8 51 | offset += word_3AE4 52 | word_3AE2 = resource_idx(gamestate[0x5A])->bytes[offset] 53 | word_3AE2 += resource_idx(gamestate[0x5A])->bytes[offset + 1] << 8 54 | 0x0051: gamestate[0xd7] = word_3AE2 55 | 0x0053: .byte 56 | 0x0054: word_3AE4 = gamestate[0x5A] 57 | 0x0056: gamestate[0xd9] = word_3AE4 58 | 0x0058: retf 59 | 0x0059: pop byte 60 | 0x005A: shl byte [word_3AE4], 1 61 | 0x005B: op_0D 0x0354 62 | 0x005E: gamestate[0xd7] = word_3AE2 63 | 0x0060: .byte 64 | 0x0061: push_byte(running_script_idx) 65 | 0x0062: pop byte 66 | 0x0063: gamestate[0xd9] = word_3AE4 67 | 0x0065: retf 68 | 0x0066: load_resource res: 0x0b, offset: 0x003f 69 | 0x006A: push_byte(word_3AE4 & 0xFF) 70 | 0x006B: data_res = resource_get(pop_byte()) 71 | 0x006C: .word 72 | 0x006D: word_3AE2 = gamestate[0xD7] 73 | 0x006F: op_64 74 | 0x0070: .byte 75 | 0x0071: jz 0x0079 76 | 0x0074: call 0x008e 77 | 0x0077: wait_escape 78 | 0x0078: retf 79 | 0x0079: clc 80 | 0x007A: retf 81 | 0x007B: load_resource res: 0x0b, offset: 0x003f 82 | 0x007F: push_byte(word_3AE4 & 0xFF) 83 | 0x0080: data_res = resource_get(pop_byte()) 84 | 0x0081: .word 85 | 0x0082: word_3AE2 = gamestate[0xD7] 86 | 0x0084: op_64 87 | 0x0085: .byte 88 | 0x0086: jz 0x0079 89 | 0x0089: wait_escape 90 | 0x008A: call 0x008e 91 | 0x008D: retf 92 | 0x008E: draw_pattern 93 | 0x008F: write_character_name 94 | 0x0090: set_msg $(" can't carry any more.") 95 | 0x00A0: stc 96 | 0x00A1: ret 97 | 0x00A2: op_73 98 | 0x00A3: .word 99 | 0x00A4: peek_and_pop 100 | 0x00A5: gamestate[0x41] = word_3AE2 101 | 0x00A7: peek_and_pop 102 | 0x00A8: gamestate[0x93] = word_3AE2 103 | 0x00AA: word_3AE4 = gamestate[0x42] 104 | 0x00AC: gamestate[0x95] = word_3AE4 105 | 0x00AE: word_3AE4 = 0 106 | 0x00AF: var offset = gamestate[0x93] 107 | offset += gamestate[0x94] << 8 108 | offset += word_3AE4 109 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 110 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 111 | 0x00B1: push (word|byte) 112 | 0x00B2: word_3AE2 = gamestate[0x41] 113 | 0x00B4: push (word|byte) 114 | 0x00B5: .byte 115 | 0x00B6: gamestate[0x4f] = 0 116 | 0x00B8: set loop = 0x03 117 | 0x00BA: var offset = gamestate[0x93] 118 | offset += gamestate[0x94] << 8 119 | offset += word_3AE4 120 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 121 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 122 | 0x00BC: word_3ADF[0x00ce] = word_3AE2 123 | 0x00BF: word_3ADF[0x01ed] = word_3AE2 124 | 0x00C2: dec [mem] 125 | 0x00C3: var offset = gamestate[0x93] 126 | offset += gamestate[0x94] << 8 127 | offset += word_3AE4 128 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 129 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 130 | 0x00C5: gamestate[0x45] = word_3AE2 131 | 0x00C7: cmp word_3EA2, 0xff 132 | 0x00C9: jz 0x00d2 133 | 0x00CC: inc byte [word_3AE4] 134 | 0x00CD: op_50 135 | 0x00CE: .word 136 | 0x00CF: jz 0x01ee 137 | 0x00D2: draw_rectangle 0x07, 0x18, 0x21, 0xa8 138 | 0x00D7: .word 139 | 0x00D8: word_3AE2 = word_3AE4 140 | 0x00D9: inc word_3AE2 141 | 0x00DA: op_2F 0x93 142 | 0x00DC: gamestate[0x93] = word_3AE2 143 | 0x00DE: word_3AE4 = 0 144 | 0x00DF: var offset = gamestate[0x93] 145 | offset += gamestate[0x94] << 8 146 | offset += word_3AE4 147 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 148 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 149 | 0x00E1: word_3AE2 &= 0x00ff 150 | 0x00E4: test word_3AE2 151 | 0x00E5: word_3AE2 = gamestate[0x93] 152 | 0x00E7: inc word_3AE2 153 | 0x00E8: jz 0x00f2 154 | 0x00EB: dec word_3AE2 155 | 0x00EC: word_3AE4 = gamestate[0x95] 156 | 0x00EE: push_byte(word_3AE4 & 0xFF) 157 | 0x00EF: data_res = resource_get(pop_byte()) 158 | 0x00F0: extract_string 159 | 0x00F1: wait_escape 160 | 0x00F2: gamestate[0x93] = word_3AE2 161 | 0x00F4: .byte 162 | 0x00F5: draw_and_set $("Who will get loot?\r\r") 163 | 0x0105: call 0x01f4 164 | 0x0108: jc 0x01ee 165 | 0x010B: draw_and_set $("Which item...\r\r") 166 | 0x0117: word_3AE4 = 0 167 | 0x0118: push_byte(word_3AE4 & 0xFF) 168 | 0x0119: word_3AE2 = word_3AE4 169 | 0x011A: word_3AE2 = word_3AE2 >> 1 170 | 0x011B: op_30 0x41 171 | 0x011D: write_number 172 | 0x011E: set_msg $(") ") 173 | 0x0122: var offset = gamestate[0x93] 174 | offset += gamestate[0x94] << 8 175 | offset += word_3AE4 176 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 177 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 178 | 0x0124: cmp word_3EA2, 0xfe 179 | 0x0126: jnz 0x013b 180 | 0x0129: set_msg $("Gold (") 181 | 0x0130: inc byte [word_3AE4] 182 | 0x0131: var offset = gamestate[0x93] 183 | offset += gamestate[0x94] << 8 184 | offset += word_3AE4 185 | word_3AE2 = resource_idx(gamestate[0x95])->bytes[offset] 186 | word_3AE2 += resource_idx(gamestate[0x95])->bytes[offset + 1] << 8 187 | 0x0133: call 0x0318 188 | 0x0136: op_82 0x37 189 | 0x0138: jmp 0x0159 190 | 0x013B: load_resource res: 0x0b, offset: 0x003f 191 | 0x013F: push_byte(word_3AE4 & 0xFF) 192 | 0x0140: data_res = resource_get(pop_byte()) 193 | 0x0141: .word 194 | 0x0142: word_3AE2 = gamestate[0xD7] 195 | 0x0144: op_30 0x000b 196 | -------------------------------------------------------------------------------- /script/script12.scr: -------------------------------------------------------------------------------- 1 | Disassembling res12.bin (1489 bytes) 2 | 0x0000: word_3AE2 = 0x01 3 | 0x0002: jmp 0x0007 4 | 0x0005: word_3AE2 = 0x80 5 | 0x0007: word_3ADF[0x0029] = word_3AE2 6 | 0x000A: draw_and_set $("Which...\r\r") 7 | 0x0013: op_63 0x0064 8 | 0x0016: word_3AE2 = gamestate[0x07] 9 | 0x0018: test word_3AE2 10 | 0x0019: jz 0x0054 11 | 0x001C: op_30 0x40 12 | 0x001E: word_3ADF[0x0026] = word_3AE2 13 | 0x0021: wait_event 0x9000, 0x81, 'A', 0x304c, "Unknown", 0x0a01, "Unknown", 0xf69b, "#[1-7]", 0x32ff, 'A', 0x0d21, '|', 0x0800, 0x07, '>', 0x44ad, '?', 0x4c00, 'Y', 0x7d76, 'x', 0x0409, 'm', 0x344c, 0x91, 0x83, '4', 0x2098, '~', 0xae09, 0x83, "LEFT", 0x0a52, "Unknown", 0x7d76, 'x', 0x450a, ' ', 0xe1b9, 'T', 0xd9cc, '`', 0x8800, 'K', 0x0a59, 0x07, '0', 0x8341, 0x09, ')', 0x5883, 0x02, '1', 0x0501, 0x07, 0x15, '|', 0x8300, '~', 0x8d09, 0x83, 'L', 0x005a, "Unknown", 0x0000, "Unknown", 0x0000, "Unknown", 0x0000, "Unknown", 0x0900, "#[1-7]", 0x8f52, "Unknown", 0x8009, 0x14, 'H', 0x7700, 'u', 0xa292, '$', 0x9c22, 'b', 0xd2a1, 'f', 0x31c6, '^', 0x0780, '"', 0x302d, 'A', 0x0983, ')', 0x0083, 0x0d, 'a', 0x7a00, "#[1-7]", 0x0e80, 0x09, ':', 0x9783, 0x0c, 0x81, 0x09, "ENTER", 0x2583, 'J', 0xa208, "Unknown", 0x0089, 0x90, 0x81, 'A', 0xcf44, "Unknown", 0x9201, "Unknown", 0xf69b, "#[1-7]", 0x32ff, 'A', 0x302a, 0x0c, '!', 0x803a, 0x12, 0x85, 0x97, "Unknown", 0x8612, 0x11, 0x87, 'L', 0xe959, "Unknown", 0x00f0, 'x', 0xfd00, "Unknown", 0xa7f4, 0x13, '9', 0x4813, "Unknown", 0x4df1, 'p', 0x68a6, '*', 0x0077, 'r', 0xf0bd, 'p', 0xf400, ' ', 0xaaa8, '`', 0x0619, 0x81, 0x11, 'A', 0x9307, 0x0d, "RIGHT", 0x1403, '%', 0x1101, 'G', 0x4811, 0x99, 'D', 0x0128, 0x07, 0x0a, 'A', 0x3c61, 'D', 0x0122, '"', 0x474e, '#', 0x4a41, 0x03, 0x18, "#[1-7]", 0x2b94, "Unknown", 0x470a, 0x15, '3', 0x0103, '.', 0x0f4a, 0x08, "#[1-7]", 0x4311, 0x11, 'D', 0x4711, 0x07, "Unknown", 0xb30d, 0x03, 0x12, 'A', 0xb50d, 0x03, '9', 0x1241, 'A', 0xb70d, 0x03, '9', 0x9941, "#[1-7]", 0x5c44, "#[1-7]", 0x4323, 0x0a, 'D', 0x4512, 'N', 0x2247, '0', 0x2105, '#', 0x4a44, 0x1e, '=', 0x0a01, 'C', 0x013e, 'A', 0x01e8, 'D', 0x0203, 'w', 0x92f5, '"', 0x2d24, 0x12, 0x14, 'L', 0x1d63, 'h', 0x1100, 'C', 0x9307, '"', 0x4750, 0x09, 0x80, 'D', 0x019b, '+', 0x0811, "Unknown", 0xd10d, 0x03, 'z', 0x0f78, 0x9a, '$', 0x9d28, "Unknown", 0x0a01, 0x08, 0x05, 'C', 0xad15, "#[1-7]", 0x2525, '%', 0x4308, 0x94, 'J', 0x8005, "#[1-7]", 0x0089, 0x80, 'L', 0x01f9, 'H', 0x01f9, 'D', 0x01f9, 'S', 0x01f9, 'M', 0x01f9, "ESC", 0x01f6, 0xff 14 | 0x0180: push_byte(word_3AE4 & 0xFF) 15 | 0x0181: word_3AE2 = word_3AE4 16 | 0x0182: op_50 0x47 17 | 0x0184: word_3AE2 = 0x80 18 | 0x0186: jz 0x19B 19 | 0x0189: shl byte [word_3AE4], 1 20 | 0x018A: gamestate[0x08] = 0 21 | 0x018C: .word 22 | 0x018D: word_3AE2 = op_0D 0x03D1 23 | 0x0190: extract_string 24 | 0x0191: set_msg $(" Magic ") 25 | 0x0198: .byte 26 | 0x0199: word_3AE2 = gamestate[0x08] 27 | 28 | 0x019B: word_3AE4 = gamestate[0x43] 29 | 0x019D: op_15 0x1AD 30 | 0x01A0: inc byte [word_3AE4] 31 | 0x01A1: inc byte [word_3AE4] 32 | 0x01A2: inc byte [word_3AE4] 33 | 0x01A3: gamestate[0x43] = word_3AE4 34 | 0x01A5: word_3AE4 = pop byte 35 | 0x01A6: if ++byte_3AE4 != 0x05 jmp 0x0180 36 | 37 | 0x01C0: draw_pattern 38 | 0x01C1: write_character_name 39 | 0x01C2: set_msg $(" only has ") 40 | 0x01CA: .word 41 | 0x01CB: word_3AE2 = get_char_data 0x1c 42 | 0x01CD: op_81 word_3AE2 43 | 0x01CE: .byte 44 | 0x01CF: dec word_3AE2 45 | 0x01D0: gamestate[0x09] = word_3AE2 46 | 0x01D2: set_msg $(" point of magic power.") 47 | 0x01E5: jmp 0x01f5 48 | 0x01E8: draw_pattern 49 | 0x01E9: write_character_name 50 | 0x01EA: set_msg $(" has no spells.") 51 | 0x01F5: wait_escape 52 | 0x01F6: .byte 53 | 0x01F7: stc 54 | 0x01F8: retf 55 | 0x01F9: .word 56 | 0x01FA: gamestate[0x4f] = 0xad 57 | gamestate[0x50] = 0x01 58 | 0x01FE: call 0x05be 59 | 0x0201: gamestate[0x45] = word_3AE2 60 | 0x0203: gamestate[0x43] = 0 61 | gamestate[0x44] = 0 62 | 0x0205: gamestate[0x47] = 0 63 | gamestate[0x48] = 0 64 | 0x0207: word_3AE2 = gamestate[0x45] 65 | 0x0209: op_34 0x06 66 | 0x020B: op_21 67 | 0x020C: word_3AE2 = 0x5600 68 | 0x020F: .word 69 | 0x0210: op_0D 0x03b3 70 | 0x0213: test word_3AE2 71 | 0x0214: .byte 72 | 0x0215: peek_and_pop 73 | 0x0216: jz 0x021f 74 | 0x0219: gamestate[0x41] = word_3AE2 75 | 0x021B: inc game_state[0x43] 76 | 0x021D: op_4E 77 | 0x021E: jns 0x2525 78 | 0x0221: inc word_3AE2 79 | 0x0222: cmp word_3EA2, 0x03 80 | 0x0224: jnc 0x020e 81 | 0x0227: word_3AE2 = gamestate[0x43] 82 | 0x0229: cmp word_3EA2, 0x01 83 | 0x022B: jz 0x027a 84 | 0x022E: draw_and_set $("Which type...\r\r") 85 | 0x023A: gamestate[0x43] = 0 86 | 0x023C: word_3AE4 = 0 87 | 0x023D: push_byte(word_3AE4 & 0xFF) 88 | 0x023E: word_3AE2 = word_3AE4 89 | 0x023F: op_50 90 | 0x0240: jns 0x8009 91 | 0x0243: jz 0x0251 92 | 0x0246: shl byte [word_3AE4], 1 93 | 0x0247: gamestate[0x08] = 0 94 | 0x0249: .word 95 | 0x024A: op_0D 0x03f0 96 | 0x024D: extract_string 97 | 0x024E: .byte 98 | 0x024F: word_3AE2 = gamestate[0x08] 99 | 0x0251: word_3AE4 = gamestate[0x43] 100 | 0x0253: op_15 0x0263 101 | 0x0256: inc byte [word_3AE4] 102 | 0x0257: inc byte [word_3AE4] 103 | 0x0258: inc byte [word_3AE4] 104 | 0x0259: gamestate[0x43] = word_3AE4 105 | 0x025B: pop byte 106 | 0x025C: if ++byte_3AE4 != 0x03 jmp 0x023d 107 | 0x0260: wait_event 0x8000, 'C', 0x0270, 'H', 0x0270, 'M', 0x0270, "ESC", 0x01f6, 0xff 108 | 0x0270: .word 109 | 0x0271: gamestate[0x4f] = 0x63 110 | gamestate[0x50] = 0x02 111 | 0x0275: call 0x05be 112 | 0x0278: gamestate[0x41] = word_3AE2 113 | 0x027A: word_3AE2 = gamestate[0x45] 114 | 0x027C: op_34 0x03 115 | 0x027E: op_2F 0x41 116 | 0x0280: gamestate[0x45] = word_3AE2 117 | 0x0282: op_21 118 | 0x0283: op_0D 0x0395 119 | 0x0286: word_3ADF[0x02a6] = word_3AE2 120 | word_3ADF[0x02A7] = (word_3AE2 & 0xFF00) >> 8 121 | 0x0289: op_0D 0x03a4 122 | 0x028C: gamestate[0x4b] = word_3AE2 123 | 0x028E: shl byte [word_3AE4], 1 124 | 0x028F: .word 125 | 0x0290: op_0D 0x03b3 126 | 0x0293: gamestate[0x47] = word_3AE2 127 | 0x0295: .byte 128 | 0x0296: gamestate[0x43] = 0 129 | 0x0298: word_3AE4 = 0 130 | 0x0299: word_3AE2 = word_3AE4 131 | 0x029A: op_50 132 | 0x029B: jns 0xa544 133 | 0x029E: op_02 134 | 0x029F: inc game_state[0x43] 135 | 0x02A1: op_2F 0x4b 136 | 0x02A3: gamestate[0x85] = word_3AE2 137 | 0x02A5: if ++byte_3AE4 != 0x05 jmp 0x0299 138 | 0x02A9: draw_and_set $("Which spell...\r\r") 139 | 0x02B6: gamestate[0x43] = 0 140 | 0x02B8: word_3AE4 = 0 141 | 0x02B9: push_byte(word_3AE4 & 0xFF) 142 | 0x02BA: word_3AE2 = word_3AE4 143 | 0x02BB: op_50 144 | 0x02BC: jns 0x8009 145 | 0x02BF: jz 0x02d2 146 | 0x02C2: word_3AE2 = word_3AE4 147 | 0x02C3: op_2F 0x4b 148 | 0x02C5: gamestate[0x85] = word_3AE2 149 | 0x02C7: gamestate[0x08] = 0 150 | 0x02C9: load_resource res: 0x0e, offset: 0x0000 151 | 0x02CD: word_3AE2 = 0x8d 152 | 0x02CF: write_number 153 | 0x02D0: word_3AE2 = gamestate[0x08] 154 | 0x02D2: word_3AE4 = gamestate[0x43] 155 | 0x02D4: op_15 0x02e4 156 | 0x02D7: inc byte [word_3AE4] 157 | 0x02D8: inc byte [word_3AE4] 158 | 0x02D9: inc byte [word_3AE4] 159 | 0x02DA: gamestate[0x43] = word_3AE4 160 | 0x02DC: pop byte 161 | 0x02DD: if ++byte_3AE4 != 0x10 jmp 0x02b9 162 | 0x02E1: wait_event 0x8000, 'A', 0x0318, 'B', 0x0318, 'C', 0x0318, 'D', 0x0318, 'E', 0x0318, 'F', 0x0318, 'G', 0x0318, 'H', 0x0318, 'I', 0x0318, 'J', 0x0318, 'K', 0x0318, 'L', 0x0318, 'M', 0x0318, 'N', 0x0318, 'O', 0x0318, 'P', 0x0318, "ESC", 0x01f6, 0xff 163 | 0x0318: .word 164 | 0x0319: gamestate[0x4f] = 0xe4 165 | gamestate[0x50] = 0x02 166 | 0x031D: call 0x05be 167 | 0x0320: op_2F 0x4b 168 | 0x0322: gamestate[0x85] = word_3AE2 169 | 0x0324: word_3AE4 = gamestate[0x85] 170 | 0x0326: op_0D 0x0406 171 | 0x0329: gamestate[0x86] = word_3AE2 172 | 0x032B: gamestate[0x87] = 0 173 | gamestate[0x88] = 0 174 | 0x032D: test word_3AE2 175 | 0x032E: js 0x033c 176 | 0x0331: .word 177 | 0x0332: word_3AE2 = get_char_data 0x1c 178 | 0x0334: cmp word_3AE2, gamestate[0x86] 179 | 0x0336: .byte 180 | 0x0337: jnc 0x01c0 181 | 0x033A: clc 182 | 0x033B: retf 183 | 0x033C: .word 184 | 0x033D: word_3AE2 = get_char_data 0x1c 185 | 0x033F: gamestate[0x37] = word_3AE2 186 | 0x0341: test word_3AE2 187 | 0x0342: .byte 188 | 0x0343: jz 0x01c0 189 | 0x0346: draw_and_set $("Power for ") 190 | 0x034F: load_resource res: 0x0e, offset: 0x0000 191 | 0x0353: set_msg $(" spell...\r\r") 192 | 0x035C: word_3AE4 = gamestate[0x86] 193 | 0x035E: shl byte [word_3AE4], 1 194 | 0x035F: op_2E 195 | 0x0360: load_char_data 0x00 196 | 0x0362: .word 197 | 0x0363: op_2A 198 | 0x0364: cmp word_3AE2, gamestate[0x37] 199 | 0x0366: jnc 0x036b 200 | 0x0369: word_3AE2 = gamestate[0x37] 201 | 0x036B: op_81 word_3AE2 202 | 0x036C: gamestate[0x86] = word_3AE2 203 | 0x036E: .byte 204 | 0x036F: set_msg $(" points maximum.\r\r") 205 | 0x037D: read_string 206 | 0x037E: op_8F 207 | 0x037F: .word 208 | 0x0380: test game_state[0x39] 209 | 0x0382: jnz 0x0392 210 | 0x0385: word_3AE2 = gamestate[0x37] 211 | 0x0387: test word_3AE2 212 | 0x0388: jz 0x01f6 213 | 0x038B: cmp word_3AE2, gamestate[0x86] 214 | 0x038D: jc 0x0392 215 | 0x0390: gamestate[0x86] = word_3AE2 216 | 0x0392: .byte 217 | 0x0393: clc 218 | 0x0394: retf 219 | 0x0395: push_byte(running_script_idx) 220 | 0x0396: .byte 221 | 0x0397: .byte 222 | 0x0398: word_3AE2 = gamestate[word_3AE4 + 0x02] 223 | 0x039A: set loop = 0x06 224 | 0x039C: op_02 225 | 0x039D: word_3AE4 = gamestate[0x0C] 226 | 0x039F: data_res = resource_get(pop_byte()) 227 | 0x03A0: word_3AE4 = gamestate[0x03] 228 | 0x03A2: .word 229 | 0x03A3: .word 230 | 0x03A4: .word 231 | 0x03A5: push_byte(running_script_idx) 232 | 0x03A6: word_3AE4 = gamestate[0x06] 233 | 0x03A8: gamestate[0x13] = 0 234 | gamestate[0x14] = 0 235 | 0x03AA: op_19 0x1f, 0x21 236 | 0x03AD: dec game_state[0x32] 237 | 0x03AF: op_35 0x3a 238 | 0x03B1: op_3A 0x3a 239 | 0x03B3: .word 240 | 0x03B4: .word 241 | 0x03B5: .word 242 | 0x03B6: .word 243 | 0x03B7: .word 244 | 0x03B8: .word 245 | 0x03B9: .word 246 | 0x03BA: .word 247 | 0x03BB: .word 248 | 0x03BC: .word 249 | 0x03BD: .word 250 | 0x03BE: .word 251 | 0x03BF: .word 252 | 0x03C0: .word 253 | 0x03C1: .word 254 | 0x03C2: .word 255 | 0x03C3: .word 256 | 0x03C4: .word 257 | 0x03C5: .word 258 | 0x03C6: .word 259 | 0x03C7: .word 260 | 0x03C8: .word 261 | 0x03C9: .word 262 | 0x03CA: .word 263 | 0x03CB: .word 264 | 0x03CC: .word 265 | 0x03CD: .word 266 | 0x03CE: .word 267 | 0x03CF: .word 268 | 0x03D0: .word 269 | -------------------------------------------------------------------------------- /script/script15.scr: -------------------------------------------------------------------------------- 1 | Disassembling script15.bin (944 bytes) 2 | 0x0000: set loop = 0x09 3 | 0x0002: gamestate[0x41] = word_3AE2 4 | 0x0004: op_0D 0x0018 5 | 0x0007: cmp word_3AE2, gamestate[0x41] 6 | 0x0009: jz 0x0010 7 | 0x000C: loop 0x0004 8 | 0x000F: retf 9 | 0x0010: shl byte [word_3AE4], 1 10 | 0x0011: .word 11 | 0x0012: op_0D 0x0022 12 | 0x0015: push (word|byte) 13 | 0x0016: .byte 14 | 0x0017: ret 15 | 16 | 0x0018: 0x9B = "ESC" 17 | 0x0019: 0xD1 = "Q" 18 | 0x001A: 0xD3 = "S" 19 | 0x001B: 0xC3 = "C" 20 | 0x001C: 0xD5 = "U" 21 | 0x001D: 0xC4 = "D" 22 | 0x001E: 0xD8 = "X" 23 | 0x001F: 0xCF = "O" 24 | 0x0020: 0x01 = #[1-7] 25 | 0x0021: 0xD0 = "P" 26 | 27 | # ESC - Escape jump point 28 | 0x0022: jmp 0x0105 29 | 30 | # Q - Quit jump point 31 | 0x0024: jmp 0x0036 32 | 33 | # S - Save game 34 | 0x0026: jmp 0x0059 35 | 36 | # C - ? 37 | 0x0028: jmp 0x0149 38 | 39 | # U - ? 40 | 0x002A: jmp 0x0191 41 | 42 | # D - ? 43 | 0x002C: jmp 0x01FA 44 | 45 | # X - ? 46 | 0x002E: jmp 0x02C8 47 | 48 | # O - ? 49 | 0x0030: jmp 0x02D5 50 | 51 | # [1-7] - Manage character data 52 | 0x0032: jmp 0x035D 53 | 54 | # P - Combat pictures (enable/disable) 55 | 0x0034: jmp 0x011E 56 | 57 | # Quit the game 58 | 0x0036: draw_rectangle 0x0c, 0x40, 0x1d, 0x78 59 | 0x003B: set_msg $("Do you wish to quit the game?\r\r") 60 | 0x0053: prompt 'Y', 'N' 61 | 0x0054: jnz 0x00ff 62 | 0x0057: draw_ui_full 63 | 0x0058: ret 64 | 65 | # Save game 66 | 0x0059: draw_rectangle 0x0c, 0x40, 0x1d, 0x78 67 | 0x005E: set_msg $("Do you wish to save your game?\r\r") 68 | 0x0075: prompt 'Y', 'N' 69 | 0x0076: jnz 0x00ff 70 | 0x0079: draw_and_set $("Saving game...\r") 71 | 0x0085: word_3AE2 = gamestate[0xDF] 72 | 0x0087: push (word|byte) 73 | 0x0088: word_3AE2 = 0x07 74 | 0x008A: word_3AE2 = load_resource(word_3AE2) 75 | 0x008B: gamestate[0x98] = word_3AE2 76 | 0x008D: gamestate[0x43] = 0x01 77 | 0x0090: .word 78 | 0x0091: gamestate[0x96] = 0 79 | gamestate[0x97] = 0 80 | 0x0093: gamestate[0x41] = 0 81 | gamestate[0x42] = 0 82 | 0x0095: .byte 83 | 0x0096: set loop = 0xff 84 | 0x0098: var offset = gamestate[0x41] 85 | offset += gamestate[0x42] << 8 86 | offset += word_3AE4 87 | word_3AE2 = resource_idx(gamestate[0x43])->bytes[offset] 88 | word_3AE2 += resource_idx(gamestate[0x43])->bytes[offset + 1] << 8 89 | 0x009A: store_data_resource 0x96 90 | 0x009C: loop 0x0098 91 | 0x009F: inc game_state[0x42] 92 | 0x00A1: inc game_state[0x97] 93 | 0x00A3: word_3AE2 = gamestate[0x42] 94 | 0x00A5: cmp word_3EA2, 0x0e 95 | 0x00A7: jnc 0x0098 96 | 0x00AA: op_0B 0x00 97 | 0x00AC: store_data_resource 0x96 98 | 0x00AE: loop 0x00aa 99 | 0x00B1: word_3AE4 = gamestate[0x98] 100 | 0x00B3: push_byte(word_3AE4 & 0xFF) 101 | 0x00B4: data_res = resource_get(pop_byte()) 102 | 0x00B5: .word 103 | 0x00B6: word_3AE4 = 0 104 | 0x00B7: word_3AE2 = 0x0f00 105 | 0x00BA: memcpy 0x700 106 | 0x00BB: .byte 107 | 0x00BC: push_byte(running_script_idx) 108 | 0x00BD: data_res = resource_get(pop_byte()) 109 | 0x00BE: word_3AE2 = 0x07 110 | 0x00C0: write_resource(word_3AE2) 111 | 0x00C1: word_3AE2 = gamestate[0x98] 112 | 0x00C3: resource_release 113 | 0x00C4: word_3AE2 = 0x10 114 | 0x00C6: word_3AE2 = load_resource(word_3AE2) 115 | 0x00C7: gamestate[0x98] = word_3AE2 116 | 0x00C9: word_3AE2 = gamestate[0x56] 117 | 0x00CB: .word 118 | 0x00CC: op_9E 119 | 0x00CD: dec word_3AE2 120 | 0x00CE: word_3AE4 = word_3AE2 121 | 0x00CF: word_3AE2 &= 0xff00 122 | 0x00D2: gamestate[0x54] = word_3AE2 123 | 0x00D4: gamestate[0x96] = word_3AE2 124 | 0x00D6: .byte 125 | 0x00D7: var offset = gamestate[0x54] 126 | offset += gamestate[0x55] << 8 127 | offset += word_3AE4 128 | word_3AE2 = resource_idx(gamestate[0x56])->bytes[offset] 129 | word_3AE2 += resource_idx(gamestate[0x56])->bytes[offset + 1] << 8 130 | 0x00D9: store_data_resource 0x96 131 | 0x00DB: loop 0x00d7 132 | 0x00DE: dec game_state[0x55] 133 | 0x00E0: dec game_state[0x97] 134 | 0x00E2: test game_state[0x55] 135 | 0x00E4: jns 0x00d7 136 | 0x00E7: word_3AE2 = 0x10 137 | 0x00E9: write_resource(word_3AE2) 138 | 0x00EA: word_3AE2 = gamestate[0x98] 139 | 0x00EC: resource_release 140 | 0x00ED: draw_and_set $("Your game is saved.") 141 | 0x00FC: wait_escape 142 | 0x00FD: peek_and_pop 143 | 0x00FE: op_1F 144 | 0x00FF: draw_ui_full 145 | 0x0100: gamestate[0x3d] = 0 146 | 0x0102: gamestate[0x3e] = 0 147 | 0x0104: retf 148 | 149 | 0x0105: draw_rectangle 0x0a, 0x38, 0x1e, 0x60 150 | 0x010A: set_msg $("The game is paused") 151 | 0x0118: wait_event 0xA244, 'R', 0x00ff, 't', 0x380a, 0x1e, 'h', 0xe00a, '<', 0x12ff, 0x09, 0x12, '`', 0xf178, 0x1e, 'Q', 0x618a, 0x82, 0x89, ':', 0xd244, 0x08, '"', 0xff60, 'C', 0xfa3b, "RIGHT", 0xfe52, '"', 0x0c1b, '\', 0x8800, 'R', 0x00ff, 'S', 0x016e, 'B', 0x016c, 'X', 0x020c, "#[1-7]", 0x4942, "#[1-7]", 0x899a, 0x9a, 0x83, 0x11, '^', 0x0658, "Unknown", 0x1100, 0x89, 0x11, "LEFT", 0xde66, 0x11, '^', 0x6c45, "#[1-7]", 0x7588, 'Y', 0x0a74, 0x18, 0x1e, '(', 0xf578, 0x92, '"', 0x2424, 'H', 0x22a2, 'L', 0xffd1, 0x1d, 'h', 0x0900, 0x81, 'X', 0x8d05, "Unknown", 0x8112, 'A', 0x0190, 'D', 0x016e, 'T', 0x6e53, "#[1-7]", 0x6c42, "#[1-7]", 0x7d76, 'x', 0x240d, 'i', 0x1863, 'o', 0x047f, 'L', 0xfede, '%', 0x8ca9, 'o', 0x9cd8, 'b', 0xd2a1, 'f', 0x1900, 0x06, 0x81, 0x89, "Unknown", 0xc980, 'F', 0xd301, 'p', 0xc101, 'c', 0x0101, 0x97, "#[1-7]", 0x919b, "#[1-7]", 0x58ff, 0x0c, "Unknown", 0x4200, 0x97, "#[1-7]", 0x0619, 0x81, 'h', 0x1207, 0x86, 0x11, 0x87, 'h', 0x1206, 0x85, 0x0a, 0x07, ':', 0x12c0, "LEFT", 0x5852, "#[1-7]", 0x0c58, "LEFT", 0x4200, 0x97, "#[1-7]", 0x0619, 0x81, 'R', 0x0158, 'X', 0x430c, 0x04, 'B', 0x0197, 'R', 0x0158, 0x05, 0x1f, '@', 0x4501, '%', 0x7402, 0x05, '8', 0x7823, 'x', 0xdff5, '@', 0x4e90, 's', 0x12e6, '*', 0x544d, '$', 0xa419, 'A', 0xa560, '0', 0x51c0, 0x9d, 'B', 0x34d3, 'f', 0x008e, "LEFT", 0x5975, 't', 0x180a, 0x1e, '(', 0x0558, '|', 0x4200, 'l', 0x7601, 'c', 0x02bd, 'B', 0x027e, 'x', 0xa2f0, '`', 0xf4dd, 0x0c, ')', 0x0613, 'o', 0x6ca0, ')', 0x3324, 'x', 0xa94a, '5', 0x9052, '@', 0x787d, 0x09, '_', 0x5413, 'Q', 0x1dff, 'h', 0x8c00, 'u', 0xbc45, 0x02, 0x05, 0x06, 0x09, "Unknown", 0x1813, '%', 0x1f3f, 'B', 0x027a, '(', 0x0a0b, 'V', 0x0b0b, 0x13, 0x0a, 'U', 0x0b13, '%', 0x6152, 0x02, '&', 0x911f, 'Y', 0x787d, 0x0a, '$', 0x0a12, '1', 0x9cba, 0x80, 'M', 0x0826, "Unknown", 0x787e, 'X', 0xe044, '*', 0x6866, '^', 0xa270, '&', 0xc314, 'g', 0x69c6, '@', 0x777d, 'P', 0xa42d, '&', 0xc419, 'E', 0x5530, '0', 0xe1cd, 0x13, 0x9f, '4', 0x219a, 0x80, '#', 0x853b, '&', 0xcd68, 0x1c, "Unknown", 0x7588, 'Y', 0x0468, '>', 0x45ff, 'F', 0x4b02, 'Z', 0x5a4c, 'S', 0x016e, 'B', 0x016c, 'X', 0x0014, "Unknown", 0xc852, 0x02, 0x06, 0x06, 0x0b, 0x0a, 0x13, 0x11, 'I', 0x02d7, 0x05, 0x1f, '@', 0x4102, 'Q', 0x7403, 0x02, 0x08, 0x16, 0x90, 'x', 0x8df3, '`', 0xf1f9, ')', 0xfea3, 'z', 0x1100, 'G', 0x4811, 0x09, "ENTER", 0x0a83, 'H', 0xb130, 0x83, 0x09, '>', 0x8983, 0x10, 0x80, "#[1-7]", 0x030e, "ESC", 0x0351, 0xff 152 | 0x030E: op_32 0xb1 153 | 0x0310: op_50 154 | 0x0311: jns 0x0445 155 | 0x0314: data_res = resource_get(pop_byte()) 156 | 0x0315: op_4E 157 | 0x0316: jns 0x0b21 158 | 0x0319: gamestate[0x05] = 0 159 | 0x031B: op_48 0x13 160 | 0x031D: word_3AE2 = gamestate[0x08] 161 | 0x031F: set loop = 0x25 162 | 0x0321: inc byte [word_3AE4] 163 | 0x0322: shl byte [word_3AE4], 1 164 | 0x0323: shl byte [word_3AE4], 1 165 | 0x0324: shl byte [word_3AE4], 1 166 | 0x0325: word_3AE2 = 0x02 167 | 0x0327: ui_draw_string 168 | 0x0328: write_character_name 169 | 0x0329: inc game_state[0x48] 170 | 0x032B: word_3AE2 = gamestate[0x48] 171 | 0x032D: inc word_3AE2 172 | 0x032E: cmp word_3AE2, gamestate[0x1F] 173 | 0x0330: jnc 0x02f9 174 | 0x0333: word_3AE2 = 0x00 175 | 0x0335: op_50 176 | 0x0336: jns 0x3e44 177 | 0x0339: data_res = resource_get(pop_byte()) 178 | 0x033A: inc word_3AE2 179 | 0x033B: jmp 0x0335 180 | 0x033E: word_3AE4 = word_3AE2 181 | 0x033F: word_3AE2 = gamestate[word_3AE4 + 0x11] 182 | 0x0341: word_3AE4 = gamestate[0x48] 183 | 0x0343: gamestate[word_3AE4 + 0x0A] = word_3AE2 184 | 0x0345: set loop = 0x06 185 | 0x0347: word_3AE2 = 0x00 186 | 0x0349: gamestate[word_3AE4 + 0x18] = word_3AE2 187 | 0x034B: loop 0x0347 188 | 0x034E: jmp 0x00ff 189 | 0x0351: set loop = 0x06 190 | 0x0353: word_3AE2 = gamestate[word_3AE4 + 0x11] 191 | 0x0355: gamestate[word_3AE4 + 0x0A] = word_3AE2 192 | 0x0357: loop 0x0353 193 | 0x035A: jmp 0x00ff 194 | 0x035D: word_3AE2 = gamestate[0x05] 195 | 0x035F: word_3AE2 &= 0x01 196 | 0x0361: test word_3AE2 197 | 0x0362: jnz 0x0104 198 | 0x0365: set loop = 0x03 199 | 0x0367: push_byte(word_3AE4 & 0xFF) 200 | 0x0368: op_0D 0x038d 201 | 0x036B: word_3AE4 = word_3AE2 202 | 0x036C: word_3AE2 = gamestate[word_3AE4 + 0x00] 203 | 0x036E: test word_3AE2 204 | 0x036F: jz 0x0383 205 | 0x0372: dec word_3AE2 206 | 0x0373: gamestate[word_3AE4 + 0x00] = word_3AE2 207 | 0x0375: test word_3AE2 208 | 0x0376: jnz 0x0383 209 | 0x0379: pop byte 210 | 0x037A: push_byte(word_3AE4 & 0xFF) 211 | 0x037B: op_0D 0x0391 212 | 0x037E: word_3AE4 = word_3AE2 213 | 0x037F: word_3AE2 = 0x00 214 | 0x0381: gamestate[word_3AE4 + 0x00] = word_3AE2 215 | 0x0383: pop byte 216 | 0x0384: loop 0x0367 217 | 0x0387: var al = game_state[6] 218 | for (game_state[6] < game_state[0x1F]; game_state[6]++) { 219 | call 0x0395 220 | } 221 | game_state[6] = al 222 | 0x038A: word_3AE2 = gamestate[0x23] 223 | 0x038C: retf 224 | 225 | -------------------------------------------------------------------------------- /script/script18.scr: -------------------------------------------------------------------------------- 1 | Disassembling res18.bin (1375 bytes) 2 | 0x0000: word_3AE4 = gamestate[0x5A] 3 | 0x0002: push_byte(word_3AE4 & 0xFF) 4 | 0x0003: .word 5 | 0x0004: set loop = 0x04 6 | 0x0006: gamestate[0x58] = 0 7 | gamestate[0x59] = 0 8 | 0x0008: var offset = gamestate[0x58] 9 | offset += gamestate[0x59] << 8 10 | offset += word_3AE4 11 | word_3AE2 = resource_idx(gamestate[0x5A])->bytes[offset] 12 | word_3AE2 += resource_idx(gamestate[0x5A])->bytes[offset + 1] << 8 13 | 0x000A: gamestate[0x58] = word_3AE2 14 | 0x000C: word_3AE4 = gamestate[0x5C] 15 | 0x000E: shl byte [word_3AE4], 1 16 | 0x000F: var offset = gamestate[0x58] 17 | offset += gamestate[0x59] << 8 18 | offset += word_3AE4 19 | word_3AE2 = resource_idx(gamestate[0x5A])->bytes[offset] 20 | word_3AE2 += resource_idx(gamestate[0x5A])->bytes[offset + 1] << 8 21 | 0x0011: data_res = resource_get(pop_byte()) 22 | 0x0012: extract_string # "You have just attracted some unwanted..." 23 | 0x0013: set_msg $("\r\r") 24 | 0x0016: push_byte(running_script_idx) 25 | 0x0017: data_res = resource_get(pop_byte()) 26 | 0x0018: .byte 27 | 0x0019: retf 28 | 0x001A: gamestate[0x47] = 0xFF 29 | 0x001C: word_3AE4 = gamestate[0x1F] 30 | 0x001E: cmp word_3AE4, 0x02 31 | 0x0020: jnc 0x0050 32 | 0x0023: dec [mem] 33 | 34 | # Set currently selected player 35 | 0x0024: gamestate[0x06] = word_3AE4 36 | 0x0026: word_3AE2 = get_char_data 0x4c (Status) 37 | 0x0028: word_3AE2 &= 0x81 38 | 0x002A: test word_3AE2 39 | 0x002B: jnz 0x0045 40 | 0x002E: gamestate[0x41] = word_3AE4 41 | 0x0030: word_3AE4-- 42 | 0x0031: gamestate[0x06] = word_3AE4 43 | 0x0033: gamestate[0x43] = word_3AE4 44 | 0x0035: word_3AE2 = get_char_data 0x4c 45 | 0x0037: word_3AE2 &= 0x81 46 | 0x0039: test word_3AE2 47 | 0x003A: jz 0x0043 48 | 0x003D: load_resource res: 0x12, offset: 0x0051 49 | 0x0041: gamestate[0x47] = 0 50 | 0x0043: word_3AE4 = gamestate[0x41] 51 | 0x0045: word_3AE4-- 52 | 0x0046: cmp word_3AE4, 0x00 53 | 0x0048: jnz 0x0024 54 | 0x004B: test game_state[0x47] 55 | 0x004D: jz 0x001a 56 | 0x0050: retf 57 | 0x0051: word_3AE4 = gamestate[0x41] 58 | 0x0053: word_3AE2 = 0x00 59 | 0x0055: gamestate[word_3AE4 + 0x18] = word_3AE2 60 | 0x0057: word_3AE2 = gamestate[word_3AE4 + 0x0A] 61 | 0x0059: push (word|byte) 62 | 0x005A: word_3AE4 = gamestate[0x43] 63 | 0x005C: word_3AE2 = 0x00 64 | 0x005E: gamestate[word_3AE4 + 0x18] = word_3AE2 65 | 0x0060: word_3AE2 = gamestate[word_3AE4 + 0x0A] 66 | 0x0062: word_3AE4 = gamestate[0x41] 67 | 0x0064: gamestate[word_3AE4 + 0x0A] = word_3AE2 68 | 0x0066: word_3AE4 = gamestate[0x43] 69 | 0x0068: peek_and_pop 70 | 0x0069: gamestate[word_3AE4 + 0x0A] = word_3AE2 71 | 0x006B: set loop = 0x09 72 | 0x006D: dec [mem] 73 | 0x006E: .word 74 | 0x006F: op_0D 0x008d 75 | 0x0072: gamestate[0x51] = word_3AE2 76 | 0x0074: push_byte(word_3AE4 & 0xFF) 77 | 0x0075: .byte 78 | 0x0076: word_3AE4 = gamestate[0x41] 79 | 0x0078: var offset = gamestate[0x51] 80 | offset += gamestate[0x52] << 8 81 | offset += word_3AE4 82 | word_3AE2 = resource_idx(gamestate[0x53])->bytes[offset] 83 | word_3AE2 += resource_idx(gamestate[0x53])->bytes[offset + 1] << 8 84 | 0x007A: push (word|byte) 85 | 0x007B: word_3AE4 = gamestate[0x43] 86 | 0x007D: var offset = gamestate[0x51] 87 | offset += gamestate[0x52] << 8 88 | offset += word_3AE4 89 | word_3AE2 = resource_idx(gamestate[0x53])->bytes[offset] 90 | word_3AE2 += resource_idx(gamestate[0x53])->bytes[offset + 1] << 8 91 | 0x007F: word_3AE4 = gamestate[0x41] 92 | 0x0081: store_data_resource 0x51 93 | 0x0083: peek_and_pop 94 | 0x0084: word_3AE4 = gamestate[0x43] 95 | 0x0086: store_data_resource 0x51 96 | 0x0088: pop byte 97 | 0x0089: loop 0x006d 98 | 0x008C: retf 99 | 100 | 0x0097: set_msg $("Will the party:\rFight\rQuickly fight") 101 | 0x00B2: word_3AE2 = 0x80 102 | 0x00B4: test game_state[0xDB] 103 | 0x00B6: jnz 0x00c0 104 | 0x00B9: set_msg $("\rRun") 105 | 0x00BE: word_3AE2 = 0xd2 106 | 0x00C0: word_3ADF[0x0101] = word_3AE2 107 | 0x00C3: word_3AE2 = 0x80 108 | 0x00C5: test game_state[0x8B] 109 | 0x00C7: js 0x00f5 110 | 0x00CA: op_19 0x53, 0x43 111 | 0x00CD: set loop = 0x03 112 | 0x00CF: word_3AE2 = word_3AE4 113 | 0x00D0: load_resource res: 0x03, offset: 0x06b1 114 | 0x00D4: jz 0x00e4 115 | 0x00D7: push_byte(word_3AE4 & 0xFF) 116 | 0x00D8: set loop = 0x09 117 | 0x00DA: var offset = gamestate[0x41] 118 | offset += gamestate[0x42] << 8 119 | offset += word_3AE4 120 | word_3AE2 = resource_idx(gamestate[0x43])->bytes[offset] 121 | word_3AE2 += resource_idx(gamestate[0x43])->bytes[offset + 1] << 8 122 | 0x00DC: pop byte 123 | 0x00DD: cmp word_3EA2, 0x02 124 | 0x00DF: word_3AE2 = 0x80 125 | 0x00E1: jnc 0x00f5 126 | 0x00E4: loop 0x00cf 127 | 0x00E7: set_msg $("\rAdvance ahead") 128 | 0x00F3: word_3AE2 = 0xc1 129 | 0x00F5: word_3ADF[0x0104] = word_3AE2 130 | 0x00F8: wait_event 0x0000, 'F', 0x0108, 'Q', 0x0108, 'R', 0x02cf, 'A', 0x02ee, 0xff 131 | 0x0108: gamestate[0x65] = word_3AE2 132 | 0x010A: gamestate[0x67] = 0 133 | 0x010C: draw_rectangle 0x16, 0x00, 0x28, 0x98 134 | 0x0111: word_3AE4 = gamestate[0x67] 135 | 0x0113: gamestate[0x06] = word_3AE4 136 | 0x0115: word_3AE2 = 0x0b 137 | 0x0117: call 0x053b 138 | 0x011A: word_3AE2 = get_char_data 0x4c 139 | 0x011C: word_3AE2 &= 0x81 140 | 0x011E: test word_3AE2 141 | 0x011F: jnz 0x023e 142 | 0x0122: word_3AE2 = 0x80 143 | 0x0124: word_3ADF[0x01ed] = word_3AE2 144 | 0x0127: word_3ADF[0x01f3] = word_3AE2 145 | 0x012A: word_3ADF[0x0205] = word_3AE2 146 | 0x012D: word_3ADF[0x01fc] = word_3AE2 147 | 0x0130: word_3ADF[0x01f6] = word_3AE2 148 | 0x0133: draw_pattern 149 | 0x0134: write_character_name 150 | 0x0135: set_msg $(", choose:\r\r") 151 | 0x013F: word_3AE4 = gamestate[0x06] 152 | 0x0141: cmp word_3AE4, 0x04 153 | 0x0143: jnc 0x015a 154 | 0x0146: op_63 0x04c3 155 | 0x0149: jnc 0x0165 156 | 0x014C: op_68 0x05 157 | 0x014E: word_3AE2 &= 0x1f 158 | 0x0150: cmp word_3EA2, 0x08 159 | 0x0152: jnc 0x0165 160 | 0x0155: cmp word_3EA2, 0x0b 161 | 0x0157: jc 0x0165 162 | 0x015A: set_msg $("Attack\r") 163 | 0x0161: word_3ADF[0x01ed] = 0xc1 164 | 0x0165: set_msg $("Dodge enemies\r") 165 | 0x0171: word_3AE4 = gamestate[0x65] 166 | 0x0173: cmp word_3AE4, 0xd1 167 | 0x0175: jz 0x0187 168 | 0x0178: set_msg $("Block attack\r") 169 | 0x0183: word_3ADF[0x01fc] = 0xc2 170 | 0x0187: .word 171 | 0x0188: word_3AE2 = get_char_data 0x1c 172 | 0x018A: test word_3AE2 173 | 0x018B: .byte 174 | 0x018C: jz 0x019d 175 | 0x018F: set_msg $("Cast spell\r") 176 | 0x0199: word_3ADF[0x01f3] = 0xc3 177 | 0x019D: set_msg $("Use item\rNew weapon\rLoad weapon\r") 178 | 0x01B5: test game_state[0xDB] 179 | 0x01B7: jnz 0x01c3 180 | 0x01BA: set_msg $("Run\r") 181 | 0x01BF: word_3ADF[0x01f6] = 0xd2 182 | 0x01C3: word_3AE4 = gamestate[0x65] 183 | 0x01C5: cmp word_3AE4, 0xd1 184 | 0x01C7: jz 0x01db 185 | 0x01CA: word_3AE4 = gamestate[0x1F] 186 | 0x01CC: cmp word_3AE4, 0x01 187 | 0x01CE: jz 0x01db 188 | 0x01D1: set_msg $("Move\r") 189 | 0x01D7: word_3ADF[0x0205] = 0xcd 190 | 0x01DB: word_3AE2 = 0x3f 191 | 0x01DD: write_number 192 | 0x01DE: set_msg $(" View the party") 193 | 0x01EA: wait_event 0xC300, 'A', 0x03bc, 'D', 0x02c5, 'C', 0x0517, 'R', 0x02ca, 'U', 0x04f6, 'B', 0x02c0, 'N', 0x02f3, 'L', 0x0354, 'M', 0x0267, '?', 0x020f, "ESC", 0x0224, 0xff 194 | 0x020F: draw_ui_full 195 | 0x0210: draw_and_set $("Viewing current party.") 196 | 0x0220: wait_escape 197 | 0x0221: jmp 0x010c 198 | 0x0224: dec game_state[0x67] 199 | 0x0226: test game_state[0x67] 200 | 0x0228: js 0x0263 201 | 0x022B: op_19 0x67, 0x06 202 | 0x022E: word_3AE2 = get_char_data 0x4c 203 | 0x0230: word_3AE2 &= 0x81 204 | 0x0232: test word_3AE2 205 | 0x0233: jz 0x0111 206 | 0x0236: jmp 0x0224 207 | 0x0239: word_3AE4 = gamestate[0x67] 208 | 0x023B: call 0x053b 209 | 0x023E: inc game_state[0x67] 210 | 0x0240: word_3AE4 = gamestate[0x67] 211 | 0x0242: check_gamestate 0x1f 212 | 0x0244: jnc 0x0111 213 | 214 | 0x0247: draw_and_set $("Use these commands?\r\r") 215 | 0x0258: prompt 'Y', 'N' 216 | 0x0259: jz 0x0265 217 | 0x025C: word_3AE4 = gamestate[0x67] 218 | 0x025E: cmp word_3AE4, 0x01 219 | 0x0260: jnz 0x0224 220 | 0x0263: stc # No, don't use these commands 221 | 0x0264: retf 222 | 223 | 0x0265: clc # Yes, use the commands 224 | 0x0266: retf 225 | 226 | 0x0267: draw_pattern 227 | 0x0268: write_character_name 228 | 0x0269: set_msg $(" moves...\r\r") 229 | 0x0272: word_3AE4 = gamestate[0x67] 230 | 0x0274: cmp word_3AE4, 0x00 231 | 0x0276: word_3AE2 = 0x80 232 | 0x0278: jz 0x0284 233 | 0x027B: set_msg $("Ahead\r") 234 | 0x0282: word_3AE2 = 0xc1 235 | 0x0284: word_3ADF[0x029e] = word_3AE2 236 | 0x0287: inc byte [word_3AE4] 237 | 0x0288: check_gamestate 0x1f 238 | 0x028A: word_3AE2 = 0x80 239 | 0x028C: jc 0x0298 240 | 0x028F: set_msg $("Behind") 241 | 0x0296: word_3AE2 = 0xc2 242 | 0x0298: word_3ADF[0x02a1] = word_3AE2 243 | 0x029B: wait_event 0x8000, 'A', 0x02a8, 'B', 0x02b4, "ESC", 0x0111, 0xff 244 | 0x02A8: word_3AE4 = gamestate[0x67] 245 | 0x02AA: word_3AE2 = word_3AE4 246 | 0x02AB: dec word_3AE2 247 | 0x02AC: call 0x0544 248 | 0x02AF: word_3AE2 = 0x08 249 | 0x02B1: jmp 0x0239 250 | 0x02B4: word_3AE4 = gamestate[0x67] 251 | 0x02B6: word_3AE2 = word_3AE4 252 | 0x02B7: inc word_3AE2 253 | 0x02B8: call 0x0544 254 | 0x02BB: word_3AE2 = 0x09 255 | 0x02BD: jmp 0x0239 256 | 0x02C0: word_3AE2 = 0x05 257 | 0x02C2: jmp 0x0239 258 | 0x02C5: word_3AE2 = 0x06 259 | 0x02C7: jmp 0x0239 260 | 0x02CA: word_3AE2 = 0x0a 261 | 0x02CC: jmp 0x0239 262 | 263 | # Run 264 | 0x02CF: word_3AE2 = 0x0a 265 | 0x02D1: gamestate[0x41] = word_3AE2 266 | 0x02D3: var al = game_state[6] 267 | for (game_state[6] < game_state[0x1F]; game_state[6]++) { 268 | call 0x02DC 269 | } 270 | game_state[6] = al 271 | 0x02D6: gamestate[0x67] = 0x01 272 | 0x02D9: jmp 0x0247 273 | 274 | 0x02DC: word_3AE4 = gamestate[0x06] 275 | 0x02DE: word_3AE2 = get_char_data 0x4c (Status) 276 | 0x02E0: word_3AE2 &= 0x81 277 | 0x02E2: test word_3AE2 278 | 0x02E3: word_3AE2 = 0x0b 279 | 0x02E5: jnz 0x02ea 280 | 281 | 0x02E8: word_3AE2 = gamestate[0x41] 282 | 0x02EA: call 0x053B 283 | 0x02ED: ret 284 | 285 | 0x02EE: word_3AE2 = 0x0d 286 | 0x02F0: jmp 0x02d1 287 | 0x02F3: load_resource res: 0x0c, offset: 0x0005 288 | 0x02F7: jc 0x0111 289 | 0x02FA: op_68 0x05 290 | 0x02FC: word_3AE2 &= 0x1f 291 | 0x02FE: cmp word_3EA2, 0x03 292 | 0x0300: jnc 0x033d 293 | 0x0303: cmp word_3EA2, 0x0c 294 | 0x0305: jc 0x033d 295 | 0x0308: op_68 0x00 296 | 0x030A: test word_3AE2 297 | 0x030B: word_3AE2 = gamestate[0x07] 298 | 0x030D: jns 0x0333 299 | 0x0310: draw_and_set $("Do you wish to deequip your ") 300 | 0x0325: op_7E 0x78 301 | 0x0327: nop 302 | 0x0328: memcpy 0x700 303 | 0x0329: op_E8 304 | 0x032A: .word 305 | 0x032B: prompt 'Y', 'N' 306 | 0x032C: jnz 0x0111 307 | 0x032F: word_3AE2 = gamestate[0x07] 308 | 0x0331: op_3A 0x80 309 | 0x0333: word_3AE4 = gamestate[0x67] 310 | 0x0335: call 0x0544 311 | 0x0338: word_3AE2 = 0x5204 312 | 0x033B: op_39 0x02 313 | 0x033D: draw_and_set $("The ") 314 | 0x0342: op_7E 0x78 315 | 0x0344: word_3AE2 = gamestate[0xA4] 316 | 0x0346: store_data_resource 0x3e 317 | 0x0348: test_player_property 0x10 318 | 0x034A: op_6C 319 | 0x034B: test_player_property 0x41 320 | 321 | 322 | # Function 53B 323 | # Writes word_3AE2 to resource in gamestate[0x53] at offset 0x4CE 324 | 0x053B: .word 325 | 0x053C: gamestate[0x51] = 0xce 326 | gamestate[0x52] = 0x04 327 | 0x0540: .byte 328 | 0x0541: store_data_resource 0x51 329 | 0x0543: ret 330 | 331 | 0x0544: .word 332 | 0x0545: gamestate[0x51] = 0xd5 333 | gamestate[0x52] = 0x04 334 | 0x0549: .byte 335 | 0x054A: store_data_resource 0x51 336 | 0x054C: ret 337 | 0x054D: .word 338 | 0x054E: gamestate[0x51] = 0xdc 339 | gamestate[0x52] = 0x04 340 | 0x0552: .byte 341 | 0x0553: store_data_resource 0x51 342 | 0x0555: ret 343 | 0x0556: .word 344 | 0x0557: gamestate[0x51] = 0xe3 345 | gamestate[0x52] = 0x04 346 | 0x055B: .byte 347 | 0x055C: store_data_resource 0x51 348 | 0x055E: ret 349 | 350 | -------------------------------------------------------------------------------- /script/script19.scr: -------------------------------------------------------------------------------- 1 | Disassembling script19.bin (1394 bytes) 2 | 0x0000: jmp 0x000c 3 | 0x0003: jmp 0x0251 4 | 0x0006: jmp 0x0317 5 | 0x0009: jmp 0x03c1 6 | 0x000C: op_9D 0x62, 0x99 7 | 0x000F: jz 0x0075 8 | 0x0012: op_9D 0x02, 0xb9 9 | 0x0015: jz 0x0022 10 | 0x0018: op_9B 0x02, 0xb9 11 | 0x001B: load_resource res: 0x08, offset: 0x0018 12 | 0x001F: push byte_3AE9 13 | 0x0020: .word 14 | 0x0021: wait_escape 15 | 0x0022: draw_rectangle 0x0a, 0x30, 0x1e, 0x70 16 | 0x0027: set_msg $("Do you wish to enter the arena?\r\r") 17 | 0x003F: prompt 'Y', 'N' 18 | 0x0040: jnz 0x00b8 19 | 0x0043: draw_and_set $("Come back when you are ready to face the challenge of combat!") 20 | 0x006C: wait_escape 21 | 0x006D: gamestate[0x03] = 0 22 | gamestate[0x04] = 0 23 | 0x006F: op_6B 24 | 0x0070: gamestate[0x3d] = 0 25 | gamestate[0x3E] = 0 26 | 0x0072: draw_ui_full 27 | 0x0073: clc 28 | 0x0074: retf 29 | 0x0075: draw_rectangle 0x0a, 0x30, 0x1e, 0x70 30 | 0x007A: set_msg $("Several gladiators bearing recent battle scars block your way. "You may only enter once!"") 31 | 0x00B5: jmp 0x006c 32 | 0x00B8: draw_rectangle 0x0a, 0x18, 0x1e, 0xa8 33 | 0x00BD: set_msg $(""Excellent!" says the guard, "And I see that you ") 34 | 0x00E0: gamestate[0x41] = 0 35 | gamestate[0x42] = 0 36 | 0x00E2: loop call 0x0240 37 | 0x00E5: word_3AE2 = gamestate[0x1F] 38 | 0x00E7: op_34 0x03 39 | 0x00E9: op_3D 0x41 40 | 0x00EB: jnc 0x01b8 41 | 0x00EE: jnz 0x01b8 42 | 0x00F1: op_31 0x41 43 | 0x00F3: gamestate[0xbb] = word_3AE2 44 | 0x00F5: set_msg $("are in need of some more equipment."") 45 | 0x010E: wait_escape 46 | 0x010F: draw_and_set $("You may choose ") 47 | 0x011B: word_3AE2 = gamestate[0xBB] 48 | 0x011D: op_81 word_3AE2 49 | 0x011E: set_msg $(" items. Who will choose an item?\r\r") 50 | 0x0137: load_resource res: 0x05, offset: 0x0012 51 | 0x013B: wait_event 0x0000, 0x01, 'B', 0xff01, 'w', 0x92f5, '"', 0x2a24, 0x99, "ESC", 0x638c, '=', 0x0700, 0x93, '"', 0x4130, 0x83, 'x', 0x61fe, 0x00, 0x0d, 'H', 0x5802, 0x0b, 0x03, 0x00, 0x93, 0x03, 0x00, 0x0a, 'W', 0x0b30, 0x00, 0x7f, 0x01, 0x04, 0x03, 0x09, 0x8d, 0x83, 0x94, 'J', 0x4f09, 0x01, 0x89, 0x00, 0x00, 'A', 0x7e49, 0x01, "ESC", 0x010f, 0xff 52 | 0x017E: op_32 0x21c1 53 | 0x0181: op_0D 0x0248 54 | 0x0184: load_resource res: 0x0b, offset: 0x0006 55 | 0x0188: jc 0x010f 56 | 0x018B: op_26 0xbb 57 | 0x018D: op_66 0xbb 58 | 0x018F: jz 0x010f 59 | 0x0192: draw_and_set $(""Don't forget to equip your items", says the guard.") 60 | 0x01B5: jmp 0x01d2 61 | 0x01B8: set_msg $("all have brought your own equipment."") 62 | 0x01D2: wait_escape 63 | 0x01D3: set_msg $(""Let the combat commence!"") 64 | 0x01E6: wait_escape 65 | 0x01E7: set_msg $("Those words hardly finish echoing in your ears as the gate slams shut behind you and the roar of the crowd raises to a cresendo.") 66 | 0x023A: wait_escape 67 | 0x023B: gamestate[0x3d] = 0 68 | gamestate[0x3E] = 0 69 | 0x023D: draw_ui_full 70 | 0x023E: stc 71 | 0x023F: retf 72 | 0x0240: op_63 0x0244 73 | 0x0243: loop ret 74 | 0x0244: inc [mem] 0x41 75 | 0x0246: clc 76 | 0x0247: loop ret 77 | 0x0248: .word 78 | 0x0249: .byte 79 | 0x024A: op_03 80 | 0x024B: push byte_3AE9 81 | 0x024C: word_3AE4 = gamestate[0x08] 82 | 0x024E: word_3AE2 = gamestate[0x0D] 83 | 0x0250: gamestate[0x74] = 0 84 | 0x0252: word_3AE4 = gamestate[0x20] 85 | 0x0254: inc [mem] 0x98 86 | 0x0256: set_msg $("This is the main gate to Purgatory -- the gate through which you were unceremoniously dumped following your arrival in port. Guards are posted here. ") 87 | 0x02B9: set_msg $("You're free to go your own way in the city, but the guards will happily kick your spleen up through your teeth if you want to rush the gate.") 88 | 0x0313: wait_escape 89 | 0x0314: draw_ui_full 90 | 0x0315: op_73 91 | 0x0316: retf 92 | 0x0317: draw_rectangle 0x04, 0x30, 0x24, 0x80 93 | 0x031C: set_msg $("Stripped of all possessions and wealth, you've been dropped naked and defenseless into the slums of Purgatory by order of Namtar, the Beast From The Pit.") 94 | 0x0382: wait_escape 95 | 0x0383: set_msg $("No one escapes Purgatory alive, and few know the luxury to die in bed within her walls.") 96 | 0x03BD: wait_escape 97 | 0x03BE: draw_ui_full 98 | 0x03BF: op_73 99 | 0x03C0: retf 100 | 0x03C1: op_73 101 | 0x03C2: op_9D 0x25, 0x99 102 | 0x03C5: jnz 0x0458 103 | 0x03C8: op_9D 0x26, 0x99 104 | 0x03CB: jz 0x0432 105 | 0x03CE: op_9B 0x26, 0x99 106 | 0x03D1: draw_rectangle 0x04, 0x28, 0x24, 0x70 107 | 0x03D6: set_msg $(""It is an honor to have you amongst us, O mighty victors of the great Humbaba! Accept this gold as a token of our gratitude!"") 108 | 0x0429: wait_escape 109 | 0x042A: load_resource res: 0x08, offset: 0x0012 110 | 0x042E: op_E8 111 | 0x042F: op_03 112 | 0x0430: .word 113 | 0x0431: .word 114 | 0x0432: draw_rectangle 0x05, 0x38, 0x23, 0x68 115 | 0x0437: set_msg $(""Enjoy your meager existence amongst us!"") 116 | 0x0455: wait_escape 117 | 0x0456: draw_ui_full 118 | 0x0457: retf 119 | 0x0458: draw_rectangle 0x04, 0x28, 0x24, 0x78 120 | 0x045D: load_resource res: 0x08, offset: 0x0015 121 | 0x0461: op_4D 122 | 0x0462: .word 123 | 0x0463: wait_escape 124 | 0x0464: set_msg $("Clopin Trouillefou growls, "There are no honest men in the Court of Miracles. You will be punished unless a thief, beggar, or tramp!"") 125 | 0x04BD: wait_escape 126 | 0x04BE: set_msg $("Do you describe yourselves as thieves, beggars, or tramps?\r\r") 127 | 0x04E7: prompt 'Y', 'N' 128 | 0x04E8: jnz 0x0554 129 | 0x04EB: draw_and_set $("You must then prove yourselves to me! Defeat the dreaded Humbaba and you will know honor in my court. You will find him at the northeast corner of my realm.") 130 | 0x0551: jmp 0x055e 131 | 0x0554: draw_pattern 132 | 0x0555: load_resource res: 0x08, offset: 0x0015 133 | 0x0559: set_gamestate: idx = 0x00 134 | 0x055B: loop call 0x056b 135 | 0x055E: wait_escape 136 | 0x055F: draw_ui_full 137 | 0x0560: gamestate[0x01] = 0x14 138 | gamestate[0x02] = 0x1a 139 | 0x0564: .word 140 | 0x0565: op_0D 0x0311 141 | 0x0568: gamestate[0x3d] = 0 142 | gamestate[0x3E] = 0 143 | 0x056A: retf 144 | 0x056B: word_3AE2 = 0x5808 145 | 0x056E: set_gamestate: idx = 0x0f 146 | 0x0570: .word 147 | 0x0571: loop ret 148 | -------------------------------------------------------------------------------- /script/script22.scr: -------------------------------------------------------------------------------- 1 | Disassembling script22.bin (78 bytes) 2 | 3 | # Set gamestate[0x41] to 0xFF, then check each player's status, if 4 | # at least one character is still alive 0x41 will be cleared. 5 | 0x0000: gamestate[0x41] = 0xFF 6 | 0x0002: var al = game_state[6] 7 | for (game_state[6] < game_state[0x1F]; game_state[6]++) { 8 | call 0x0044 9 | } 10 | game_state[6] = al 11 | 0x0005: test game_state[0x41] 12 | 0x0007: jz 0x0043 13 | 14 | # All dead 15 | 0x000A: draw_rectangle 0x05, 0x28, 0x23, 0x90 16 | 0x000F: set_msg $("Alas, your brave party has met its match! Your current adventure is over.") 17 | 0x0040: wait_escape 18 | 0x0041: draw_ui_full 19 | 0x0042: op_1E 20 | 0x0043: retf 21 | 22 | ##### 23 | # Check a player's status, if he is ok, set gamestate[0x41] to 0. 24 | 0x0044: word_3AE2 = 0x07 25 | 0x0046: test_player_property 0x4c 26 | 0x0048: jnz 0x004d 27 | 0x004B: gamestate[0x41] = 0 28 | 0x004D: ret 29 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | project(opendw) 3 | 4 | option(ENABLE_TOOLS "Enable developer tools" OFF) 5 | option(ENABLE_TESTS "Enable tests" OFF) 6 | 7 | # This code uses "insecure" (as determined by Microsoft) CRT functions 8 | # Disable warnings about this across the board. 9 | if(MSVC) 10 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 11 | endif() 12 | 13 | add_subdirectory(lib) 14 | add_subdirectory(fe) 15 | if (ENABLE_TOOLS) 16 | add_subdirectory(tools) 17 | endif() 18 | if (ENABLE_TESTS) 19 | add_subdirectory(tests) 20 | endif() 21 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Master makefile for opendw 2 | 3 | .PHONY: all clean 4 | 5 | all: 6 | @(cd lib && $(MAKE)) 7 | @(cd fe && $(MAKE)) 8 | 9 | clean: 10 | @(cd lib && $(MAKE) clean) 11 | @(cd fe && $(MAKE) clean) 12 | 13 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # OpenDW Source layout 2 | 3 | | Directory | Purpose | 4 | |-----------|-----------------------------------------------------------| 5 | | fe | Various front ends (SDL, x11, null) | 6 | | lib | Core functionality used by all front ends | 7 | | tools | Extra scripts, programs, tests, etc. for working with lib | 8 | | tests | Unit tests, requires Check unit testing library | 9 | -------------------------------------------------------------------------------- /src/fe/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | 3 | project(opendw) 4 | set(TARGET_NAME sdldragon) 5 | 6 | # C and C++ sources are freely mixed. 7 | set(SOURCES main.c vga_sdl.c) 8 | 9 | add_executable(${TARGET_NAME} ${SOURCES}) 10 | list(APPEND INC_DIRS ${SDL2_INCLUDE_DIRS}) 11 | list(APPEND LINK_LIBRARIES ${SDL2_LIBRARIES}) 12 | 13 | target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/lib ${INC_DIRS}) 14 | target_link_libraries(${TARGET_NAME} PRIVATE dragon ${LINK_LIBRARIES}) 15 | 16 | # Also build ndragon (OpenDW without any video driver) 17 | add_executable(ndragon main.c vga_null.c) 18 | target_include_directories(ndragon PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 19 | target_link_libraries(ndragon PRIVATE dragon) 20 | 21 | install(TARGETS sdldragon DESTINATION bin) 22 | 23 | if(MSVC) 24 | # Not yet with /WX 25 | #target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX) 26 | target_compile_options(${TARGET_NAME} PRIVATE /W4) 27 | else() 28 | # Not yet with -Werror 29 | #target_compile_options(${TARGET_NAME} PRIVATE -Wall -Werror) 30 | target_compile_options(${TARGET_NAME} PRIVATE -Wall) 31 | endif() 32 | -------------------------------------------------------------------------------- /src/fe/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dragon wars 2 | 3 | .PHONY: all clean 4 | 5 | SRCS = main.c 6 | 7 | # VGA drivers 8 | NULL_SRC = vga_null.c 9 | SDL_SRC = vga_sdl.c 10 | X_SRC = vga_xlib.c 11 | 12 | VGA_OBJS = vga_null.o vga_sdl.o vga_xlib.o 13 | 14 | DEP_INCLUDES = -I. -I../lib 15 | DEP_LIBS = -L../lib -ldragon 16 | 17 | # SDL VGA implementation. 18 | SDL_INCLUDES = $(shell pkg-config sdl2 --cflags) 19 | SDL_LIBS = $(shell pkg-config sdl2 --libs) 20 | 21 | X_LIBS = -lX11 22 | 23 | OBJS = $(SRCS:.c=.o) 24 | DEPS = $(SRCS:.c=.d) $(VGA_OBJS:.o=.d) 25 | 26 | # Debugging flags 27 | CFLAGS = -Wall -g3 28 | #CFLAGS = -Wall -O2 29 | 30 | EXES = sdldragon ndragon 31 | 32 | # If you have X, uncomment this line. 33 | EXES += xdragon 34 | 35 | all: $(EXES) 36 | 37 | sdldragon: $(OBJS) vga_sdl.o 38 | $(CC) $(CFLAGS) -o $@ $(OBJS) vga_sdl.o $(DEP_LIBS) $(SDL_LIBS) 39 | 40 | xdragon: $(OBJS) vga_xlib.o 41 | $(CC) $(CFLAGS) -o $@ $(OBJS) vga_xlib.o $(DEP_LIBS) $(X_LIBS) 42 | 43 | ndragon: $(OBJS) vga_null.o 44 | $(CC) $(CFLAGS) -o $@ $(OBJS) vga_null.o $(DEP_LIBS) 45 | 46 | vga_sdl.o: vga_sdl.c 47 | $(CC) $(CFLAGS) $(DEP_INCLUDES) $(SDL_INCLUDES) -MMD -MP -MT $@ -o $@ -c vga_sdl.c 48 | 49 | vga_xlib.o: vga_xlib.c 50 | $(CC) $(CFLAGS) $(DEP_INCLUDES) -MMD -MP -MT $@ -o $@ -c vga_xlib.c 51 | 52 | vga_null.o: vga_null.c 53 | $(CC) $(CFLAGS) $(DEP_INCLUDES) -MMD -MP -MT $@ -o $@ -c vga_null.c 54 | 55 | .c.o: 56 | $(CC) $(CFLAGS) $(DEP_INCLUDES) -MMD -MP -MT $@ -o $@ -c $< 57 | 58 | clean: 59 | rm -f $(VGA_OBJS) 60 | rm -f $(DEPS) 61 | rm -f $(EXES) 62 | 63 | # Include automatically generated dependency files 64 | -include $(DEPS) 65 | 66 | -------------------------------------------------------------------------------- /src/fe/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #include "engine.h" 20 | #include "offsets.h" 21 | #include "resource.h" 22 | #include "state.h" 23 | #include "tables.h" 24 | #include "utils.h" 25 | #include "ui.h" 26 | #include "vga.h" 27 | 28 | /* Original Dragon Wars resoluation */ 29 | #define GAME_WIDTH 320 30 | #define GAME_HEIGHT 200 31 | 32 | static void 33 | title_adjust(const struct resource *title) 34 | { 35 | unsigned char *src, *dst; 36 | int i, counter = 0x3E30; 37 | uint16_t ax, si; 38 | 39 | src = title->bytes; 40 | dst = title->bytes + 0xA0; 41 | 42 | for (i = 0; i < counter; i++) { 43 | ax = *src++; 44 | ax += *(src++) << 8; 45 | 46 | si = *(src + 0x9E); 47 | si += *(src + 0x9F) << 8; 48 | 49 | ax = ax ^ si; 50 | 51 | /* write output_idx (16 bits) to output in little endian format. */ 52 | *(dst++) = (ax & 0xff); 53 | *(dst++) = (ax & 0xff00) >> 8; 54 | } 55 | } 56 | 57 | static void 58 | title_build(const struct resource *output) 59 | { 60 | uint8_t al; 61 | unsigned char *src = output->bytes; 62 | int i, counter = 64000; 63 | int hi, lo; 64 | uint8_t *framebuffer = vga_memory(); 65 | 66 | for (i = 0; i < counter; i += 2) { 67 | 68 | al = *src++; 69 | 70 | /* Each nibble represents a color */ 71 | /* for example 0x82 represents color 8 then color 2. */ 72 | hi = (al >> 4) & 0xf; 73 | lo = al & 0xf; 74 | 75 | framebuffer[i] = hi; 76 | framebuffer[i + 1] = lo; 77 | } 78 | } 79 | 80 | /* 0x387 */ 81 | static void 82 | run_title(void) 83 | { 84 | const struct resource *title_res = resource_load(RESOURCE_TITLE3); 85 | if (title_res == NULL) 86 | return; 87 | 88 | title_adjust(title_res); 89 | 90 | dump_hex(title_res->bytes, 32); 91 | title_build(title_res); 92 | 93 | vga_update(); 94 | resource_index_release(title_res->index); 95 | 96 | vga_waitkey(); 97 | } 98 | 99 | int check_file(const char *fname) 100 | { 101 | FILE *fp = fopen(fname, "rb"); 102 | if (fp == NULL) { 103 | fprintf(stderr, "Failed to open %s. Can't proceed.\n", fname); 104 | return 0; 105 | } 106 | fclose(fp); 107 | return 1; 108 | } 109 | 110 | int 111 | check_files(void) 112 | { 113 | /* We only check for the existance of data1, data2, and dragon.com. 114 | * We can't do signature checks on these files yet because they are actually 115 | * modified by the game (yes even dragon.com writes data back into itself). 116 | */ 117 | 118 | return check_file("dragon.com") && 119 | check_file("data1") && 120 | check_file("data2"); 121 | } 122 | 123 | int 124 | main(int argc, char *argv[]) 125 | { 126 | if (check_files() == 0) { 127 | return -1; 128 | } 129 | 130 | if (rm_init() != 0) { 131 | goto done; 132 | } 133 | 134 | // Register our VGA driver. 135 | video_setup(); 136 | 137 | setup_memory(); 138 | 139 | init_offsets(0x50); 140 | load_chr_table(); 141 | 142 | byte_4F0F = 0xFF; 143 | set_game_state("main", 87, 0xFF); 144 | set_game_state("main", 91, 0xFF); 145 | set_game_state("main", 86, 0xFF); 146 | set_game_state("main", 90, 0xFF); 147 | set_game_state("main", 8, 0xFF); 148 | byte_4F10 = 0xFF; 149 | 150 | // Not sure where this is done or where it goes. 151 | // Indicates that part of the UI is drawn? 152 | for (int i = 24; i < 31; i++) { 153 | set_game_state("main", i, 0xFF); 154 | } 155 | 156 | if (vga_initialize(GAME_WIDTH, GAME_HEIGHT) != 0) { 157 | goto done; 158 | } 159 | 160 | set_game_state("main", 0xDC, 0); // Not sure this if correct. 161 | 162 | ui_set_background(0); 163 | run_title(); 164 | ui_load(); 165 | sub_37C8(); 166 | 167 | draw_rect.x = 1; 168 | draw_rect.y = 8; 169 | draw_rect.w = 39; 170 | draw_rect.h = 184; 171 | ui_drawn_yet = 0xFF; 172 | 173 | ui_draw_full(); 174 | 175 | run_engine(); 176 | 177 | ui_clean(); 178 | 179 | done: 180 | unload_chr_table(); 181 | rm_exit(); 182 | vga_end(); 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /src/fe/vga_dos.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include // int86 20 | 21 | #include "vga.h" 22 | 23 | /* Represents 0xA0000 (0xA000:0000) memory. */ 24 | static unsigned char FAR *framebuffer = (byte far*)0xA0000000L;; 25 | 26 | static int 27 | display_start(int game_width, int game_height) 28 | { 29 | union REGS regs; 30 | 31 | regs.h.ah = 0x00; /* function 00h = mode set */ 32 | regs.h.al = 0x13; /* 320x200x256-color */ 33 | int86(0x10, ®s, ®s); /* do it! */ 34 | 35 | return 0; 36 | } 37 | 38 | static void 39 | display_end(void) 40 | { 41 | union REGS regs; 42 | 43 | regs.h.ah = 0x00; 44 | regs.h.al = 0x03; /* text mode is mode 3 */ 45 | int86(0x10, ®s, ®s); 46 | } 47 | 48 | static void 49 | display_update(void) 50 | { 51 | } 52 | 53 | static void waitkey() 54 | { 55 | getchar(); 56 | } 57 | 58 | static uint8_t * 59 | get_fb_mem() 60 | { 61 | return framebuffer; 62 | } 63 | 64 | struct vga_driver null_driver = { 65 | "dos", 66 | display_start, 67 | display_end, 68 | display_update, 69 | waitkey, 70 | get_fb_mem 71 | }; 72 | -------------------------------------------------------------------------------- /src/fe/vga_null.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2021 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | void video_setup() 18 | { 19 | // No video for NULL driver. 20 | } 21 | -------------------------------------------------------------------------------- /src/fe/vga_xlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "vga.h" 24 | 25 | #define WIN_WIDTH 640 26 | #define WIN_HEIGHT 400 27 | #define VGA_WIDTH 320 28 | #define VGA_HEIGHT 200 29 | 30 | /* http://www.brackeen.com/vga/basics.html */ 31 | #if 0 32 | static const SDL_Color sdl_palette[] = { 33 | { 0x00, 0x00, 0x00, 0xFF }, /* BLACK */ 34 | { 0x00, 0x00, 0x80, 0xFF }, /* BLUE */ 35 | { 0x00, 0x80, 0x00, 0xFF }, /* GREEN */ 36 | { 0x00, 0x80, 0x80, 0xFF }, /* CYAN */ 37 | { 0x80, 0x00, 0x00, 0xFF }, /* RED */ 38 | { 0x80, 0x00, 0x80, 0xFF }, /* MAGENTA */ 39 | { 0x80, 0x80, 0x00, 0xFF }, /* BROWN */ 40 | { 0xC0, 0xC0, 0xC0, 0xFF }, /* LIGHT GRAY */ 41 | { 0x80, 0x80, 0x80, 0xFF }, /* DARK GRAY */ 42 | { 0x00, 0x00, 0xFF, 0xFF }, /* LIGHT BLUE */ 43 | { 0x00, 0xFF, 0x00, 0xFF }, /* LIGHT GREEN */ 44 | { 0x00, 0xFF, 0xFF, 0xFF }, /* LIGHT CYAN */ 45 | { 0xFF, 0x00, 0x00, 0xFF }, /* LIGHT RED */ 46 | { 0xFF, 0x00, 0xFF, 0xFF }, /* LIGHT MAGENTA */ 47 | { 0xFF, 0xFF, 0x00, 0xFF }, /* YELLOW */ 48 | { 0xFF, 0xFF, 0xFF, 0xFF } /* WHITE */ 49 | }; 50 | #endif 51 | 52 | static Display *dpy; 53 | static int screen, index_mode; 54 | static Window root, win; 55 | static XVisualInfo *vi; 56 | static Colormap cmap; 57 | static XColor clr[256]; 58 | static GC gc; 59 | static uint8_t *framebuffer; 60 | static uint8_t *imagebuffer; 61 | static XImage *img; 62 | 63 | static int detect_visual(int depth, int class) 64 | { 65 | XVisualInfo vinfo; 66 | int i, num_visuals; 67 | 68 | vinfo.screen = screen; 69 | vinfo.depth = depth; 70 | vinfo.class = class; 71 | 72 | vi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | 73 | VisualClassMask, &vinfo, &num_visuals); 74 | if (vi && (num_visuals > 0)) { 75 | index_mode = depth == 8 ? 1 : 0; 76 | 77 | printf("%d: rm:%lu gm:%lu bm:%lu cs:%d bpr:%d\n", depth, vi->red_mask, 78 | vi->green_mask, vi->blue_mask, vi->colormap_size, vi->bits_per_rgb); 79 | 80 | cmap = XCreateColormap(dpy, root, vi->visual, AllocAll); 81 | if (index_mode == 1) { 82 | for (i = 0; i < 256; i++) { 83 | clr[i].pixel = i; 84 | clr[i].flags = DoRed | DoGreen | DoBlue; 85 | } 86 | } 87 | 88 | return 1; 89 | } 90 | return 0; 91 | } 92 | 93 | static int setup_visual_colormap() 94 | { 95 | return detect_visual(8, PseudoColor) || 96 | detect_visual(15, TrueColor) || 97 | detect_visual(16, TrueColor) || 98 | detect_visual(24, TrueColor) || 99 | detect_visual(32, TrueColor); 100 | } 101 | 102 | int 103 | display_start(int game_width, int game_height) 104 | { 105 | const char *disp_env; 106 | XSetWindowAttributes attr; 107 | XGCValues gcvalues; 108 | XSizeHints sizehints; 109 | int attrmask; 110 | int bpp; 111 | 112 | disp_env = getenv("DISPLAY"); 113 | dpy = XOpenDisplay(disp_env); 114 | if (dpy == NULL) { 115 | fprintf(stderr, "Failed to open X display %s\n", XDisplayName(disp_env)); 116 | return -1; 117 | } 118 | screen = DefaultScreen(dpy); 119 | root = RootWindow(dpy, screen); 120 | 121 | if (setup_visual_colormap() == 0) { 122 | fprintf(stderr, "No usable visual found!\n"); 123 | return -1; 124 | } 125 | 126 | attr.colormap = cmap; 127 | attr.event_mask = KeyPressMask | KeyReleaseMask | ExposureMask; 128 | attrmask = CWColormap | CWEventMask; 129 | win = XCreateWindow(dpy, root, 0, 0, 320, 200, 0, CopyFromParent, 130 | InputOutput, vi->visual, attrmask, &attr); 131 | if (win == None) { 132 | fprintf(stderr, "Could not create window!\n"); 133 | return -1; 134 | } 135 | 136 | gcvalues.foreground = BlackPixel(dpy, screen); 137 | gcvalues.background = WhitePixel(dpy, screen); 138 | gc = XCreateGC(dpy, win, GCForeground | GCBackground, &gcvalues); 139 | 140 | sizehints.min_width = 320; 141 | sizehints.min_height = 200; 142 | sizehints.max_width = 320; 143 | sizehints.max_height = 200; 144 | sizehints.base_width = 320; 145 | sizehints.base_height = 200; 146 | sizehints.flags = PMinSize | PMaxSize | PBaseSize; 147 | 148 | //XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, &sizehints, None, None); 149 | XStoreName(dpy, win, "OpenDW"); 150 | XSetIconName(dpy, win, "OpenDW"); 151 | 152 | switch (vi->depth) { 153 | case 15: 154 | case 16: 155 | bpp = 2; 156 | break; 157 | case 24: 158 | bpp = 3; 159 | break; 160 | case 32: 161 | bpp = 4; 162 | break; 163 | default: 164 | bpp = 1; 165 | break; 166 | } 167 | 168 | if ((framebuffer = calloc(VGA_WIDTH * VGA_HEIGHT, 1)) == NULL) { 169 | fprintf(stderr, "Framebuffer could not be allocated.\n"); 170 | return -1; 171 | } 172 | 173 | if (index_mode) { 174 | imagebuffer = framebuffer; 175 | } else { 176 | if ((imagebuffer = calloc(VGA_WIDTH * VGA_HEIGHT, bpp)) == NULL) { 177 | fprintf(stderr, "Image buffer could not be allocated.\n"); 178 | return -1; 179 | } 180 | } 181 | 182 | img = XCreateImage(dpy, vi->visual, vi->depth, ZPixmap, 0, 183 | (char *)imagebuffer, 320, 200, 8, 320 * bpp); 184 | if (img == NULL) { 185 | fprintf(stderr, "Could not create X11 pixmap image.\n"); 186 | return -1; 187 | } 188 | 189 | XMapWindow(dpy, win); 190 | 191 | return 0; 192 | } 193 | 194 | void 195 | display_end(void) 196 | { 197 | } 198 | 199 | void 200 | display_update(void) 201 | { 202 | } 203 | 204 | void waitkey() 205 | { 206 | } 207 | 208 | static uint8_t * 209 | get_fb_mem() 210 | { 211 | return framebuffer; 212 | } 213 | 214 | static uint16_t 215 | get_key() 216 | { 217 | return 0; 218 | } 219 | 220 | struct vga_driver x_driver = { 221 | "xlib", 222 | display_start, 223 | display_end, 224 | display_update, 225 | waitkey, 226 | get_fb_mem, 227 | get_key 228 | }; 229 | 230 | struct vga_driver *vga = &x_driver; 231 | -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | project(opendw) 3 | set(TARGET_NAME dragon) 4 | 5 | # C and C++ sources are freely mixed. 6 | set(SOURCES 7 | bufio.c bufio.h compress.c compress.h engine.c engine.h log.c 8 | log.h mouse.c mouse.h offsets.c offsets.h player.c player.h 9 | resource.c resource.h state.c state.h tables.c tables.h 10 | timers.c timers.h ui.c ui.h 11 | utils.c utils.h vga.c vga.h) 12 | 13 | add_library(${TARGET_NAME} STATIC ${SOURCES}) 14 | target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 15 | 16 | if(MSVC) 17 | # Not yet with /WX 18 | #target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX) 19 | target_compile_options(${TARGET_NAME} PRIVATE /W4) 20 | else() 21 | # Not yet with -Werror 22 | #target_compile_options(${TARGET_NAME} PRIVATE -Wall -Werror) 23 | target_compile_options(${TARGET_NAME} PRIVATE -Wall) 24 | endif() 25 | -------------------------------------------------------------------------------- /src/lib/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for dragon wars (core lib) 2 | 3 | .PHONY: all clean 4 | 5 | SRCS = bithelp.c bufio.c compress.c engine.c log.c offsets.c \ 6 | player.c resource.c state.c tables.c ui.c utils.c 7 | 8 | DEP_INCLUDES = -I. 9 | DEP_LIBS = 10 | 11 | OBJS = $(SRCS:.c=.o) 12 | DEPS = $(SRCS:.c=.d) 13 | 14 | AR? = ar 15 | RM? = rm -f 16 | RANLIB = ranlib 17 | 18 | # Debugging flags 19 | CFLAGS = -Wall -g3 20 | #CFLAGS = -Wall -O2 21 | 22 | LIB = libdragon 23 | STATIC_LIB = $(LIB).a 24 | 25 | all: $(STATIC_LIB) 26 | 27 | $(STATIC_LIB): $(OBJS) 28 | $(RM) $(STATIC_LIB) 29 | $(AR) cru $(STATIC_LIB) $(OBJS) 30 | $(RANLIB) $(STATIC_LIB) 31 | 32 | .c.o: 33 | $(CC) $(CFLAGS) $(DEP_INCLUDES) -MMD -MP -MT $@ -o $@ -c $< 34 | 35 | clean: 36 | rm -f $(OBJS) 37 | rm -f $(DEPS) 38 | rm -f $(STATIC_LIB) 39 | 40 | # Include automatically generated dependency files 41 | -include $(DEPS) 42 | 43 | -------------------------------------------------------------------------------- /src/lib/bufio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | /* Buffer writer implementation */ 23 | struct buf_wri * 24 | buf_wri_init(size_t len) 25 | { 26 | struct buf_wri *w; 27 | 28 | w = malloc(sizeof(struct buf_wri)); 29 | if (w == NULL) 30 | return NULL; 31 | 32 | w->base = malloc(len); 33 | if (w->base == NULL) { 34 | free(w); 35 | return NULL; 36 | } 37 | w->len = 0; 38 | 39 | return w; 40 | } 41 | 42 | void 43 | buf_wri_free(struct buf_wri *w) 44 | { 45 | if (w != NULL) { 46 | free(w->base); 47 | free(w); 48 | } 49 | } 50 | 51 | void 52 | buf_add8(struct buf_wri *w, uint8_t val) 53 | { 54 | w->base[w->len++] = val; 55 | } 56 | 57 | /* Buffer reader implementation */ 58 | struct buf_rdr * 59 | buf_rdr_init(unsigned char *data, size_t len) 60 | { 61 | struct buf_rdr *r; 62 | 63 | r = malloc(sizeof(struct buf_rdr)); 64 | if (r == NULL) return NULL; 65 | 66 | r->data = data; 67 | r->len = len; 68 | r->offset = 0; 69 | 70 | return r; 71 | } 72 | 73 | void 74 | buf_reset(struct buf_rdr *r) 75 | { 76 | r->offset = 0; 77 | } 78 | 79 | uint8_t 80 | buf_get8(struct buf_rdr *r) 81 | { 82 | return r->data[r->offset++]; 83 | } 84 | 85 | uint16_t 86 | buf_get16le(struct buf_rdr *r) 87 | { 88 | uint16_t ret; 89 | 90 | ret = r->data[r->offset++]; 91 | ret += r->data[r->offset++] << 8; 92 | 93 | return ret; 94 | } 95 | 96 | uint16_t 97 | buf_get16be(struct buf_rdr *r) 98 | { 99 | uint16_t ret; 100 | 101 | ret = r->data[r->offset++] << 8; 102 | ret += r->data[r->offset++]; 103 | 104 | return ret; 105 | } 106 | 107 | uint32_t 108 | buf_get32le(struct buf_rdr *r) 109 | { 110 | uint32_t ret; 111 | 112 | ret = r->data[r->offset++]; 113 | ret += r->data[r->offset++] << 8; 114 | ret += r->data[r->offset++] << 16; 115 | ret += r->data[r->offset++] << 24; 116 | 117 | return ret; 118 | } 119 | 120 | uint32_t 121 | buf_get32be(struct buf_rdr *r) 122 | { 123 | uint32_t ret; 124 | 125 | ret = r->data[r->offset++] << 24; 126 | ret += r->data[r->offset++] << 16; 127 | ret += r->data[r->offset++] << 8; 128 | ret += r->data[r->offset++]; 129 | 130 | return ret; 131 | } 132 | 133 | void 134 | buf_rdr_free(struct buf_rdr *r) 135 | { 136 | if (r == NULL) return; 137 | free(r); 138 | } 139 | -------------------------------------------------------------------------------- /src/lib/bufio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Provides "readers" and "writers" over a buffer of bytes. */ 18 | 19 | #ifndef __DW_BUFIO_H__ 20 | #define __DW_BUFIO_H__ 21 | 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | struct buf_wri { 29 | unsigned char *base; 30 | size_t len; 31 | }; 32 | 33 | struct buf_rdr { 34 | unsigned char *data; 35 | size_t len; 36 | int offset; 37 | }; 38 | 39 | /* Buffer writing functions */ 40 | struct buf_wri *buf_wri_init(size_t len); 41 | void buf_wri_free(struct buf_wri *w); 42 | 43 | void buf_add8(struct buf_wri *w, uint8_t val); 44 | 45 | 46 | /* Buffer reader functions */ 47 | struct buf_rdr *buf_rdr_init(unsigned char *data, size_t len); 48 | void buf_rdr_free(struct buf_rdr *r); 49 | 50 | /* read data */ 51 | void buf_reset(struct buf_rdr *r); 52 | uint8_t buf_get8(struct buf_rdr *r); 53 | uint16_t buf_get16le(struct buf_rdr *r); 54 | uint16_t buf_get16be(struct buf_rdr *r); 55 | uint32_t buf_get32le(struct buf_rdr *r); 56 | uint32_t buf_get32be(struct buf_rdr *r); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif /* __DW_BUFIO_H__ */ 63 | 64 | -------------------------------------------------------------------------------- /src/lib/compress.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | struct compress_ctx { 24 | int counter; 25 | uint16_t dx; 26 | int output_idx; 27 | 28 | unsigned char *dict_base; 29 | unsigned char *dict_ptr; 30 | }; 31 | 32 | /* Constructs a dictionary from input buffered reader. */ 33 | static void build_dictionary(struct buf_rdr *input, struct compress_ctx *ctx) 34 | { 35 | uint16_t res; 36 | uint8_t al; 37 | uint16_t ax = 0; 38 | 39 | ctx->counter--; 40 | 41 | if (ctx->counter < 0) { 42 | ctx->dx = buf_get16be(input); 43 | ctx->counter = 15; 44 | } 45 | 46 | res = ctx->dx << 1; 47 | if (res < ctx->dx) { 48 | ctx->dx = res; 49 | 50 | /* Shift overflow */ 51 | 52 | if (ctx->counter == 0) { 53 | al = buf_get8(input); 54 | ax = (ax & 0xFF00); 55 | ax += al; 56 | } else { 57 | if (ctx->counter >= 8) { 58 | ctx->counter -= 8; 59 | } else { 60 | al = buf_get8(input); 61 | ax = (al << 8 & 0xFF00) + (ctx->counter & 0xFF00 >> 8); 62 | ax = ax >> (ctx->counter & 0xFF); 63 | ctx->dx = ctx->dx | ax; 64 | } 65 | 66 | /* mov al, dh */ 67 | ax = (ax & 0xFF00); 68 | ax += (ctx->dx & 0xFF00) >> 8; 69 | 70 | /* mov dh, dl 71 | * mov dl, ch */ 72 | ctx->dx = (ctx->dx & 0x00FF) << 8; 73 | ctx->dx += (ctx->counter & 0xFF00) >> 8; 74 | } 75 | ax = (ax & 0x00FF); 76 | ax += (ctx->counter & 0xFF00); 77 | 78 | /* Write 16 bit (word) ax to output buffer in little endian format. */ 79 | *(ctx->dict_ptr + 2) = (ax & 0xff); 80 | *(ctx->dict_ptr + 3) = (ax & 0xff00) >> 8; 81 | 82 | /* mov al, ah */ 83 | ax = (ax & 0xFF00); // wipe low. 84 | ax += ((ax & 0xFF00) >> 8); 85 | 86 | /* Write 16 bit (word) ax to output buffer in little endian format. */ 87 | *(ctx->dict_ptr) = (ax & 0xff); 88 | *(ctx->dict_ptr + 1) = (ax & 0xff00) >> 8; 89 | return; 90 | } 91 | else { 92 | unsigned char *save_offset; 93 | ctx->dx = res; 94 | ctx->output_idx += 4; 95 | 96 | /* write output_idx (16 bits) to output in little endian format. */ 97 | *(ctx->dict_ptr) = (ctx->output_idx & 0xff); 98 | *(ctx->dict_ptr + 1) = (ctx->output_idx & 0xff00) >> 8; 99 | 100 | save_offset = ctx->dict_ptr; 101 | ctx->dict_ptr = ctx->dict_base + ctx->output_idx; 102 | 103 | build_dictionary(input, ctx); 104 | 105 | ctx->dict_ptr = save_offset; 106 | ctx->output_idx += 4; 107 | *(ctx->dict_ptr + 2) = (ctx->output_idx & 0xff); 108 | *(ctx->dict_ptr + 3) = (ctx->output_idx & 0xff00) >> 8; 109 | save_offset = ctx->dict_ptr; 110 | 111 | ctx->dict_ptr = ctx->dict_base + ctx->output_idx; 112 | build_dictionary(input, ctx); 113 | ctx->dict_ptr = save_offset; 114 | } 115 | } 116 | 117 | /* Decompress the data into output. */ 118 | static void decompress(struct buf_rdr *input, struct compress_ctx *ctx, 119 | int size, struct buf_wri *output) 120 | { 121 | uint16_t res; 122 | uint16_t ax; 123 | int bx; 124 | int bp = ctx->counter; 125 | unsigned char *ptr; 126 | 127 | ctx->counter = size; 128 | 129 | bx = 0; 130 | while (ctx->counter > 0) { 131 | ptr = ctx->dict_base + bx; 132 | 133 | /* little endian fetch from dictionary */ 134 | ax = *ptr; 135 | ax += *(ptr + 1) << 8; 136 | 137 | if (ax != 0) { 138 | bp--; 139 | if (bp < 0) { 140 | ctx->dx = buf_get16be(input); 141 | bp = 15; 142 | } 143 | res = ctx->dx << 1; 144 | if (res < ctx->dx) { 145 | /* Carry */ 146 | ctx->dx = res; 147 | bx = *(ptr + 2); 148 | bx += *(ptr + 3) << 8; 149 | } else { 150 | /* No carry */ 151 | ctx->dx = res; 152 | bx = ax; /* .loc_4FA5 */ 153 | } 154 | 155 | } else { 156 | uint8_t al = *(ptr + 2); 157 | 158 | buf_add8(output, al); 159 | ctx->counter--; 160 | bx = 0; 161 | } 162 | } 163 | } 164 | 165 | /* 166 | * Decompress buffered data from input and write to output. The expected 167 | * output is written into size. 168 | * 169 | * The algorithm used is likely LZSS. 170 | * 171 | */ 172 | void decompress_data1(struct buf_rdr *input, struct buf_wri *output, int size) 173 | { 174 | struct compress_ctx ctx; 175 | unsigned char *dictionary; 176 | 177 | dictionary = malloc(2048); 178 | 179 | ctx.counter = 0; 180 | ctx.dx = 0; 181 | ctx.output_idx = 0; 182 | ctx.dict_ptr = dictionary; 183 | ctx.dict_base = dictionary; 184 | 185 | build_dictionary(input, &ctx); 186 | printf("%d\n", input->offset); 187 | dump_hex(ctx.dict_base, 64); 188 | 189 | printf("Counter: %d\n", ctx.counter); 190 | printf("DX: %04x\n", ctx.dx); 191 | 192 | /* Using above dictionary, decompress */ 193 | decompress(input, &ctx, size, output); 194 | 195 | free(dictionary); 196 | } 197 | 198 | // Extract "n" bits out of each byte. 199 | // bit_buffer contains leftover bit buffer. 200 | // bits are shifted left, with carry which becomes output. 201 | // 202 | // 0x1D86 -> 1D8C(6) 203 | // 0x1D8A -> 1D8C(5) 204 | // 0x1D8C (num_bits passed in BL) 205 | uint8_t bit_extract(struct bit_extractor *be, int n) 206 | { 207 | uint8_t al = 0; 208 | 209 | for (int i = 0; i < n; i++) { 210 | if (be->num_bits == 0) { 211 | be->bit_buffer = be->data[be->offset]; 212 | be->num_bits = 8; 213 | be->offset++; 214 | } 215 | // 0x1D96 216 | uint8_t tmp = be->bit_buffer; 217 | be->bit_buffer = be->bit_buffer << 1; 218 | be->num_bits--; 219 | 220 | // rcl al, 1 221 | uint8_t carry = 0; 222 | if (tmp > be->bit_buffer) { 223 | carry = 1; 224 | } 225 | al = al << 1; 226 | al += carry; 227 | } 228 | return al; 229 | } 230 | 231 | // 0x1D2A - 0x1D85 232 | // Characters of the alphabet OR'd with 0x80 233 | static unsigned char alphabet[] = { 234 | 0xa0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xeb, 0xec, 235 | 0xed, 0xee, 0xef, 0xf0, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9, 0xae, 236 | 0xa2, 0xa7, 0xac, 0xa1, 0x8d, 0xea, 0xf1, 0xf8, 0xfa, 0xb0, 0xb1, 0xb2, 237 | 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0x30, 0x31, 0x32, 0x33, 0x34, 238 | 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 239 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 240 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xa8, 0xa9, 0xaf, 0xdc, 0xa3, 241 | 0xaa, 0xbf, 0xbc, 0xbe, 0xba, 0xbb, 0xad, 0xa5 242 | }; 243 | 244 | // 0x1CF8 245 | uint8_t extract_letter(struct bit_extractor *be) 246 | { 247 | while (1) { 248 | uint8_t ret = bit_extract(be, 5); 249 | if (ret == 0) 250 | return 0; 251 | 252 | if (ret == 0x1E) { 253 | // Next byte should be an uppercase letter. 254 | 255 | // stc 256 | // rcr byte [byte_1CE4], 1 257 | // rotate carry right bit. 258 | be->upper_case = be->upper_case >> 1; 259 | be->upper_case += 0x80; 260 | continue; 261 | } 262 | 263 | // 0x1F ? 264 | if (ret > 0x1E) { 265 | ret = bit_extract(be, 6); 266 | ret += 0x1E; 267 | } 268 | 269 | // ret != 0x1E 270 | 271 | // 0x1D0A 272 | // offset 273 | uint8_t al = alphabet[ret - 1]; 274 | be->upper_case = be->upper_case >> 1; 275 | 276 | // If we need an uppercase letter and al is 'a' through 'z' 277 | if (be->upper_case >= 0x40 && al >= 0xE1 && al <= 0xFA) { 278 | // Make uppercase. 279 | al = al & 0xDF; 280 | } 281 | // test al, al 282 | return al; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/lib/compress.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __COMPRESS_H__ 18 | #define __COMPRESS_H__ 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | // Container for extracting bits from a buffer. 27 | struct bit_extractor { 28 | // 0x1CE3 29 | // Represents number of bits that are remaining to be read from bit_buffer. 30 | uint8_t num_bits; 31 | 32 | // 0x1CE4 33 | // Uppercase flag 34 | uint8_t upper_case; 35 | 36 | // 0x1CE5 37 | // Will contain actual remaining bits. 38 | uint8_t bit_buffer; 39 | 40 | const unsigned char *data; 41 | uint16_t offset; 42 | }; 43 | 44 | /*!*************************************************************************** 45 | * @short Extract n number of bits from bit_extractor. 46 | * @param be Bit extractor to extract bits from. 47 | * @param n Number of bits to extract, n must be <= 8. 48 | * @return Extracted bit value. 49 | *****************************************************************************/ 50 | uint8_t bit_extract(struct bit_extractor *be, int n); 51 | 52 | /*!*************************************************************************** 53 | * @short Extracts a single letter from a bit_extractor container. 54 | * @param be Bit extractor to extract letter from. 55 | * @return Extracted letter. 56 | *****************************************************************************/ 57 | uint8_t extract_letter(struct bit_extractor *be); 58 | 59 | /*!*************************************************************************** 60 | * @short Decompress data1 content. 61 | * @param input Buffered reader to read from. 62 | * @param output Buffered writer to write to. 63 | * @param size number of bytes expected to decompress to. 64 | * @return 65 | *****************************************************************************/ 66 | void decompress_data1(struct buf_rdr *input, struct buf_wri *output, int size); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* __COMPRESS_H__ */ 73 | -------------------------------------------------------------------------------- /src/lib/engine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_ENGINE_H 18 | #define DW_ENGINE_H 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | extern unsigned char byte_104E; 27 | extern unsigned char byte_2476; 28 | extern unsigned char data_2AAA[32]; 29 | extern unsigned char word_4C31[4]; 30 | extern unsigned char byte_4F0F; // always 0xFF ? 31 | extern unsigned char byte_4F10; 32 | 33 | void reset_game_state(); 34 | void run_engine(); 35 | void sub_4D82(); 36 | 37 | uint16_t extract_string(const unsigned char *src_ptr, uint16_t offset, void (*func)(unsigned char)); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif /* DW_ENGINE_H */ 44 | -------------------------------------------------------------------------------- /src/lib/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "log.h" 24 | 25 | #define MAX_CALLBACKS 32 26 | 27 | typedef struct { 28 | log_LogFn fn; 29 | void *udata; 30 | int level; 31 | } Callback; 32 | 33 | static struct { 34 | void *udata; 35 | log_LockFn lock; 36 | int level; 37 | bool quiet; 38 | Callback callbacks[MAX_CALLBACKS]; 39 | } L; 40 | 41 | 42 | static const char *level_strings[] = { 43 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 44 | }; 45 | 46 | static const char *level_colors[] = { 47 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 48 | }; 49 | 50 | 51 | static void stdout_callback(log_Event *ev) { 52 | char buf[16]; 53 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0'; 54 | fprintf( 55 | ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 56 | buf, level_colors[ev->level], level_strings[ev->level], 57 | ev->file, ev->line); 58 | vfprintf(ev->udata, ev->fmt, ev->ap); 59 | fprintf(ev->udata, "\n"); 60 | fflush(ev->udata); 61 | } 62 | 63 | 64 | static void file_callback(log_Event *ev) { 65 | char buf[64]; 66 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; 67 | fprintf( 68 | ev->udata, "%s %-5s %s:%d: ", 69 | buf, level_strings[ev->level], ev->file, ev->line); 70 | vfprintf(ev->udata, ev->fmt, ev->ap); 71 | fprintf(ev->udata, "\n"); 72 | fflush(ev->udata); 73 | } 74 | 75 | 76 | static void lock(void) { 77 | if (L.lock) { L.lock(true, L.udata); } 78 | } 79 | 80 | 81 | static void unlock(void) { 82 | if (L.lock) { L.lock(false, L.udata); } 83 | } 84 | 85 | 86 | const char* log_level_string(int level) { 87 | return level_strings[level]; 88 | } 89 | 90 | 91 | void log_set_lock(log_LockFn fn, void *udata) { 92 | L.lock = fn; 93 | L.udata = udata; 94 | } 95 | 96 | 97 | void log_set_level(int level) { 98 | L.level = level; 99 | } 100 | 101 | 102 | void log_set_quiet(bool enable) { 103 | L.quiet = enable; 104 | } 105 | 106 | 107 | int log_add_callback(log_LogFn fn, void *udata, int level) { 108 | for (int i = 0; i < MAX_CALLBACKS; i++) { 109 | if (!L.callbacks[i].fn) { 110 | L.callbacks[i] = (Callback) { fn, udata, level }; 111 | return 0; 112 | } 113 | } 114 | return -1; 115 | } 116 | 117 | 118 | int log_add_fp(FILE *fp, int level) { 119 | return log_add_callback(file_callback, fp, level); 120 | } 121 | 122 | 123 | static void init_event(log_Event *ev, void *udata) { 124 | if (!ev->time) { 125 | time_t t = time(NULL); 126 | ev->time = localtime(&t); 127 | } 128 | ev->udata = udata; 129 | } 130 | 131 | 132 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 133 | log_Event ev = { 134 | .fmt = fmt, 135 | .file = file, 136 | .line = line, 137 | .level = level, 138 | }; 139 | 140 | lock(); 141 | 142 | if (!L.quiet && level >= L.level) { 143 | init_event(&ev, stderr); 144 | va_start(ev.ap, fmt); 145 | stdout_callback(&ev); 146 | va_end(ev.ap); 147 | } 148 | 149 | for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) { 150 | Callback *cb = &L.callbacks[i]; 151 | if (level >= cb->level) { 152 | init_event(&ev, cb->udata); 153 | va_start(ev.ap, fmt); 154 | cb->fn(&ev); 155 | va_end(ev.ap); 156 | } 157 | } 158 | 159 | unlock(); 160 | } 161 | -------------------------------------------------------------------------------- /src/lib/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | typedef struct { 19 | va_list ap; 20 | const char *fmt; 21 | const char *file; 22 | struct tm *time; 23 | void *udata; 24 | int line; 25 | int level; 26 | } log_Event; 27 | 28 | typedef void (*log_LogFn)(log_Event *ev); 29 | typedef void (*log_LockFn)(bool lock, void *udata); 30 | 31 | enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; 32 | 33 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 34 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 35 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 36 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 37 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 38 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 39 | 40 | const char* log_level_string(int level); 41 | void log_set_lock(log_LockFn fn, void *udata); 42 | void log_set_level(int level); 43 | void log_set_quiet(bool enable); 44 | int log_add_callback(log_LogFn fn, void *udata, int level); 45 | int log_add_fp(FILE *fp, int level); 46 | 47 | void log_log(int level, const char *file, int line, const char *fmt, ...); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/lib/mouse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Routines for working with the mouse */ 18 | 19 | #include 20 | #include 21 | 22 | #include "mouse.h" 23 | #include "vga.h" 24 | #include "ui.h" 25 | 26 | // 0x246D 27 | // Indicates the type of mouse cursor to use. 28 | uint16_t mouse_cursor_idx; 29 | 30 | // 0x2476 31 | // Indicates whether mouse cursor is visible? 32 | unsigned char mouse_cursor_visible = 0; 33 | 34 | // 0x3854 - 0x3859 35 | struct mouse_status mouse; 36 | 37 | // Mouse cursors (see mouse.cpp in tools) 38 | uint16_t data_6470[] = { 0x647C, 0x64EE, 0x6654, 0x65D2, 0x66C6, 0x6550 }; 39 | 40 | static void sub_2061(); 41 | 42 | // 0x1F10 43 | void mouse_show_cursor() 44 | { 45 | if (mouse.enabled == 0) { 46 | return; 47 | } 48 | 49 | if (mouse_cursor_visible == 0) { 50 | // 0x1F26 51 | sub_2061(); // Draw mouse cursor 52 | } else { 53 | // 0x1F1E 54 | } 55 | 56 | if (mouse_cursor_visible != 0) { 57 | //sub_1F38(); 58 | } 59 | 60 | // 0x1F26 61 | printf("%s: 0x1F17 unimplemented\n", __func__); 62 | exit(1); 63 | } 64 | 65 | // Restores saved screen buffer where cursor was. 66 | // 0x1F54 67 | void mouse_restore_screen_buffer(uint8_t al) 68 | { 69 | if (mouse_cursor_visible == 0) { 70 | return; 71 | } 72 | 73 | // 0x1F5B 74 | printf("%s: 0x1F5B unhandled.\n", __func__); 75 | exit(1); 76 | } 77 | 78 | // 0x1F8F 79 | void mouse_disable_cursor() 80 | { 81 | if (mouse_cursor_visible == 0) 82 | return; 83 | 84 | printf("%s: 0x1F96 unimplemented\n", __func__); 85 | exit(1); 86 | } 87 | 88 | // 0x2061 89 | static void sub_2061() 90 | { 91 | uint16_t si = data_6470[mouse_cursor_idx]; 92 | 93 | //sub_23A1(); 94 | 95 | mouse_cursor_visible = 0xff; 96 | } 97 | 98 | // 0x2AEE 99 | // Check if mouse is inbounds on a rectangle? 100 | // Side effects, set's carry flag and also mouse cursor 101 | int sub_2AEE(uint16_t flags) 102 | { 103 | uint16_t ax; 104 | int cf; 105 | 106 | mouse_cursor_idx = 2; 107 | ax = mouse.x; 108 | ax = ax << 3; 109 | 110 | if (ax > draw_rect.x) { 111 | printf("%s: 0x2BO2 unimplemented\n", __func__); 112 | exit(1); 113 | } 114 | 115 | if ((flags & 0x04) != 0) { 116 | ax = ax & 0xFF00; 117 | mouse_cursor_idx = 0; 118 | cf = 1; 119 | return 1; 120 | } 121 | if ((flags & 0x10) != 0) { 122 | ax = mouse.x; 123 | if (ax >= 0xD8) { 124 | // 0x2B4B 125 | printf("%s: 0x2B4B unimplemented\n", __func__); 126 | exit(1); 127 | } 128 | } 129 | // 0x2B81 130 | if ((flags & 0x20) != 0) { 131 | ax = mouse.x; 132 | if (ax >= 0x10) { 133 | printf("%s: 0x2B8E unimplemented\n", __func__); 134 | exit(1); 135 | } 136 | } 137 | 138 | // 0x2BCF 139 | return 0; 140 | } 141 | 142 | // 0x3824 143 | void poll_mouse() 144 | { 145 | // No support for reading mouse position at this point. 146 | // This is determined by 0x3855. 147 | 148 | mouse.enabled = 0; 149 | mouse.x = 0; 150 | mouse.y = 0; 151 | mouse.clicked = 0; 152 | } 153 | 154 | // 0x3840 155 | uint8_t mouse_get_clicked() 156 | { 157 | // Mouse clicked will store the last 2 clicks in the high bits 158 | // of mouse.clicked. 159 | return mouse.clicked & 0xC0; 160 | } 161 | 162 | 163 | -------------------------------------------------------------------------------- /src/lib/mouse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Routines for working with the mouse */ 18 | 19 | #ifndef DW_MOUSE_H 20 | #define DW_MOUSE_H 21 | 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | void mouse_show_cursor(); 29 | void mouse_restore_screen_buffer(uint8_t al); 30 | void mouse_disable_cursor(); 31 | int sub_2AEE(uint16_t flags); 32 | void poll_mouse(); 33 | uint8_t mouse_get_clicked(); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif /* DW_MOUSE_H */ 40 | -------------------------------------------------------------------------------- /src/lib/offsets.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #include "offsets.h" 20 | 21 | #define NUM_OFFSETS 0x88 22 | 23 | // 0xB042 24 | uint16_t offsets[NUM_OFFSETS]; 25 | 26 | // 0x1053 (always gets set to 0x50 ?) 27 | unsigned short word_1053 = 0; 28 | 29 | // 0x17DD (called with 0x50) / 0x17E2 (called with 0x90) 30 | // called with values of 0x50 or 0x90 31 | void init_offsets(unsigned short dx) 32 | { 33 | int i; 34 | uint16_t val = 0; 35 | 36 | // 0x17E5 37 | word_1053 = dx; 38 | 39 | // 0x17E5 40 | for (i = 0; i < NUM_OFFSETS; i++) { 41 | offsets[i] = val; 42 | val += dx; 43 | } 44 | } 45 | 46 | uint16_t get_offset(int pos) 47 | { 48 | return offsets[pos]; 49 | } 50 | -------------------------------------------------------------------------------- /src/lib/offsets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_OFFSETS_H 18 | #define DW_OFFSETS_H 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | extern unsigned short word_1053; 27 | 28 | void init_offsets(unsigned short dx); 29 | uint16_t get_offset(int pos); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* DW_OFFSETS_H */ 36 | -------------------------------------------------------------------------------- /src/lib/player.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #include "player.h" 20 | 21 | // Much of the source of this material comes from the following blog: 22 | // https://dewimorgan.livejournal.com/35639.html 23 | // 24 | // Player data is loaded from DATA1. The characters are stored at the following offsets: 25 | // 2E26, 3036, 3226, 3426, 3626 (and onwards) 26 | 27 | struct skill_info { 28 | unsigned char arcane_lore; // 0x20 29 | unsigned char cave_lore; // 0x21 30 | unsigned char forest_lore; // 0x22 31 | unsigned char mountain_lore; // 0x23 32 | unsigned char town_lore; // 0x24 33 | unsigned char bandage; // 0x25 34 | unsigned char climb; // 0x26 35 | unsigned char fistfighting; // 0x27 36 | unsigned char hide; // 0x28 37 | unsigned char lockpick; // 0x29 38 | unsigned char pickpocket; // 0x2A 39 | unsigned char swim; // 0x2B 40 | unsigned char tracking; // 0x2C 41 | unsigned char bureaucracy; // 0x2D 42 | unsigned char druid_magic; // 0x2E 43 | unsigned char high_magic; // 0x2F 44 | unsigned char low_magic; // 0x30 45 | unsigned char merchant; // 0x31 46 | unsigned char sun_magic; // 0x32 47 | unsigned char axe; // 0x33 48 | unsigned char flail; // 0x34 49 | unsigned char mace; // 0x35 50 | unsigned char sword; // 0x36 51 | unsigned char two_handed_sword; // 0x37 52 | unsigned char bow; // 0x38 53 | unsigned char crossbow; // 0x39 54 | unsigned char thrown_weapons; // 0x3A 55 | }; 56 | 57 | // Every spell byte below is a bitmask. 58 | struct spell_info { 59 | // 0x01 - Elvar's Fire 60 | // 0x02 - Fire Light 61 | // (Low Magic) 62 | // 0x04 - Mage Light 63 | // 0x08 - Lesser Heal 64 | // 0x10 - Luck 65 | // 0x20 - Charm 66 | // 0x40 - Disarm 67 | // 0x80 - Mage Fire 68 | unsigned char spell1; // 0x3C 69 | 70 | // 0x01 - Vorn's Guard 71 | // 0x02 - Sala's Swift 72 | // 0x04 - Reveal Glamor 73 | // 0x08 - Mystic Might 74 | // 0x10 - Dazzle 75 | // 0x20 - Big Chill 76 | // 0x40 - Ice Chill 77 | // 0x80 - Poog's Vortex 78 | unsigned char spell2; // 0x3D 79 | 80 | // 0x01 - Water Summon 81 | // 0x02 - Earth Summon 82 | // 0x04 - Air Summon 83 | // 0x08 - Sense Traps 84 | // 0x10 - Cloak Arcane 85 | // 0x20 - Group Heal 86 | // 0x40 - Healing 87 | // 0x80 - Cowardice 88 | unsigned char spell3; // 0x3E 89 | 90 | // 0x01 - Greater Healing 91 | // 0x02 - Brambles 92 | // 0x04 - Scare 93 | // 0x08 - Whirlwind 94 | // 0x10 - Insect Plague 95 | // 0x20 - Fire Blast 96 | // 0x40 - Death Curse 97 | // (High magic) 98 | // 0x80 - Fire Summon 99 | unsigned char spell4; // 0x3F 100 | 101 | // 0x01 - Exorcism 102 | // 0x02 - Sunstroke 103 | // (Druid magic) 104 | // 0x04 - Wood Spirit 105 | // 0x08 - Beast Call 106 | // 0x10 - Invoke Spirit 107 | // 0x20 - Soften Stone 108 | // 0x40 - Create Wall 109 | // 0x80 - Cure All 110 | unsigned char spell5; // 0x40 111 | 112 | // 0x01 - Exorcism 113 | // 0x02 - Sunstroke 114 | // 0x04 - Wood Spirit 115 | // 0x08 - Beast Call 116 | // 0x10 - Invoke Spirit 117 | // 0x20 - Soften Stone 118 | // 0x40 - Create Wall 119 | // 0x80 - Cure All 120 | unsigned char spell6; // 0x41 121 | 122 | // 0x01 - Mithras' Bless 123 | // 0x02 - Column of Fire 124 | // 0x04 - Battle Power 125 | // 0x08 - Holy Aim 126 | // 0x10 - Inferno 127 | // 0x20 - Fire Storm 128 | // 0x40 - Wrath of Mithras 129 | // 0x80 - Rage of Mithras 130 | unsigned char spell7; // 0x42 131 | 132 | // 0x01 - Radiance 133 | // 0x02 - Guidance 134 | // 0x04 - Disarm Trap 135 | // (Sun Spells) 136 | // 0x08 - Major Heal 137 | // 0x10 - Heal 138 | // 0x20 - Sun Light 139 | // 0x40 - Armor of Light 140 | // 0x80 - Light Flash 141 | unsigned char spell8; // 0x43 142 | 143 | // 0x01 - [Not used] 144 | // 0x02 - [Not used] 145 | // 0x04 - [Not used] 146 | // (Misc spells) 147 | // 0x08 - Poison 148 | // 0x10 - Kill Ray 149 | // 0x20 - Zak's Speed 150 | // 0x40 - Charger 151 | // 0x80 - Summon Salamander 152 | unsigned char spell9; // 0x44 153 | }; 154 | 155 | struct item_info { 156 | unsigned char wielded; // 8 if wielded, 0 if not. 157 | unsigned char req1; // Bits 2 & 3 are the stat required to use the 158 | // weapon. 0x02=Dex, 0x04=Int, 0x06=Spi. Bit 0 159 | // seems to do nothing, and using any higher bits 160 | // in the byte gives garbage skillnames when 161 | // taking the item to be evaluated at a trader. 162 | 163 | unsigned char req2; // Bits 1-5 are how many points are required for the 164 | // above skill, 0 to 31. Don't know what the top 3 165 | // bits are for, but setting them does not affect the 166 | // displayed skill requirement when evaluating. 167 | unsigned char unknown1; // Seen as 0x00 (hand axe) and 0x10 (battle axe, 168 | // dagger, shortsword). 169 | unsigned char type; // See below. 170 | unsigned char unknown2; // Unknown. Only seen as 0x80. 171 | unsigned char unknown3; // Unknown. Only seen as 0x00. 172 | unsigned char unknown4; // Unknown. Seen as 0x40 (hand axe), 173 | // 0x80 (battle axe), 0x00 (dagger), 0x20 (shortsword). 174 | unsigned char unknown5; // Unknown. Only seen as 0x00. 175 | unsigned char unknown6; // Unknown. Only seen as 0x01. 176 | unsigned char name[12]; // All bytes, except the last byte have the high bit set. 177 | }; 178 | 179 | // Item types 180 | // 0x00 - General Item 181 | // 0x01 - Shield 182 | // 0x02 - Full Shield 183 | // 0x03 - Axe 184 | // 0x04 - Flail 185 | // 0x05 - Sword 186 | // 0x06 - Two-handed sword 187 | // 0x07 - Mace 188 | // 0x08 - Bow 189 | // 0x09 - Crossbow 190 | // 0x0A - Gun 191 | // 0x0B - Thrown weapon 192 | // 0x0C - Ammunition 193 | // 0x0D - Gloves 194 | // 0x0E - Mage Gloves 195 | // 0x0F - Ammo Clip 196 | // 0x10 - Cloth Armor 197 | // 0x11 - Leather Armor 198 | // 0x12 - Cuir Bouilli Armor 199 | // 0x13 - Brigandine Armor 200 | // 0x14 - Scale Armor 201 | // 0x15 - Chain Armor 202 | // 0x16 - Plate And Chain Armor 203 | // 0x17 - Full plate Armor 204 | // 0x18 - Helmet 205 | // 0x19 - Scroll 206 | // 0x1A - Pair of Boots 207 | // 0x1B and above - blank (no text). 208 | 209 | 210 | // This should be 512 (0x200) bytes long. 211 | struct player_record { 212 | // All bytes, except the last byte have the high bit set. 213 | // "Muskels" => 4D75736B656C73 214 | // | 0x80 => CDF5F3EBE5EC73 215 | unsigned char name[12]; 216 | 217 | // Stats 218 | unsigned char strength; // 0x0C 219 | unsigned char max_strength; // 0x0D 220 | unsigned char dexterity; // 0x0E 221 | unsigned char max_dexterity; // 0x0F 222 | unsigned char intel; // 0x10 223 | unsigned char max_intel; // 0x11 224 | unsigned char spirit; // 0x12 225 | unsigned char max_spirit; // 0x13 226 | unsigned short health; // 0x14,0x15 227 | unsigned short max_health; // 0x16,0x17 228 | unsigned short stun; // 0x18-0x19 229 | unsigned short max_stun; // 0x1A-0x1B 230 | unsigned short power; // 0x1C-0x1D 231 | unsigned short max_power; // 0x1E-0x1F 232 | 233 | // Skills 234 | struct skill_info skills; // 0x20 -> 0x3A 235 | unsigned char advancement_points; // 0x3B 236 | 237 | struct spell_info spells; // 0x3C -> 0x44 238 | 239 | unsigned char unknown[8]; // 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B 240 | unsigned char status; // bitfield (0 = Ok, 1 = dead, 2 = chained, 4 = poisoned, 8 = stunned) 0x4C 241 | unsigned char unknown_byte; // always 0 ? 0x4D 242 | unsigned char gender; // 0 = male, 1 = female // 0x4E 243 | unsigned short level; // 0x4F-0x50 244 | unsigned int xp; // 0x51, 0x52, 0x53, 0x54 245 | unsigned int gold; // 0x55, 0x56, 0x57, 0x58 246 | unsigned char armor; // 0x59 247 | unsigned char defense; // 0x5A 248 | unsigned char armor_class; // 0x5B 249 | unsigned char unknown_byte2; // 0x5C 250 | 251 | unsigned char padding[143]; 252 | 253 | struct item_info inventory_items[12]; 254 | }; 255 | 256 | // In the dragon.com implementation this occupies 0E73:0000-0DFF, but in the 257 | // COM file it's at 0x1DD:C960 (where CS = 0x1DD) 258 | // 259 | // This is because it is calculated as 260 | // ax = 0xC960 >> 4 261 | // mov bx, cs 262 | // add ax, bx 263 | // 264 | // Where CS is 0x1DD 265 | // 0xE73 - (0xC960 >> 4) + 0x1DD 266 | // 267 | // 01DD:C960 -> 0E73:0000 268 | // 269 | // This is character data. A Dragon Wars party can be 7 people. 270 | // Each character uses 512 bytes (0x200) so 512 * 7 = 0xE00 271 | static unsigned char data_C960[0xE00] = { 0 }; 272 | 273 | #define SIZE_OF_PLAYER 512 274 | 275 | unsigned char *get_player_data_base() 276 | { 277 | return data_C960; 278 | } 279 | 280 | unsigned char *get_player_data(int player) 281 | { 282 | size_t offset = player * SIZE_OF_PLAYER; 283 | return data_C960 + offset; 284 | } 285 | 286 | unsigned char get_player_data_byte(int player, int property) 287 | { 288 | size_t offset = player * SIZE_OF_PLAYER; 289 | return data_C960[offset + property]; 290 | } 291 | 292 | const char *player_property_name(int prop_idx) 293 | { 294 | switch (prop_idx) { 295 | case 0x0C: 296 | return "Strength"; 297 | break; 298 | case 0x0D: 299 | return "Max Strength"; 300 | break; 301 | case 0x0E: 302 | return "Dexterity"; 303 | break; 304 | case 0x0F: 305 | return "Max Dexterity"; 306 | break; 307 | case 0x4C: 308 | return "Status"; 309 | break; 310 | case 0x4E: 311 | return "Gender"; 312 | break; 313 | } 314 | 315 | return "Unknown Property"; 316 | } 317 | -------------------------------------------------------------------------------- /src/lib/player.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_PLAYER_H 18 | #define DW_PLAYER_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | unsigned char *get_player_data_base(); 25 | unsigned char *get_player_data(int player); 26 | 27 | unsigned char get_player_data_byte(int player, int property); 28 | 29 | const char *player_property_name(int prop_idx); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* DW_PLAYER_H */ 36 | -------------------------------------------------------------------------------- /src/lib/resource.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "bufio.h" 21 | #include "compress.h" 22 | #include 23 | #include "player.h" 24 | #include "ui.h" 25 | 26 | /* Only deals with data1 */ 27 | /* I'm not sure yet how data2 is used */ 28 | 29 | // 0xBC52 30 | static unsigned char data1_hdr[768]; 31 | static struct buf_rdr *header_rdr = NULL; 32 | 33 | static struct resource allocations[128] = { 0 }; 34 | static struct resource *resource_load_cache_miss(enum resource_section sec); 35 | 36 | static unsigned char *ptr3; // 0x313E 37 | 38 | #ifndef nitems 39 | #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 40 | #endif /* nitems */ 41 | 42 | static int 43 | load_data1_header(void) 44 | { 45 | FILE *fp; 46 | size_t n; 47 | int rc = -1; 48 | 49 | fp = fopen("data1", "rb"); 50 | if (fp == NULL) { 51 | fprintf(stderr, "Failed to open data1 file.\n"); 52 | return -1; 53 | } 54 | 55 | n = fread(data1_hdr, 1, sizeof(data1_hdr), fp); 56 | if (n != sizeof(data1_hdr)) { 57 | fprintf(stderr, "Failed to read data1 header bytes.\n"); 58 | goto done; 59 | } 60 | 61 | if ((header_rdr = buf_rdr_init(data1_hdr, sizeof(data1_hdr))) == NULL) { 62 | fprintf(stderr, "Failed to create header reader.\n"); 63 | goto done; 64 | } 65 | 66 | rc = 0; 67 | done: 68 | fclose(fp); 69 | return rc; 70 | } 71 | 72 | /* 0x1348 */ 73 | struct resource* game_memory_alloc(size_t nbytes, int marker, int tag) 74 | { 75 | int i; 76 | struct resource *a; 77 | 78 | for (i = 0; i < nitems(allocations); i++) { 79 | a = &allocations[i]; 80 | if (a->usage_type == 0) { 81 | break; 82 | } 83 | } 84 | 85 | if (i == nitems(allocations)) 86 | return NULL; 87 | 88 | a = &allocations[i]; 89 | a->bytes = malloc(nbytes); 90 | if (a->bytes == NULL) 91 | return NULL; 92 | 93 | a->usage_type = marker; 94 | a->len = nbytes; 95 | a->tag = tag; 96 | 97 | return a; 98 | } 99 | 100 | /* 0x12C0 inside dragon.com 101 | * XXX: Rename this. */ 102 | int find_index_by_tag(int tag) 103 | { 104 | int i; 105 | for (i = 0; i < nitems(allocations); i++) { 106 | struct resource *a = &allocations[i]; 107 | if (a->tag == tag && a->usage_type != 0) { 108 | return i; 109 | } 110 | } 111 | 112 | return -1; 113 | } 114 | 115 | // 0x1CF 116 | int 117 | rm_init(void) 118 | { 119 | /* First two entries are some unknown data in the COM file, I think, 120 | * but I'm not sure how they are used. 121 | * For now we just load unknown_data and hope they aren't used. */ 122 | allocations[0].bytes = get_player_data_base(); 123 | allocations[0].tag = 0xFFFF; 124 | allocations[0].usage_type = 0xFF; 125 | allocations[0].len = 1; 126 | 127 | allocations[1].bytes = get_player_data_base(); 128 | allocations[1].tag = 0xFFFF; 129 | allocations[1].usage_type = 0xFF; 130 | allocations[1].len = 0x0E00; 131 | 132 | for (int i = 0; i < nitems(allocations); i++) { 133 | allocations[i].index = i; 134 | } 135 | 136 | // first allocation will be saved at 0x02. 137 | return load_data1_header(); 138 | } 139 | 140 | void 141 | rm_exit(void) 142 | { 143 | if (header_rdr != NULL) { 144 | buf_rdr_free(header_rdr); 145 | } 146 | 147 | // Clean up resource cache. 148 | for (int i = 0; i < nitems(allocations); i++) { 149 | if (allocations[i].bytes != NULL && allocations[i].usage_type == 1) { 150 | free(allocations[i].bytes); 151 | } 152 | } 153 | } 154 | 155 | // Essentially 0x2EB0 but not exactly. 156 | // AL is always loaded with the marker character (always 1?) 157 | // BX contains the section to load. 158 | struct resource* resource_load(enum resource_section sec) 159 | { 160 | if (sec >= RESOURCE_MAX) { 161 | fprintf(stderr, "Attempted to load unknown resource, returning NULL\n"); 162 | return NULL; 163 | } 164 | 165 | // Check cache. 166 | int index = find_index_by_tag(sec); 167 | if (index != -1) { 168 | return &allocations[index]; 169 | } 170 | 171 | // Not found. 172 | return resource_load_cache_miss(sec); 173 | } 174 | 175 | // 0x12A8 176 | struct resource* resource_get_by_index(int index) 177 | { 178 | return &allocations[index]; 179 | } 180 | 181 | // 0x1270 182 | void resource_index_release(int index) 183 | { 184 | if (index == 0xFF) 185 | return; 186 | 187 | if (index < 2) 188 | return; 189 | 190 | allocations[index].usage_type = 0; 191 | free(allocations[index].bytes); 192 | allocations[index].bytes = NULL; 193 | } 194 | 195 | // 0x1297 196 | void resource_set_usage_type(int index, int usage_type) 197 | { 198 | if (index == 0xFF) 199 | return; 200 | 201 | if (index < 2) 202 | return; 203 | 204 | allocations[index].usage_type = usage_type; 205 | } 206 | 207 | // 0x128D 208 | void resource_set_flagged(int index) 209 | { 210 | // We still don't really know what usage_type 0x2 means, so we're 211 | // going with "flagged" for now. 212 | resource_set_usage_type(index, 0x2); 213 | } 214 | 215 | static struct resource * 216 | resource_load_cache_miss(enum resource_section sec) 217 | { 218 | int i; 219 | unsigned int offset = sizeof(data1_hdr); 220 | FILE *fp; 221 | size_t n; 222 | uint16_t len; 223 | int needs_decompression = 0; 224 | 225 | buf_reset(header_rdr); 226 | for (i = 0; i < sec; i++) { 227 | uint16_t header_val = buf_get16le(header_rdr); 228 | if (header_val < 0xFF00) { 229 | offset += header_val; 230 | } 231 | } 232 | len = buf_get16le(header_rdr); 233 | printf("Section (0x%02x), Offset: 0x%04x Size: 0x%04x\n", sec, offset, 234 | (unsigned int)len); 235 | 236 | struct resource* res = game_memory_alloc(len, 1, sec); 237 | if (sec > 0x17) { 238 | needs_decompression = 1; 239 | } 240 | 241 | fp = fopen("data1", "rb"); 242 | if (fp == NULL) { 243 | fprintf(stderr, "Failed to open data1 file.\n"); 244 | return NULL; 245 | } 246 | 247 | fseek(fp, offset, SEEK_SET); 248 | n = fread(res->bytes, 1, res->len, fp); 249 | if (n != res->len) { 250 | fprintf(stderr, "Failed to read section in data1 file.\n"); 251 | free(res->bytes); 252 | free(res); 253 | fclose(fp); 254 | return NULL; 255 | } 256 | fclose(fp); 257 | 258 | if (needs_decompression) { 259 | struct buf_rdr *compression_rdr; 260 | struct buf_wri *uncompressed_wri; 261 | unsigned short uncompressed_sz; 262 | 263 | compression_rdr = buf_rdr_init(res->bytes, res->len); 264 | uncompressed_sz = buf_get16le(compression_rdr); 265 | printf("Section 0x%02X needs decompression. %d -> %d\n", sec, 266 | (unsigned int)res->len, uncompressed_sz); 267 | uncompressed_wri = buf_wri_init(uncompressed_sz); 268 | decompress_data1(compression_rdr, uncompressed_wri, uncompressed_sz); 269 | 270 | // After decompressing we can get rid of the compressed copy, and store the 271 | // data directly in the resource. 272 | free(res->bytes); 273 | res->len = uncompressed_sz; 274 | // move pointer. 275 | res->bytes = uncompressed_wri->base; 276 | free(uncompressed_wri); 277 | buf_rdr_free(compression_rdr); 278 | } 279 | 280 | return res; 281 | } 282 | 283 | // 0x2F4D 284 | void resource_write_to_disk(enum resource_section sec, const struct resource *res) 285 | { 286 | int i; 287 | unsigned int offset = sizeof(data1_hdr); 288 | FILE *fp; 289 | size_t n; 290 | uint16_t len; 291 | 292 | buf_reset(header_rdr); 293 | for (i = 0; i < sec; i++) { 294 | uint16_t header_val = buf_get16le(header_rdr); 295 | if (header_val < 0xFF00) { 296 | offset += header_val; 297 | } 298 | } 299 | len = buf_get16le(header_rdr); 300 | printf("Section (0x%02x), Offset: 0x%04x Size: 0x%04x\n", sec, offset, 301 | (unsigned int)len); 302 | 303 | if (sec > 0x17) { 304 | fprintf(stderr, "No support for compression when writing resources.\n"); 305 | exit(1); 306 | } 307 | 308 | fp = fopen("data1", "r+b"); 309 | if (fp == NULL) { 310 | fprintf(stderr, "Failed to open data1 file.\n"); 311 | return; 312 | } 313 | 314 | fseek(fp, offset, SEEK_SET); 315 | n = fwrite(res->bytes, 1, res->len, fp); 316 | if (n != res->len) { 317 | fprintf(stderr, "Failed to write section to data1 file.\n"); 318 | fclose(fp); 319 | return; 320 | } 321 | fclose(fp); 322 | } 323 | 324 | /* The DOS COM executable format sets the origin by default at 0x100. So when 325 | * extracting from COM files we subtract this from the offset. 326 | * 327 | * Quite a few assets/resources are actually embedded within the DRAGON.COM 328 | * file, so we extract them with this function. */ 329 | 330 | #define COM_ORG_START 0x100 331 | unsigned char *com_extract(size_t off, size_t sz) 332 | { 333 | unsigned char *ptr; 334 | FILE *fp; 335 | 336 | if (off < COM_ORG_START) { 337 | fprintf(stderr, "com_extract: Invalid offset specified, too low!\n"); 338 | return NULL; 339 | } 340 | off -= COM_ORG_START; 341 | 342 | fp = fopen("dragon.com", "rb"); 343 | if (fp == NULL) 344 | return NULL; 345 | 346 | ptr = malloc(sz); 347 | if (ptr == NULL) 348 | return NULL; 349 | 350 | fseek(fp, off, SEEK_SET); 351 | if (fread(ptr, 1, sz, fp) != sz) { 352 | fprintf(stderr, "com_extract: Failed to read %zu bytes from file.\n", sz); 353 | free(ptr); 354 | return NULL; 355 | } 356 | 357 | fclose(fp); 358 | return ptr; 359 | } 360 | 361 | // 0x0A25 362 | void setup_memory() 363 | { 364 | init_viewport_memory(); 365 | ptr3 = malloc(0x370 * 16); 366 | } 367 | -------------------------------------------------------------------------------- /src/lib/resource.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_RESOURCE_H 18 | #define DW_RESOURCE_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | 26 | /* Resource file maps to "data1" and "data2" files. */ 27 | enum resource_section { 28 | RESOURCE_SCRIPT = 0x00, 29 | RESOURCE_CHARACTER_DATA = 0x07, 30 | RESOURCE_TITLE0 = 0x18, 31 | RESOURCE_TITLE1 = 0x19, 32 | RESOURCE_TITLE2 = 0x1A, 33 | RESOURCE_TITLE3 = 0x1D, 34 | RESOURCE_UNKNOWN = 0x47, 35 | RESOURCE_LAST = 0x106, 36 | RESOURCE_MAX 37 | }; 38 | 39 | struct resource { 40 | unsigned char *bytes; 41 | size_t len; 42 | // Seems to be one of these values: 43 | // 0x00 - Not used, free spot. 44 | // 0x01 - dynamically allocated, must be free'd 45 | // 0x02 - ??? 46 | // ... - ??? (not sure if values other than 0, 1, 2, and 0xFF are used) 47 | // 0xFF - Statically allocated 48 | int usage_type; 49 | int tag; 50 | int index; 51 | }; 52 | 53 | int rm_init(void); 54 | void rm_exit(void); 55 | 56 | struct resource* resource_get_by_index(int index); 57 | void resource_index_release(int index); 58 | void resource_set_usage_type(int index, int usage_type); 59 | 60 | void resource_set_flagged(int index); 61 | 62 | // 0x2EB0 63 | struct resource* resource_load(enum resource_section sec); 64 | void resource_write_to_disk(enum resource_section sec, const struct resource *res); 65 | 66 | int find_index_by_tag(int tag); 67 | unsigned char *com_extract(size_t off, size_t sz); 68 | struct resource* game_memory_alloc(size_t nbytes, int marker, int tag); 69 | void setup_memory(); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif /* DW_RESOURCE_H */ 76 | -------------------------------------------------------------------------------- /src/lib/state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "log.h" 18 | #include "state.h" 19 | 20 | // The game state is a 256 byte "scratch/work" area for the game engine 21 | // to manage and keep track of various aspects of the game. 22 | // 23 | // It starts at address 0x3860 24 | struct game_state game_state = {0}; 25 | 26 | // Offsets 33, 34, 35, 36 hold first 4 bytes of level data. 27 | 28 | void set_game_state(const char *func_src, int offset, unsigned char value) 29 | { 30 | log_trace("%s: [%d] = 0x%02X (%s)", __func__, offset, value, func_src); 31 | if (offset == 31) { 32 | log_trace(" SETTING MONSTER?\n"); 33 | } 34 | if (offset == 0x47) { 35 | log_trace(" HMM?"); 36 | } 37 | if (offset == 65) { // 0x41 38 | log_trace(" SETTING MONSTER?\n"); 39 | } 40 | if (offset == 88) { // 0x58 41 | log_trace(" SETTING MONSTER?\n"); 42 | } 43 | if (offset == 0xBE) { 44 | log_trace(" SETTING Direction to %d\n", value); 45 | } 46 | game_state.unknown[offset] = value; 47 | } 48 | 49 | unsigned char get_game_state(const char *func_src, int offset) 50 | { 51 | log_trace("%s: [%d] (%s)", __func__, offset, func_src); 52 | return game_state.unknown[offset]; 53 | } 54 | -------------------------------------------------------------------------------- /src/lib/state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_STATE_H 18 | #define DW_STATE_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | // Directions 25 | enum { 26 | DIRECTION_NORTH = 0, 27 | DIRECTION_EAST = 1, 28 | DIRECTION_SOUTH = 2, 29 | DIRECTION_WEST = 3 30 | }; 31 | 32 | // Managing game state. 33 | // 34 | 35 | 36 | // We should break this apart. 37 | struct game_state { 38 | // 0x00 - X position of player 39 | // 0x01 - Y position of player 40 | // 0x02 - Current world number? (e.g. Purgatory?) 41 | // 0x03 - Current direction (0 = North, 1 = East, 2 = South, 3 = West) 42 | // 0x06 - Currently selected player. 43 | // 0x18 - Boolean flag for if we have player 1 (0 = yes, -1 = no) 44 | // 0x19 - Boolean flag for if we have player 2 (0 = yes, -1 = no) 45 | // 0x1A - Boolean flag for if we have player 3 (0 = yes, -1 = no) 46 | // 0x1B - Boolean flag for if we have player 4 (0 = yes, -1 = no) 47 | // 0x1C - Boolean flag for if we have player 5 (0 = yes, -1 = no) 48 | // 0x1D - Boolean flag for if we have player 6 (0 = yes, -1 = no) 49 | // 0x1E - Boolean flag for if we have player 7 (0 = yes, -1 = no) 50 | // 0x1F - Number of characters in the party. 51 | // 0x56 - Resource index. 52 | // 0x6A - 0x6D (Gold) 53 | // 0x6E - 0x71 (Experience) 54 | // 0xBE - Direction? (0 = North, 1 = East, 2 = South, 3 = West) 55 | // 0xC6 - 0x?? - New character name. 56 | unsigned char unknown[256]; 57 | }; 58 | 59 | extern struct game_state game_state; 60 | 61 | void set_game_state(const char *func_src, int offset, unsigned char value); 62 | unsigned char get_game_state(const char *func_src, int offset); 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif /* DW_STATE_H */ 69 | -------------------------------------------------------------------------------- /src/lib/tables.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_TABLES_H 18 | #define DW_TABLES_H 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | void load_chr_table(); 27 | void unload_chr_table(); 28 | const unsigned char *get_chr(int chr_num); 29 | uint8_t get_b152_table(uint8_t offset); 30 | uint8_t get_and_table(uint8_t offset); 31 | uint8_t get_or_table(uint8_t offset); 32 | uint8_t get_1BC1_table(uint8_t offset); 33 | uint16_t get_line_offset(uint8_t offset); 34 | uint16_t get_unknown_4456(uint8_t index); 35 | uint16_t get_and_table_B452(uint8_t offset); 36 | uint16_t get_or_table_B652(uint8_t offset); 37 | uint16_t get_ba52_data(uint8_t offset); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif /* DW_TABLES_H */ 44 | -------------------------------------------------------------------------------- /src/lib/timers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Routines for working with the timers */ 18 | 19 | #include "timers.h" 20 | 21 | struct timer_ctx timers; 22 | 23 | void initialize_timers() 24 | { 25 | timers.timer0 = 1; 26 | timers.timer1 = 1; 27 | timers.timer2 = 1; 28 | timers.timer3 = 1; 29 | timers.timer4 = 1; 30 | timers.timer5 = 0; 31 | } 32 | 33 | // 0x4B10 34 | void timer_tick_proc() 35 | { 36 | if (timers.timer2 > 0) { 37 | timers.timer2--; 38 | } 39 | 40 | if (timers.timer4 > 0) { 41 | timers.timer4--; 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/lib/timers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Routines for working with the timers */ 18 | 19 | #ifndef DW_TIMER_H 20 | #define DW_TIMER_H 21 | 22 | #include 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | struct timer_ctx { 29 | uint8_t timer0; // 0x4C35 30 | uint8_t timer1; // 0x4C36 31 | uint8_t timer2; // 0x4C37 32 | uint16_t timer3; // 0x4C38 33 | uint16_t timer4; // 0x4C3A 34 | uint8_t timer5; // 0x4C3C (appears to be more of a flag) 35 | }; 36 | 37 | extern struct timer_ctx timers; 38 | 39 | void initialize_timers(); 40 | 41 | // 0x4B10 42 | void timer_tick_proc(); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif /* DW_TIMER_H */ 49 | -------------------------------------------------------------------------------- /src/lib/ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_UI_H 18 | #define DW_UI_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | #include "resource.h" 26 | 27 | // data_320C 28 | struct ui_string_line { 29 | int len; 30 | unsigned char bytes[40]; 31 | }; 32 | 33 | struct viewport_data { 34 | uint16_t xpos; // Sometimes set as 36C0 35 | int ypos; // Sometimes set as 36C4 36 | int runlength; // Sometimes set as 1048 37 | int numruns; // sometimes set as 104D 38 | int unknown1; 39 | int unknown2; 40 | unsigned char *data; 41 | }; 42 | 43 | // 0x320C 44 | extern struct ui_string_line ui_string; 45 | 46 | struct ui_rect { 47 | uint16_t x; // 0x2697 48 | uint16_t y; // 0x2699 49 | uint16_t w; // 0x269B 50 | uint16_t h; // 0x269D 51 | }; 52 | 53 | struct ui_point { 54 | uint16_t x; // 0x32BF 55 | uint16_t y; // 0x32C1 56 | }; 57 | 58 | extern uint8_t ui_drawn_yet; // 0x268E 59 | // 2697 60 | extern struct ui_rect draw_rect; 61 | // 32BF 62 | extern struct ui_point draw_point; 63 | 64 | void ui_load(); 65 | void sub_37C8(); 66 | void update_viewport(); 67 | void sub_CF8(unsigned char *data, struct viewport_data *vp); 68 | void draw_viewport(); 69 | void ui_draw(); 70 | void ui_draw_full(); 71 | void ui_clean(); 72 | 73 | void ui_header_reset(); 74 | void ui_header_draw(); 75 | void ui_header_set_byte(unsigned char byte); 76 | 77 | void ui_draw_box_segment(uint8_t chr); 78 | void ui_draw_chr_piece(uint8_t chr); 79 | void sub_35A0(uint8_t piece_index); 80 | void draw_pattern(struct ui_rect *rect); 81 | void ui_set_background(uint16_t val); 82 | void ui_draw_string(void); 83 | 84 | void ui_draw_horizontal_line( 85 | uint8_t color_idx, 86 | uint16_t x1, uint16_t x2, 87 | uint16_t y); 88 | 89 | void ui_draw_double_horizontal_line( 90 | uint8_t color_idx, 91 | uint16_t x1, uint16_t x2, 92 | uint16_t y); 93 | 94 | void draw_right_pillar(); 95 | void reset_ui_background(); 96 | 97 | void ui_rect_expand(); 98 | void ui_rect_shrink(); 99 | 100 | int ui_adjust_rect(uint8_t input); 101 | 102 | int ui_rect_redraw(uint8_t input); 103 | void ui_set_byte_3236(uint8_t val); 104 | uint8_t ui_get_byte_3236(); 105 | void init_viewport_memory(); 106 | void viewport_save(); 107 | void sub_4C95(struct resource *r); 108 | void sub_4DE3(uint16_t input, const struct resource *r); 109 | void draw_rectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h); 110 | 111 | unsigned char *ui_get_minimap_viewport(); 112 | unsigned char *ui_get_viewport_mem(); 113 | unsigned char *ui_get_data_6820(); 114 | 115 | void ui_viewport_reset(); 116 | void ui_update_viewport(size_t vp_offset); 117 | void ui_set_viewport_width(int new_width); 118 | void ui_set_viewport_height(int new_height); 119 | void ui_set_viewport_offset(int new_offset); 120 | 121 | void zero_out_2AAA(); 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif /* DW_UI_H */ 128 | -------------------------------------------------------------------------------- /src/lib/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | #include "utils.h" 23 | 24 | void dump_hex(const void *vp, size_t len) 25 | { 26 | char linebuf[80]; 27 | int i; 28 | int linebuf_dirty = 0; 29 | const unsigned char *p = (const unsigned char *)vp; 30 | 31 | memset(linebuf, ' ', sizeof(linebuf)); 32 | linebuf[70] = '\0'; 33 | 34 | for (i = 0; i < len; i++) { 35 | int x = i % 16; 36 | int ch = (unsigned)p[i]; 37 | char hex[20]; 38 | 39 | if (x >= 8) 40 | x = x * 3 + 1; 41 | else 42 | x = x * 3; 43 | snprintf(hex, sizeof(hex), "%02x", ch); 44 | linebuf[x] = hex[0]; 45 | linebuf[x + 1] = hex[1]; 46 | 47 | if (isprint(ch)) 48 | linebuf[52 + (i % 16)] = ch; 49 | else 50 | linebuf[52 + (i % 16)] = '.'; 51 | 52 | linebuf_dirty = 1; 53 | if (!((i + 1) % 16)) { 54 | fprintf(stderr, "%s\n", linebuf); 55 | memset(linebuf, ' ', sizeof(linebuf)); 56 | linebuf[70] = '\0'; 57 | linebuf_dirty = 0; 58 | } 59 | } 60 | if (linebuf_dirty == 1) 61 | fprintf(stderr, "%s\n", linebuf); 62 | } 63 | 64 | void hexdump(void *ptr, int buflen) 65 | { 66 | unsigned char *buf = (unsigned char*)ptr; 67 | int i, j; 68 | for (i=0; i 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __UTILS_H__ 18 | #define __UTILS_H__ 19 | 20 | #include /* size_t */ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | void dump_hex(const void *vp, size_t len); 27 | void hexdump(void *ptr, int buflen); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif /* __UTILS_H__ */ 34 | -------------------------------------------------------------------------------- /src/lib/vga.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | #include "vga.h" 21 | 22 | #ifndef nitems 23 | #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 24 | #endif /* nitems */ 25 | 26 | static struct vga_driver *sys_ctx = NULL; 27 | 28 | #define VGA_WIDTH 320 29 | #define VGA_HEIGHT 200 30 | 31 | /* Represents 0xA0000 (0xA000:0000) memory. */ 32 | static uint8_t *framebuffer; 33 | 34 | static struct key_buffer { 35 | int buffer[32]; 36 | int head; 37 | int tail; 38 | int count; 39 | } vga_keyb; 40 | 41 | static int 42 | display_start(int game_width, int game_height) 43 | { 44 | if ((framebuffer = calloc(VGA_WIDTH * VGA_HEIGHT, 1)) == NULL) { 45 | fprintf(stderr, "Framebuffer could not be allocated.\n"); 46 | return -1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | static void 53 | display_end(void) 54 | { 55 | free(framebuffer); 56 | } 57 | 58 | static uint8_t * 59 | get_fb_mem() 60 | { 61 | return framebuffer; 62 | } 63 | 64 | void register_vga_driver(struct vga_driver *driver) 65 | { 66 | sys_ctx = driver; 67 | } 68 | 69 | int vga_initialize(int game_width, int game_height) 70 | { 71 | vga_keyb.head = 0; 72 | vga_keyb.tail = 0; 73 | vga_keyb.count = 0; 74 | 75 | if (sys_ctx != NULL && sys_ctx->initialize != NULL) { 76 | return sys_ctx->initialize(game_width, game_height); 77 | } 78 | return display_start(game_width, game_height); 79 | } 80 | 81 | uint8_t* vga_memory() 82 | { 83 | if (sys_ctx != NULL && sys_ctx->memory != NULL) { 84 | return sys_ctx->memory(); 85 | } 86 | return get_fb_mem(); 87 | } 88 | 89 | void vga_update() 90 | { 91 | if (sys_ctx != NULL && sys_ctx->update != NULL) { 92 | sys_ctx->update(); 93 | } 94 | } 95 | 96 | uint16_t vga_getkey() 97 | { 98 | if (sys_ctx != NULL && sys_ctx->getkey != NULL) { 99 | return sys_ctx->getkey(); 100 | } 101 | return 0; 102 | } 103 | 104 | void vga_waitkey() 105 | { 106 | if (sys_ctx != NULL && sys_ctx->waitkey != NULL) { 107 | sys_ctx->waitkey(); 108 | } 109 | } 110 | 111 | void vga_end() 112 | { 113 | if (sys_ctx != NULL && sys_ctx->end != NULL) { 114 | sys_ctx->end(); 115 | } else { 116 | display_end(); 117 | } 118 | } 119 | 120 | int vga_poll_events() 121 | { 122 | if (sys_ctx != NULL && sys_ctx->poll != NULL) { 123 | return sys_ctx->poll(); 124 | } 125 | return 0; 126 | } 127 | 128 | void sys_delay(unsigned int ms) 129 | { 130 | if (sys_ctx != NULL && sys_ctx->delay != NULL) { 131 | sys_ctx->delay(ms); 132 | } 133 | } 134 | 135 | unsigned short sys_ticks() 136 | { 137 | if (sys_ctx != NULL && sys_ctx->ticks != NULL) { 138 | return sys_ctx->ticks(); 139 | } 140 | // Default? 141 | return 0x1234; 142 | } 143 | 144 | void vga_addkey(int key) 145 | { 146 | if (vga_keyb.count == nitems(vga_keyb.buffer)) { 147 | return; 148 | } 149 | 150 | printf("Adding key: 0x%04X\n", key); 151 | 152 | vga_keyb.buffer[vga_keyb.head] = key; 153 | vga_keyb.head = (vga_keyb.head + 1) % nitems(vga_keyb.buffer); 154 | vga_keyb.count++; 155 | } 156 | 157 | int vga_getkey2() 158 | { 159 | int key; 160 | 161 | if (vga_keyb.count == 0) { 162 | return 0; 163 | } 164 | 165 | key = vga_keyb.buffer[vga_keyb.tail]; 166 | vga_keyb.count--; 167 | 168 | vga_keyb.tail = (vga_keyb.tail + 1) % nitems(vga_keyb.buffer); 169 | return key; 170 | } 171 | -------------------------------------------------------------------------------- /src/lib/vga.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018-2020 Devin Smith 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef DW_VGA_INTERFACE_H 18 | #define DW_VGA_INTERFACE_H 19 | 20 | #include 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | struct vga_driver { 27 | const char *driver_name; 28 | int (*initialize)(int game_width, int game_height); 29 | void (*end)(); 30 | void (*update)(); 31 | void (*waitkey)(); 32 | uint8_t* (*memory)(); 33 | uint16_t (*getkey)(); 34 | int (*poll)(); 35 | void (*delay)(unsigned int ms); 36 | unsigned short (*ticks)(); 37 | }; 38 | 39 | struct mouse_status { 40 | // clicked contains the last 2 left mouse clicks 41 | // (in order to track double clicks) 42 | // as the most significant bytes 43 | // 44 | // The value of clicked could then be: 45 | // 0 -> 00000000b (no button clicked) 46 | // 80 -> 10000000b (single click) 47 | // C0 -> 11000000b (double click) 48 | uint8_t clicked; // 0x3854 49 | 50 | uint8_t enabled; // 0x3855 (might be enabled or num of buttons) 51 | 52 | uint16_t x; // 0x3556 53 | uint16_t y; // 0x3558 54 | }; 55 | 56 | void register_vga_driver(struct vga_driver *driver); 57 | 58 | int vga_initialize(int game_width, int game_height); 59 | uint8_t* vga_memory(); 60 | void vga_update(); 61 | uint16_t vga_getkey(); 62 | void vga_waitkey(); 63 | void vga_end(); 64 | void vga_addkey(int key); 65 | int vga_getkey2(); 66 | int vga_poll_events(); 67 | void sys_delay(unsigned int ms); 68 | unsigned short sys_ticks(); 69 | 70 | // Intended for use by various vga drivers. 71 | void video_setup(); 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif /* DW_VGA_INTERFACE_H */ 78 | -------------------------------------------------------------------------------- /src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | 3 | project(opendw) 4 | 5 | # Check's CMake find module does not correctly set cflags and libs. 6 | # Use pkg-config as a fallback for now. 7 | #find_package(Check REQUIRED) 8 | find_package(PkgConfig REQUIRED) 9 | find_package(Threads REQUIRED) 10 | pkg_check_modules(CHECK REQUIRED check) 11 | 12 | 13 | set(TARGET_NAME opendw_test) 14 | 15 | set(SOURCES test_compress.cpp test_opendw.cpp test_vga.cpp) 16 | 17 | add_executable(${TARGET_NAME} ${SOURCES}) 18 | target_compile_options(${TARGET_NAME} PRIVATE ${CHECK_CFLAGS}) 19 | target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/lib ${CHECK_INCLUDE_DIRS}) 20 | target_link_libraries(${TARGET_NAME} PUBLIC dragon ${CHECK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 21 | 22 | add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME}) 23 | 24 | if(MSVC) 25 | target_compile_options(${TARGET_NAME} PRIVATE /W4 /WX) 26 | else() 27 | target_compile_options(${TARGET_NAME} PRIVATE -Wall -Werror) 28 | endif() 29 | 30 | -------------------------------------------------------------------------------- /src/tests/test_compress.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Tests for compress.c 17 | 18 | #include "test_compress.h" 19 | 20 | #include "compress.h" 21 | 22 | START_TEST(test_be_basic) 23 | { 24 | unsigned char test_data[] = { 0xf2 }; 25 | 26 | bit_extractor be { 0 }; 27 | be.data = test_data; 28 | uint8_t val = bit_extract(&be, 5); 29 | ck_assert_int_eq(val, 0x1e); 30 | // Bytes left over: 3 31 | ck_assert_int_eq(be.num_bits, 3); 32 | // Advanced 1 byte. 33 | ck_assert_int_eq(be.offset, 1); 34 | // Buffer contains left over. 35 | ck_assert_int_eq(be.bit_buffer, (uint8_t)(test_data[0] << 5)); 36 | } 37 | END_TEST 38 | 39 | START_TEST(test_be_multibyte) 40 | { 41 | unsigned char test_data[] = { 0xf2, 0x9d }; 42 | 43 | bit_extractor be { 0 }; 44 | be.data = test_data; 45 | uint8_t val = bit_extract(&be, 5); 46 | ck_assert_int_eq(val, 0x1e); 47 | ck_assert_int_eq(be.num_bits, 3); 48 | ck_assert_int_eq(be.offset, 1); 49 | ck_assert_int_eq(be.bit_buffer, (uint8_t)(test_data[0] << 5)); 50 | 51 | val = bit_extract(&be, 5); 52 | ck_assert_int_eq(val, 0x0A); 53 | ck_assert_int_eq(be.num_bits, 6); 54 | ck_assert_int_eq(be.offset, 2); 55 | ck_assert_int_eq(be.bit_buffer, (uint8_t)(test_data[1] << 2)); 56 | } 57 | END_TEST 58 | 59 | START_TEST(test_extract_letter) 60 | { 61 | unsigned char test_data[] = { 0xf2, 0x9d }; 62 | 63 | bit_extractor be { 0 }; 64 | be.data = test_data; 65 | 66 | uint8_t letter = extract_letter(&be); 67 | ck_assert_int_eq(letter, 'I' | 0x80); 68 | ck_assert_int_eq(be.num_bits, 6); 69 | ck_assert_int_eq(be.offset, 2); 70 | ck_assert_int_eq(be.bit_buffer, (uint8_t)(test_data[1] << 2)); 71 | 72 | letter = extract_letter(&be); 73 | ck_assert_int_eq(letter, 'n' | 0x80); 74 | ck_assert_int_eq(be.num_bits, 1); 75 | ck_assert_int_eq(be.offset, 2); 76 | ck_assert_int_eq(be.bit_buffer, (uint8_t)(test_data[1] << 7)); 77 | 78 | } 79 | END_TEST 80 | 81 | Suite* compress_suite() 82 | { 83 | Suite *s = suite_create("compress"); 84 | 85 | TCase *tc_core = tcase_create("core"); 86 | tcase_add_test(tc_core, test_be_basic); 87 | tcase_add_test(tc_core, test_be_multibyte); 88 | tcase_add_test(tc_core, test_extract_letter); 89 | suite_add_tcase(s, tc_core); 90 | 91 | return s; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/tests/test_compress.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Tests for compress.c 17 | 18 | #ifndef OD_TEST_COMPRESS_H 19 | #define OD_TEST_COMPRESS_H 20 | 21 | #include 22 | 23 | Suite* compress_suite(); 24 | 25 | #endif /* OD_TEST_COMPRESS_H */ 26 | -------------------------------------------------------------------------------- /src/tests/test_opendw.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Tests for opendw 17 | 18 | #include 19 | #include 20 | 21 | #include "test_compress.h" 22 | #include "test_vga.h" 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | Suite *s = compress_suite(); 27 | SRunner *sr = srunner_create(s); 28 | srunner_add_suite(sr, vga_suite()); 29 | 30 | srunner_run_all(sr, CK_NORMAL); 31 | int number_failed = srunner_ntests_failed(sr); 32 | srunner_free(sr); 33 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 34 | } 35 | -------------------------------------------------------------------------------- /src/tests/test_vga.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Tests for vga.c 17 | 18 | #include "test_vga.h" 19 | 20 | #include "vga.h" 21 | 22 | START_TEST(test_vga_keyb) 23 | { 24 | vga_initialize(64, 64); 25 | 26 | for (int i = 0; i < 32; i++) { 27 | vga_addkey(i); 28 | } 29 | 30 | for (int i = 0; i < 32; i++) { 31 | int actual = vga_getkey2(); 32 | ck_assert_int_eq(actual, i); 33 | } 34 | 35 | 36 | int key = vga_getkey2(); 37 | ck_assert_int_eq(key, 0); 38 | 39 | for (int i = 0; i < 32; i++) { 40 | vga_addkey(i); 41 | } 42 | 43 | key = vga_getkey2(); 44 | ck_assert_int_eq(key, 0); 45 | 46 | vga_end(); 47 | } 48 | END_TEST 49 | 50 | Suite* vga_suite() 51 | { 52 | Suite *s = suite_create("vga"); 53 | 54 | TCase *tc_core = tcase_create("core"); 55 | tcase_add_test(tc_core, test_vga_keyb); 56 | suite_add_tcase(s, tc_core); 57 | 58 | return s; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/tests/test_vga.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Tests for vga 17 | 18 | #ifndef OD_TEST_VGA_H 19 | #define OD_TEST_VGA_H 20 | 21 | #include 22 | 23 | Suite* vga_suite(); 24 | 25 | #endif /* OD_TEST_VGA_H */ 26 | -------------------------------------------------------------------------------- /src/tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | 3 | project(opendw) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | set(DISASM_SOURCES disasm.cpp) 9 | set(RESEXT_SOURCES resextract.cpp) 10 | set(STREXT_SOURCES strextract.cpp) 11 | set(PPMWRITE_SOURCES ppm.cpp) 12 | set(MOUSE_SOURCES mouse.cpp) 13 | set(MONSTER_INFO_SOURCES monster_info.cpp) 14 | 15 | add_executable(disasm ${DISASM_SOURCES}) 16 | target_include_directories(disasm PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 17 | target_link_libraries(disasm PUBLIC dragon) 18 | if(MSVC) 19 | target_compile_options(disasm PRIVATE /W4 /WX) 20 | else() 21 | target_compile_options(disasm PRIVATE -Wall -Werror) 22 | endif() 23 | 24 | add_executable(resextract ${RESEXT_SOURCES}) 25 | target_include_directories(resextract PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 26 | target_link_libraries(resextract PUBLIC dragon) 27 | 28 | add_executable(strextract ${STREXT_SOURCES}) 29 | target_include_directories(strextract PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 30 | target_link_libraries(strextract PUBLIC dragon) 31 | 32 | # Rather useless tool 33 | #add_executable(kbhit kbhit.cpp) 34 | 35 | add_executable(ppmwrite ${PPMWRITE_SOURCES}) 36 | target_include_directories(ppmwrite PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 37 | target_link_libraries(ppmwrite PUBLIC dragon) 38 | if(MSVC) 39 | target_compile_options(ppmwrite PRIVATE /W4 /WX) 40 | else() 41 | target_compile_options(ppmwrite PRIVATE -Wall -Werror) 42 | endif() 43 | 44 | add_executable(mouse ${MOUSE_SOURCES}) 45 | target_include_directories(mouse PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 46 | target_link_libraries(mouse PUBLIC dragon) 47 | if(MSVC) 48 | target_compile_options(mouse PRIVATE /W4 /WX) 49 | else() 50 | target_compile_options(mouse PRIVATE -Wall -Werror) 51 | endif() 52 | 53 | add_executable(monster_info ${MONSTER_INFO_SOURCES}) 54 | target_include_directories(monster_info PRIVATE ${CMAKE_SOURCE_DIR}/src/lib) 55 | target_link_libraries(monster_info PUBLIC dragon) 56 | 57 | 58 | 59 | install(TARGETS disasm DESTINATION bin) 60 | #if(MSVC) 61 | # target_compile_options(resextract PRIVATE /W4 /WX) 62 | #else() 63 | # target_compile_options(resextract PRIVATE -Wall -Werror) 64 | #endif() 65 | 66 | -------------------------------------------------------------------------------- /src/tools/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for tools 2 | 3 | .PHONY: all clean 4 | 5 | C_SRCS = 6 | CPP_SRCS = disasm.cpp resextract.cpp 7 | 8 | OBJS = $(C_SRCS:.c=.o) $(CPP_SRCS:.cpp=.o) 9 | DEPS = $(C_SRCS:.c=.d) $(CPP_SRCS:.cpp=.d) 10 | 11 | DEP_INCLUDES = -I../lib 12 | DEP_LIBS = -L../lib -ldragon 13 | 14 | CFLAGS = -Wall -g3 15 | EXES = disasm resextract 16 | 17 | all: $(EXES) 18 | 19 | disasm: disasm.o 20 | $(CXX) $(CFLAGS) -o $@ $< $(DEP_LFLAGS) $(DEP_LIBS) 21 | 22 | # XXX: Remove requirement to link a vga driver in for extractor 23 | resextract: resextract.o 24 | $(CXX) $(CFLAGS) -o $@ resextract.o ../fe/vga_null.o $(DEP_LFLAGS) $(DEP_LIBS) 25 | 26 | .c.o: 27 | $(CC) $(CFLAGS) $(DEP_INCLUDES) -MMD -MP -MT $@ -o $@ -c $< 28 | 29 | .cpp.o: 30 | $(CXX) $(CFLAGS) $(DEP_INCLUDES) -std=c++11 -MMD -MP -MT $@ -o $@ -c $< 31 | 32 | clean: 33 | rm -f $(OBJS) 34 | rm -f $(DEPS) 35 | rm -f $(EXE) 36 | 37 | # Include automatically generated dependency files 38 | -include $(DEPS) 39 | 40 | -------------------------------------------------------------------------------- /src/tools/kbhit.cpp: -------------------------------------------------------------------------------- 1 | // From https://github.com/dmsc/emu2/blob/master/src/keyb.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define CTRL_C 0x2e03 11 | 12 | static int term_raw = 0; 13 | static int tty_fd = -1; 14 | static int mod_state = 0; 15 | 16 | enum mod_keys 17 | { 18 | MOD_SHIFT = 1, 19 | MOD_RSHIFT = 2, 20 | MOD_CTRL = 4, 21 | MOD_ALT = 8 22 | }; 23 | 24 | static void set_raw_term(int raw) 25 | { 26 | static struct termios oldattr; // Initial terminal state 27 | if (raw == term_raw) 28 | return; 29 | 30 | term_raw = raw; 31 | if (term_raw) { 32 | struct termios newattr; 33 | tcgetattr(tty_fd, &oldattr); 34 | newattr = oldattr; 35 | cfmakeraw(&newattr); 36 | newattr.c_cc[VMIN] = 0; 37 | newattr.c_cc[VTIME] = 0; 38 | tcsetattr(tty_fd, TCSANOW, &newattr); 39 | } else { 40 | tcsetattr(tty_fd, TCSANOW, &oldattr); 41 | } 42 | } 43 | 44 | static void exit_keyboard(void) 45 | { 46 | set_raw_term(0); 47 | close(tty_fd); 48 | } 49 | 50 | static void init_keyboard(void) 51 | { 52 | if (tty_fd < 0) { 53 | tty_fd = open("/dev/tty", O_NOCTTY | O_RDONLY); 54 | if (tty_fd < 0) { 55 | perror("error at open TTY"); 56 | exit(1); 57 | } 58 | atexit(exit_keyboard); 59 | } 60 | set_raw_term(1); 61 | } 62 | 63 | static int get_scancode(int i) 64 | { 65 | if (i >= 'a' && i <= 'z') 66 | i = i - 'a' + 'A'; 67 | 68 | switch (i) { 69 | case 'C': return 0x2E00; 70 | default: return i & 0xFF00; 71 | } 72 | } 73 | 74 | 75 | static int add_scancode(int i) 76 | { 77 | // Exclude ESC, ENTER, and TAB. 78 | if (i < 0x20 && i != 0x1B && i != 0x0D && i != 0x09) { 79 | // CTRL+KEY 80 | mod_state |= MOD_CTRL; 81 | int orig = i; 82 | if (i == 0x1C) i = '\\'; 83 | else if (i == 0x1D) i = ']'; 84 | else if (i == 0x1E) i = '6'; 85 | else if (i == 0x1F) i = '-'; 86 | else if (i == 0x08) orig = 0x7F; 87 | else i = i + 0x40; // Why does emu2 have 0x20?? 88 | 89 | return orig | get_scancode(i); 90 | } 91 | return i; 92 | } 93 | 94 | static int read_key(void) 95 | { 96 | char ch = 0xFF; 97 | // Reads first key code 98 | if (read(tty_fd, &ch, 1) == 0) 99 | return -1; // No data 100 | 101 | if (ch == 0x1B) 102 | return ch; 103 | 104 | mod_state = 0; 105 | // Normal key 106 | if ((ch & 0xFF) < 0x80) 107 | return add_scancode(ch); 108 | 109 | return 0; 110 | } 111 | 112 | int main(int argc, char *argv[]) 113 | { 114 | 115 | printf("Press CTRL-C to exit\n"); 116 | 117 | init_keyboard(); 118 | 119 | int k; 120 | 121 | while ((k = read_key()) != CTRL_C) { 122 | if (k == -1) { 123 | sleep(1); 124 | } else { 125 | printf("0x%04X\r\n", k); 126 | } 127 | } 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /src/tools/le16bit.cpp: -------------------------------------------------------------------------------- 1 | // Tool for extracting contents out of dragon.com 2 | // and creating constant tables in C / C++ code. 3 | // 4 | // Yes, xxd can probably handle this with the right 5 | // arguments... 6 | 7 | #include 8 | #include 9 | 10 | void extract_b152(FILE *fp) 11 | { 12 | uint16_t com_start = 0xB152; 13 | 14 | if (fseek(fp, com_start - 0x100, SEEK_SET) != 0) { 15 | printf("Can't seek to offset\n"); 16 | return; 17 | } 18 | 19 | printf("static uint8_t b152[256] = {\n "); 20 | for (int i = 0; i < (256 / 8); i++) 21 | { 22 | for (int j = 0; j < 8; j++) { 23 | uint8_t dval = fgetc(fp); 24 | printf(" "); 25 | printf("0x%02X,", dval); 26 | } 27 | printf(" // 0x%04X-0x%04X\n ", com_start, com_start + 7); 28 | com_start += 8; 29 | } 30 | printf("\r};\n"); 31 | } 32 | 33 | void extract_ba52(FILE *fp) 34 | { 35 | uint16_t com_start = 0xBA52; 36 | 37 | if (fseek(fp, com_start - 0x100, SEEK_SET) != 0) { 38 | printf("Can't seek to offset\n"); 39 | return; 40 | } 41 | 42 | printf("static uint16_t ba52_table[256] = {\n "); 43 | for (int i = 0; i < (256 / 8); i++) 44 | { 45 | for (int j = 0; j < 8; j++) { 46 | uint16_t dval = fgetc(fp); 47 | dval += fgetc(fp) << 8; 48 | printf(" "); 49 | printf("0x%04X,", dval); 50 | } 51 | printf(" // 0x%04X\n ", com_start); 52 | com_start += 0x10; 53 | } 54 | printf("\r};\n"); 55 | } 56 | 57 | // Little endian 16 bit extractor 58 | void extract_b652(FILE *fp) 59 | { 60 | uint16_t com_start = 0xB652; 61 | 62 | if (fseek(fp, 0xB552, SEEK_SET) != 0) { 63 | printf("Can't seek to offset\n"); 64 | return; 65 | } 66 | 67 | printf("static uint16_t or_table_B652[256] = {\n "); 68 | for (int i = 0; i < (256 / 8); i++) 69 | { 70 | for (int j = 0; j < 8; j++) { 71 | uint16_t dval = fgetc(fp); 72 | dval += fgetc(fp) << 8; 73 | printf(" "); 74 | printf("0x%04X,", dval); 75 | } 76 | printf(" // 0x%04X\n ", com_start); 77 | com_start += 0x10; 78 | } 79 | printf("\r};\n"); 80 | } 81 | 82 | 83 | 84 | int main(void) 85 | { 86 | FILE *fp = fopen("DRAGON.COM", "rb"); 87 | 88 | if (fp == NULL) { 89 | printf("Cannot open DRAGON.COM\n"); 90 | return -1; 91 | } 92 | 93 | extract_ba52(fp); 94 | //extract_b152(fp); 95 | //extract_b652(fp); 96 | 97 | fclose(fp); 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/tools/monster_info.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Devin Smith 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // Dragon Wars resource extractor. 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../lib/bufio.h" 22 | #include "engine.h" 23 | 24 | unsigned char gamestate_b[256]; 25 | unsigned short gamestate_s[256]; 26 | 27 | unsigned short script_data[] = { 0x3D6, 0x412, 0x404, 0x48A }; 28 | 29 | unsigned char monster_data[256]; 30 | unsigned char monster_name[20]; 31 | 32 | struct buf_rdr *load_file() 33 | { 34 | FILE *fp = fopen("res31.bin", "rb"); 35 | if (fp == nullptr) { 36 | fprintf(stderr, "Failed to load res31.bin\n"); 37 | return nullptr; 38 | } 39 | 40 | fseek(fp, 0, SEEK_END); 41 | auto len = ftell(fp); 42 | auto *buffer = new unsigned char[len]; 43 | fseek(fp, 0, SEEK_SET); 44 | auto bytes_read = fread(buffer, 1, len, fp); 45 | if (bytes_read != len) { 46 | fprintf(stderr, "Failed to read res31.bin.\n"); 47 | delete[] buffer; 48 | fclose(fp); 49 | return nullptr; 50 | } 51 | 52 | auto *buf_rdr = buf_rdr_init(buffer, len); 53 | 54 | fclose(fp); 55 | 56 | return buf_rdr; 57 | } 58 | 59 | void sub_6B5(uint8_t input) 60 | { 61 | uint8_t word_3AE4 = input; 62 | uint16_t ax = 0x4C6; 63 | 64 | word_3AE4 = word_3AE4 >> 1; 65 | ax += word_3AE4; 66 | 67 | uint16_t offset = script_data[ax]; 68 | 69 | gamestate_s[0x41] = offset; 70 | 71 | offset += 10; // 0x0A 72 | 73 | // Check script data at offset. 74 | // test it ( 75 | 76 | 77 | 78 | 79 | 80 | } 81 | 82 | struct len_string { 83 | int len; 84 | char string[255]; 85 | }; 86 | len_string str; 87 | 88 | static void pop_len_string(unsigned char ch) 89 | { 90 | str.string[str.len] = ch & 0x7f; 91 | str.len++; 92 | } 93 | 94 | static int extract_test(const unsigned char *args) 95 | { 96 | str.len = 0; 97 | str.string[0] = '\0'; 98 | 99 | uint16_t count = extract_string(args, 0, pop_len_string); 100 | printf("Count: %d\n", count); 101 | 102 | printf("$(\""); 103 | for (int i = 0; i < str.len; i++) { 104 | char ch = str.string[i]; 105 | if (ch == '\r') { 106 | printf("\\r"); 107 | } else { 108 | printf("%c", ch); 109 | } 110 | } 111 | printf("\")\n"); 112 | 113 | return count; 114 | } 115 | 116 | 117 | int main(int argc, char *argv[]) 118 | { 119 | auto *rdr = load_file(); 120 | if (rdr == nullptr) { 121 | return -1; 122 | } 123 | 124 | // 0x4F6 125 | rdr->offset = 2; 126 | uint16_t initial_offset = buf_get16le(rdr); 127 | gamestate_s[0x58] = initial_offset; 128 | 129 | printf("Multiplier offset: 0x%04X\n", initial_offset); 130 | 131 | // Now in byte mode. 132 | rdr->offset = initial_offset; 133 | uint8_t multiplier = buf_get8(rdr); 134 | printf("Multiplier: 0x%02X\n", multiplier); 135 | 136 | // A 1x Multiplier will not raise high ticks greater than 16 bit 137 | // Meaning that any 16 bit number multiplied by 1 is still a 16 bit number 138 | // op_4d will then return 0. 139 | uint16_t mul_result = 0; 140 | 141 | // Offset here could be determined. 142 | rdr->offset = initial_offset + (mul_result) + 1; // 0x50B 143 | 144 | uint16_t next_offset = buf_get16le(rdr); 145 | printf("Next offset: 0x%04X\n", next_offset); 146 | 147 | rdr->offset = next_offset; 148 | uint8_t unknown1 = buf_get8(rdr); 149 | printf("Unknown1: 0x%02X\n", unknown1); 150 | 151 | // 0x516 152 | // New multipler? 153 | gamestate_b[0x28] = (unknown1 >> 6) + 1; // 0x516 154 | gamestate_b[0x5c] = (unknown1 & 0x3F); // 0x521 155 | 156 | printf("\nBytes:\n"); 157 | for (int i = 1; i < 0xD; i++) { 158 | uint8_t unknown2 = buf_get8(rdr); 159 | if (i == 1) { 160 | printf(" 0x%02X <-- New multiplier?\n", unknown2); 161 | } else { 162 | printf(" 0x%02X\n", unknown2); 163 | } 164 | gamestate_b[i + 0x28] = unknown2; 165 | } 166 | 167 | // 0x52E and 0x530 168 | gamestate_b[0x47] = 0; 169 | gamestate_b[0x48] = 0; 170 | 171 | gamestate_s[0x58] = 0; 172 | 173 | rdr->offset = 0; 174 | uint16_t first_offset = buf_get16le(rdr); 175 | gamestate_s[0x58] = first_offset; 176 | 177 | printf("First offset: 0x%04X\n", first_offset); 178 | 179 | // Byte mode, 0x53A 180 | uint8_t byte_3AE4 = gamestate_b[0x48]; 181 | uint8_t byte_3AE2 = gamestate_b[0x29 + byte_3AE4]; 182 | byte_3AE2 &= 0x1f; 183 | byte_3AE2--; 184 | printf("Byte 3AE2: 0x%02X\n", byte_3AE2); 185 | 186 | if (byte_3AE2 >= 0x80) { 187 | // determine new multiplier ? 188 | rdr->offset = first_offset; 189 | multiplier = buf_get8(rdr); 190 | 191 | printf("Multiplier: 0x%02X\n", multiplier); 192 | 193 | mul_result = 0x15; // Hardcoding for now, but it's the result of multiplier times game ticks 194 | // See op_4D for more information. 195 | byte_3AE2 = mul_result; 196 | } 197 | 198 | // 0x54A 199 | byte_3AE4 = byte_3AE2; 200 | byte_3AE4 = byte_3AE4 << 1; 201 | byte_3AE4++; 202 | 203 | printf("054C: word_3AE4 = 0x%02X\n", byte_3AE4); 204 | 205 | rdr->offset = first_offset + byte_3AE4; 206 | uint16_t monster_offset = buf_get16le(rdr); 207 | 208 | printf("monster_offset = 0x%04X\n", monster_offset); 209 | 210 | rdr->offset = monster_offset; 211 | 212 | sub_6B5(gamestate_b[0x47]); 213 | 214 | byte_3AE4 = 0; 215 | 216 | for (int i = 0; i < 21; i++) { 217 | uint8_t unknown2 = buf_get8(rdr); 218 | if (i == 0xB) { 219 | printf("0x%02X - 0x%02X (Resource index of monster)\n", i, unknown2 + 0x8A); 220 | } else { 221 | printf("0x%02X - 0x%02X\n", i, unknown2); 222 | } 223 | monster_data[i] = unknown2; 224 | } 225 | 226 | //rdr->offset += 8; 227 | rdr->offset = monster_offset + 0x21; 228 | 229 | printf("Name bytes:\n"); 230 | for (int i = 0; i < 18; i++) { 231 | uint8_t unknown2 = buf_get8(rdr); 232 | printf(" 0x%02X\n", unknown2); 233 | monster_name[i] = unknown2; 234 | } 235 | 236 | extract_test(monster_name); 237 | 238 | 239 | delete[] rdr->data; 240 | buf_rdr_free(rdr); 241 | return 0; 242 | } 243 | -------------------------------------------------------------------------------- /src/tools/mouse.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // A simple program to extract mouse cursors from dragon.com 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | struct mouse_cursor { 25 | const char *name; 26 | uint16_t offset; 27 | int width; 28 | int height; 29 | unsigned char *data; 30 | }; 31 | 32 | static struct mouse_cursor cursors[6] = { 33 | { "Pointer with finger", 0x647C }, 34 | { "Hand Stop/Wait cursor", 0x64EE }, 35 | { "Forward",0x6654, }, 36 | { "Turn around", 0x65D2, }, 37 | { "Turn right", 0x66C6, }, 38 | { "Turn left", 0x6550 } 39 | }; 40 | 41 | static constexpr size_t num_cursors = sizeof(cursors) / sizeof(cursors[0]); 42 | 43 | static const int fb_width = 320; 44 | static const int fb_height = 200; 45 | static const int BYTES_PER_PIXEL = 3; // RGB 46 | static const size_t fb_size = fb_width * fb_height * BYTES_PER_PIXEL; // RGB 47 | static unsigned char *fb_mem; 48 | 49 | struct palette { 50 | unsigned char r; 51 | unsigned char g; 52 | unsigned char b; 53 | unsigned char a; 54 | }; 55 | 56 | static const struct palette dos_palette[] = { 57 | { 0x00, 0x00, 0x00, 0xFF }, /* BLACK */ 58 | { 0x00, 0x00, 0xAA, 0xFF }, /* BLUE */ 59 | { 0x00, 0xAA, 0x00, 0xFF }, /* GREEN */ 60 | { 0x00, 0xAA, 0xAA, 0xFF }, /* CYAN */ 61 | { 0xAA, 0x00, 0x00, 0xFF }, /* RED */ 62 | { 0xAA, 0x00, 0xAA, 0xFF }, /* MAGENTA */ 63 | { 0xAA, 0x55, 0x00, 0xFF }, /* BROWN */ 64 | { 0xAA, 0xAA, 0xAA, 0xFF }, /* LIGHT GRAY */ 65 | { 0x55, 0x55, 0x55, 0xFF }, /* DARK GRAY */ 66 | { 0x55, 0x55, 0xFF, 0xFF }, /* LIGHT BLUE */ 67 | { 0x55, 0xFF, 0x55, 0xFF }, /* LIGHT GREEN */ 68 | { 0x55, 0xFF, 0xFF, 0xFF }, /* LIGHT CYAN */ 69 | { 0xFF, 0x55, 0x55, 0xFF }, /* LIGHT RED */ 70 | { 0xFF, 0x55, 0xFF, 0xFF }, /* LIGHT MAGENTA */ 71 | { 0xFF, 0xFF, 0x55, 0xFF }, /* YELLOW */ 72 | { 0xFF, 0xFF, 0xFF, 0xFF } /* WHITE */ 73 | }; 74 | 75 | static void init_buffers() 76 | { 77 | fb_mem = new unsigned char[fb_size]; 78 | memset(fb_mem, 0, fb_size); 79 | } 80 | 81 | // 0x2463 82 | static void plot_pixel(uint8_t line_num, int color, int x_off) 83 | { 84 | size_t fb_off = get_line_offset(line_num); 85 | fb_off *= BYTES_PER_PIXEL; 86 | fb_off += (x_off * BYTES_PER_PIXEL); 87 | 88 | fb_mem[fb_off++] = dos_palette[color].r; 89 | fb_mem[fb_off++] = dos_palette[color].g; 90 | fb_mem[fb_off++] = dos_palette[color].b; 91 | } 92 | 93 | void sub_23A1(const struct mouse_cursor *cursor, int x_pos, int y_pos) 94 | { 95 | int dx; 96 | uint8_t line_pos; 97 | const uint8_t *ds = cursor->data; 98 | 99 | line_pos = (uint8_t)y_pos; 100 | 101 | // 241E 102 | for (int i = 0; i < cursor->height; i++) { 103 | dx = x_pos; 104 | // 0x2426 105 | for (int j = 0; j < cursor->width; j++) { 106 | uint8_t pixel_byte = *ds++; 107 | if (dx >= fb_width) { 108 | // 245E 109 | ds += (cursor->width - j); 110 | break; 111 | } 112 | 113 | // Extract nibble (4 bits) out of byte 114 | // Most significant nibble 115 | uint8_t msn = pixel_byte; 116 | msn = msn >> 4; 117 | msn = msn & 0xF; 118 | if (msn != 6) { // Don't plot brown pixels? 119 | plot_pixel(line_pos, msn, dx); 120 | } 121 | 122 | dx++; 123 | if (dx >= fb_width) { 124 | // 245E 125 | ds += (cursor->width - j); 126 | break; 127 | } 128 | 129 | // Least significant nibble 130 | pixel_byte = pixel_byte & 0x0F; 131 | if (pixel_byte != 6) { 132 | plot_pixel(line_pos, pixel_byte, dx); 133 | } 134 | dx++; 135 | } 136 | line_pos++; 137 | } 138 | } 139 | 140 | static bool load_cursors() 141 | { 142 | FILE *dragonfp = fopen("dragon.com", "rb"); 143 | if (dragonfp == nullptr) { 144 | perror("Failed to load dragon.com"); 145 | return false; 146 | } 147 | 148 | for (size_t i = 0; i < num_cursors; i++) { 149 | long off = cursors[i].offset - 0x100; // COM START 150 | 151 | fseek(dragonfp, off, SEEK_SET); 152 | 153 | cursors[i].width = fgetc(dragonfp); 154 | cursors[i].height = fgetc(dragonfp); 155 | 156 | size_t data_sz = cursors[i].width * cursors[i].height; 157 | cursors[i].data = new unsigned char[data_sz]; 158 | 159 | if (fread(cursors[i].data, 1, data_sz, dragonfp) != data_sz) { 160 | fprintf(stderr, "Failed to read %zu bytes from dragon.com file.\n", data_sz); 161 | delete [] cursors[i].data; 162 | return false; 163 | } 164 | } 165 | 166 | fclose(dragonfp); 167 | 168 | return true; 169 | } 170 | 171 | int main() 172 | { 173 | if (!load_cursors()) 174 | return -1; 175 | 176 | init_buffers(); 177 | 178 | int x_pos = 10; 179 | 180 | for (size_t i = 0; i < num_cursors; i++) { 181 | printf("Drawing mouse cursor %zu\n", i); 182 | 183 | sub_23A1(&cursors[i], x_pos, 10); 184 | 185 | x_pos += 20; 186 | } 187 | 188 | // Dump to PPM 189 | FILE *imageFile = fopen("mouse.ppm","wb"); 190 | 191 | if (imageFile == nullptr){ 192 | perror("ERROR: Cannot open output file"); 193 | exit(EXIT_FAILURE); 194 | } 195 | 196 | fprintf(imageFile,"P6\n"); // P6 filetype 197 | fprintf(imageFile,"%d %d\n", fb_width, fb_height); // dimensions 198 | fprintf(imageFile,"255\n"); // Max pixel 199 | 200 | fwrite(fb_mem, 1, fb_size, imageFile); 201 | fclose(imageFile); 202 | 203 | delete[] fb_mem; 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /src/tools/ppm.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | static const size_t fb_width = 320; 24 | static const size_t fb_height = 200; 25 | static const int BYTES_PER_PIXEL = 3; // RGB 26 | static const size_t fb_size = fb_width * fb_height * BYTES_PER_PIXEL; // RGB 27 | static unsigned char *fb_mem; 28 | 29 | struct palette { 30 | unsigned char r; 31 | unsigned char g; 32 | unsigned char b; 33 | unsigned char a; 34 | }; 35 | 36 | static const struct palette dos_palette[] = { 37 | { 0x00, 0x00, 0x00, 0xFF }, /* BLACK */ 38 | { 0x00, 0x00, 0xAA, 0xFF }, /* BLUE */ 39 | { 0x00, 0xAA, 0x00, 0xFF }, /* GREEN */ 40 | { 0x00, 0xAA, 0xAA, 0xFF }, /* CYAN */ 41 | { 0xAA, 0x00, 0x00, 0xFF }, /* RED */ 42 | { 0xAA, 0x00, 0xAA, 0xFF }, /* MAGENTA */ 43 | { 0xAA, 0x55, 0x00, 0xFF }, /* BROWN */ 44 | { 0xAA, 0xAA, 0xAA, 0xFF }, /* LIGHT GRAY */ 45 | { 0x55, 0x55, 0x55, 0xFF }, /* DARK GRAY */ 46 | { 0x55, 0x55, 0xFF, 0xFF }, /* LIGHT BLUE */ 47 | { 0x55, 0xFF, 0x55, 0xFF }, /* LIGHT GREEN */ 48 | { 0x55, 0xFF, 0xFF, 0xFF }, /* LIGHT CYAN */ 49 | { 0xFF, 0x55, 0x55, 0xFF }, /* LIGHT RED */ 50 | { 0xFF, 0x55, 0xFF, 0xFF }, /* LIGHT MAGENTA */ 51 | { 0xFF, 0xFF, 0x55, 0xFF }, /* YELLOW */ 52 | { 0xFF, 0xFF, 0xFF, 0xFF } /* WHITE */ 53 | }; 54 | 55 | static void write_data() 56 | { 57 | int line_num = 8; 58 | int rows = 0x88; 59 | int cols = 0x50; 60 | 61 | const unsigned char *src = ui_get_viewport_mem(); 62 | 63 | for (int y = 0; y < rows; y++) { 64 | size_t fb_off = (line_num * (fb_width * BYTES_PER_PIXEL)) + 65 | (16 * BYTES_PER_PIXEL); // indentation 66 | for (int x = 0; x < cols; x++) { 67 | uint8_t al = *src++; 68 | 69 | int hi, lo; 70 | 71 | /* Each nibble represents a color */ 72 | /* for example 0x82 represents color 8 then color 2. */ 73 | hi = (al >> 4) & 0xf; 74 | lo = al & 0xf; 75 | 76 | fb_mem[fb_off++] = dos_palette[hi].r; 77 | fb_mem[fb_off++] = dos_palette[hi].g; 78 | fb_mem[fb_off++] = dos_palette[hi].b; 79 | fb_mem[fb_off++] = dos_palette[lo].r; 80 | fb_mem[fb_off++] = dos_palette[lo].g; 81 | fb_mem[fb_off++] = dos_palette[lo].b; 82 | } 83 | line_num++; 84 | } 85 | } 86 | 87 | static void init_buffers() 88 | { 89 | fb_mem = new unsigned char[fb_size]; 90 | memset(fb_mem, 0, fb_size); 91 | } 92 | 93 | static unsigned char* read_file(const char *fname) 94 | { 95 | FILE *fp = fopen(fname, "rb"); 96 | if (fp == nullptr) { 97 | return nullptr; 98 | } 99 | 100 | fseek(fp, 0, SEEK_END); 101 | long fsize = ftell(fp); 102 | if (fsize == -1) { 103 | fclose(fp); 104 | return nullptr; 105 | } 106 | rewind(fp); 107 | 108 | auto *bytes = new unsigned char[fsize]; 109 | auto br = fread(bytes, 1, fsize, fp); 110 | if (br != (unsigned long)fsize) { 111 | fclose(fp); 112 | delete[] bytes; 113 | return nullptr; 114 | } 115 | 116 | return bytes; 117 | } 118 | 119 | 120 | static void process_input(unsigned char *src, int offset, int xpos, int ypos) 121 | { 122 | unsigned char *ds = src + offset; 123 | 124 | uint16_t ax; 125 | ax = *ds; 126 | ds++; 127 | ax += (*ds) << 8; 128 | printf("%s: BX: 0x%04X AX: 0x%04X\n", __func__, offset, ax); 129 | 130 | if (ax == 0) 131 | return; 132 | 133 | uint16_t word_104F = ax; 134 | // 0xCF8 135 | ds = src + word_104F; 136 | viewport_data vp{}; 137 | vp.data = ds; 138 | vp.xpos = (uint16_t)xpos; 139 | vp.ypos = (uint16_t)ypos; 140 | sub_CF8(vp.data, &vp); 141 | } 142 | 143 | static void process_ground(unsigned char *src) 144 | { 145 | process_input(src, 8, 96, 88); // Top right 146 | process_input(src, 4, 0, 88); // Top left 147 | process_input(src, 6, 48, 88); // Top middle 148 | process_input(src, 14, 112, 104); // Middle right 149 | process_input(src, 10, 0, 104); // Middle left 150 | process_input(src, 12, 32, 104); // Middle middle 151 | process_input(src, 20, 128, 120); // Bottom right 152 | process_input(src, 16, 0, 120); // Bottom left 153 | process_input(src, 18, 16, 120); // Bottom middle 154 | } 155 | 156 | static void usage(int err) 157 | { 158 | if (err) 159 | fprintf(stderr, "Invalid argument\n"); 160 | 161 | fprintf(stderr, "usage ppmwrite (-i image)\n"); 162 | fprintf(stderr, "\t-o offset - offset of image\n"); 163 | fprintf(stderr, "\t-g - plot ground\n"); 164 | 165 | exit(err); 166 | } 167 | 168 | static void args_required(char *arg, const char *argname) 169 | { 170 | fprintf(stderr, "option '%s' expects a parameter (%s)\n", arg, argname); 171 | exit(1); 172 | } 173 | 174 | int main(int argc, char *argv[]) 175 | { 176 | const char *img_file = nullptr; 177 | int offset = 4; 178 | int x = 0; 179 | int y = 0; 180 | bool ground = false; 181 | 182 | while (--argc) { 183 | char *p = *++argv; 184 | 185 | if (!strcmp(p, "-i")) { 186 | if (!argv[1]) { 187 | args_required(p, "image"); 188 | } 189 | img_file = *++argv, --argc; 190 | } else if (!strcmp(p, "-o")) { 191 | if (!argv[1]) { 192 | args_required(p, "offset"); 193 | } 194 | offset = atoi(*++argv), --argc; 195 | } else if (!strcmp(p, "-g")) { 196 | ground = true; 197 | } else if (!strcmp(p, "-x")) { 198 | if (!argv[1]) { 199 | args_required(p, "x"); 200 | } 201 | x = atoi(*++argv), --argc; 202 | } else if (!strcmp(p, "-y")) { 203 | if (!argv[1]) { 204 | args_required(p, "y"); 205 | } 206 | y = atoi(*++argv), --argc; 207 | } 208 | } 209 | 210 | if (img_file == nullptr) { 211 | usage(0); 212 | } 213 | 214 | auto *src_bytes = read_file(img_file); 215 | if (src_bytes == nullptr) { 216 | fprintf(stderr, "Failed to load: %s\n", img_file); 217 | exit(1); 218 | } 219 | 220 | init_buffers(); 221 | init_offsets(0x50); 222 | init_viewport_memory(); 223 | 224 | if (ground) { 225 | process_ground(src_bytes); 226 | } else { 227 | process_input(src_bytes, offset, x, y); 228 | } 229 | 230 | delete[] src_bytes; 231 | 232 | // Dump to PPM 233 | FILE *imageFile = fopen("viewport.ppm","wb"); 234 | 235 | if (imageFile == nullptr){ 236 | perror("ERROR: Cannot open output file"); 237 | exit(EXIT_FAILURE); 238 | } 239 | 240 | fprintf(imageFile,"P6\n"); // P6 filetype 241 | fprintf(imageFile,"%zu %zu\n", fb_width, fb_height); // dimensions 242 | fprintf(imageFile,"255\n"); // Max pixel 243 | 244 | write_data(); 245 | 246 | fwrite(fb_mem, 1, fb_size, imageFile); 247 | fclose(imageFile); 248 | } 249 | -------------------------------------------------------------------------------- /src/tools/resextract.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Dragon Wars resource extractor. 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "../lib/resource.h" 23 | 24 | const char *_pname = "resextract"; 25 | 26 | static void usage(int err) 27 | { 28 | if (err) 29 | fprintf(stderr, "Invalid argument\n"); 30 | 31 | fprintf(stderr, "usage: %s (-i index) " 32 | "[[--help] | [-h]]\n", _pname); 33 | fprintf(stderr, "\t-i (index) - Extract this index.\n"); 34 | fprintf(stderr, "\t-h | --help - Show this message\n"); 35 | fprintf(stderr, "\t-d data_file - Use this data file (instead of data1).\n"); 36 | fprintf(stderr, "\t-o output.bin - Output binary.\n"); 37 | exit(err); 38 | } 39 | 40 | static void args_required(char *arg, const char *argname) 41 | { 42 | fprintf(stderr, "%s: option '%s' expects a parameter (%s)\n", _pname, arg, 43 | argname); 44 | exit(1); 45 | } 46 | 47 | static void dump(const struct resource *r, const char *out_file) 48 | { 49 | FILE *fp = fopen(out_file, "wb"); 50 | if (fp == nullptr) { 51 | perror("Failed to open dump file"); 52 | return; 53 | } 54 | 55 | fwrite(r->bytes, 1, r->len, fp); 56 | 57 | fclose(fp); 58 | } 59 | 60 | int main(int argc, char *argv[]) 61 | { 62 | int index = -1; 63 | const char *data = "data1"; 64 | const char *output = "output.bin"; 65 | 66 | // quick and dirty argument parser 67 | while (--argc) { 68 | char *p = *++argv; 69 | 70 | if (!strcmp(p, "-i")) { 71 | if (!argv[1]) { 72 | args_required(p, "index"); 73 | } 74 | index = atoi(*++argv), --argc; 75 | } else if (!strcmp(p, "-d")) { 76 | if (!argv[1]) { 77 | args_required(p, "data"); 78 | } 79 | data = *++argv, --argc; 80 | } else if (!strcmp(p, "-o")) { 81 | if (!argv[1]) { 82 | args_required(p, "output"); 83 | } 84 | output = *++argv, --argc; 85 | } else if (!strcmp(p, "--help")) { 86 | usage(0); 87 | } else if (!strcmp(p, "-h")) { 88 | usage(0); 89 | } else { 90 | usage(1); 91 | } 92 | } 93 | 94 | if (index == -1) { 95 | usage(1); 96 | } 97 | 98 | if (rm_init() == 0) { 99 | const struct resource *res = resource_load((resource_section)index); 100 | 101 | if (res != nullptr) { 102 | dump(res, output); 103 | } 104 | } 105 | rm_exit(); 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /src/tools/script_ops.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | FILE *fp = fopen("DRAGON.COM", "rb"); 7 | if (fp == NULL) { 8 | printf("Cannot open DRAGON.COM\n"); 9 | return -1; 10 | } 11 | 12 | if (fseek(fp, 0x3860, SEEK_SET) != 0) { 13 | printf("Can't seek to offset\n"); 14 | goto done; 15 | } 16 | 17 | for (int i = 0; i <= 0xFF; i++) 18 | { 19 | printf("static void op_%02X();\n", i); 20 | } 21 | 22 | for (int i = 0; i <= 0xFF; i++) 23 | { 24 | 25 | uint16_t addr = fgetc(fp); 26 | addr += fgetc(fp) << 8; 27 | 28 | printf("{ op_%02X, \"0x%04X\" },\n", i, addr); 29 | } 30 | 31 | done: 32 | fclose(fp); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /src/tools/strextract.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Devin Smith 3 | // 4 | // Permission to use, copy, modify, and distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | // Dragon Wars script disassembler. 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | struct len_string { 24 | int len; 25 | char string[255]; 26 | }; 27 | len_string str; 28 | 29 | static void pop_len_string(unsigned char ch) 30 | { 31 | str.string[str.len] = ch & 0x7f; 32 | str.len++; 33 | } 34 | 35 | static int extract_test(const unsigned char *args) 36 | { 37 | str.len = 0; 38 | str.string[0] = '\0'; 39 | 40 | uint16_t count = extract_string(args, 0, pop_len_string); 41 | printf("Count: %d\n", count); 42 | 43 | printf("$(\""); 44 | for (int i = 0; i < str.len; i++) { 45 | char ch = str.string[i]; 46 | if (ch == '\r') { 47 | printf("\\r"); 48 | } else { 49 | printf("%c", ch); 50 | } 51 | } 52 | printf("\")\n"); 53 | 54 | return count; 55 | } 56 | 57 | int main(int argc, char *argv[]) 58 | { 59 | unsigned char data1[] = { 0xfb, 0x9f, 0xab, 0xed, 0x06, 0x6f, 0x09, 0xbe, 0x12, 0xa6, 0x00 }; 60 | unsigned char data2[] = { 0xfb, 0x9f, 0xab, 0xed, 0x06, 0x6f, 0x09, 0x1e, 0xe9, 0xa9, 0xd4, 0x30, 0x00 }; 61 | unsigned char data3[] = { 0xf4, 0x22, 0x69, 0x48, 0x3f, 0x73, 0xf5, 0x7d, 0xa0, 0x00 }; 62 | unsigned char data4[] = { 0xfb, 0x9f, 0xab, 0xed, 0x06, 0x6f, 0x0a, 0x1e, 0x11, 0x88, 0x8b, 0x00 }; 63 | 64 | unsigned char world0[] = { 0xf4, 0x29, 0x14, 0x0a, 0x6f, 0x8d, 0xc0 }; 65 | 66 | unsigned char data5[] = { 0xf1, 0x04, 0xe7, 0x28, 0x62, 0x67, 0xf5, 0x90, 0x00 }; 67 | 68 | unsigned char data6[] = { 0xf4, 0x5E, 0x45, 0x87, 0xd2, 0x82, 0x8a, 0x68, 0xff }; 69 | unsigned char data7[] = { 0x54, 0x82, 0x00}; 70 | 71 | extract_test(data1); 72 | extract_test(data2); 73 | extract_test(data3); 74 | extract_test(data4); 75 | extract_test(world0); 76 | extract_test(data5); 77 | extract_test(data6); 78 | extract_test(data7); 79 | return 0; 80 | } 81 | --------------------------------------------------------------------------------