squashed rp2040-doom changes
101
.github/CONTRIBUTING.md
vendored
|
|
@ -1,101 +0,0 @@
|
|||
Thanks for contributing to Chocolate Doom! Whatever your contribution,
|
||||
whether it's code or just a bug report, it's greatly appreciated.
|
||||
|
||||
The project is governed by the
|
||||
[Contributor Covenant](http://contributor-covenant.org/version/1/4/)
|
||||
version 1.4. By contributing to the project you agree to abide by its
|
||||
terms. To report violations, please send an email to fraggle@gmail.com.
|
||||
|
||||
### Reporting bugs
|
||||
|
||||
Before reporting a bug, it's worth checking if this really is a bug.
|
||||
Chocolate Doom's mission is to reproduce the Vanilla (DOS) versions of
|
||||
the Doom engine games, bugs and all. Check out the
|
||||
[NOT-BUGS](../NOT-BUGS.md) file for a list of common issues which aren't
|
||||
really bugs at all. You might also try searching [the GitHub issues
|
||||
list](https://github.com/chocolate-doom/chocolate-doom/issues) to see
|
||||
if your bug has already been reported.
|
||||
|
||||
If you're confident that you've found a real bug (or even if you're
|
||||
not sure!) please go ahead and [file an issue on
|
||||
GitHub](https://github.com/chocolate-doom/chocolate-doom/issues/new).
|
||||
You'll need a GitHub account, but it's pretty easy to sign up.
|
||||
|
||||
Please try to give as much information as possible:
|
||||
|
||||
* What version of Chocolate Doom are you using? Check the title bar of
|
||||
the window for the version number.
|
||||
|
||||
* Chocolate Doom runs on many different operating systems (not just
|
||||
Windows!). Please say which operating system and what version of it
|
||||
you're using.
|
||||
|
||||
* Please say which game you're playing (Doom 1, Doom 2, Heretic,
|
||||
Hexen, Strife, etc.) and if you're using any fan-made WADs or mods,
|
||||
please say which mods (and where they can be downloaded!). It helps
|
||||
to give the full command line you're using to start the game.
|
||||
|
||||
* Please mention if you have any special configuration you think may be
|
||||
relevant, too.
|
||||
|
||||
### Feature requests
|
||||
|
||||
Chocolate Doom is always open to new feature requests; however, please
|
||||
be aware that the project is designed around a deliberately limited
|
||||
[philosophy](../PHILOSOPHY.md), and many features common in other source
|
||||
ports will not be accepted. Here are a few common requests which are
|
||||
often rejected:
|
||||
|
||||
* "High resolution" rendering (greater than 320x200 display).
|
||||
|
||||
* An option to disable Vanilla limits, such as the visplane rendering
|
||||
limit.
|
||||
|
||||
* Ability to play "No Rest For The Living", the expansion pack which
|
||||
comes with the XBLA / BFG Edition of Doom.
|
||||
|
||||
If you're not sure whether your feature is in line with the project
|
||||
philosophy, don't worry - just ask anyway!
|
||||
To make a feature request, [file an issue on
|
||||
GitHub](https://github.com/chocolate-doom/chocolate-doom/issues/new).
|
||||
|
||||
### Bug fixes / code submission
|
||||
|
||||
Thank you for contributing code to Chocolate Doom! Please check the
|
||||
following guidelines before opening a pull request:
|
||||
|
||||
* All code must be licensed under [the GNU General Public License,
|
||||
version 2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
|
||||
Please don't reuse code that isn't GPL, or that is GPLv3 licensed.
|
||||
Be aware that by submitting your code to the project, you're agreeing
|
||||
to license it under the GPL.
|
||||
|
||||
* Please follow the coding style guidelines described in the
|
||||
[HACKING](../HACKING.md) file.
|
||||
|
||||
* Please don't make unnecessary changes which just change formatting
|
||||
without any actual change to program logic. While being consistent
|
||||
is nice, such changes destroy the ability to use the `git blame`
|
||||
command to see when code was last changed.
|
||||
|
||||
* The guidelines given above in the "feature requests" section also
|
||||
apply here. New features which aren't in line with the project
|
||||
philosophy are likely to be rejected. If you're not sure, open a
|
||||
feature request first and ask before you start implementing your
|
||||
feature.
|
||||
|
||||
* Follow the guidelines for [how to write a Git commit
|
||||
message](http://chris.beams.io/posts/git-commit/). In short: the
|
||||
first line should be a short summary; keep to an 80 column limit;
|
||||
use the imperative mood ("fix bug X", rather than "fixed bug X" or
|
||||
"fixing bug X"). If your change fixes a particular subsystem,
|
||||
prefix the summary with that subsystem: eg. "doom: Fix bug X" or
|
||||
"textscreen: Change size of X".
|
||||
|
||||
* If you're making a change related to a bug, reference the GitHub
|
||||
issue number in the commit message, eg. "This is a partial fix
|
||||
for #646". This will link your commit into the issue comments. If
|
||||
your change is a fix for the bug, put the word "fixes" before the
|
||||
issue number to automatically close the issue once your change
|
||||
is merged.
|
||||
|
||||
28
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,28 +0,0 @@
|
|||
<!--
|
||||
Thank you for reporting a bug in Chocolate Doom. Please complete
|
||||
the following template so that we can better diagnose the source
|
||||
of your problem.
|
||||
|
||||
To save yourself some time, you may want to check the FAQ and the
|
||||
NOT-BUGS list for solutions to some common problems:
|
||||
|
||||
https://www.chocolate-doom.org/wiki/index.php/FAQ
|
||||
https://www.chocolate-doom.org/not-bugs
|
||||
-->
|
||||
|
||||
### Background
|
||||
|
||||
Version of Chocolate Doom:
|
||||
|
||||
Operating System and version:
|
||||
|
||||
Game: (Doom/Heretic/Hexen/Strife/other)
|
||||
|
||||
Any loaded WADs and mods (please include full command line):
|
||||
|
||||
### Bug description
|
||||
|
||||
Observed behavior:
|
||||
|
||||
Expected behavior:
|
||||
|
||||
7
.gitignore
vendored
|
|
@ -7,7 +7,7 @@ aclocal.m4
|
|||
autom4te.cache
|
||||
autotools
|
||||
bin
|
||||
config.h
|
||||
/config.h
|
||||
config.hin
|
||||
config.log
|
||||
config.status
|
||||
|
|
@ -52,3 +52,8 @@ GPATH
|
|||
GRTAGS
|
||||
GTAGS
|
||||
/HTML/
|
||||
*.midx
|
||||
*.midx.z
|
||||
*.wad
|
||||
*.whd
|
||||
/cmake-*/
|
||||
|
|
|
|||
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "3rdparty/tinyusb"]
|
||||
path = 3rdparty/tinyusb
|
||||
url = git@github.com:liamfraser/tinyusb.git
|
||||
1
3rdparty/tinyusb
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 900f9372c35be9bdb5374af66b0fe3d9d32aae2b
|
||||
|
|
@ -1,7 +1,30 @@
|
|||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
cmake_minimum_required(VERSION 3.7.2)
|
||||
project("Chocolate Doom" VERSION 3.0.0 LANGUAGES C)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# We use PICO_SDK_PATH being set as an indicator that we're doing a Pico BUILD
|
||||
if (PICO_SDK_PATH)
|
||||
include(pico_sdk_import.cmake)
|
||||
include(pico_extras_import.cmake)
|
||||
endif()
|
||||
|
||||
project("Chocolate Doom" VERSION 3.0.0 LANGUAGES C CXX)
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
if (PICO_SDK_PATH)
|
||||
# we are using git@github.com:liamfraser/tinyusb.git as it has some RP2040 fixes that aren't upstreamed yet
|
||||
set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_LIST_DIR}/3rdparty/tinyusb)
|
||||
# this only affects device builds device, but we want to use zone for malloc in this case so we don't have two separate elastic spaces and can fit more in
|
||||
set(SKIP_PICO_MALLOC 1)
|
||||
pico_sdk_init()
|
||||
if (PICO_ON_DEVICE AND NOT CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||
message(WARNING "You should do a MinSizeRel build when targeting the RP2040
|
||||
(with -DCMAKE_BUILD_TYPE=MinSizeRel)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
# Autotools variables
|
||||
set(top_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
|
@ -24,26 +47,30 @@ if(MSVC)
|
|||
add_definitions("/D_CRT_SECURE_NO_WARNINGS" "/D_CRT_SECURE_NO_DEPRECATE"
|
||||
"/D_CRT_NONSTDC_NO_DEPRECATE")
|
||||
else()
|
||||
add_compile_options("-Wall" "-Wdeclaration-after-statement"
|
||||
"-Wredundant-decls")
|
||||
#add_compile_options("-Wall" "-Wdeclaration-after-statement" "-Wredundant-decls")
|
||||
add_compile_options("-Wall" "-Wredundant-decls")
|
||||
endif()
|
||||
|
||||
find_package(SDL2 2.0.1)
|
||||
find_package(SDL2_mixer 2.0.0)
|
||||
find_package(SDL2_net 2.0.0)
|
||||
# Note PICO_SDK path is set by the SDK initialization if it occurs above
|
||||
if (NOT PICO_SDK)
|
||||
find_package(SDL2 2.0.1 REQUIRED)
|
||||
find_package(SDL2_mixer 2.0.0 REQUIRED)
|
||||
find_package(SDL2_net 2.0.0 REQUIRED)
|
||||
|
||||
# Check for libsamplerate.
|
||||
find_package(samplerate)
|
||||
if(SAMPLERATE_FOUND)
|
||||
set(HAVE_LIBSAMPLERATE TRUE)
|
||||
endif()
|
||||
|
||||
# Check for libpng.
|
||||
find_package(PNG)
|
||||
if(PNG_FOUND)
|
||||
set(HAVE_LIBPNG TRUE)
|
||||
# Check for libsamplerate.
|
||||
find_package(samplerate)
|
||||
if(SAMPLERATE_FOUND)
|
||||
set(HAVE_LIBSAMPLERATE TRUE)
|
||||
endif()
|
||||
|
||||
# Check for libpng.
|
||||
find_package(PNG)
|
||||
if(PNG_FOUND)
|
||||
set(HAVE_LIBPNG TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(HAVE_MMAP 1)
|
||||
find_package(m)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
|
|
|
|||
104
README-chocolate.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# Chocolate Doom
|
||||
|
||||
Chocolate Doom aims to accurately reproduce the original DOS version of
|
||||
Doom and other games based on the Doom engine in a form that can be
|
||||
run on modern computers.
|
||||
|
||||
Originally, Chocolate Doom was only a Doom source port. The project
|
||||
now includes ports of Heretic and Hexen, and Strife.
|
||||
|
||||
Chocolate Doom’s aims are:
|
||||
|
||||
* To always be 100% Free and Open Source software.
|
||||
* Portability to as many different operating systems as possible.
|
||||
* Accurate reproduction of the original DOS versions of the games,
|
||||
including bugs.
|
||||
* Compatibility with the DOS demo, configuration and savegame files.
|
||||
* To provide an accurate retro “feel” (display and input should
|
||||
behave the same).
|
||||
|
||||
More information about the philosophy and design behind Chocolate Doom
|
||||
can be found in the PHILOSOPHY file distributed with the source code.
|
||||
|
||||
## Setting up gameplay
|
||||
|
||||
For instructions on how to set up Chocolate Doom for play, see the
|
||||
INSTALL file.
|
||||
|
||||
## Configuration File
|
||||
|
||||
Chocolate Doom is compatible with the DOS Doom configuration file
|
||||
(normally named `default.cfg`). Existing configuration files for DOS
|
||||
Doom should therefore simply work out of the box. However, Chocolate
|
||||
Doom also provides some extra settings. These are stored in a
|
||||
separate file named `chocolate-doom.cfg`.
|
||||
|
||||
The configuration can be edited using the chocolate-setup tool.
|
||||
|
||||
## Command line options
|
||||
|
||||
Chocolate Doom supports a number of command line parameters, including
|
||||
some extras that were not originally suported by the DOS versions. For
|
||||
binary distributions, see the CMDLINE file included with your
|
||||
download; more information is also available on the Chocolate Doom
|
||||
website.
|
||||
|
||||
## Playing TCs
|
||||
|
||||
With Vanilla Doom there is no way to include sprites in PWAD files.
|
||||
Chocolate Doom’s ‘-file’ command line option behaves exactly the same
|
||||
as Vanilla Doom, and trying to play TCs by adding the WAD files using
|
||||
‘-file’ will not work.
|
||||
|
||||
Many Total Conversions (TCs) are distributed as a PWAD file which must
|
||||
be merged into the main IWAD. Typically a copy of DEUSF.EXE is
|
||||
included which performs this merge. Chocolate Doom includes a new
|
||||
option, ‘-merge’, which will simulate this merge. Essentially, the
|
||||
WAD directory is merged in memory, removing the need to modify the
|
||||
IWAD on disk.
|
||||
|
||||
To play TCs using Chocolate Doom, run like this:
|
||||
|
||||
```
|
||||
chocolate-doom -merge thetc.wad
|
||||
```
|
||||
|
||||
Here are some examples:
|
||||
|
||||
```
|
||||
chocolate-doom -merge batman.wad -deh batman.deh vbatman.deh (Batman Doom)
|
||||
chocolate-doom -merge aoddoom1.wad -deh aoddoom1.deh (Army of Darkness Doom)
|
||||
```
|
||||
|
||||
## Other information
|
||||
|
||||
* Chocolate Doom includes a number of different options for music
|
||||
playback. See the README.Music file for more details.
|
||||
|
||||
* More information, including information about how to play various
|
||||
classic TCs, is available on the Chocolate Doom website:
|
||||
|
||||
https://www.chocolate-doom.org/
|
||||
|
||||
You are encouraged to sign up and contribute any useful information
|
||||
you may have regarding the port!
|
||||
|
||||
* Chocolate Doom is not perfect. Although it aims to accurately
|
||||
emulate and reproduce the DOS executables, some behavior can be very
|
||||
difficult to reproduce. Because of the nature of the project, you
|
||||
may also encounter Vanilla Doom bugs; these are intentionally
|
||||
present; see the NOT-BUGS file for more information.
|
||||
|
||||
New bug reports can be submitted to the issue tracker on Github:
|
||||
|
||||
https://github.com/chocolate-doom/chocolate-doom/issues
|
||||
|
||||
* Source code patches are welcome, but please follow the style
|
||||
guidelines - see the file named HACKING included with the source
|
||||
distribution.
|
||||
|
||||
* Chocolate Doom is distributed under the GNU GPL. See the COPYING
|
||||
file for more information.
|
||||
|
||||
* Please send any feedback, questions or suggestions to
|
||||
chocolate-doom-dev-list@chocolate-doom.org. Thanks!
|
||||
259
README.md
|
|
@ -1,104 +1,219 @@
|
|||
# Chocolate Doom
|
||||
# RP2040 Doom
|
||||
|
||||
Chocolate Doom aims to accurately reproduce the original DOS version of
|
||||
Doom and other games based on the Doom engine in a form that can be
|
||||
run on modern computers.
|
||||
This is a port of Doom for RP2040 devices, derived from [Chocolate Doom](https://github.com/chocolate-doom/chocolate-doom).
|
||||
|
||||
Originally, Chocolate Doom was only a Doom source port. The project
|
||||
now includes ports of Heretic and Hexen, and Strife.
|
||||
Significant changes have been made to support running on the RP2040 device, but particularly to support running the
|
||||
entire shareware `DOOM1.WAD` which is 4M big on a Raspberry Pi Pico with only 2M flash!
|
||||
|
||||
Chocolate Doom’s aims are:
|
||||
You can read many details on this port in the blog post [here](https://kilograham.github.io/rp2040-doom/).
|
||||
|
||||
* To always be 100% Free and Open Source software.
|
||||
* Portability to as many different operating systems as possible.
|
||||
* Accurate reproduction of the original DOS versions of the games,
|
||||
including bugs.
|
||||
* Compatibility with the DOS demo, configuration and savegame files.
|
||||
* To provide an accurate retro “feel” (display and input should
|
||||
behave the same).
|
||||
Note that a hopefully-fully-functional `chocolate-doom` executable is buildable from this RP2040 code base as a
|
||||
means of
|
||||
verification that everything still works, but whilst they can still be built, Hexen, Strife and Heretic are almost
|
||||
certainly broken, so are not built by default.
|
||||
|
||||
More information about the philosophy and design behind Chocolate Doom
|
||||
can be found in the PHILOSOPHY file distributed with the source code.
|
||||
This chocolate-doom commit that the code is branched off can be found in the `upstream` branch.
|
||||
|
||||
## Setting up gameplay
|
||||
The original Chocolate Doom README is [here](README-chocolate.md).
|
||||
|
||||
For instructions on how to set up Chocolate Doom for play, see the
|
||||
INSTALL file.
|
||||
## Code State
|
||||
|
||||
## Configuration File
|
||||
Thus far, the focus has been entirely on getting RP2040 Doom running. Not a lot of time has been
|
||||
spent
|
||||
cleaning
|
||||
the code up. There are a bunch of defunct `#ifdefs` and other code that was useful at some point,
|
||||
but no longer are, and indeed changing them may result in non-functional code. This is particularly
|
||||
true of
|
||||
the
|
||||
`whd_gen` tool
|
||||
used to
|
||||
convert/compress WADs
|
||||
who's code is
|
||||
likely completely incomprehensible!
|
||||
|
||||
Chocolate Doom is compatible with the DOS Doom configuration file
|
||||
(normally named `default.cfg`). Existing configuration files for DOS
|
||||
Doom should therefore simply work out of the box. However, Chocolate
|
||||
Doom also provides some extra settings. These are stored in a
|
||||
separate file named `chocolate-doom.cfg`.
|
||||
## Artifacts
|
||||
|
||||
The configuration can be edited using the chocolate-setup tool.
|
||||
You can find a RP2040 Doom UF2s based on the standard VGA/I2S pins in the
|
||||
releases of this repository. There are also versions with the shareware DOOM1.WAD already embedded.
|
||||
|
||||
## Command line options
|
||||
Note you can always use `picotool info -a <UF2 file>` to see the pins used by a particular build.
|
||||
|
||||
Chocolate Doom supports a number of command line parameters, including
|
||||
some extras that were not originally suported by the DOS versions. For
|
||||
binary distributions, see the CMDLINE file included with your
|
||||
download; more information is also available on the Chocolate Doom
|
||||
website.
|
||||
## Goals
|
||||
|
||||
## Playing TCs
|
||||
The main goals for this port were:
|
||||
|
||||
With Vanilla Doom there is no way to include sprites in PWAD files.
|
||||
Chocolate Doom’s ‘-file’ command line option behaves exactly the same
|
||||
as Vanilla Doom, and trying to play TCs by adding the WAD files using
|
||||
‘-file’ will not work.
|
||||
1. Everything should match the original game experience, i.e. all the graphics at classic 320x200 resolution, stereo
|
||||
sound,
|
||||
OPL2 music, save/load, demo playback, cheats, network multiplayer... basically it should feel like the original game.
|
||||
2. `DOOM1.WAD` should run on a Raspberry Pi Pico. There was also to be no sneaky discarding of splash screens, altering of levels, down-sampling of
|
||||
textures or whatever. RP2040 boards with 8M should be able to play at least the full *Ultimate Doom* and *DOOM II*
|
||||
WADs.
|
||||
3. The RP2040 should output directly to VGA (16 color pins for RGB565 along with HSync/VSync) along with stereo sound.
|
||||
|
||||
Many Total Conversions (TCs) are distributed as a PWAD file which must
|
||||
be merged into the main IWAD. Typically a copy of DEUSF.EXE is
|
||||
included which performs this merge. Chocolate Doom includes a new
|
||||
option, ‘-merge’, which will simulate this merge. Essentially, the
|
||||
WAD directory is merged in memory, removing the need to modify the
|
||||
IWAD on disk.
|
||||
## Results
|
||||
|
||||
To play TCs using Chocolate Doom, run like this:
|
||||
[](https://youtu.be/eDVazQVycP4)
|
||||
|
||||
```
|
||||
chocolate-doom -merge thetc.wad
|
||||
Features:
|
||||
|
||||
* Full `DOOM1.WAD` playable on Raspberry Pi Pico with 2M flash.
|
||||
* *Ultimate Doom* and *Doom II* are playable on 8M devices.
|
||||
* 320x200x60 VGA output (really 1280x1024x60).
|
||||
* 9 Channel OPL2 Sound at 49716Hz.
|
||||
* 9 Channel Stereo Sound Effects.
|
||||
* I2C networking for up to 4 players.
|
||||
* Save/Load of games.
|
||||
* All cheats supported.
|
||||
* Demos from original WADs run correctly.
|
||||
* USB Keyboard Input support.
|
||||
* All end scenes, intermissions, help screens etc. supported.
|
||||
* Good frame rate; generally 30-35+ FPS.
|
||||
* Uses 270Mhz overclock (requires flash chip that will run at 135Mhz)
|
||||
|
||||
# Building
|
||||
|
||||
RP2040 Doom should build fine on Linux and macOS. The RP2040 targeting builds should also work on Windows, though I
|
||||
haven't tried.
|
||||
|
||||
The build uses `CMake`.
|
||||
|
||||
## Regular chocolate-doom/native builds
|
||||
|
||||
To build everything, assuming you have SDL2 dependencies installed, you can create a build directory:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
```
|
||||
|
||||
Here are some examples:
|
||||
And then run `make` or `make -j<num_cpus>` from that directory. To build a particular target e.g. `chocolate-doom`,
|
||||
do `make chocolate-doom`
|
||||
|
||||
```
|
||||
chocolate-doom -merge batman.wad -deh batman.deh vbatman.deh (Batman Doom)
|
||||
chocolate-doom -merge aoddoom1.wad -deh aoddoom1.deh (Army of Darkness Doom)
|
||||
Note this is the way you build the `whd_gen` tool too.
|
||||
|
||||
## RP2040 Doom builds
|
||||
|
||||
You must have [pico-sdk](https://github.com/raspberrypi/pico-sdk) and
|
||||
**the latest version of** [pico-extras](https://github.com/raspberrypi/pico-extras) installed, along with the regular
|
||||
pico-sdk requisites (e.g.
|
||||
`arm-none-eabi-gcc`). If in doubt, see the Raspberry Pi
|
||||
[documentation](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). I have been building against
|
||||
the `develop` branch of `pico-sdk`, so I recommend that..
|
||||
|
||||
For USB keyboard input support, RP2040 Doom currently uses a modified version of TinyUSB included as a submodule.
|
||||
Make sure you have initialized this submodule via `git submodule update --init`
|
||||
|
||||
You can create a build directly like this:
|
||||
|
||||
```bash
|
||||
mkdir rp2040-build
|
||||
cd rp2040-build
|
||||
cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DPICO_BOARD=vgaboard -DPICO_SDK_PATH=/path/to/pico-sdk -DPICO_EXTRAS_PATH=/path/to/pico-extras ..
|
||||
```
|
||||
|
||||
## Other information
|
||||
Note that the `PICO_BOARD` setting is for the standard VGA demo board which has RGB on pins 0->15, sync pins on 16,17
|
||||
and
|
||||
I2S on 26,27,28.
|
||||
|
||||
* Chocolate Doom includes a number of different options for music
|
||||
playback. See the README.Music file for more details.
|
||||
As before, use `make` or `make <target>` to build.
|
||||
|
||||
* More information, including information about how to play various
|
||||
classic TCs, is available on the Chocolate Doom website:
|
||||
The RP2040 version has four targets, each of which create a similarly named `UF2` file (e.g. `doom_tiny_nh.uf2`).
|
||||
These UF2 files contain the executable code/data, but they do not contain the WAD data which is converted into a
|
||||
RP2040 Domom
|
||||
specific WHD/WHX format by `whd_gen` (for more see below). The WHD/WHX file must also be loaded onto the device at a
|
||||
specific address which varies by binary.
|
||||
|
||||
https://www.chocolate-doom.org/
|
||||
"super-tiny" refers to RP2040 Doom builds that use the more compressed WHX format, and
|
||||
required for`DOOM1.
|
||||
WAD` to
|
||||
run
|
||||
on a 2M Raspberry Pi Pico. "Non super-tiny" refers to RP2040 Doom builds that use the WHD format which is larger, but
|
||||
also is
|
||||
required for *Ultimate Doom* and *Doom II* WADs. These binaries are distinct as supporting both formats in the same
|
||||
binary would just have made things bigger and slower.
|
||||
|
||||
You are encouraged to sign up and contribute any useful information
|
||||
you may have regarding the port!
|
||||
|
||||
* Chocolate Doom is not perfect. Although it aims to accurately
|
||||
emulate and reproduce the DOS executables, some behavior can be very
|
||||
difficult to reproduce. Because of the nature of the project, you
|
||||
may also encounter Vanilla Doom bugs; these are intentionally
|
||||
present; see the NOT-BUGS file for more information.
|
||||
* **doom_tiny_nh** This is a "super tiny" version with no USB keyboard support. You can use
|
||||
[SDL Event Forwarder](https://github.com/kilograham/sdl_event_forwarder) to tunnel keyboard input from your host
|
||||
computer over UART. The WHX file must be loaded at `0x10040000`.
|
||||
* **doom_tiny_nh_cd** This is a "super tiny" version with additional USB keyboard support. Because of the extra USB
|
||||
code, the WHX file must be loaded at `0x10042000`. As you can see USB support via TinyUSB causes the binary to
|
||||
grow by 2K (hence the move of the WHX file address) leaving less space for saved games (which are also stored in
|
||||
flash).
|
||||
* **doom_tiny_nh_nost** This is a "non super tiny" version of `doom_tiny_nh` supporting larger WADs stored as WHD. The WHD
|
||||
file must be loaded at `0x10048000`
|
||||
* **doom_tiny_nh_nost_cd** This is a "non super tiny" version of `doom_tiny_nh_cd` supporting larger WADs stored as
|
||||
WHD. The WHD
|
||||
file must be loaded at `0x10048000`
|
||||
|
||||
New bug reports can be submitted to the issue tracker on Github:
|
||||
You can load you WHD/WHX file using [picotool](https://github.com/raspberrypi/picotool). e.g.
|
||||
|
||||
https://github.com/chocolate-doom/chocolate-doom/issues
|
||||
```bash
|
||||
picotool load -v -t bin doom1.whx -o 0x10042000.
|
||||
```
|
||||
|
||||
* Source code patches are welcome, but please follow the style
|
||||
guidelines - see the file named HACKING included with the source
|
||||
distribution.
|
||||
See `whd_gen` further below for generating `WHX` or `WHD` files.
|
||||
|
||||
* Chocolate Doom is distributed under the GNU GPL. See the COPYING
|
||||
file for more information.
|
||||
#### USB keyboard support
|
||||
|
||||
Note that TinyUSB host mode support for keyboard may not work with all keyboards especially since the RP2040 Doom
|
||||
has been built with small limits for number/sizes of hubs etc. I know that Raspberry Pi keyboards work fine, as
|
||||
did my ancient
|
||||
Dell keyboard. Your keyboard may just do nothing, or may cause a crash. If so, for now, you are stuck forwarding
|
||||
keys from another PC via sdl_event_forwarder.
|
||||
|
||||
### RP2040 Doom builds not targeting an RP2040 device
|
||||
|
||||
You can also build the RP2040 Doom to run on your host computer (Linux or macOS) by using
|
||||
[pico_host_sdl](https://github.com/raspberrypi/pico-host-sdl) which simulates RP2040 based video/audio output using SDL.
|
||||
|
||||
This version currently embeds the WHD/WHX in `src/tiny.whd.h` so you must generate this file.
|
||||
|
||||
You can do this via `./cup.sh <whd/whx_file>`
|
||||
|
||||
```bash
|
||||
mkdir host-build
|
||||
cd host-build
|
||||
cmake -DPICO_PLATFORM=host -DPICO_SDK_PATH=/path/to/pico-sdk -DPICO_EXTRAS_PATH=/path/to/pico-extras -DPICO_SDK_PRE_LIST_DIRS=/path/to/pico_host_sdl ..
|
||||
```
|
||||
|
||||
... and then `make` as usual.
|
||||
|
||||
## whd_gen
|
||||
|
||||
`doom1.whx` is includd in this repository, otherwise you need to build `whd_gen` using the regular native build
|
||||
instructions above.
|
||||
|
||||
To generate a WHX file (you must use this to convert DOOM1.WAD to run on a 2M Raspberry Pi Pico)
|
||||
|
||||
```bash
|
||||
whd_gen <wad_file> <whx_file>
|
||||
```
|
||||
|
||||
The larger WADs (e.g. *Ultimate Doom* or *Doom II* have levels which are too complex to convert into a super tiny
|
||||
WHX file. These larger WADs are not going to fit in a 2M flash anywy, so the less compressed WHD format can be used
|
||||
given that the device now probably has 8M of flash.
|
||||
|
||||
```bash
|
||||
whd_gen <wad_file> <whd_file> -no-super-tiny
|
||||
```
|
||||
|
||||
Note that `whd_gen` has not been tested with a wide variety of WADs, so whilst it is possible that non Id WADs may
|
||||
work, it is by no means guaranteed!
|
||||
|
||||
NOTE: You should use a release build of `whd_gen` for the best sound effect fidelity, as the debug build
|
||||
deliberately lowers the encoding quality for the sake of speed.
|
||||
|
||||
# Future
|
||||
|
||||
*Evilution* and *Plutonia* are not yet supported. There is an issue tracking it
|
||||
[here](https://github.com/kilograham/rp2040-doom/issues/1).
|
||||
|
||||
# RP2040 Doom Licenses
|
||||
|
||||
* Any code derived from chocolate-doom matinains its existing license (generally GPLv2).
|
||||
* New RP2040 Doom specific code not implementing existing chocolate-doom interfaces is licensed BSD-3.
|
||||
* ADPCM-XA is unmodified and is licensed BSD-3.
|
||||
* Modified emu8950 derived code retains its MIT license.
|
||||
|
||||
* Please send any feedback, questions or suggestions to
|
||||
chocolate-doom-dev-list@chocolate-doom.org. Thanks!
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@
|
|||
#cmakedefine HAVE_LIBSAMPLERATE
|
||||
#cmakedefine HAVE_LIBPNG
|
||||
#cmakedefine HAVE_DIRENT_H
|
||||
#cmakedefine HAVE_MMAP
|
||||
#cmakedefine01 HAVE_DECL_STRCASECMP
|
||||
#cmakedefine01 HAVE_DECL_STRNCASECMP
|
||||
|
|
|
|||
1
cup.sh
Executable file
|
|
@ -0,0 +1 @@
|
|||
xxd -i $1 | sed "s/unsigned/const unsigned/g" | sed "s/$1/tiny_whd/g" >src/tiny.whd.h
|
||||
BIN
doom1.whx
Normal file
BIN
m_deathmch.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
m_game.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
m_host.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
m_join.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
m_name.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
m_network.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
m_two.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -1,15 +1,41 @@
|
|||
add_library(opl STATIC
|
||||
opl_internal.h
|
||||
opl.c opl.h
|
||||
opl_linux.c
|
||||
opl_obsd.c
|
||||
opl_queue.c opl_queue.h
|
||||
opl_sdl.c
|
||||
opl_timer.c opl_timer.h
|
||||
opl_win32.c
|
||||
ioperm_sys.c ioperm_sys.h
|
||||
opl3.c opl3.h)
|
||||
target_include_directories(opl
|
||||
INTERFACE "."
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(opl SDL2::mixer)
|
||||
if (PICO_SDK)
|
||||
add_library(opl INTERFACE)
|
||||
target_sources(opl INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_api.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/emu8950.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/slot_render.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_pico.c)
|
||||
target_compile_options(opl INTERFACE -fms-extensions) # want OPL_SLOT_RENDER to be unnamed within OPL_SLOT
|
||||
if (APPLE)
|
||||
target_compile_options(opl INTERFACE -Wno-microsoft-anon-tag)
|
||||
endif()
|
||||
target_compile_definitions(opl INTERFACE
|
||||
EMU8950_NO_RATECONV
|
||||
)
|
||||
target_include_directories(opl INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_link_libraries(opl INTERFACE pico_audio_i2s hardware_gpio)
|
||||
if (PICO_ON_DEVICE)
|
||||
target_sources(opl INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/slot_render_pico.S
|
||||
)
|
||||
target_link_libraries(opl INTERFACE hardware_interp)
|
||||
endif()
|
||||
else()
|
||||
add_library(opl INTERFACE)
|
||||
target_sources(opl INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_api.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_linux.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_obsd.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_queue.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_sdl.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_timer.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl_win32.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/ioperm_sys.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/emu8950.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/opl3.c)
|
||||
target_include_directories(opl
|
||||
INTERFACE "."
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(opl INTERFACE SDL2::mixer)
|
||||
endif()
|
||||
|
|
|
|||
2028
opl/emu8950.c
Normal file
229
opl/emu8950.h
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
#ifndef _EMU8950_H_
|
||||
#define _EMU8950_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "slot_render.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define OPL_DEBUG 0
|
||||
|
||||
/* mask */
|
||||
#define OPL_MASK_CH(x) (1 << (x))
|
||||
#define OPL_MASK_HH (1 << 9)
|
||||
#define OPL_MASK_CYM (1 << 10)
|
||||
#define OPL_MASK_TOM (1 << 11)
|
||||
#define OPL_MASK_SD (1 << 12)
|
||||
#define OPL_MASK_BD (1 << 13)
|
||||
#define OPL_MASK_ADPCM (1 << 14)
|
||||
#define OPL_MASK_RHYTHM (OPL_MASK_HH | OPL_MASK_CYM | OPL_MASK_TOM | OPL_MASK_SD | OPL_MASK_BD)
|
||||
|
||||
#if !EMU8950_NO_RATECONV
|
||||
/* rate conveter */
|
||||
typedef struct __OPL_RateConv {
|
||||
int ch;
|
||||
double timer;
|
||||
double f_ratio;
|
||||
int16_t *sinc_table;
|
||||
int16_t **buf;
|
||||
} OPL_RateConv;
|
||||
|
||||
OPL_RateConv *OPL_RateConv_new(double f_inp, double f_out, int ch);
|
||||
void OPL_RateConv_reset(OPL_RateConv *conv);
|
||||
void OPL_RateConv_putData(OPL_RateConv *conv, int ch, int16_t data);
|
||||
int16_t OPL_RateConv_getData(OPL_RateConv *conv, int ch);
|
||||
void OPL_RateConv_delete(OPL_RateConv *conv);
|
||||
#endif
|
||||
/* slot */
|
||||
typedef struct __OPL_SLOT {
|
||||
struct SLOT_RENDER;
|
||||
uint8_t number;
|
||||
|
||||
#if !EMU8950_NO_PERCUSSION_MODE // only use was to set based on percussion mode
|
||||
/* type flags:
|
||||
* 000000SM
|
||||
* |+-- M: 0:modulator 1:carrier
|
||||
* +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym)
|
||||
*/
|
||||
uint8_t type;
|
||||
#endif
|
||||
|
||||
OPL_PATCH __patch;
|
||||
|
||||
/* phase generator (pg) */
|
||||
uint32_t pg_out; /* pg output, as index of wave table */
|
||||
#if !EMU8950_NO_PERCUSSION_MODE
|
||||
uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */
|
||||
#endif
|
||||
uint16_t blk_fnum; /* (block << 9) | f-number */
|
||||
|
||||
uint32_t update_requests; /* flags to debounce update */
|
||||
|
||||
#if OPL_DEBUG
|
||||
uint8_t last_eg_state;
|
||||
#endif
|
||||
} OPL_SLOT;
|
||||
|
||||
typedef struct __OPL {
|
||||
uint32_t clk;
|
||||
uint32_t rate;
|
||||
|
||||
#if !EMU8950_NO_TIMER
|
||||
uint8_t csm_mode;
|
||||
uint8_t csm_key_count;
|
||||
#endif
|
||||
uint8_t notesel;
|
||||
|
||||
uint32_t inp_step;
|
||||
uint32_t out_step;
|
||||
uint32_t out_time;
|
||||
|
||||
#if EMU8950_LINEAR
|
||||
#if EMU8950_SLOT_RENDER
|
||||
uint8_t *lfo_am_buffer_lsl3;
|
||||
#else
|
||||
uint8_t *lfo_am_buffer;
|
||||
#endif
|
||||
int16_t *mod_buffer;
|
||||
int32_t *buffer;
|
||||
#endif
|
||||
#if !EMU8950_NO_TEST_FLAG
|
||||
uint8_t test_flag;
|
||||
#endif
|
||||
uint32_t slot_key_status;
|
||||
#if !EMU8950_NO_PERCUSSION_MODE
|
||||
uint8_t perc_mode;
|
||||
#endif
|
||||
|
||||
uint32_t eg_counter;
|
||||
|
||||
uint32_t pm_phase;
|
||||
uint32_t pm_dphase;
|
||||
|
||||
#if !EMU8950_NO_TEST_FLAG
|
||||
int32_t am_phase;
|
||||
#else
|
||||
uint8_t am_phase_index;
|
||||
#endif
|
||||
uint8_t lfo_am;
|
||||
|
||||
#if !EMU8950_NO_PERCUSSION_MODE
|
||||
uint32_t noise;
|
||||
uint8_t short_noise;
|
||||
#endif
|
||||
|
||||
uint8_t reg[0x100];
|
||||
uint8_t ch_alg[9]; // alg for each channels
|
||||
|
||||
uint8_t pan[16];
|
||||
|
||||
uint32_t mask;
|
||||
uint8_t am_mode;
|
||||
uint8_t pm_mode;
|
||||
|
||||
/* channel output */
|
||||
/* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14:adpcm */
|
||||
int16_t ch_out[15];
|
||||
|
||||
int16_t mix_out[2];
|
||||
|
||||
OPL_SLOT slot[18];
|
||||
|
||||
#if !EMU8950_NO_RATECONV
|
||||
OPL_RateConv *conv;
|
||||
#endif
|
||||
|
||||
#if !EMU8950_NO_TIMER
|
||||
uint32_t timer1_counter; // 80us counter
|
||||
uint32_t timer2_counter; // 320us counter
|
||||
void *timer1_user_data;
|
||||
void *timer2_user_data;
|
||||
void (*timer1_func)(void *user);
|
||||
void (*timer2_func)(void *user);
|
||||
#endif
|
||||
uint8_t status;
|
||||
|
||||
} OPL;
|
||||
|
||||
#if !EMU8950_NO_TEST_FLAG
|
||||
#define opl_test_flag(opl) opl->test_flag
|
||||
#else
|
||||
// waveform enable only
|
||||
#define opl_test_flag(opl) 0x20
|
||||
#endif
|
||||
|
||||
OPL *OPL_new(uint32_t clk, uint32_t rate);
|
||||
void OPL_delete(OPL *);
|
||||
|
||||
void OPL_reset(OPL *);
|
||||
|
||||
/**
|
||||
* Set output wave sampling rate.
|
||||
* @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is disabled.
|
||||
*/
|
||||
void OPL_setRate(OPL *opl, uint32_t rate);
|
||||
|
||||
/**
|
||||
* Set internal calcuration quality. Currently no effects, just for compatibility.
|
||||
* >= v1.0.0 always synthesizes internal output at clock/72 Hz.
|
||||
*/
|
||||
void OPL_setQuality(OPL *opl, uint8_t q);
|
||||
|
||||
/**
|
||||
* Set fine-grained panning
|
||||
* @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved
|
||||
* @param pan output strength of left/right channel.
|
||||
* pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center.
|
||||
*/
|
||||
void OPL_setPanFine(OPL *opl, uint32_t ch, float pan[2]);
|
||||
|
||||
void OPL_writeIO(OPL *opl, uint32_t reg, uint8_t val);
|
||||
void OPL_writeReg(OPL *opl, uint32_t reg, uint8_t val);
|
||||
|
||||
/**
|
||||
* Calculate sample
|
||||
*/
|
||||
int16_t OPL_calc(OPL *opl);
|
||||
|
||||
void OPL_calc_buffer(OPL *opl, int16_t *buffer, uint32_t nsamples);
|
||||
// LE left/right channels int16:int16
|
||||
void OPL_calc_buffer_stereo(OPL *opl, int32_t *buffer, uint32_t nsamples);
|
||||
|
||||
/**
|
||||
* Set channel mask
|
||||
* @param mask mask flag: OPL_MASK_* can be used.
|
||||
* - bit 0..8: mask for ch 1 to 9 (OPL_MASK_CH(i))
|
||||
* - bit 9: mask for Hi-Hat (OPL_MASK_HH)
|
||||
* - bit 10: mask for Top-Cym (OPL_MASK_CYM)
|
||||
* - bit 11: mask for Tom (OPL_MASK_TOM)
|
||||
* - bit 12: mask for Snare Drum (OPL_MASK_SD)
|
||||
* - bit 13: mask for Bass Drum (OPL_MASK_BD)
|
||||
*/
|
||||
uint32_t OPL_setMask(OPL *, uint32_t mask);
|
||||
|
||||
/**
|
||||
* Read OPL status register
|
||||
* @returns
|
||||
* 76543210
|
||||
* ||||| +- D0: PCM/BSY
|
||||
* ||||+---- D3: BUF/RDY
|
||||
* |||+----- D4: EOS
|
||||
* ||+------ D5: TIMER2
|
||||
* |+------- D6: TIMER1
|
||||
* +-------- D7: IRQ
|
||||
*/
|
||||
uint8_t OPL_status(OPL *opl);
|
||||
|
||||
/* for compatibility */
|
||||
#define OPL_set_rate OPL_setRate
|
||||
#define OPL_set_quality OPL_setQuality
|
||||
#define OPL_set_pan OPL_setPan
|
||||
#define OPL_set_pan_fine OPL_setPanFine
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
445
opl/opl.c
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -20,435 +21,12 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "opl.h"
|
||||
#include "opl_internal.h"
|
||||
|
||||
//#define OPL_DEBUG_TRACE
|
||||
#include "SDL.h"
|
||||
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM)
|
||||
extern opl_driver_t opl_linux_driver;
|
||||
#endif
|
||||
#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
|
||||
extern opl_driver_t opl_openbsd_driver;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
extern opl_driver_t opl_win32_driver;
|
||||
#endif
|
||||
extern opl_driver_t opl_sdl_driver;
|
||||
|
||||
static opl_driver_t *drivers[] =
|
||||
{
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM)
|
||||
&opl_linux_driver,
|
||||
#endif
|
||||
#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
|
||||
&opl_openbsd_driver,
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
&opl_win32_driver,
|
||||
#endif
|
||||
&opl_sdl_driver,
|
||||
NULL
|
||||
};
|
||||
|
||||
static opl_driver_t *driver = NULL;
|
||||
static int init_stage_reg_writes = 1;
|
||||
|
||||
unsigned int opl_sample_rate = 22050;
|
||||
|
||||
//
|
||||
// Init/shutdown code.
|
||||
//
|
||||
|
||||
// Initialize the specified driver and detect an OPL chip. Returns
|
||||
// true if an OPL is detected.
|
||||
|
||||
static opl_init_result_t InitDriver(opl_driver_t *_driver,
|
||||
unsigned int port_base)
|
||||
{
|
||||
opl_init_result_t result1, result2;
|
||||
|
||||
// Initialize the driver.
|
||||
|
||||
if (!_driver->init_func(port_base))
|
||||
{
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
// The driver was initialized okay, so we now have somewhere
|
||||
// to write to. It doesn't mean there's an OPL chip there,
|
||||
// though. Perform the detection sequence to make sure.
|
||||
// (it's done twice, like how Doom does it).
|
||||
|
||||
driver = _driver;
|
||||
init_stage_reg_writes = 1;
|
||||
|
||||
result1 = OPL_Detect();
|
||||
result2 = OPL_Detect();
|
||||
if (result1 == OPL_INIT_NONE || result2 == OPL_INIT_NONE)
|
||||
{
|
||||
printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
|
||||
_driver->shutdown_func();
|
||||
driver = NULL;
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
init_stage_reg_writes = 0;
|
||||
|
||||
printf("OPL_Init: Using driver '%s'.\n", driver->name);
|
||||
|
||||
return result2;
|
||||
}
|
||||
|
||||
// Find a driver automatically by trying each in the list.
|
||||
|
||||
static opl_init_result_t AutoSelectDriver(unsigned int port_base)
|
||||
{
|
||||
int i;
|
||||
opl_init_result_t result;
|
||||
|
||||
for (i=0; drivers[i] != NULL; ++i)
|
||||
{
|
||||
result = InitDriver(drivers[i], port_base);
|
||||
if (result != OPL_INIT_NONE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
printf("OPL_Init: Failed to find a working driver.\n");
|
||||
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
// Initialize the OPL library. Return value indicates type of OPL chip
|
||||
// detected, if any.
|
||||
|
||||
opl_init_result_t OPL_Init(unsigned int port_base)
|
||||
{
|
||||
char *driver_name;
|
||||
int i;
|
||||
int result;
|
||||
|
||||
driver_name = getenv("OPL_DRIVER");
|
||||
|
||||
if (driver_name != NULL)
|
||||
{
|
||||
// Search the list until we find the driver with this name.
|
||||
|
||||
for (i=0; drivers[i] != NULL; ++i)
|
||||
{
|
||||
if (!strcmp(driver_name, drivers[i]->name))
|
||||
{
|
||||
result = InitDriver(drivers[i], port_base);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OPL_Init: Failed to initialize "
|
||||
"driver: '%s'.\n", driver_name);
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("OPL_Init: unknown driver: '%s'.\n", driver_name);
|
||||
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AutoSelectDriver(port_base);
|
||||
}
|
||||
}
|
||||
|
||||
// Shut down the OPL library.
|
||||
|
||||
void OPL_Shutdown(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->shutdown_func();
|
||||
driver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the sample rate used for software OPL emulation.
|
||||
|
||||
void OPL_SetSampleRate(unsigned int rate)
|
||||
{
|
||||
opl_sample_rate = rate;
|
||||
}
|
||||
|
||||
void OPL_WritePort(opl_port_t port, unsigned int value)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_write: %i, %x\n", port, value);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
driver->write_port_func(port, value);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int OPL_ReadPort(opl_port_t port)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_read: %i...\n", port);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
result = driver->read_port_func(port);
|
||||
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_read: %i -> %x\n", port, result);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Higher-level functions, based on the lower-level functions above
|
||||
// (register write, etc).
|
||||
//
|
||||
|
||||
unsigned int OPL_ReadStatus(void)
|
||||
{
|
||||
return OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
}
|
||||
|
||||
// Write an OPL register value
|
||||
|
||||
void OPL_WriteRegister(int reg, int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (reg & 0x100)
|
||||
{
|
||||
OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPL_WritePort(OPL_REGISTER_PORT, reg);
|
||||
}
|
||||
|
||||
// For timing, read the register port six times after writing the
|
||||
// register number to cause the appropriate delay
|
||||
|
||||
for (i=0; i<6; ++i)
|
||||
{
|
||||
// An oddity of the Doom OPL code: at startup initialization,
|
||||
// the spacing here is performed by reading from the register
|
||||
// port; after initialization, the data port is read, instead.
|
||||
|
||||
if (init_stage_reg_writes)
|
||||
{
|
||||
OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPL_ReadPort(OPL_DATA_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
OPL_WritePort(OPL_DATA_PORT, value);
|
||||
|
||||
// Read the register port 24 times after writing the value to
|
||||
// cause the appropriate delay
|
||||
|
||||
for (i=0; i<24; ++i)
|
||||
{
|
||||
OPL_ReadStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the presence of an OPL chip
|
||||
|
||||
opl_init_result_t OPL_Detect(void)
|
||||
{
|
||||
int result1, result2;
|
||||
int i;
|
||||
|
||||
// Reset both timers:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
|
||||
// Enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
// Read status
|
||||
result1 = OPL_ReadStatus();
|
||||
|
||||
// Set timer:
|
||||
OPL_WriteRegister(OPL_REG_TIMER1, 0xff);
|
||||
|
||||
// Start timer 1:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21);
|
||||
|
||||
// Wait for 80 microseconds
|
||||
// This is how Doom does it:
|
||||
|
||||
for (i=0; i<200; ++i)
|
||||
{
|
||||
OPL_ReadStatus();
|
||||
}
|
||||
|
||||
OPL_Delay(1 * OPL_MS);
|
||||
|
||||
// Read status
|
||||
result2 = OPL_ReadStatus();
|
||||
|
||||
// Reset both timers:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
|
||||
// Enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0)
|
||||
{
|
||||
result1 = OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3);
|
||||
if (result1 == 0x00)
|
||||
{
|
||||
return OPL_INIT_OPL3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OPL_INIT_OPL2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize registers on startup
|
||||
|
||||
void OPL_InitRegisters(int opl3)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Initialize level registers
|
||||
|
||||
for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x3f);
|
||||
}
|
||||
|
||||
// Initialize other registers
|
||||
// These two loops write to registers that actually don't exist,
|
||||
// but this is what Doom does ...
|
||||
// Similarly, the <= is also intenational.
|
||||
|
||||
for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x00);
|
||||
}
|
||||
|
||||
// More registers ...
|
||||
|
||||
for (r=1; r < OPL_REGS_LEVEL; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x00);
|
||||
}
|
||||
|
||||
// Re-initialize the low registers:
|
||||
|
||||
// Reset both timers and enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
// "Allow FM chips to control the waveform of each operator":
|
||||
OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
|
||||
|
||||
if (opl3)
|
||||
{
|
||||
OPL_WriteRegister(OPL_REG_NEW, 0x01);
|
||||
|
||||
// Initialize level registers
|
||||
|
||||
for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x3f);
|
||||
}
|
||||
|
||||
// Initialize other registers
|
||||
// These two loops write to registers that actually don't exist,
|
||||
// but this is what Doom does ...
|
||||
// Similarly, the <= is also intenational.
|
||||
|
||||
for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x00);
|
||||
}
|
||||
|
||||
// More registers ...
|
||||
|
||||
for (r=1; r < OPL_REGS_LEVEL; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard split point on (?)
|
||||
OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
|
||||
|
||||
if (opl3)
|
||||
{
|
||||
OPL_WriteRegister(OPL_REG_NEW, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Timer functions.
|
||||
//
|
||||
|
||||
void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->set_callback_func(us, callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_ClearCallbacks(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->clear_callbacks_func();
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_Lock(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->lock_func();
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_Unlock(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->unlock_func();
|
||||
}
|
||||
}
|
||||
extern opl_driver_t *driver;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -504,20 +82,3 @@ void OPL_Delay(uint64_t us)
|
|||
SDL_DestroyMutex(delay_data.mutex);
|
||||
SDL_DestroyCond(delay_data.cond);
|
||||
}
|
||||
|
||||
void OPL_SetPaused(int paused)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->set_paused_func(paused);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_AdjustCallbacks(float value)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->adjust_callbacks_func(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -126,7 +127,7 @@ void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data);
|
|||
// Adjust callback times by the specified factor. For example, a value of
|
||||
// 0.5 will halve all remaining times.
|
||||
|
||||
void OPL_AdjustCallbacks(float factor);
|
||||
void OPL_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo);
|
||||
|
||||
// Clear all OPL callbacks that have been set.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT)
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -49,6 +50,7 @@ typedef struct _opl3_slot opl3_slot;
|
|||
typedef struct _opl3_channel opl3_channel;
|
||||
typedef struct _opl3_chip opl3_chip;
|
||||
|
||||
|
||||
struct _opl3_slot {
|
||||
opl3_channel *channel;
|
||||
opl3_chip *chip;
|
||||
|
|
|
|||
483
opl/opl_api.c
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// OPL interface.
|
||||
//
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "opl.h"
|
||||
#include "opl_internal.h"
|
||||
|
||||
//#define OPL_DEBUG_TRACE
|
||||
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM)
|
||||
extern opl_driver_t opl_linux_driver;
|
||||
#endif
|
||||
#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
|
||||
extern opl_driver_t opl_openbsd_driver;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
extern opl_driver_t opl_win32_driver;
|
||||
#endif
|
||||
#if PICO_BUILD
|
||||
extern opl_driver_t opl_pico_driver;
|
||||
#else
|
||||
extern opl_driver_t opl_sdl_driver;
|
||||
#endif
|
||||
|
||||
static const opl_driver_t *drivers[] =
|
||||
{
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_IOPERM)
|
||||
&opl_linux_driver,
|
||||
#endif
|
||||
#if defined(HAVE_LIBI386) || defined(HAVE_LIBAMD64)
|
||||
&opl_openbsd_driver,
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
&opl_win32_driver,
|
||||
#endif
|
||||
#if PICO_BUILD
|
||||
&opl_pico_driver,
|
||||
#else
|
||||
&opl_sdl_driver,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
const opl_driver_t *driver = NULL;
|
||||
static int init_stage_reg_writes = 1;
|
||||
|
||||
unsigned int opl_sample_rate = 22050;
|
||||
|
||||
//
|
||||
// Init/shutdown code.
|
||||
//
|
||||
|
||||
// Initialize the specified driver and detect an OPL chip. Returns
|
||||
// true if an OPL is detected.
|
||||
|
||||
static opl_init_result_t InitDriver(const opl_driver_t *_driver,
|
||||
unsigned int port_base)
|
||||
{
|
||||
opl_init_result_t result1, result2;
|
||||
|
||||
// Initialize the driver.
|
||||
|
||||
if (!_driver->init_func(port_base))
|
||||
{
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
// The driver was initialized okay, so we now have somewhere
|
||||
// to write to. It doesn't mean there's an OPL chip there,
|
||||
// though. Perform the detection sequence to make sure.
|
||||
// (it's done twice, like how Doom does it).
|
||||
|
||||
driver = _driver;
|
||||
init_stage_reg_writes = 1;
|
||||
|
||||
result1 = OPL_Detect();
|
||||
result2 = OPL_Detect();
|
||||
if (result1 == OPL_INIT_NONE || result2 == OPL_INIT_NONE)
|
||||
{
|
||||
printf("OPL_Init: No OPL detected using '%s' driver.\n", _driver->name);
|
||||
_driver->shutdown_func();
|
||||
driver = NULL;
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
init_stage_reg_writes = 0;
|
||||
|
||||
printf("OPL_Init: Using driver '%s'.\n", driver->name);
|
||||
|
||||
return result2;
|
||||
}
|
||||
|
||||
// Find a driver automatically by trying each in the list.
|
||||
|
||||
static opl_init_result_t AutoSelectDriver(unsigned int port_base)
|
||||
{
|
||||
int i;
|
||||
opl_init_result_t result;
|
||||
|
||||
for (i=0; drivers[i] != NULL; ++i)
|
||||
{
|
||||
result = InitDriver(drivers[i], port_base);
|
||||
if (result != OPL_INIT_NONE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
printf("OPL_Init: Failed to find a working driver.\n");
|
||||
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
|
||||
// Initialize the OPL library. Return value indicates type of OPL chip
|
||||
// detected, if any.
|
||||
|
||||
opl_init_result_t OPL_Init(unsigned int port_base)
|
||||
{
|
||||
#if PICO_BUILD
|
||||
driver = drivers[0];
|
||||
driver->init_func(0);
|
||||
return OPL_INIT_OPL2;
|
||||
#else
|
||||
char *driver_name;
|
||||
int i;
|
||||
int result;
|
||||
|
||||
driver_name = getenv("OPL_DRIVER");
|
||||
|
||||
if (driver_name != NULL)
|
||||
{
|
||||
// Search the list until we find the driver with this name.
|
||||
|
||||
for (i=0; drivers[i] != NULL; ++i)
|
||||
{
|
||||
if (!strcmp(driver_name, drivers[i]->name))
|
||||
{
|
||||
result = InitDriver(drivers[i], port_base);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("OPL_Init: Failed to initialize "
|
||||
"driver: '%s'.\n", driver_name);
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("OPL_Init: unknown driver: '%s'.\n", driver_name);
|
||||
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AutoSelectDriver(port_base);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Shut down the OPL library.
|
||||
|
||||
void OPL_Shutdown(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->shutdown_func();
|
||||
driver = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the sample rate used for software OPL emulation.
|
||||
|
||||
void OPL_SetSampleRate(unsigned int rate)
|
||||
{
|
||||
opl_sample_rate = rate;
|
||||
}
|
||||
|
||||
void OPL_WritePort(opl_port_t port, unsigned int value)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_write: %i, %x\n", port, value);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
driver->write_port_func(port, value);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int OPL_ReadPort(opl_port_t port)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
unsigned int result;
|
||||
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_read: %i...\n", port);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
result = driver->read_port_func(port);
|
||||
|
||||
#ifdef OPL_DEBUG_TRACE
|
||||
printf("OPL_read: %i -> %x\n", port, result);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Higher-level functions, based on the lower-level functions above
|
||||
// (register write, etc).
|
||||
//
|
||||
|
||||
unsigned int OPL_ReadStatus(void)
|
||||
{
|
||||
return OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
}
|
||||
|
||||
// Write an OPL register value
|
||||
|
||||
void OPL_WriteRegister(int reg, int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (reg & 0x100)
|
||||
{
|
||||
OPL_WritePort(OPL_REGISTER_PORT_OPL3, reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPL_WritePort(OPL_REGISTER_PORT, reg);
|
||||
}
|
||||
|
||||
// For timing, read the register port six times after writing the
|
||||
// register number to cause the appropriate delay
|
||||
|
||||
for (i=0; i<6; ++i)
|
||||
{
|
||||
// An oddity of the Doom OPL code: at startup initialization,
|
||||
// the spacing here is performed by reading from the register
|
||||
// port; after initialization, the data port is read, instead.
|
||||
|
||||
if (init_stage_reg_writes)
|
||||
{
|
||||
OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
}
|
||||
else
|
||||
{
|
||||
OPL_ReadPort(OPL_DATA_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
OPL_WritePort(OPL_DATA_PORT, value);
|
||||
|
||||
// Read the register port 24 times after writing the value to
|
||||
// cause the appropriate delay
|
||||
|
||||
for (i=0; i<24; ++i)
|
||||
{
|
||||
OPL_ReadStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Detect the presence of an OPL chip
|
||||
|
||||
opl_init_result_t OPL_Detect(void)
|
||||
{
|
||||
int result1, result2;
|
||||
int i;
|
||||
|
||||
// Reset both timers:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
|
||||
// Enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
// Read status
|
||||
result1 = OPL_ReadStatus();
|
||||
|
||||
// Set timer:
|
||||
OPL_WriteRegister(OPL_REG_TIMER1, 0xff);
|
||||
|
||||
// Start timer 1:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x21);
|
||||
|
||||
// Wait for 80 microseconds
|
||||
// This is how Doom does it:
|
||||
|
||||
for (i=0; i<200; ++i)
|
||||
{
|
||||
OPL_ReadStatus();
|
||||
}
|
||||
|
||||
OPL_Delay(1 * OPL_MS);
|
||||
|
||||
// Read status
|
||||
result2 = OPL_ReadStatus();
|
||||
|
||||
// Reset both timers:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
|
||||
// Enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
if ((result1 & 0xe0) == 0x00 && (result2 & 0xe0) == 0xc0)
|
||||
{
|
||||
result1 = OPL_ReadPort(OPL_REGISTER_PORT);
|
||||
result2 = OPL_ReadPort(OPL_REGISTER_PORT_OPL3);
|
||||
if (result1 == 0x00)
|
||||
{
|
||||
return OPL_INIT_OPL3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OPL_INIT_OPL2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return OPL_INIT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize registers on startup
|
||||
|
||||
void OPL_InitRegisters(int opl3)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Initialize level registers
|
||||
|
||||
for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x3f);
|
||||
}
|
||||
|
||||
// Initialize other registers
|
||||
// These two loops write to registers that actually don't exist,
|
||||
// but this is what Doom does ...
|
||||
// Similarly, the <= is also intenational.
|
||||
|
||||
for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x00);
|
||||
}
|
||||
|
||||
// More registers ...
|
||||
|
||||
for (r=1; r < OPL_REGS_LEVEL; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r, 0x00);
|
||||
}
|
||||
|
||||
// Re-initialize the low registers:
|
||||
|
||||
// Reset both timers and enable interrupts:
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
|
||||
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
|
||||
|
||||
// "Allow FM chips to control the waveform of each operator":
|
||||
OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
|
||||
|
||||
if (opl3)
|
||||
{
|
||||
OPL_WriteRegister(OPL_REG_NEW, 0x01);
|
||||
|
||||
// Initialize level registers
|
||||
|
||||
for (r=OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x3f);
|
||||
}
|
||||
|
||||
// Initialize other registers
|
||||
// These two loops write to registers that actually don't exist,
|
||||
// but this is what Doom does ...
|
||||
// Similarly, the <= is also intenational.
|
||||
|
||||
for (r=OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x00);
|
||||
}
|
||||
|
||||
// More registers ...
|
||||
|
||||
for (r=1; r < OPL_REGS_LEVEL; ++r)
|
||||
{
|
||||
OPL_WriteRegister(r | 0x100, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard split point on (?)
|
||||
OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
|
||||
|
||||
if (opl3)
|
||||
{
|
||||
OPL_WriteRegister(OPL_REG_NEW, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Timer functions.
|
||||
//
|
||||
|
||||
void OPL_SetCallback(uint64_t us, opl_callback_t callback, void *data)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->set_callback_func(us, callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_ClearCallbacks(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->clear_callbacks_func();
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_Lock(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->lock_func();
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_Unlock(void)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->unlock_func();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OPL_SetPaused(int paused)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->set_paused_func(paused);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
if (driver != NULL)
|
||||
{
|
||||
driver->adjust_callbacks_func(old_tempo, new_tempo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -32,7 +33,7 @@ typedef void (*opl_clear_callbacks_func)(void);
|
|||
typedef void (*opl_lock_func)(void);
|
||||
typedef void (*opl_unlock_func)(void);
|
||||
typedef void (*opl_set_paused_func)(int paused);
|
||||
typedef void (*opl_adjust_callbacks_func)(float value);
|
||||
typedef void (*opl_adjust_callbacks_func)(unsigned int old_tempo, unsigned int new_tempo);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -37,12 +38,12 @@ static int OPL_Linux_Init(unsigned int port_base)
|
|||
|
||||
if (ioperm(port_base, 2, 1) < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to get I/O port permissions for 0x%x: %s\n",
|
||||
stderr_print( "Failed to get I/O port permissions for 0x%x: %s\n",
|
||||
port_base, strerror(errno));
|
||||
|
||||
if (errno == EPERM)
|
||||
{
|
||||
fprintf(stderr,
|
||||
stderr_print(
|
||||
"\tYou may need to run the program as root in order\n"
|
||||
"\tto acquire I/O port permissions for OPL MIDI playback.\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -60,7 +61,7 @@ static int OPL_OpenBSD_Init(unsigned int port_base)
|
|||
|
||||
if (set_iopl(3) < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to get raise I/O privilege level: "
|
||||
stderr_print( "Failed to get raise I/O privilege level: "
|
||||
"check that you are running as root.\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
605
opl/opl_pico.c
Normal file
|
|
@ -0,0 +1,605 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// OPL SDL interface.
|
||||
//
|
||||
|
||||
// todo replace opl_queue with pheap
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "pico/mutex.h"
|
||||
#include "pico/audio_i2s.h"
|
||||
#include "pico/util/pheap.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "i_picosound.h"
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
#include "woody_opl.h"
|
||||
#elif USE_EMU8950_OPL
|
||||
#include "emu8950.h"
|
||||
#else
|
||||
#include "opl3.h"
|
||||
#endif
|
||||
|
||||
#include "opl.h"
|
||||
#include "opl_internal.h"
|
||||
|
||||
#include "opl_queue.h"
|
||||
|
||||
#define MAX_SOUND_SLICE_TIME 100 /* ms */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int rate; // Number of times the timer is advanced per sec.
|
||||
unsigned int enabled; // Non-zero if timer is enabled.
|
||||
unsigned int value; // Last value that was set.
|
||||
uint64_t expire_time; // Calculated time that timer will expire.
|
||||
} opl_timer_t;
|
||||
|
||||
// When the callback mutex is locked using OPL_Lock, callback functions
|
||||
// are not invoked.
|
||||
|
||||
static mutex_t callback_mutex;
|
||||
|
||||
// Queue of callbacks waiting to be invoked.
|
||||
|
||||
static opl_callback_queue_t *callback_queue;
|
||||
|
||||
// Mutex used to control access to the callback queue.
|
||||
|
||||
static mutex_t callback_queue_mutex;
|
||||
|
||||
// Current time, in us since startup:
|
||||
|
||||
static uint64_t current_time;
|
||||
|
||||
// If non-zero, playback is currently paused.
|
||||
|
||||
static int opl_pico_paused;
|
||||
|
||||
// Time offset (in us) due to the fact that callbacks
|
||||
// were previously paused.
|
||||
|
||||
static uint64_t pause_offset;
|
||||
|
||||
// OPL software emulator structure.
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
// todo configure this based on woody build flag
|
||||
#define opl_op3mode 0
|
||||
#elif USE_EMU8950_OPL
|
||||
#define opl_op3mode 0
|
||||
static OPL *emu8950_opl;
|
||||
#else
|
||||
static opl3_chip opl_chip;
|
||||
static int opl_opl3mode;
|
||||
#endif
|
||||
|
||||
// Register number that was written.
|
||||
|
||||
static int register_num = 0;
|
||||
|
||||
#if !EMU8950_NO_TIMER
|
||||
// Timers; DBOPL does not do timer stuff itself.
|
||||
|
||||
static opl_timer_t timer1 = { 12500, 0, 0, 0 };
|
||||
static opl_timer_t timer2 = { 3125, 0, 0, 0 };
|
||||
#endif
|
||||
|
||||
// SDL parameters.
|
||||
|
||||
static bool audio_was_initialized = 0;
|
||||
|
||||
static inline void Pico_LockMutex(mutex_t *mutex) {
|
||||
|
||||
}
|
||||
|
||||
static inline void Pico_UnlockMutex(mutex_t *mutex) {
|
||||
|
||||
}
|
||||
|
||||
// Advance time by the specified number of samples, invoking any
|
||||
// callback functions as appropriate.
|
||||
|
||||
static void AdvanceTime(unsigned int nsamples)
|
||||
{
|
||||
opl_callback_t callback;
|
||||
void *callback_data;
|
||||
uint64_t us;
|
||||
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
|
||||
// Advance time.
|
||||
|
||||
us = ((uint64_t) nsamples * OPL_SECOND) / PICO_SOUND_SAMPLE_FREQ;
|
||||
current_time += us;
|
||||
|
||||
if (opl_pico_paused)
|
||||
{
|
||||
pause_offset += us;
|
||||
}
|
||||
|
||||
// Are there callbacks to invoke now? Keep invoking them
|
||||
// until there are no more left.
|
||||
|
||||
while (!OPL_Queue_IsEmpty(callback_queue)
|
||||
&& current_time >= OPL_Queue_Peek(callback_queue) + pause_offset)
|
||||
{
|
||||
// Pop the callback from the queue to invoke it.
|
||||
|
||||
if (!OPL_Queue_Pop(callback_queue, &callback, &callback_data))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// The mutex stuff here is a bit complicated. We must
|
||||
// hold callback_mutex when we invoke the callback (so that
|
||||
// the control thread can use OPL_Lock() to prevent callbacks
|
||||
// from being invoked), but we must not be holding
|
||||
// callback_queue_mutex, as the callback must be able to
|
||||
// call OPL_SetCallback to schedule new callbacks.
|
||||
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
|
||||
Pico_LockMutex(&callback_mutex);
|
||||
callback(callback_data);
|
||||
Pico_UnlockMutex(&callback_mutex);
|
||||
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
}
|
||||
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
}
|
||||
|
||||
// Call the OPL emulator code to fill the specified buffer.
|
||||
|
||||
// Callback function to fill a new sound buffer:
|
||||
|
||||
#define LIMITED_CALLBACK_TYPES 1
|
||||
|
||||
#if LIMITED_CALLBACK_TYPES
|
||||
extern void RestartSong(void *unused);
|
||||
extern void TrackTimerCallback(void *track);
|
||||
#endif
|
||||
|
||||
#if DOOM_TINY
|
||||
extern uint8_t restart_song_state;
|
||||
#endif
|
||||
|
||||
void OPL_Pico_Mix_callback(audio_buffer_t *audio_buffer)
|
||||
{
|
||||
unsigned int filled, buffer_samples;
|
||||
#if DOOM_TINY
|
||||
if (restart_song_state == 2) {
|
||||
RestartSong(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Repeatedly call the OPL emulator update function until the buffer is
|
||||
// full.
|
||||
filled = 0;
|
||||
buffer_samples = audio_buffer->max_sample_count;
|
||||
|
||||
//#if PICO_ON_DEVICE
|
||||
// absolute_time_t t0 = get_absolute_time();
|
||||
// gpio_set_mask(1);
|
||||
//#endif
|
||||
while (filled < buffer_samples) {
|
||||
//#if PICO_ON_DEVICE
|
||||
// gpio_set_mask(32);
|
||||
//#endif
|
||||
uint64_t next_callback_time;
|
||||
uint64_t nsamples;
|
||||
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
|
||||
// Work out the time until the next callback waiting in
|
||||
// the callback queue must be invoked. We can then fill the
|
||||
// buffer with this many samples.
|
||||
|
||||
if (opl_pico_paused || OPL_Queue_IsEmpty(callback_queue)) {
|
||||
nsamples = buffer_samples - filled;
|
||||
} else {
|
||||
next_callback_time = OPL_Queue_Peek(callback_queue) + pause_offset;
|
||||
|
||||
nsamples = (next_callback_time - current_time) * PICO_SOUND_SAMPLE_FREQ;
|
||||
nsamples = (nsamples + OPL_SECOND - 1) / OPL_SECOND;
|
||||
|
||||
if (nsamples > buffer_samples - filled) {
|
||||
nsamples = buffer_samples - filled;
|
||||
}
|
||||
}
|
||||
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
|
||||
// Add emulator output to buffer.
|
||||
|
||||
//OPL3_GenerateStream(&opl_chip, (Bit16s *) (audio_buffer->buffer->bytes + filled * 4), nsamples);
|
||||
#if USE_WOODY_OPL
|
||||
int16_t *sndptr = (int16_t *) (audio_buffer->buffer->bytes + filled * 4);
|
||||
// todo store in stereo?
|
||||
adlib_getsample(sndptr, nsamples);
|
||||
for(int i=nsamples-1; i>=0; i--) {
|
||||
sndptr[i*2] = sndptr[i*2 + 1] = sndptr[i];
|
||||
}
|
||||
#elif USE_EMU8950_OPL
|
||||
if (nsamples) {
|
||||
int32_t *sndptr32 = (int32_t *) (audio_buffer->buffer->bytes + filled * 4);
|
||||
OPL_calc_buffer_stereo(emu8950_opl, sndptr32, nsamples);
|
||||
}
|
||||
#else
|
||||
int16_t *sndptr = (int16_t *) (audio_buffer->buffer->bytes + filled * 4);
|
||||
for(int i = 0; i < nsamples; i++)
|
||||
{
|
||||
OPL3_GenerateResampled(&opl_chip, sndptr);
|
||||
sndptr += 2;
|
||||
}
|
||||
#endif
|
||||
filled += nsamples;
|
||||
|
||||
// Invoke callbacks for this point in time.
|
||||
|
||||
//#if PICO_ON_DEVICE
|
||||
// gpio_clr_mask(32);
|
||||
//#endif
|
||||
AdvanceTime(nsamples);
|
||||
}
|
||||
audio_buffer->sample_count = audio_buffer->max_sample_count;
|
||||
#if !USE_WOODY_OPL
|
||||
int16_t *samples = (int16_t *)audio_buffer->buffer->bytes;
|
||||
for(uint i=0;i<audio_buffer->sample_count * 2; i++) {
|
||||
samples[i] <<= 3;
|
||||
}
|
||||
#endif
|
||||
//#if PICO_ON_DEVICE
|
||||
// gpio_clr_mask(1);
|
||||
// int32_t t = (int32_t)absolute_time_diff_us(t0, get_absolute_time());
|
||||
// static int max_t;
|
||||
// static int ii;
|
||||
// static int total;
|
||||
// total += t;
|
||||
// if (t > max_t) {
|
||||
// max_t = t;
|
||||
// }
|
||||
// ii++;
|
||||
// if (!(ii &127)) {
|
||||
// printf("AVG %d MAX %d\n", total / 128, max_t);
|
||||
// max_t = 0;
|
||||
// total = 0;
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
|
||||
static void OPL_Pico_Shutdown(void)
|
||||
{
|
||||
if (audio_was_initialized)
|
||||
{
|
||||
I_PicoSoundSetMusicGenerator(NULL);
|
||||
OPL_Queue_Destroy(callback_queue);
|
||||
audio_was_initialized = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int OPL_Pico_Init(unsigned int port_base)
|
||||
{
|
||||
if (I_PicoSoundIsInitialized()) {
|
||||
opl_pico_paused = 0;
|
||||
pause_offset = 0;
|
||||
|
||||
// Queue structure of callbacks to invoke.
|
||||
|
||||
callback_queue = OPL_Queue_Create();
|
||||
current_time = 0;
|
||||
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
adlib_init(mixing_freq);
|
||||
#elif USE_EMU8950_OPL
|
||||
emu8950_opl = OPL_new(3579552, PICO_SOUND_SAMPLE_FREQ); // todo check rate
|
||||
#else
|
||||
OPL3_Reset(&opl_chip, PICO_SOUND_SAMPLE_FREQ);
|
||||
opl_opl3mode = 0;
|
||||
#endif
|
||||
|
||||
// // Set postmix that adds the OPL music. This is deliberately done
|
||||
// // as a postmix and not using Mix_HookMusic() as the latter disables
|
||||
// // normal Pico_mixer music mixing.
|
||||
// Mix_SetPostMix(OPL_Mix_Callback, NULL);
|
||||
I_PicoSoundSetMusicGenerator(OPL_Pico_Mix_callback);
|
||||
audio_was_initialized = 1;
|
||||
} else {
|
||||
audio_was_initialized = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned int OPL_Pico_PortRead(opl_port_t port)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
|
||||
if (port == OPL_REGISTER_PORT_OPL3)
|
||||
{
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
#if !EMU8950_NO_TIMER
|
||||
if (timer1.enabled && current_time > timer1.expire_time)
|
||||
{
|
||||
result |= 0x80; // Either have expired
|
||||
result |= 0x40; // Timer 1 has expired
|
||||
}
|
||||
|
||||
if (timer2.enabled && current_time > timer2.expire_time)
|
||||
{
|
||||
result |= 0x80; // Either have expired
|
||||
result |= 0x20; // Timer 2 has expired
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void OPLTimer_CalculateEndTime(opl_timer_t *timer)
|
||||
{
|
||||
int tics;
|
||||
|
||||
// If the timer is enabled, calculate the time when the timer
|
||||
// will expire.
|
||||
|
||||
if (timer->enabled)
|
||||
{
|
||||
tics = 0x100 - timer->value;
|
||||
timer->expire_time = current_time
|
||||
+ ((uint64_t) tics * OPL_SECOND) / timer->rate;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteRegister(unsigned int reg_num, unsigned int value)
|
||||
{
|
||||
switch (reg_num)
|
||||
{
|
||||
#if !EMU8950_NO_TIMER
|
||||
case OPL_REG_TIMER1:
|
||||
timer1.value = value;
|
||||
OPLTimer_CalculateEndTime(&timer1);
|
||||
break;
|
||||
|
||||
case OPL_REG_TIMER2:
|
||||
timer2.value = value;
|
||||
OPLTimer_CalculateEndTime(&timer2);
|
||||
break;
|
||||
|
||||
case OPL_REG_TIMER_CTRL:
|
||||
if (value & 0x80)
|
||||
{
|
||||
timer1.enabled = 0;
|
||||
timer2.enabled = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((value & 0x40) == 0)
|
||||
{
|
||||
timer1.enabled = (value & 0x01) != 0;
|
||||
OPLTimer_CalculateEndTime(&timer1);
|
||||
}
|
||||
|
||||
if ((value & 0x20) == 0)
|
||||
{
|
||||
timer1.enabled = (value & 0x02) != 0;
|
||||
OPLTimer_CalculateEndTime(&timer2);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
case OPL_REG_NEW:
|
||||
#if !USE_WOODY_OPL && !USE_EMU8950_OPL
|
||||
opl_opl3mode = value & 0x01;
|
||||
#endif
|
||||
default:
|
||||
#if USE_WOODY_OPL
|
||||
adlib_write(reg_num, value);
|
||||
#elif USE_EMU8950_OPL
|
||||
OPL_writeReg(emu8950_opl, reg_num, value);
|
||||
#else
|
||||
OPL3_WriteRegBuffered(&opl_chip, reg_num, value);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void OPL_Pico_PortWrite(opl_port_t port, unsigned int value)
|
||||
{
|
||||
if (port == OPL_REGISTER_PORT)
|
||||
{
|
||||
register_num = value;
|
||||
}
|
||||
else if (port == OPL_REGISTER_PORT_OPL3)
|
||||
{
|
||||
register_num = value | 0x100;
|
||||
}
|
||||
else if (port == OPL_DATA_PORT)
|
||||
{
|
||||
WriteRegister(register_num, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void OPL_Pico_SetCallback(uint64_t us, opl_callback_t callback,
|
||||
void *data)
|
||||
{
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
OPL_Queue_Push(callback_queue, callback, data,
|
||||
current_time - pause_offset + us);
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
}
|
||||
|
||||
static void OPL_Pico_ClearCallbacks(void)
|
||||
{
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
OPL_Queue_Clear(callback_queue);
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
}
|
||||
|
||||
static void OPL_Pico_Lock(void)
|
||||
{
|
||||
Pico_LockMutex(&callback_mutex);
|
||||
}
|
||||
|
||||
static void OPL_Pico_Unlock(void)
|
||||
{
|
||||
Pico_UnlockMutex(&callback_mutex);
|
||||
}
|
||||
|
||||
static void OPL_Pico_SetPaused(int paused)
|
||||
{
|
||||
opl_pico_paused = paused;
|
||||
}
|
||||
|
||||
static void OPL_Pico_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
Pico_LockMutex(&callback_queue_mutex);
|
||||
OPL_Queue_AdjustCallbacks(callback_queue, current_time, old_tempo, new_tempo);
|
||||
Pico_UnlockMutex(&callback_queue_mutex);
|
||||
}
|
||||
|
||||
const opl_driver_t opl_pico_driver =
|
||||
{
|
||||
"Pico",
|
||||
OPL_Pico_Init,
|
||||
OPL_Pico_Shutdown,
|
||||
OPL_Pico_PortRead,
|
||||
OPL_Pico_PortWrite,
|
||||
OPL_Pico_SetCallback,
|
||||
OPL_Pico_ClearCallbacks,
|
||||
OPL_Pico_Lock,
|
||||
OPL_Pico_Unlock,
|
||||
OPL_Pico_SetPaused,
|
||||
OPL_Pico_AdjustCallbacks,
|
||||
};
|
||||
|
||||
void OPL_Delay(uint64_t us) {
|
||||
sleep_us(us); // todo not sure we want to block
|
||||
}
|
||||
|
||||
// todo really limited to 1 event per track i think, so could go smaller
|
||||
#define MAX_OPL_QUEUE 10
|
||||
PHEAP_DEFINE_STATIC(opl_heap, MAX_OPL_QUEUE + 1);
|
||||
|
||||
// todo note also the callback is likely only one of a couple of functions
|
||||
typedef struct queue_entry {
|
||||
uint64_t time; // todo graham can likely be 32 bit, too much work atm
|
||||
#if !LIMITED_CALLBACK_TYPES
|
||||
opl_callback_t callback;
|
||||
#endif
|
||||
void *data;
|
||||
} queue_entry_t;
|
||||
|
||||
struct opl_callback_queue_s {
|
||||
queue_entry_t entries[MAX_OPL_QUEUE];
|
||||
};
|
||||
|
||||
static struct opl_callback_queue_s queue;
|
||||
|
||||
static inline queue_entry_t *get_entry(opl_callback_queue_t *queue, pheap_node_id_t id) {
|
||||
assert(id && id <= opl_heap.max_nodes);
|
||||
return queue->entries + id - 1;
|
||||
}
|
||||
|
||||
bool opl_queue_comparator(void *user_data, pheap_node_id_t a, pheap_node_id_t b) {
|
||||
opl_callback_queue_t *q = (opl_callback_queue_t *)user_data;
|
||||
return get_entry(q, a)->time < get_entry(q, b)->time;
|
||||
}
|
||||
|
||||
opl_callback_queue_t *OPL_Queue_Create(void) {
|
||||
ph_post_alloc_init(&opl_heap, MAX_OPL_QUEUE, opl_queue_comparator, &queue);
|
||||
return &queue;
|
||||
}
|
||||
|
||||
int OPL_Queue_IsEmpty(opl_callback_queue_t *queue) {
|
||||
return ph_peek_head(&opl_heap) == 0;
|
||||
}
|
||||
|
||||
void OPL_Queue_Clear(opl_callback_queue_t *queue) {
|
||||
ph_clear(&opl_heap);
|
||||
}
|
||||
|
||||
void OPL_Queue_Destroy(opl_callback_queue_t *queue) {
|
||||
|
||||
}
|
||||
|
||||
void OPL_Queue_Push(opl_callback_queue_t *queue,
|
||||
opl_callback_t callback, void *data,
|
||||
uint64_t time) {
|
||||
pheap_node_id_t id = ph_new_node(&opl_heap);
|
||||
assert(id); // check not full
|
||||
queue_entry_t *qe = get_entry(queue, id);
|
||||
qe->time = time;
|
||||
#if !LIMITED_CALLBACK_TYPES
|
||||
qe->callback = callback
|
||||
#else
|
||||
assert(data || callback == RestartSong);
|
||||
assert(!data || callback == TrackTimerCallback);
|
||||
#endif
|
||||
qe->data = data;
|
||||
ph_insert_node(&opl_heap, id);
|
||||
}
|
||||
|
||||
int OPL_Queue_Pop(opl_callback_queue_t *queue,
|
||||
opl_callback_t *callback, void **data) {
|
||||
if (!ph_peek_head(&opl_heap)) return 0;
|
||||
pheap_node_id_t id = ph_remove_head(&opl_heap, true);
|
||||
queue_entry_t *qe = get_entry(queue, id);
|
||||
#if !LIMITED_CALLBACK_TYPES
|
||||
*callback = qe->callback;
|
||||
#else
|
||||
*callback = qe->data ? TrackTimerCallback : RestartSong;
|
||||
#endif
|
||||
*data = qe->data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue) {
|
||||
pheap_node_id_t head = ph_peek_head(&opl_heap);
|
||||
if (head) {
|
||||
return get_entry(queue, head)->time;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint AdjustCallbacks(pheap_node_id_t id, uint64_t time, unsigned int old_tempo, unsigned int new_tempo) {
|
||||
uint count = 0;
|
||||
if (id) {
|
||||
pheap_node_t *node = ph_get_node(&opl_heap, id);
|
||||
queue_entry_t *entry = get_entry(&queue, id);
|
||||
uint64_t offset = entry->time - time;
|
||||
entry->time = time + (offset * new_tempo) / old_tempo;
|
||||
AdjustCallbacks(node->child, time, old_tempo, new_tempo);
|
||||
AdjustCallbacks(node->sibling, time, old_tempo, new_tempo);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *_queue,
|
||||
uint64_t time, unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
assert(_queue == &queue);
|
||||
AdjustCallbacks(ph_peek_head(&opl_heap), time, old_tempo, new_tempo);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "opl_queue.h"
|
||||
|
||||
#define MAX_OPL_QUEUE 64
|
||||
|
|
@ -71,7 +73,7 @@ void OPL_Queue_Push(opl_callback_queue_t *queue,
|
|||
|
||||
if (queue->num_entries >= MAX_OPL_QUEUE)
|
||||
{
|
||||
fprintf(stderr, "OPL_Queue_Push: Exceeded maximum callbacks\n");
|
||||
stderr_print( "OPL_Queue_Push: Exceeded maximum callbacks\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -202,11 +204,12 @@ uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue)
|
|||
}
|
||||
|
||||
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
|
||||
uint64_t time, float factor)
|
||||
uint64_t time, unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
int64_t offset;
|
||||
int i;
|
||||
|
||||
float factor = old_tempo / new_tempo;
|
||||
for (i = 0; i < queue->num_entries; ++i)
|
||||
{
|
||||
offset = queue->entries[i].time - time;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -32,8 +33,8 @@ void OPL_Queue_Push(opl_callback_queue_t *queue,
|
|||
int OPL_Queue_Pop(opl_callback_queue_t *queue,
|
||||
opl_callback_t *callback, void **data);
|
||||
uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue);
|
||||
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
|
||||
uint64_t time, float factor);
|
||||
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, uint64_t time,
|
||||
unsigned int old_tempo, unsigned int new_tempo);
|
||||
|
||||
#endif /* #ifndef OPL_QUEUE_H */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -24,8 +25,15 @@
|
|||
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
#include "doomtype.h"
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
#include "woody_opl.h"
|
||||
#elif USE_EMU8950_OPL
|
||||
#include "emu8950.h"
|
||||
#else
|
||||
#include "opl3.h"
|
||||
#endif
|
||||
|
||||
#include "opl.h"
|
||||
#include "opl_internal.h"
|
||||
|
|
@ -70,8 +78,16 @@ static uint64_t pause_offset;
|
|||
|
||||
// OPL software emulator structure.
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
// todo configure this based on woody build flag
|
||||
#define opl_op3mode 0
|
||||
#elif USE_EMU8950_OPL
|
||||
#define opl_op3mode 0
|
||||
static OPL *emu8950_opl;
|
||||
#else
|
||||
static opl3_chip opl_chip;
|
||||
static int opl_opl3mode;
|
||||
#endif
|
||||
|
||||
// Temporary mixing buffer used by the mixing callback.
|
||||
|
||||
|
|
@ -164,7 +180,20 @@ static void FillBuffer(uint8_t *buffer, unsigned int nsamples)
|
|||
|
||||
// OPL output is generated into temporary buffer and then mixed
|
||||
// (to avoid overflows etc.)
|
||||
#if USE_WOODY_OPL
|
||||
Bit16s *mb16 = (Bit16s *) mix_buffer;
|
||||
adlib_getsample(mb16, nsamples);
|
||||
for(int i=nsamples-1; i>=0; i--) {
|
||||
mb16[i*2] = mb16[i*2 + 1] = mb16[i];
|
||||
}
|
||||
#elif USE_EMU8950_OPL
|
||||
int16_t *buf = (int16_t *) mix_buffer;
|
||||
for(int i=0;i<nsamples;i++) {
|
||||
buf[i*2] = buf[i*2+1] = OPL_calc(emu8950_opl);
|
||||
}
|
||||
#else
|
||||
OPL3_GenerateStream(&opl_chip, (Bit16s *) mix_buffer, nsamples);
|
||||
#endif
|
||||
SDL_MixAudioFormat(buffer, mix_buffer, AUDIO_S16SYS, nsamples * 4,
|
||||
SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
|
|
@ -288,13 +317,13 @@ static int OPL_SDL_Init(unsigned int port_base)
|
|||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to set up sound.\n");
|
||||
stderr_print( "Unable to set up sound.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize()) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
|
||||
stderr_print( "Error initialising SDL_mixer: %s\n", Mix_GetError());
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return 0;
|
||||
|
|
@ -328,7 +357,7 @@ static int OPL_SDL_Init(unsigned int port_base)
|
|||
|
||||
if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
|
||||
{
|
||||
fprintf(stderr,
|
||||
stderr_print(
|
||||
"OPL_SDL only supports native signed 16-bit LSB, "
|
||||
"stereo format!\n");
|
||||
|
||||
|
|
@ -341,8 +370,14 @@ static int OPL_SDL_Init(unsigned int port_base)
|
|||
|
||||
// Create the emulator structure:
|
||||
|
||||
#if USE_WOODY_OPL
|
||||
adlib_init(mixing_freq);
|
||||
#elif USE_EMU8950_OPL
|
||||
emu8950_opl = OPL_new(3579552, mixing_freq); // todo check rate
|
||||
#else
|
||||
OPL3_Reset(&opl_chip, mixing_freq);
|
||||
opl_opl3mode = 0;
|
||||
#endif
|
||||
|
||||
callback_mutex = SDL_CreateMutex();
|
||||
callback_queue_mutex = SDL_CreateMutex();
|
||||
|
|
@ -432,10 +467,17 @@ static void WriteRegister(unsigned int reg_num, unsigned int value)
|
|||
break;
|
||||
|
||||
case OPL_REG_NEW:
|
||||
#if !USE_WOODY_OPL && !USE_EMU8950_OPL
|
||||
opl_opl3mode = value & 0x01;
|
||||
|
||||
#endif
|
||||
default:
|
||||
#if USE_WOODY_OPL
|
||||
adlib_write(reg_num, value);
|
||||
#elif USE_EMU8950_OPL
|
||||
OPL_writeReg(emu8950_opl, reg_num, value);
|
||||
#else
|
||||
OPL3_WriteRegBuffered(&opl_chip, reg_num, value);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -487,10 +529,10 @@ static void OPL_SDL_SetPaused(int paused)
|
|||
opl_sdl_paused = paused;
|
||||
}
|
||||
|
||||
static void OPL_SDL_AdjustCallbacks(float factor)
|
||||
static void OPL_SDL_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
SDL_LockMutex(callback_queue_mutex);
|
||||
OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor);
|
||||
OPL_Queue_AdjustCallbacks(callback_queue, current_time, old_tempo, new_tempo);
|
||||
SDL_UnlockMutex(callback_queue_mutex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -224,10 +225,10 @@ void OPL_Timer_ClearCallbacks(void)
|
|||
SDL_UnlockMutex(callback_queue_mutex);
|
||||
}
|
||||
|
||||
void OPL_Timer_AdjustCallbacks(float factor)
|
||||
void OPL_Timer_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo)
|
||||
{
|
||||
SDL_LockMutex(callback_queue_mutex);
|
||||
OPL_Queue_AdjustCallbacks(callback_queue, current_time, factor);
|
||||
OPL_Queue_AdjustCallbacks(callback_queue, current_time, old_tempo, new_tempo);
|
||||
SDL_UnlockMutex(callback_queue_mutex);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,7 +28,7 @@ void OPL_Timer_ClearCallbacks(void);
|
|||
void OPL_Timer_Lock(void);
|
||||
void OPL_Timer_Unlock(void);
|
||||
void OPL_Timer_SetPaused(int paused);
|
||||
void OPL_Timer_AdjustCallbacks(float factor);
|
||||
void OPL_Timer_AdjustCallbacks(unsigned int old_tempo, unsigned int new_tempo);
|
||||
|
||||
#endif /* #ifndef OPL_TIMER_H */
|
||||
|
||||
|
|
|
|||
655
opl/slot_render.cpp
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
/**
|
||||
* Copyright (C) 2001-2020 Mitsutaka Okazaki
|
||||
* Copyright (C) 2021-2022 Graham Sanderson
|
||||
*/
|
||||
#include "slot_render.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#if PICO_ON_DEVICE
|
||||
#include "hardware/interp.h"
|
||||
#define SLOT_RENDER_DATA __scratch_y("slot_render_cpp")
|
||||
#else
|
||||
#define SLOT_RENDER_DATA
|
||||
#endif
|
||||
|
||||
static_assert(PM_DPHASE > 0, "");
|
||||
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
|
||||
#if EMU8950_SLOT_RENDER
|
||||
#include <algorithm>
|
||||
|
||||
#define INLINE inline
|
||||
// todo combine all these tablles into 1
|
||||
static uint8_t eg_step_tables_fast[4][8] = {
|
||||
{1, 1, 1, 1, 1, 1, 1, 1},
|
||||
{1, 1, 1, 2, 1, 1, 1, 2},
|
||||
{1, 2, 1, 2, 1, 2, 1, 2},
|
||||
{1, 2, 2, 2, 1, 2, 2, 2},
|
||||
};
|
||||
static uint8_t eg_step_tables_fast2[4][8] = {
|
||||
{2, 2, 2, 2, 2, 2, 2, 2},
|
||||
{2, 2, 2, 4, 2, 2, 2, 4},
|
||||
{2, 4, 2, 4, 2, 4, 2, 4},
|
||||
{2, 4, 4, 4, 2, 4, 4, 4},
|
||||
};
|
||||
static uint8_t eg_step_tables[4][8] = {
|
||||
{0, 1, 0, 1, 0, 1, 0, 1},
|
||||
{0, 1, 0, 1, 1, 1, 0, 1},
|
||||
{0, 1, 1, 1, 0, 1, 1, 1},
|
||||
{0, 1, 1, 1, 1, 1, 1, 1},
|
||||
};
|
||||
static uint8_t eg_step_table4[8] = {
|
||||
4,4,4,4,4,4,4,4
|
||||
};
|
||||
// too can find this elesewhere
|
||||
static uint8_t eg_step_table0[8] = {
|
||||
0,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
// todo combine all these tablles into 1 - seems slower for now probably not in asm tho
|
||||
//static uint8_t eg_step[14][8] = {
|
||||
//#define ST_NORMAL 0
|
||||
// {0, 1, 0, 1, 0, 1, 0, 1},
|
||||
// {0, 1, 0, 1, 1, 1, 0, 1},
|
||||
// {0, 1, 1, 1, 0, 1, 1, 1},
|
||||
// {0, 1, 1, 1, 1, 1, 1, 1},
|
||||
//#define ST_FAST 4
|
||||
// {1, 1, 1, 1, 1, 1, 1, 1},
|
||||
// {1, 1, 1, 2, 1, 1, 1, 2},
|
||||
// {1, 2, 1, 2, 1, 2, 1, 2},
|
||||
// {1, 2, 2, 2, 1, 2, 2, 2},
|
||||
//#define ST_FAST2 8
|
||||
// {2, 2, 2, 2, 2, 2, 2, 2},
|
||||
// {2, 2, 2, 4, 2, 2, 2, 4},
|
||||
// {2, 4, 2, 4, 2, 4, 2, 4},
|
||||
// {2, 4, 4, 4, 2, 4, 4, 4},
|
||||
//#define ST_FOUR 12
|
||||
// {4, 4, 4, 4, 4, 4, 4, 4},
|
||||
//#define ST_ZERO 13
|
||||
// {0, 0, 0, 0, 0, 0, 0, 0},
|
||||
//};
|
||||
|
||||
/* offset to fnum, rough approximation of 14 cents depth. */
|
||||
static int8_t pm_table[8][PM_PG_WIDTH] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxx
|
||||
{0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxx
|
||||
{0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxx
|
||||
{0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxx
|
||||
{0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxx
|
||||
{0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxx
|
||||
{0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxx
|
||||
{0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxx
|
||||
};
|
||||
// todo we are probably fine with pm_table_half[x] = pm_table[x/2] >> 1 as an approcimation, but keeping separate for diff for now
|
||||
static int8_t pm_table_half[8][PM_PG_WIDTH] = {
|
||||
{0 >> 1, 0 >> 1, 0 >> 1, 0 >> 1, 0 >> 1, 0 >> 1, 0 >> 1, 0 >> 1}, // fnum = 000xxxxx
|
||||
{0 >> 1, 0 >> 1, 1 >> 1, 0 >> 1, 0 >> 1, 0 >> 1, -1 >> 1, 0 >> 1}, // fnum = 001xxxxx
|
||||
{0 >> 1, 1 >> 1, 2 >> 1, 1 >> 1, 0 >> 1, -1 >> 1, -2 >> 1, -1 >> 1}, // fnum = 010xxxxx
|
||||
{0 >> 1, 1 >> 1, 3 >> 1, 1 >> 1, 0 >> 1, -1 >> 1, -3 >> 1, -1 >> 1}, // fnum = 011xxxxx
|
||||
{0 >> 1, 2 >> 1, 4 >> 1, 2 >> 1, 0 >> 1, -2 >> 1, -4 >> 1, -2 >> 1}, // fnum = 100xxxxx
|
||||
{0 >> 1, 2 >> 1, 5 >> 1, 2 >> 1, 0 >> 1, -2 >> 1, -5 >> 1, -2 >> 1}, // fnum = 101xxxxx
|
||||
{0 >> 1, 3 >> 1, 6 >> 1, 3 >> 1, 0 >> 1, -3 >> 1, -6 >> 1, -3 >> 1}, // fnum = 110xxxxx
|
||||
{0 >> 1, 3 >> 1, 7 >> 1, 3 >> 1, 0 >> 1, -3 >> 1, -7 >> 1, -3 >> 1}, // fnum = 111xxxxx
|
||||
};
|
||||
|
||||
/* clang-format off */
|
||||
/* exp_table[255-x] = round((exp2((double)x / 256.0) - 1) * 1024) */
|
||||
static uint16_t exp_table[256] = {
|
||||
1024+1018, 1024+1013, 1024+1007, 1024+1002, 1024+996, 1024+991, 1024+986, 1024+980, 1024+975, 1024+969, 1024+964, 1024+959, 1024+953, 1024+948, 1024+942, 1024+937,
|
||||
1024+932, 1024+927, 1024+921, 1024+916, 1024+911, 1024+906, 1024+900, 1024+895, 1024+890, 1024+885, 1024+880, 1024+874, 1024+869, 1024+864, 1024+859, 1024+854,
|
||||
1024+849, 1024+844, 1024+839, 1024+834, 1024+829, 1024+824, 1024+819, 1024+814, 1024+809, 1024+804, 1024+799, 1024+794, 1024+789, 1024+784, 1024+779, 1024+774,
|
||||
1024+770, 1024+765, 1024+760, 1024+755, 1024+750, 1024+745, 1024+741, 1024+736, 1024+731, 1024+726, 1024+722, 1024+717, 1024+712, 1024+708, 1024+703, 1024+698,
|
||||
1024+693, 1024+689, 1024+684, 1024+680, 1024+675, 1024+670, 1024+666, 1024+661, 1024+657, 1024+652, 1024+648, 1024+643, 1024+639, 1024+634, 1024+630, 1024+625,
|
||||
1024+621, 1024+616, 1024+612, 1024+607, 1024+603, 1024+599, 1024+594, 1024+590, 1024+585, 1024+581, 1024+577, 1024+572, 1024+568, 1024+564, 1024+560, 1024+555,
|
||||
1024+551, 1024+547, 1024+542, 1024+538, 1024+534, 1024+530, 1024+526, 1024+521, 1024+517, 1024+513, 1024+509, 1024+505, 1024+501, 1024+496, 1024+492, 1024+488,
|
||||
1024+484, 1024+480, 1024+476, 1024+472, 1024+468, 1024+464, 1024+460, 1024+456, 1024+452, 1024+448, 1024+444, 1024+440, 1024+436, 1024+432, 1024+428, 1024+424,
|
||||
1024+420, 1024+416, 1024+412, 1024+409, 1024+405, 1024+401, 1024+397, 1024+393, 1024+389, 1024+385, 1024+382, 1024+378, 1024+374, 1024+370, 1024+367, 1024+363,
|
||||
1024+359, 1024+355, 1024+352, 1024+348, 1024+344, 1024+340, 1024+337, 1024+333, 1024+329, 1024+326, 1024+322, 1024+318, 1024+315, 1024+311, 1024+308, 1024+304,
|
||||
1024+300, 1024+297, 1024+293, 1024+290, 1024+286, 1024+283, 1024+279, 1024+276, 1024+272, 1024+268, 1024+265, 1024+262, 1024+258, 1024+255, 1024+251, 1024+248,
|
||||
1024+244, 1024+241, 1024+237, 1024+234, 1024+231, 1024+227, 1024+224, 1024+220, 1024+217, 1024+214, 1024+210, 1024+207, 1024+204, 1024+200, 1024+197, 1024+194,
|
||||
1024+190, 1024+187, 1024+184, 1024+181, 1024+177, 1024+174, 1024+171, 1024+168, 1024+164, 1024+161, 1024+158, 1024+155, 1024+152, 1024+148, 1024+145, 1024+142,
|
||||
1024+139, 1024+136, 1024+133, 1024+130, 1024+126, 1024+123, 1024+120, 1024+117, 1024+114, 1024+111, 1024+108, 1024+105, 1024+102, 1024+99, 1024+96, 1024+93,
|
||||
1024+90, 1024+87, 1024+84, 1024+81, 1024+78, 1024+75, 1024+72, 1024+69, 1024+66, 1024+63, 1024+60, 1024+57, 1024+54, 1024+51, 1024+48, 1024+45,
|
||||
1024+42, 1024+40, 1024+37, 1024+34, 1024+31, 1024+28, 1024+25, 1024+22, 1024+20, 1024+17, 1024+14, 1024+11, 1024+8, 1024+6, 1024+3, 1024+0,
|
||||
};
|
||||
|
||||
/* logsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */
|
||||
|
||||
static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2,
|
||||
8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2};
|
||||
|
||||
#if !EMU8950_NO_WAVE_TABLE_MAP
|
||||
#define LOGSIN_TABLE_SIZE PG_WIDTH / 4
|
||||
#else
|
||||
#define LOGSIN_TABLE_SIZE PG_WIDTH / 2
|
||||
#endif
|
||||
static uint16_t SLOT_RENDER_DATA logsin_table[LOGSIN_TABLE_SIZE] = {
|
||||
2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869,
|
||||
846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609,
|
||||
598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461,
|
||||
453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358,
|
||||
352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280,
|
||||
276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219,
|
||||
215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169,
|
||||
167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129,
|
||||
127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96,
|
||||
94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69,
|
||||
67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47,
|
||||
46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30,
|
||||
29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17,
|
||||
16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7,
|
||||
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
|
||||
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
#if EMU8950_NO_WAVE_TABLE_MAP
|
||||
// double the table size to include second 1/4 cycle
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2,
|
||||
2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7,
|
||||
7, 8, 8, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 15, 15, 16,
|
||||
17, 17, 18, 19, 20, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46,
|
||||
47, 48, 49, 51, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66, 67,
|
||||
69, 70, 72, 74, 75, 77, 78, 80, 82, 83, 85, 87, 89, 91, 92, 94,
|
||||
96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 125, 127,
|
||||
129, 131, 134, 136, 138, 141, 143, 146, 148, 151, 153, 156, 159, 161, 164, 167,
|
||||
169, 172, 175, 178, 181, 184, 187, 190, 193, 196, 199, 202, 205, 209, 212, 215,
|
||||
219, 222, 226, 229, 233, 236, 240, 244, 248, 251, 255, 259, 263, 267, 271, 276,
|
||||
280, 284, 289, 293, 297, 302, 307, 311, 316, 321, 326, 331, 336, 341, 347, 352,
|
||||
358, 363, 369, 375, 380, 386, 392, 399, 405, 411, 418, 425, 432, 439, 446, 453,
|
||||
461, 468, 476, 484, 492, 501, 509, 518, 527, 536, 546, 556, 566, 576, 587, 598,
|
||||
609, 621, 633, 646, 659, 672, 687, 701, 717, 732, 749, 767, 785, 804, 825, 846,
|
||||
869, 894, 920, 949, 979, 1013, 1050, 1091, 1137, 1190, 1252, 1326, 1419, 1543, 1731, 2137,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if EMU8950_NO_WAVE_TABLE_MAP
|
||||
// we start with
|
||||
// _ _
|
||||
// / \/ \ which is abs(sine) wave
|
||||
//
|
||||
static uint16_t wav_or_table_lookup[4][4] = {
|
||||
{0x0000, 0x0000, 0x8000, 0x8000}, // .. negate second half
|
||||
{0x0000, 0x0000, 0x0fff, 0x0fff}, // .. attenuate second half
|
||||
{0x0000, 0x0000, 0x0000, 0x0000}, // .. leave second half alone
|
||||
{0x0000, 0x0fff, 0x0000, 0x0fff}, // .. attenuate 1 and 3
|
||||
};
|
||||
#endif
|
||||
|
||||
static INLINE int16_t calc_sample(const SLOT_RENDER *slot, uint32_t index, int16_t am) {
|
||||
|
||||
#if !EMU8950_NO_WAVE_TABLE_MAP
|
||||
uint16_t h = slot->wave_table[index & (PG_WIDTH - 1)];
|
||||
#else
|
||||
#if PICO_ON_DEVICE
|
||||
interp0->accum[0] = index << 1;
|
||||
uint16_t h = *(uint16_t *)(interp0->peek[0]) | *(uint16_t *)(interp0->peek[1]);
|
||||
#else
|
||||
uint16_t h = slot->wav_or_table[(index >> (PG_BITS - 2)) & 3] | slot->logsin_table[(index & (PG_WIDTH / 2 - 1))];
|
||||
#endif
|
||||
#endif
|
||||
uint16_t att = h + slot->eg_out_tll_lsl3 + am;
|
||||
int16_t t = exp_table[att&0xff];
|
||||
// note we're really just bit clearing the original top bit 15 ..
|
||||
// todo presumably the & is ignored on ARM?
|
||||
int16_t res = t >> ((att>>8)&127);
|
||||
if (!res) return res; // maybe make things more compatible
|
||||
#if EMU8950_LINEAR_NEG_NOT_NOT
|
||||
return ((att & 0x8000) ? -res : res) << 1;
|
||||
#else
|
||||
return ((att & 0x8000) ? ~res : res) << 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
static uint8_t *get_attack_step_table(SLOT_RENDER *slot) {
|
||||
int index = slot->eg_rate_l;
|
||||
uint32_t hm1 = (slot->eg_rate_h - 1);
|
||||
if (hm1 < 12) {
|
||||
index += ST_NORMAL;
|
||||
} else if (hm1 >= 14) {
|
||||
// 0 and 15
|
||||
index = ST_ZERO;
|
||||
} else if (hm1 == 12) {
|
||||
index += ST_FAST;
|
||||
} else {
|
||||
index += ST_FAST2;
|
||||
}
|
||||
return eg_step[index];
|
||||
}
|
||||
|
||||
// note this is the same as attack except for 15 => ST_FOUR
|
||||
static uint8_t *get_decay_step_table(SLOT_RENDER *slot) {
|
||||
uint32_t hm1 = (slot->eg_rate_h - 1);
|
||||
int index = slot->eg_rate_l;
|
||||
if (hm1 < 12) {
|
||||
index += ST_NORMAL;
|
||||
} else if (hm1 >= 14) {
|
||||
index = hm1 == 14 ? ST_FOUR : ST_ZERO;
|
||||
} else if (hm1 == 12) {
|
||||
index += ST_FAST;
|
||||
} else {
|
||||
index += ST_FAST2;
|
||||
}
|
||||
return eg_step[index];
|
||||
}
|
||||
#else
|
||||
static uint8_t *get_attack_step_table(SLOT_RENDER *slot) {
|
||||
switch (slot->eg_rate_h) {
|
||||
case 13:
|
||||
return eg_step_tables_fast[slot->eg_rate_l];
|
||||
case 14:
|
||||
return eg_step_tables_fast2[slot->eg_rate_l];
|
||||
case 0:
|
||||
case 15:
|
||||
return eg_step_table0;
|
||||
default:
|
||||
return eg_step_tables[slot->eg_rate_l];
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t *get_decay_step_table(SLOT_RENDER *slot) {
|
||||
switch (slot->eg_rate_h) {
|
||||
case 0:
|
||||
return eg_step_table0;
|
||||
case 13:
|
||||
return eg_step_tables_fast[slot->eg_rate_l];
|
||||
case 14:
|
||||
return eg_step_tables_fast2[slot->eg_rate_l];
|
||||
case 15:
|
||||
return eg_step_table4;
|
||||
default:
|
||||
return eg_step_tables[slot->eg_rate_l];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <int EG_STATE> int get_parameter_rate(SLOT_RENDER *slot) {
|
||||
switch (EG_STATE) {
|
||||
case ATTACK:
|
||||
return slot->patch->AR;
|
||||
case DECAY:
|
||||
return slot->patch->DR;
|
||||
case SUSTAIN:
|
||||
return slot->patch->EG ? 0 : slot->patch->RR;
|
||||
case RELEASE:
|
||||
return slot->patch->RR;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <int EG_STATE> void commit_slot_update_eg_only(SLOT_RENDER *slot) {
|
||||
int p_rate = get_parameter_rate<EG_STATE>(slot);
|
||||
|
||||
if (p_rate == 0) {
|
||||
slot->eg_shift = 0;
|
||||
slot->eg_rate_h = 0;
|
||||
slot->eg_rate_l = 0;
|
||||
} else {
|
||||
slot->eg_rate_h = std::min(15, p_rate + (slot->rks >> 2));
|
||||
slot->eg_rate_l = slot->rks & 3;
|
||||
if (EG_STATE == ATTACK) {
|
||||
slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
|
||||
} else {
|
||||
slot->eg_shift = (slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool PM> uint32_t advance_phase(SLOT_RENDER *slot, uint32_t &pm_phase) {
|
||||
int8_t pm = 0;
|
||||
if (PM) {
|
||||
#if !PICO_ON_DEVICE
|
||||
// todo if we do this with interpolator, then we can just skip the if
|
||||
// todo PM_DPHASE == 512
|
||||
pm_phase = (pm_phase + PM_DPHASE) & (PM_DP_WIDTH - 1);
|
||||
pm = slot->efix_pm_table[pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
|
||||
#else
|
||||
interp1->add_raw[1] = 1;
|
||||
// printf("%08x %08x\n", interp1->accum[1], interp1->peek[1]);
|
||||
pm = *(int8_t *)interp1->peek[1];
|
||||
#endif
|
||||
}
|
||||
#if !PICO_ON_DEVICE
|
||||
slot->pg_phase += slot->efix_pg_pm_x_fnum3ff + pm * slot->efix_pg_phase_multiplier;
|
||||
#if EMU8950_NIT_PICKS
|
||||
slot->pg_phase &= (DP_WIDTH - 1) * 2; // note the clear bottom bit is just for exact compatability for comparison with original code
|
||||
#else
|
||||
slot->pg_phase &= (DP_WIDTH * 2 - 1); // should be quicker on ARM
|
||||
#endif
|
||||
return slot->pg_phase >> (DP_BASE_BITS + 1);
|
||||
#else
|
||||
static_assert(((DP_WIDTH * 2 - 1) >> (DP_BASE_BITS + 1)) == (1 << PG_BITS) -1, "");
|
||||
// pg_phase += pm * slot->efix_pg_phase_multiplier
|
||||
if (pm) {
|
||||
interp1->add_raw[0] = pm * slot->efix_pg_phase_multiplier;
|
||||
}
|
||||
interp1->add_raw[0] = interp1->base[0];
|
||||
// tmp = slot->pg_phase >> (DP_BASE_BITS + 1) & ((1 << PG_BITS) - 1);
|
||||
// slot->pg_phase += slot->efix_pg_pm_x_fnum3ff
|
||||
// return tmp;
|
||||
return interp1->add_raw[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
template<bool PM> void mod_am1_fb1_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
int16_t fm = (slot->output[1] + slot->output[0]) >> slot->nine_minus_FB;
|
||||
slot->output[1] = slot->output[0];
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
slot->mod_buffer[s] = slot->output[0] = calc_sample(slot, pg_out + fm, slot->lfo_am_buffer_lsl3[s]);
|
||||
}
|
||||
|
||||
template<bool PM> void mod_am1_fb0_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
slot->mod_buffer[s] = calc_sample(slot, pg_out, slot->lfo_am_buffer_lsl3[s]);
|
||||
}
|
||||
|
||||
template <bool PM> void mod_am0_fb1_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
int16_t fm = (slot->output[1] + slot->output[0]) >> slot->nine_minus_FB;
|
||||
slot->output[1] = slot->output[0];
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
|
||||
slot->mod_buffer[s] = slot->output[0] = calc_sample(slot, pg_out + fm, 0);
|
||||
}
|
||||
|
||||
template <bool PM> void mod_am0_fb0_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
slot->mod_buffer[s] = calc_sample(slot, pg_out, 0);
|
||||
|
||||
}
|
||||
|
||||
template <bool PM> void alg0_am1_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
// todo is this masking realy necessary; i doubt it. .. seems to be always even anyway
|
||||
// int32_t fm = 2 * (opl->mod_buffer[s] >> 1);
|
||||
int32_t fm = slot->mod_buffer[s];
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
int32_t val = calc_sample(slot, pg_out + fm, slot->lfo_am_buffer_lsl3[s]);
|
||||
slot->buffer[s] += val;
|
||||
}
|
||||
|
||||
template <bool PM> void alg0_am0_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
// todo could reset these when we start a new note
|
||||
// slot->output[1] = slot->output[0];
|
||||
|
||||
// todo is this masking realy necessary; i doubt it. .. seems to be always even anyway
|
||||
// int32_t fm = 2 * (opl->mod_buffer[s] >> 1);
|
||||
int32_t fm = slot->mod_buffer[s];
|
||||
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
int32_t val = calc_sample(slot, pg_out + fm, 0);
|
||||
slot->buffer[s] += val;
|
||||
}
|
||||
|
||||
|
||||
template <bool PM> void alg1_am1_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
int16_t val = calc_sample(slot, pg_out, slot->lfo_am_buffer_lsl3[s]);
|
||||
slot->buffer[s] += val + slot->mod_buffer[s];
|
||||
}
|
||||
|
||||
template <bool PM> void alg1_am0_fn(SLOT_RENDER *slot, uint32_t& pm_phase, uint32_t s) {
|
||||
uint32_t pg_out = advance_phase<PM>(slot, pm_phase);
|
||||
int16_t val = calc_sample(slot, pg_out, 0);
|
||||
slot->buffer[s] += val + slot->mod_buffer[s];
|
||||
}
|
||||
|
||||
#if PICO_ON_DEVICE
|
||||
extern "C" uint32_t test_slot_asm(SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint fn);
|
||||
#endif
|
||||
|
||||
template <int F_NUM, typename F> uint32_t slot_envelope_loop(F&& fn, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
// factored out as it is constant per call
|
||||
slot->efix_pg_phase_multiplier = ml_table[slot->patch->ML] << slot->blk;
|
||||
uint32_t efix_pg_pm_x_fnum3ff = (slot->fnum & 0x3ff) * slot->efix_pg_phase_multiplier;
|
||||
// pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
|
||||
// pm >>= (slot->pm_mode ? 0 : 1);
|
||||
int8_t *efix_pm_table = slot->pm_mode ? pm_table[(slot->fnum >> 7) & 7] :
|
||||
pm_table_half[(slot->fnum >> 7) & 7];
|
||||
#if !PICO_ON_DEVICE
|
||||
slot->wav_or_table = wav_or_table_lookup[slot->patch->WS & 3];
|
||||
slot->logsin_table = logsin_table;
|
||||
slot->efix_pg_pm_x_fnum3ff = efix_pg_pm_x_fnum3ff;
|
||||
slot->efix_pm_table = efix_pm_table;
|
||||
#else
|
||||
// note that index is pre-doubled
|
||||
// wave_table[(index >> (PG_BITS - 2)) & 3]
|
||||
interp_config c = interp_default_config();
|
||||
interp_config_set_shift(&c, PG_BITS - 2);
|
||||
interp_config_set_mask(&c, 1, 2);
|
||||
interp_set_config(interp0, 0, &c);
|
||||
interp0->base[0] = (uintptr_t)wav_or_table_lookup[slot->patch->WS & 3];
|
||||
c = interp_default_config();
|
||||
interp_config_set_cross_input(&c, true);
|
||||
interp_config_set_mask(&c, 1, PG_BITS - 1);
|
||||
interp_set_config(interp0, 1, &c);
|
||||
// logsin_table[(index & (PG_WIDTH / 2 - 1))];
|
||||
interp0->base[1] = (uintptr_t)logsin_table;
|
||||
|
||||
// return slot->pg_phase >> (DP_BASE_BITS + 1) & ((1 << PG_BITS) - 1);
|
||||
// post increment with
|
||||
c = interp_default_config();
|
||||
interp_config_set_add_raw(&c, true);
|
||||
interp_config_set_shift(&c, DP_BASE_BITS + 1);
|
||||
interp_config_set_mask(&c, 0, PG_BITS - 1);
|
||||
interp_set_config(interp1, 0, &c);
|
||||
slot->efix_pg_pm_x_fnum3ff = efix_pg_pm_x_fnum3ff;
|
||||
interp1->base[0] = efix_pg_pm_x_fnum3ff;
|
||||
interp1->accum[0] = slot->pg_phase;
|
||||
|
||||
c = interp_default_config();
|
||||
static_assert(PM_DPHASE == 1, ""); // better for everyone!
|
||||
// pm_phase = (pm_phase + PM_DPHASE) & (PM_DP_WIDTH - 1);
|
||||
// pm = slot->efix_pm_table[pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
|
||||
interp_config_set_shift(&c, PM_DP_BITS - PM_PG_BITS);
|
||||
interp_config_set_mask(&c, 0, PM_PG_BITS - 1);
|
||||
interp_set_config(interp1, 1, &c);
|
||||
interp1->base[1] = (uintptr_t)efix_pm_table;
|
||||
interp1->accum[1] = pm_phase;
|
||||
// we lookup in the lfo_am_buffer_lsl3 at inter0->base[2] + sample_ptr_in_mod_buffer / 2 for mod
|
||||
// or at inter0->base[2] + sample_ptr_in_buffer / 4
|
||||
if (F_NUM <8) {
|
||||
interp0->base[2] = (uintptr_t)slot->lfo_am_buffer_lsl3 - ((uintptr_t)slot->mod_buffer) / 2;
|
||||
} else {
|
||||
interp0->base[2] = (uintptr_t)slot->lfo_am_buffer_lsl3 - ((uintptr_t)slot->buffer) / 4;
|
||||
}
|
||||
interp1->base[2] = slot->efix_pg_phase_multiplier;
|
||||
#endif
|
||||
uint32_t s = 0;
|
||||
uint32_t nsamples_bak = nsamples;
|
||||
slot->eg_out_tll_lsl3 = std::min(EG_MAX/*EG_MUTE*/, slot->eg_out + slot->tll) << 3; // note EG_MAX not EG_MUTE to avoid overflow check later
|
||||
|
||||
#if PICO_ON_DEVICE && EMU8950_ASM
|
||||
// we lookup in mod_buffer at buffer_mod_buffer_offset + sample_ptr_in_buffer / 2
|
||||
slot->buffer_mod_buffer_offset = (uintptr_t)slot->mod_buffer - ((uintptr_t)slot->buffer) / 2;
|
||||
s = test_slot_asm(slot, nsamples, eg_counter, F_NUM);
|
||||
slot->pg_phase = interp1->accum[0];
|
||||
return s;
|
||||
#endif
|
||||
// todo eg_rate_h == 0 ...
|
||||
// todo prate == 0 ?
|
||||
if (slot->eg_state == ATTACK) {
|
||||
// note we roll eg_rate_h > 0 check which is (was) always paired with (eg_counter++ && eg_shift_mask) == 0
|
||||
// and guarantee that eg_counter is never zero because bit 31-30 area always set to 10 on entry
|
||||
uint32_t eg_shift_mask = slot->eg_rate_h > 0 ? (1 << slot->eg_shift) - 1 : 0xffffffff;
|
||||
uint8_t *eg_step_table = get_attack_step_table(slot);
|
||||
|
||||
// this original code did something like this every loop
|
||||
//
|
||||
// if ((++eg_counter & eg_shift_mask) == 0 && 0 < slot->eg_out)) {
|
||||
// // update eg_out etc.
|
||||
// }
|
||||
// if (!eg_out) {
|
||||
// // enter decay next time
|
||||
// }
|
||||
//
|
||||
// since eg_out can only change as a result of the if, we do
|
||||
//
|
||||
// a) before the loop
|
||||
//
|
||||
// if (!eg_out) {
|
||||
// eg_counter++;
|
||||
// // enter decay next time
|
||||
// }
|
||||
//
|
||||
// b) inside the loop
|
||||
//
|
||||
// if ((++eg_counter & eg_shift_mask) == 0) {
|
||||
// // update eg_out etc.
|
||||
// if (!eg_out) {
|
||||
// // enter decay next time
|
||||
// }
|
||||
// }
|
||||
if (slot->eg_out == 0) {
|
||||
// straight into decay
|
||||
eg_counter++;
|
||||
slot->eg_state = DECAY;
|
||||
commit_slot_update_eg_only<DECAY>(slot);
|
||||
fn(slot, pm_phase, s++);
|
||||
} else {
|
||||
for (; s < nsamples; s++) {
|
||||
#if DUMPO
|
||||
if (hack_ch == 17 && s == 12) breako();
|
||||
#endif
|
||||
if (unlikely((++eg_counter & eg_shift_mask) == 0)) {
|
||||
uint8_t step = eg_step_table[(eg_counter >> slot->eg_shift) & 7];
|
||||
slot->eg_out += (~slot->eg_out * step) >> 3;
|
||||
slot->eg_out_tll_lsl3 = std::min(EG_MAX/*EG_MUTE*/, slot->eg_out + slot->tll) << 3; // note EG_MAX not EG_MUTE to avoid overflow check later
|
||||
if (unlikely(slot->eg_out == 0)) {
|
||||
slot->eg_state = DECAY;
|
||||
commit_slot_update_eg_only<DECAY>(slot);
|
||||
nsamples = s;
|
||||
}
|
||||
}
|
||||
fn(slot, pm_phase, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slot->eg_state == DECAY) {
|
||||
nsamples = nsamples_bak;
|
||||
// todo if note ends we can stop early
|
||||
uint32_t eg_shift_mask = slot->eg_rate_h > 0 ? (1 << slot->eg_shift) - 1 : 0xffffffff;
|
||||
uint8_t *eg_step_table = get_decay_step_table(slot);
|
||||
// the original code checked for sustain every loop, however we have move it inside the once per 1<<eg_shift_mask check
|
||||
// so we must check it here outside the sample loop unless the first iteration of the sample loop would check it because of the
|
||||
// value of eg_counter
|
||||
if (!(((eg_counter+1) & eg_shift_mask) == 0) &&
|
||||
((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL)) {
|
||||
if (s < nsamples) {
|
||||
// because we have moved the second line of the test above into the per !(++eg_coiunter & eg_shift_maask))
|
||||
// test below, we have to half unroll the first loop
|
||||
slot->eg_state = SUSTAIN;
|
||||
eg_counter++;
|
||||
commit_slot_update_eg_only<SUSTAIN>(slot);
|
||||
fn(slot, pm_phase, s++);
|
||||
}
|
||||
} else {
|
||||
for (; s < nsamples; s++) {
|
||||
if (unlikely((++eg_counter & eg_shift_mask) == 0)) {
|
||||
slot->eg_out = static_cast<int16_t>(std::min(EG_MUTE, slot->eg_out + (int) eg_step_table[
|
||||
(eg_counter >> slot->eg_shift) & 7]));
|
||||
slot->eg_out_tll_lsl3 = std::min(EG_MAX/*EG_MUTE*/, slot->eg_out + slot->tll) << 3; // note EG_MAX not EG_MUTE to avoid overflow check later
|
||||
// todo check for decay to zero
|
||||
if (unlikely((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL)) {
|
||||
slot->eg_state = SUSTAIN;
|
||||
commit_slot_update_eg_only<SUSTAIN>(slot);
|
||||
nsamples = s;
|
||||
#if EMU8950_LINEAR_END_OF_NOTE_OPTIMIZATION
|
||||
} else if (slot->eg_out == EG_MUTE) {
|
||||
// we have decayed to zero, so give up
|
||||
nsamples = s;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
fn(slot, pm_phase, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slot->eg_state == SUSTAIN || slot->eg_state == RELEASE) {
|
||||
nsamples = nsamples_bak;
|
||||
// todo if note ends we can stop early
|
||||
uint32_t eg_shift_mask = slot->eg_rate_h > 0 ? (1 << slot->eg_shift) - 1 : 0xffffffff;
|
||||
uint8_t *eg_step_table = get_decay_step_table(slot);
|
||||
for (; s < nsamples; s++) {
|
||||
if (unlikely((++eg_counter & eg_shift_mask) == 0)) {
|
||||
slot->eg_out = static_cast<int16_t>(std::min(EG_MUTE, slot->eg_out + (int)eg_step_table[(eg_counter >> slot->eg_shift)&7]));
|
||||
#if EMU8950_LINEAR_END_OF_NOTE_OPTIMIZATION
|
||||
if (slot->eg_out == EG_MUTE) {
|
||||
// we have decayed to zero, so give up
|
||||
nsamples = s;
|
||||
}
|
||||
#endif
|
||||
slot->eg_out_tll_lsl3 = std::min(EG_MAX/*EG_MUTE*/, slot->eg_out + slot->tll) << 3; // note EG_MAX not EG_MUTE to avoid overflow check later
|
||||
}
|
||||
fn(slot, pm_phase, s);
|
||||
}
|
||||
}
|
||||
#if PICO_ON_DEVICE
|
||||
slot->pg_phase = interp1->accum[0];
|
||||
#endif
|
||||
// not necessary for correctness
|
||||
//#if EMU8950_NIT_PICKS
|
||||
// slot->pg_phase &= (DP_WIDTH - 1) * 2;
|
||||
//#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
typedef void OPL;
|
||||
|
||||
template<bool PM> uint32_t slot_mod_linear(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
if (slot->patch->AM) {
|
||||
if (slot->patch->FB) {
|
||||
slot->nine_minus_FB = 9 - slot->patch->FB;
|
||||
return slot_envelope_loop<6+PM>(mod_am1_fb1_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
} else {
|
||||
return slot_envelope_loop<2+PM>(mod_am1_fb0_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
} else {
|
||||
if (slot->patch->FB) {
|
||||
slot->nine_minus_FB = 9 - slot->patch->FB;
|
||||
return slot_envelope_loop<4+PM>(mod_am0_fb1_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
} else {
|
||||
return slot_envelope_loop<0+PM>(mod_am0_fb0_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" uint32_t slot_mod_linear(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
if (slot->patch->PM)
|
||||
return slot_mod_linear<true>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
else
|
||||
return slot_mod_linear<false>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
|
||||
template<bool PM> uint32_t slot_car_linear_alg0(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
if (slot->patch->AM) {
|
||||
return slot_envelope_loop<10+PM>(alg0_am1_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
} else {
|
||||
return slot_envelope_loop<8+PM>(alg0_am0_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" uint32_t slot_car_linear_alg0(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
if (slot->patch->PM)
|
||||
return slot_car_linear_alg0<true>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
else
|
||||
return slot_car_linear_alg0<false>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
|
||||
template<bool PM> uint32_t slot_car_linear_alg1(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
|
||||
if (slot->patch->AM) {
|
||||
return slot_envelope_loop<14+PM>(alg1_am1_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
} else {
|
||||
return slot_envelope_loop<12+PM>(alg1_am0_fn<PM>, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" uint32_t slot_car_linear_alg1(OPL *opl, SLOT_RENDER *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
|
||||
if (slot->patch->PM)
|
||||
return slot_car_linear_alg1<true>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
else
|
||||
return slot_car_linear_alg1<false>(opl, slot, nsamples, eg_counter, pm_phase);
|
||||
}
|
||||
#endif
|
||||
141
opl/slot_render.h
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* Copyright (C) 2001-2020 Mitsutaka Okazaki
|
||||
* Copyright (C) 2021-2022 Graham Sanderson
|
||||
*/
|
||||
#ifndef _SLOT_RENDER_H_
|
||||
#define _SLOT_RENDER_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#define EG_BITS 9
|
||||
#define EG_MUTE ((1 << EG_BITS) - 1)
|
||||
#define EG_MAX (0x1f0) // 93dB
|
||||
|
||||
/* sine table */
|
||||
#define PG_BITS 10 /* 2^10 = 1024 length sine table */
|
||||
#define PG_WIDTH (1 << PG_BITS)
|
||||
|
||||
/* phase increment counter */
|
||||
#define DP_BITS 20
|
||||
#define DP_WIDTH (1 << DP_BITS)
|
||||
#define DP_BASE_BITS (DP_BITS - PG_BITS)
|
||||
|
||||
/* pitch modulator */
|
||||
#define PM_PG_BITS 3
|
||||
#define PM_PG_WIDTH (1 << PM_PG_BITS)
|
||||
|
||||
// no benefit that I can see to this being 22 - it just makes PM_DPHASE unruly at 512
|
||||
#if 0
|
||||
#define PM_DP_BITS 22
|
||||
#else
|
||||
#define PM_DP_BITS 13
|
||||
#endif
|
||||
#define PM_DP_WIDTH (1 << PM_DP_BITS)
|
||||
#define PM_DPHASE (PM_DP_WIDTH / (1024 * 8))
|
||||
|
||||
/* voice data */
|
||||
typedef struct __OPL_PATCH {
|
||||
#if !EMU8950_NO_TLL
|
||||
/* 0 */ uint8_t TL;
|
||||
/* 1 */ uint8_t KL;
|
||||
#else
|
||||
/* 0 */ uint8_t TL4;
|
||||
/* 1 */ uint8_t KL_SHIFT;
|
||||
#endif
|
||||
/* 2 */ uint8_t FB;
|
||||
/* 3 */ uint8_t EG;
|
||||
/* 4 */ uint8_t ML;
|
||||
/* 5 */ uint8_t AR;
|
||||
/* 6 */ uint8_t DR;
|
||||
/* 7 */ uint8_t SL;
|
||||
/* 8 */ uint8_t RR;
|
||||
/* 9 */ uint8_t KR;
|
||||
/* a */ uint8_t AM;
|
||||
/* b */ uint8_t PM;
|
||||
uint8_t WS;
|
||||
} OPL_PATCH;
|
||||
|
||||
enum __OPL_EG_STATE
|
||||
{
|
||||
ATTACK, DECAY, SUSTAIN, RELEASE, UNKNOWN
|
||||
};
|
||||
|
||||
struct SLOT_RENDER {
|
||||
// stutff the assembly cares about is duplicate at the top so we can be sure of its order witout #ifs
|
||||
#if EMU8950_ASM
|
||||
// note there is code that writes zero to all for at once
|
||||
/* 0x00 */ uint8_t eg_state; /* current state */
|
||||
/* 0x01 */ uint8_t eg_rate_h; /* eg speed rate high 4bits */
|
||||
/* 0x02 */ uint8_t eg_rate_l; /* eg speed rate low 2bits */
|
||||
/* 0x03 */ uint8_t eg_shift; /* shift for eg global counter, controls envelope speed */
|
||||
|
||||
/* 0x04 */ uint8_t nine_minus_FB;
|
||||
/* 0x05 */ uint8_t rks; /* key scale offset (rks) for eg speed */
|
||||
uint16_t pad2;
|
||||
/* 0x08 */ int16_t eg_out; /* eg output */
|
||||
/* 0x0a */ uint16_t tll; /* total level + key scale level*/
|
||||
/* 0x0c */ int32_t eg_out_tll_lsl3; /* (eg_out + tll) << 3 */
|
||||
// only used for mod slot
|
||||
/* 0x10 */ int32_t output[2]; /* output value, latest and previous. */
|
||||
// these are actually opl local not slot local
|
||||
/* 0x18 */ int16_t *mod_buffer;
|
||||
/* 0x1c */ int32_t *buffer;
|
||||
/* 0x20 */ uintptr_t buffer_mod_buffer_offset;
|
||||
/* 0x24 */ OPL_PATCH *patch;
|
||||
#else
|
||||
uint8_t eg_state; /* current state */
|
||||
uint8_t eg_rate_h; /* eg speed rate high 4bits */
|
||||
uint8_t eg_rate_l; /* eg speed rate low 2bits */
|
||||
#if EMU8950_SLOT_RENDER
|
||||
uint8_t nine_minus_FB;
|
||||
int32_t eg_out_tll_lsl3; /* (eg_out + tll) << 3 */
|
||||
#endif
|
||||
uint8_t rks; /* key scale offset (rks) for eg speed */
|
||||
int16_t eg_out; /* eg output */
|
||||
uint16_t tll; /* total level + key scale level*/
|
||||
// only used for mod slot
|
||||
int32_t output[2]; /* output value, latest and previous. */
|
||||
// these are actually opl local not slot local
|
||||
int16_t *mod_buffer;
|
||||
int32_t *buffer;
|
||||
uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */
|
||||
OPL_PATCH *patch;
|
||||
#endif
|
||||
uint16_t fnum; /* f-number (9 bits) */
|
||||
uint16_t efix_pg_phase_multiplier; /* ml_table[slot->patch->ML]) << slot->blk >> 1 */
|
||||
uint8_t blk; /* block (3 bits) */
|
||||
uint32_t pg_phase; /* pg phase */ // note this moves twice as fast in slot_render version
|
||||
#if EMU8950_SLOT_RENDER
|
||||
//#if !PICO_ON_DEVICE
|
||||
uint32_t efix_pg_pm_x_fnum3ff; /* efix_pg_phase_multiplier * (fnum & 0x3ff) */
|
||||
//#endif
|
||||
#endif
|
||||
|
||||
#if EMU8950_SLOT_RENDER
|
||||
uint8_t *lfo_am_buffer_lsl3;
|
||||
#else
|
||||
uint8_t *lfo_am_buffer;
|
||||
#endif
|
||||
uint8_t pm_mode;
|
||||
|
||||
#if !EMU8950_NO_WAVE_TABLE_MAP
|
||||
uint16_t *wave_table; /* wave table */
|
||||
#else
|
||||
#if EMU8950_SLOT_RENDER
|
||||
#if !PICO_ON_DEVICE
|
||||
uint16_t *logsin_table;
|
||||
int8_t *efix_pm_table;
|
||||
#endif
|
||||
#endif
|
||||
#if !PICO_ON_DEVICE
|
||||
uint16_t *wav_or_table;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
789
opl/slot_render_pico.S
Normal file
|
|
@ -0,0 +1,789 @@
|
|||
//
|
||||
// Copyright (C) 2001-2020 Mitsutaka Okazaki
|
||||
// Copyright (C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
.syntax unified
|
||||
|
||||
#define ATTACK 0
|
||||
#define DECAY 1
|
||||
#define SUSTAIN 2
|
||||
#define RELEASE 3
|
||||
|
||||
//.section .data.slot_render_asm
|
||||
.section .scratch_y.slot_render_S, "a"
|
||||
exp_table:
|
||||
.hword 1024+1018, 1024+1013, 1024+1007, 1024+1002, 1024+996, 1024+991, 1024+986, 1024+980, 1024+975, 1024+969, 1024+964, 1024+959, 1024+953, 1024+948, 1024+942, 1024+937
|
||||
.hword 1024+932, 1024+927, 1024+921, 1024+916, 1024+911, 1024+906, 1024+900, 1024+895, 1024+890, 1024+885, 1024+880, 1024+874, 1024+869, 1024+864, 1024+859, 1024+854
|
||||
.hword 1024+849, 1024+844, 1024+839, 1024+834, 1024+829, 1024+824, 1024+819, 1024+814, 1024+809, 1024+804, 1024+799, 1024+794, 1024+789, 1024+784, 1024+779, 1024+774
|
||||
.hword 1024+770, 1024+765, 1024+760, 1024+755, 1024+750, 1024+745, 1024+741, 1024+736, 1024+731, 1024+726, 1024+722, 1024+717, 1024+712, 1024+708, 1024+703, 1024+698
|
||||
.hword 1024+693, 1024+689, 1024+684, 1024+680, 1024+675, 1024+670, 1024+666, 1024+661, 1024+657, 1024+652, 1024+648, 1024+643, 1024+639, 1024+634, 1024+630, 1024+625
|
||||
.hword 1024+621, 1024+616, 1024+612, 1024+607, 1024+603, 1024+599, 1024+594, 1024+590, 1024+585, 1024+581, 1024+577, 1024+572, 1024+568, 1024+564, 1024+560, 1024+555
|
||||
.hword 1024+551, 1024+547, 1024+542, 1024+538, 1024+534, 1024+530, 1024+526, 1024+521, 1024+517, 1024+513, 1024+509, 1024+505, 1024+501, 1024+496, 1024+492, 1024+488
|
||||
.hword 1024+484, 1024+480, 1024+476, 1024+472, 1024+468, 1024+464, 1024+460, 1024+456, 1024+452, 1024+448, 1024+444, 1024+440, 1024+436, 1024+432, 1024+428, 1024+424
|
||||
.hword 1024+420, 1024+416, 1024+412, 1024+409, 1024+405, 1024+401, 1024+397, 1024+393, 1024+389, 1024+385, 1024+382, 1024+378, 1024+374, 1024+370, 1024+367, 1024+363
|
||||
.hword 1024+359, 1024+355, 1024+352, 1024+348, 1024+344, 1024+340, 1024+337, 1024+333, 1024+329, 1024+326, 1024+322, 1024+318, 1024+315, 1024+311, 1024+308, 1024+304
|
||||
.hword 1024+300, 1024+297, 1024+293, 1024+290, 1024+286, 1024+283, 1024+279, 1024+276, 1024+272, 1024+268, 1024+265, 1024+262, 1024+258, 1024+255, 1024+251, 1024+248
|
||||
.hword 1024+244, 1024+241, 1024+237, 1024+234, 1024+231, 1024+227, 1024+224, 1024+220, 1024+217, 1024+214, 1024+210, 1024+207, 1024+204, 1024+200, 1024+197, 1024+194
|
||||
.hword 1024+190, 1024+187, 1024+184, 1024+181, 1024+177, 1024+174, 1024+171, 1024+168, 1024+164, 1024+161, 1024+158, 1024+155, 1024+152, 1024+148, 1024+145, 1024+142
|
||||
.hword 1024+139, 1024+136, 1024+133, 1024+130, 1024+126, 1024+123, 1024+120, 1024+117, 1024+114, 1024+111, 1024+108, 1024+105, 1024+102, 1024+99, 1024+96, 1024+93
|
||||
.hword 1024+90, 1024+87, 1024+84, 1024+81, 1024+78, 1024+75, 1024+72, 1024+69, 1024+66, 1024+63, 1024+60, 1024+57, 1024+54, 1024+51, 1024+48, 1024+45
|
||||
.hword 1024+42, 1024+40, 1024+37, 1024+34, 1024+31, 1024+28, 1024+25, 1024+22, 1024+20, 1024+17, 1024+14, 1024+11, 1024+8, 1024+6, 1024+3, 1024+0
|
||||
|
||||
eg_step_tables: // note the defines have 0x80 to indicate that there are 4 x 8 entries based for each of eg_rate_l
|
||||
#define ST_NORMAL ((0 * 8) | 0x80)
|
||||
.byte 0, 1, 0, 1, 0, 1, 0, 1
|
||||
.byte 0, 1, 0, 1, 1, 1, 0, 1
|
||||
.byte 0, 1, 1, 1, 0, 1, 1, 1
|
||||
.byte 0, 1, 1, 1, 1, 1, 1, 1
|
||||
#define ST_FAST ((4 * 8) | 0x80)
|
||||
.byte 1, 1, 1, 1, 1, 1, 1, 1
|
||||
.byte 1, 1, 1, 2, 1, 1, 1, 2
|
||||
.byte 1, 2, 1, 2, 1, 2, 1, 2
|
||||
.byte 1, 2, 2, 2, 1, 2, 2, 2
|
||||
#define ST_FAST2 ((8 * 8) | 0x80)
|
||||
.byte 2, 2, 2, 2, 2, 2, 2, 2
|
||||
.byte 2, 2, 2, 4, 2, 2, 2, 4
|
||||
.byte 2, 4, 2, 4, 2, 4, 2, 4
|
||||
.byte 2, 4, 4, 4, 2, 4, 4, 4
|
||||
#define ST_FOUR (12 * 8)
|
||||
.byte 4, 4, 4, 4, 4, 4, 4, 4
|
||||
#define ST_ZERO (13 * 8)
|
||||
.byte 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
attack_rate_def_table:
|
||||
.byte ST_ZERO, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_FAST, ST_FAST2, ST_ZERO
|
||||
|
||||
decay_rate_def_table:
|
||||
.byte ST_ZERO, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_NORMAL, ST_NORMAL, ST_NORMAL
|
||||
.byte ST_NORMAL, ST_FAST, ST_FAST2, ST_FOUR
|
||||
|
||||
.section .text.slot_render_asm
|
||||
#include "pico/asm_helper.S"
|
||||
#include "hardware/regs/sio.h"
|
||||
|
||||
#define rl_slot r0
|
||||
#define rl_sample_out r1
|
||||
#define rl_eg_counter r2
|
||||
#define rl_eg_shift_mask r3
|
||||
#define rl_tmp0 r4
|
||||
#define rl_tmp1 r5
|
||||
#define rl_tmp2 r6
|
||||
#define rl_interp r7
|
||||
|
||||
#define rh_begin_loop r8
|
||||
#define rh_end_loop r9
|
||||
#define rh_sample_out_end r10
|
||||
#define rh_fn r11
|
||||
#define rh_exp_table r12
|
||||
// only used for alg0 and alg1; hmm. don't care
|
||||
#define rh_buffer_mod_buffer_offset lr
|
||||
|
||||
#define INTERP0_ACCUM0 (SIO_INTERP0_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP0_PEEK0 (SIO_INTERP0_PEEK_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP0_PEEK1 (SIO_INTERP0_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP0_BASE2_AKA_LFO_AM_BUFFER_LSL3_OFFSET (SIO_INTERP0_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_PEEK0 (SIO_INTERP1_PEEK_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_PEEK1 (SIO_INTERP1_PEEK_LANE1_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_POP0 (SIO_INTERP1_POP_LANE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_ADD_RAW0 (SIO_INTERP1_ACCUM0_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_ADD_RAW1 (SIO_INTERP1_ACCUM1_ADD_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_BASE0 (SIO_INTERP1_BASE0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
#define INTERP1_BASE2_AKA_PG_PHASE_MULTIPLIER (SIO_INTERP1_BASE2_OFFSET - SIO_INTERP0_ACCUM0_OFFSET)
|
||||
|
||||
#define SLOTB_EG_STATE 0x00
|
||||
#define SLOTB_EG_RATE_H 0x01
|
||||
#define SLOTB_EG_RATE_L 0x02
|
||||
#define SLOTB_EG_SHIFT 0x03
|
||||
#define SLOTB_NINE_MINUS_FB 0x04
|
||||
#define SLOTB_RKS 0x05
|
||||
#define SLOTH_EG_OUT 0x08
|
||||
#define SLOTH_TLL 0x0a
|
||||
#define SLOTW_EG_OUT_TLL_LSL3 0x0c
|
||||
#define SLOTW_OUTPUT0 0x10
|
||||
#define SLOTW_OUTPUT1 0x14
|
||||
#define SLOTW_MOD_BUFFER 0x18
|
||||
#define SLOTW_BUFFER 0x1c
|
||||
#define SLOTW_BUFFER_MOD_BUFFER_OFFSET 0x20
|
||||
#define SLOTW_PATCH 0x24
|
||||
|
||||
#define PATCH_EG 0x3
|
||||
#define PATCH_DR 0x6
|
||||
#define PATCH_SL 0x7
|
||||
#define PATCH_RR 0x8
|
||||
|
||||
.macro advance_phase_pm0
|
||||
ldr rl_tmp0, [rl_interp, #INTERP1_BASE0]
|
||||
str rl_tmp0, [rl_interp, #INTERP1_ADD_RAW0]
|
||||
ldr rl_tmp0, [rl_interp, #INTERP1_ADD_RAW0]
|
||||
.endm
|
||||
|
||||
.macro advance_phase_pm1
|
||||
movs rl_tmp1, #1
|
||||
str rl_tmp1, [rl_interp, #INTERP1_ADD_RAW1]
|
||||
ldr rl_tmp0, [rl_interp, #INTERP1_PEEK1]
|
||||
ldrb rl_tmp0, [rl_tmp0] // pm
|
||||
sxtb rl_tmp0, rl_tmp0
|
||||
ldr rl_tmp1, [rl_interp, #INTERP1_BASE2_AKA_PG_PHASE_MULTIPLIER]
|
||||
muls rl_tmp0, rl_tmp1
|
||||
ldr rl_tmp1, [rl_interp, #INTERP1_BASE0]
|
||||
adds rl_tmp0, rl_tmp1
|
||||
str rl_tmp0, [rl_interp, #INTERP1_ADD_RAW0]
|
||||
ldr rl_tmp0, [rl_interp, #INTERP1_ADD_RAW0]
|
||||
.endm
|
||||
|
||||
.macro calc_sample is_am
|
||||
// interp0->accum[0] = index << 1;
|
||||
lsls rl_tmp0, #1 // todo can we avoid this
|
||||
str rl_tmp0, [rl_interp, #INTERP0_ACCUM0]
|
||||
|
||||
// uint16_t h = *(uint16_t *)(interp0->peek[0]) | *(uint16_t *)(interp0->peek[1]);
|
||||
ldr rl_tmp1, [rl_interp, #INTERP0_PEEK0]
|
||||
ldr rl_tmp0, [rl_interp, #INTERP0_PEEK1]
|
||||
ldrh rl_tmp0, [rl_tmp0]
|
||||
ldrh rl_tmp1, [rl_tmp1]
|
||||
orrs rl_tmp1, rl_tmp0 // rl_tmp1 = att
|
||||
|
||||
// uint16_t att = h + slot->eg_out_tll_lsl3
|
||||
ldr rl_tmp0, [rl_slot, #SLOTW_EG_OUT_TLL_LSL3]
|
||||
adds rl_tmp1, rl_tmp0
|
||||
// if (am) { h += am }
|
||||
.if \is_am
|
||||
adds rl_tmp1, rl_tmp2
|
||||
.endif
|
||||
|
||||
// int16_t t = exp_table[att&0xff];
|
||||
lsls rl_tmp0, rl_tmp1, #24
|
||||
lsrs rl_tmp0, rl_tmp0, #23 // rl_tmp0 = (att&0xff) *2
|
||||
add rl_tmp0, rh_exp_table // or load
|
||||
ldrh rl_tmp0, [rl_tmp0]
|
||||
|
||||
// int16_t res = t >> ((att>>8)&127);
|
||||
// todo this is currently res : ~res but we can inverted our OR table
|
||||
// return ((att & 0x8000) ? ~res : res);
|
||||
lsls rl_tmp1, #17
|
||||
sbcs rl_tmp2, rl_tmp2
|
||||
|
||||
mvns rl_tmp2, rl_tmp2 // todo remove this too, but we need to fix up fm
|
||||
|
||||
lsrs rl_tmp1, #25
|
||||
lsrs rl_tmp0, rl_tmp1
|
||||
// todo: inaudible todo force zeros for now
|
||||
beq 9f
|
||||
# note this matches the original ~res << 1 code, however
|
||||
# we don't have an early cutoff for full attenuation, so get -2 not 0 in some
|
||||
# cases, however this was true of the original code too; the annoying thing
|
||||
# is that it isn't trivial to fix up the - otherwise I think unimportant - differences for the sake of diff
|
||||
eors rl_tmp0, rl_tmp2
|
||||
lsls rl_tmp0, #1 // todo remove
|
||||
9:
|
||||
.endm
|
||||
|
||||
.macro calc_sample_am0
|
||||
calc_sample 0
|
||||
.endm
|
||||
.macro calc_sample_am1_lsr1
|
||||
// must preserve rl_tmp0... set am << 3 in rl_tmp2
|
||||
ldr rl_tmp1, [rl_interp, #INTERP0_BASE2_AKA_LFO_AM_BUFFER_LSL3_OFFSET]
|
||||
lsrs rl_tmp2, rl_sample_out, #1
|
||||
ldrb rl_tmp2, [rl_tmp1, rl_tmp2]
|
||||
calc_sample 1
|
||||
.endm
|
||||
|
||||
.macro calc_sample_am1_lsr2
|
||||
// must preserve rl_tmp0... set am << 3 in rl_tmp2
|
||||
ldr rl_tmp1, [rl_interp, #INTERP0_BASE2_AKA_LFO_AM_BUFFER_LSL3_OFFSET]
|
||||
lsrs rl_tmp2, rl_sample_out, #2
|
||||
ldrb rl_tmp2, [rl_tmp1, rl_tmp2]
|
||||
calc_sample 1
|
||||
.endm
|
||||
|
||||
.macro mod_sample_done
|
||||
strh rl_tmp0, [rl_sample_out]
|
||||
adds rl_sample_out, #2
|
||||
cmp rl_sample_out, rh_sample_out_end
|
||||
bge 9f
|
||||
bx rh_begin_loop
|
||||
9: bx rh_end_loop
|
||||
.endm
|
||||
|
||||
.macro add_fb_fm
|
||||
ldr rl_tmp1, [rl_slot, #SLOTW_OUTPUT0]
|
||||
ldr rl_tmp2, [rl_slot, #SLOTW_OUTPUT1]
|
||||
str rl_tmp1, [rl_slot, #SLOTW_OUTPUT1]
|
||||
adds rl_tmp1, rl_tmp2
|
||||
ldrb rl_tmp2, [rl_slot, #SLOTB_NINE_MINUS_FB]
|
||||
lsrs rl_tmp1, rl_tmp2
|
||||
adds rl_tmp0, rl_tmp1
|
||||
.endm
|
||||
|
||||
.thumb_func
|
||||
mod_am0_fb0_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
calc_sample_am0 // tmp0 = pg_out -> tmp0 = sample
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am0_fb0_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
calc_sample_am0 // tmp0 = pg_out -> tmp0 = sample
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am1_fb0_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
calc_sample_am1_lsr1 // tmp0 = pg_out -> tmp0 = sample
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am1_fb0_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
|
||||
calc_sample_am1_lsr1 // tmp0 = pg_out -> tmp0 = sample
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am0_fb1_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
add_fb_fm // tmp0 = pg_out -> tmp0 = pg_out + self_fm()
|
||||
calc_sample_am0 // tmp0 = pg_out + fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am0_fb1_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
add_fb_fm // tmp0 = pg_out -> tmp0 = pg_out + self_fm()
|
||||
calc_sample_am0 // tmp0 = pg_out + fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am1_fb1_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
add_fb_fm // tmp0 = pg_out -> tmp0 = pg_out + self_fm()
|
||||
calc_sample_am1_lsr1 // tmp0 = pg_out + fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
mod_sample_done
|
||||
|
||||
.thumb_func
|
||||
mod_am1_fb1_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
add_fb_fm // tmp0 = pg_out -> tmp0 = pg_out + self_fm()
|
||||
calc_sample_am1_lsr1 // tmp0 = pg_out + fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
mod_sample_done
|
||||
|
||||
.macro alg_sample_done
|
||||
ldr rl_tmp1, [rl_sample_out]
|
||||
add rl_tmp1, rl_tmp0
|
||||
str rl_tmp1, [rl_sample_out]
|
||||
adds rl_sample_out, #4
|
||||
cmp rl_sample_out, rh_sample_out_end
|
||||
bge 9f
|
||||
bx rh_begin_loop
|
||||
9: bx rh_end_loop
|
||||
.endm
|
||||
|
||||
// ah these two are the same, just called in differnt places
|
||||
.macro alg0_f_mod
|
||||
lsrs rl_tmp1, rl_sample_out, #1
|
||||
add rl_tmp1, rh_buffer_mod_buffer_offset
|
||||
ldrh rl_tmp2, [rl_tmp1]
|
||||
add rl_tmp0, rl_tmp2
|
||||
.endm
|
||||
|
||||
.macro alg1_a_mod
|
||||
lsrs rl_tmp1, rl_sample_out, #1
|
||||
add rl_tmp1, rh_buffer_mod_buffer_offset
|
||||
ldrh rl_tmp2, [rl_tmp1]
|
||||
add rl_tmp0, rl_tmp2
|
||||
.endm
|
||||
|
||||
// alg0 is FM by the mod slot
|
||||
.thumb_func
|
||||
alg0_am0_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
alg0_f_mod // tmp0 = pg_out -> tmp0 = pg_out + mod_slot[s]
|
||||
calc_sample_am0 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg0_am0_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
alg0_f_mod // tmp0 = pg_out -> tmp0 = pg_out + mod_slot[s]
|
||||
calc_sample_am0 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg0_am1_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
alg0_f_mod // tmp0 = pg_out -> tmp0 = pg_out + mod_slot[s]
|
||||
calc_sample_am1_lsr2 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg0_am1_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
alg0_f_mod // tmp0 = pg_out -> tmp0 = pg_out + mod_slot[s]
|
||||
calc_sample_am1_lsr2 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
str rl_tmp0, [rl_slot, #SLOTW_OUTPUT0]
|
||||
alg_sample_done
|
||||
|
||||
// alg1 is AM by the mod slot
|
||||
.thumb_func
|
||||
alg1_am0_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
calc_sample_am0 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
alg1_a_mod // tmp0 = sample-> tmp- = sample + mod_slot[s]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg1_am0_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
calc_sample_am0 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
alg1_a_mod // tmp0 = sample-> tmp- = sample + mod_slot[s]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg1_am1_pm0_fn:
|
||||
advance_phase_pm0 // -> tmp0 = pg_out
|
||||
calc_sample_am1_lsr2 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
alg1_a_mod // tmp0 = sample-> tmp- = sample + mod_slot[s]
|
||||
alg_sample_done
|
||||
|
||||
.thumb_func
|
||||
alg1_am1_pm1_fn:
|
||||
advance_phase_pm1 // -> tmp0 = pg_out
|
||||
calc_sample_am1_lsr2 // tmp0 = pg_out + mod_fm -> tmp0 = sample
|
||||
alg1_a_mod // tmp0 = sample-> tmp- = sample + mod_slot[s]
|
||||
alg_sample_done
|
||||
|
||||
.global slot_render_fn_table
|
||||
.align 4
|
||||
slot_render_fn_table:
|
||||
.word mod_am0_fb0_pm0_fn
|
||||
.word mod_am0_fb0_pm1_fn
|
||||
.word mod_am1_fb0_pm0_fn
|
||||
.word mod_am1_fb0_pm1_fn
|
||||
.word mod_am0_fb1_pm0_fn
|
||||
.word mod_am0_fb1_pm1_fn
|
||||
.word mod_am1_fb1_pm0_fn
|
||||
.word mod_am1_fb1_pm1_fn
|
||||
.word alg0_am0_pm0_fn
|
||||
.word alg0_am0_pm1_fn
|
||||
.word alg0_am1_pm0_fn
|
||||
.word alg0_am1_pm1_fn
|
||||
.word alg1_am0_pm0_fn
|
||||
.word alg1_am0_pm1_fn
|
||||
.word alg1_am1_pm0_fn
|
||||
.word alg1_am1_pm1_fn
|
||||
|
||||
.macro set_eg_shift_mask
|
||||
ldrb rl_eg_shift_mask, [rl_slot, #SLOTB_EG_RATE_H]
|
||||
cmp rl_eg_shift_mask, #0
|
||||
beq 9f
|
||||
ldrb rl_tmp0, [rl_slot, #SLOTB_EG_SHIFT]
|
||||
movs rl_eg_shift_mask, #1
|
||||
lsls rl_eg_shift_mask, rl_tmp0
|
||||
9:
|
||||
subs rl_eg_shift_mask, #1
|
||||
.endm
|
||||
|
||||
// rl_tmp0 = eg_out
|
||||
.macro update_eg_out_tll_lsl3
|
||||
//slot->eg_out_tll_lsl3 = std::min(EG_MAX/*EG_MUTE*/, slot->eg_out + slot->tll) << 3; // note EG_MAX not EG_MUTE to avoid overflow check later
|
||||
ldrh rl_tmp1, [rl_slot, #SLOTH_TLL]
|
||||
adds rl_tmp0, rl_tmp1
|
||||
movs rl_tmp2, #0x1f
|
||||
lsls rl_tmp2, #4
|
||||
cmp rl_tmp0, rl_tmp2
|
||||
ble 9f
|
||||
movs rl_tmp0, rl_tmp2
|
||||
9:
|
||||
lsls rl_tmp0, #3
|
||||
str rl_tmp0, [rl_slot, #SLOTW_EG_OUT_TLL_LSL3]
|
||||
.endm
|
||||
|
||||
#define FRAMEW_EG_STEP_TABLE 0
|
||||
#define FRAMEW_SAMPLE_OUT_END_BACK 4
|
||||
#define FRAME_SIZE 8
|
||||
|
||||
.macro set_step_table table_def
|
||||
// get_attack_step_table
|
||||
ldr rl_tmp0, =eg_step_tables
|
||||
ldr rl_tmp1, =\table_def
|
||||
ldrb rl_tmp2, [rl_slot, SLOTB_EG_RATE_H]
|
||||
ldrb rl_tmp2, [rl_tmp1, rl_tmp2]
|
||||
lsls rl_tmp1, rl_tmp2, #25
|
||||
bcc 1f
|
||||
lsrs rl_tmp2, rl_tmp1, #25
|
||||
ldrb rl_tmp1, [rl_slot, SLOTB_EG_RATE_L]
|
||||
lsls rl_tmp1, #3
|
||||
adds rl_tmp2, rl_tmp1
|
||||
1:
|
||||
adds rl_tmp0, rl_tmp2
|
||||
str rl_tmp0, [sp, FRAMEW_EG_STEP_TABLE]
|
||||
.endm
|
||||
|
||||
.macro update_eg_vars_for_non_zero_rate
|
||||
ldrb rl_tmp1, [rl_slot, #SLOTB_RKS]
|
||||
// slot->eg_rate_l = slot->rks & 3;
|
||||
lsls rl_tmp2, rl_tmp1, #30
|
||||
lsrs rl_tmp2, rl_tmp2, #30
|
||||
strb rl_tmp2, [rl_slot, #SLOTB_EG_RATE_L]
|
||||
// slot->eg_rate_h = std::min(15, p_rate + (slot->rks >> 2));
|
||||
lsrs rl_tmp1, #2
|
||||
adds rl_tmp0, rl_tmp1
|
||||
cmp rl_tmp0, #15
|
||||
ble 9f
|
||||
movs rl_tmp0, #15
|
||||
9:
|
||||
strb rl_tmp0, [rl_slot, #SLOTB_EG_RATE_H]
|
||||
// slot->eg_shift = (slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
|
||||
// note eg_shift was already zeroed above
|
||||
movs rl_tmp1, #12
|
||||
subs rl_tmp1, rl_tmp0
|
||||
bcc 1f
|
||||
strb rl_tmp1, [rl_slot, #SLOTB_EG_SHIFT]
|
||||
.endm
|
||||
|
||||
// (slot, nsamples, eg_counter, fn);
|
||||
.global test_slot_asm
|
||||
.thumb_func
|
||||
test_slot_asm:
|
||||
#if 0
|
||||
#define rl_slot r0 i
|
||||
#define rl_sample_out r1 x
|
||||
#define rl_eg_counter r2 i
|
||||
#define rl_eg_shift_mask r3 x
|
||||
#define rl_tmp0 r4 -
|
||||
#define rl_tmp1 r5 -
|
||||
#define rl_tmp2 r6 -
|
||||
#define rl_interp r7
|
||||
#endif
|
||||
|
||||
// rh_begin_loop r8 x
|
||||
// rh_end_loop r9 x
|
||||
// rh_sample_out_end r10 x
|
||||
// rh_fn r11 x
|
||||
// rh_exp_table r12 x
|
||||
// rh_mod_buffer_offset lr -
|
||||
|
||||
push {r4-r7, lr}
|
||||
mov r4, r8
|
||||
mov r5, r9
|
||||
mov r6, r10
|
||||
mov r7, r11
|
||||
push {r4-r7}
|
||||
mov r4, r12
|
||||
push {r3, r4} // we want to save r3 as well
|
||||
sub sp, #FRAME_SIZE
|
||||
|
||||
// note r1 == rl_sample_out
|
||||
lsrs r5, r3, #3
|
||||
bne 1f
|
||||
// is a mod fn
|
||||
lsls r4, r1, #1
|
||||
ldr rl_sample_out, [rl_slot, #SLOTW_MOD_BUFFER]
|
||||
add r4, rl_sample_out
|
||||
b 2f
|
||||
1:
|
||||
// is an arg fn
|
||||
lsls r4, r1, #2
|
||||
ldr rl_sample_out, [rl_slot, #SLOTW_BUFFER]
|
||||
add r4, rl_sample_out
|
||||
2:
|
||||
mov rh_sample_out_end, r4
|
||||
str r4, [sp, #FRAMEW_SAMPLE_OUT_END_BACK]
|
||||
|
||||
ldr r4, =slot_render_fn_table
|
||||
lsls r3, #2
|
||||
ldr r4, [r4, r3]
|
||||
mov rh_fn, r4
|
||||
|
||||
ldr rl_tmp0, =exp_table
|
||||
mov rh_exp_table, rl_tmp0
|
||||
|
||||
ldr rl_tmp0, [rl_slot, #SLOTW_BUFFER_MOD_BUFFER_OFFSET]
|
||||
mov rh_buffer_mod_buffer_offset, rl_tmp0
|
||||
|
||||
ldr rl_interp, =0xd0000080
|
||||
|
||||
ldrb rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
cmp rl_tmp0, #ATTACK
|
||||
bne check_decay
|
||||
attack:
|
||||
ldr rl_tmp0, =check_decay
|
||||
mov rh_end_loop, rl_tmp0
|
||||
|
||||
set_eg_shift_mask
|
||||
set_step_table attack_rate_def_table
|
||||
|
||||
// if (slot->eg_out == 0) {
|
||||
ldrh rl_tmp1, [rl_slot, #SLOTH_EG_OUT]
|
||||
cmp rl_tmp1, #0
|
||||
bne attack_loop_enter
|
||||
|
||||
// early enter decay state
|
||||
adds rl_eg_counter, #1
|
||||
b enter_decay_state
|
||||
|
||||
attack_loop_enter:
|
||||
ldr rl_tmp0, =attack_loop
|
||||
mov rh_begin_loop, rl_tmp0
|
||||
.thumb_func
|
||||
attack_loop:
|
||||
adds rl_eg_counter, #1
|
||||
tst rl_eg_counter, rl_eg_shift_mask
|
||||
beq 1f
|
||||
bx rh_fn
|
||||
1:
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
// cmp rl_tmp0, #0
|
||||
// bhi 1f
|
||||
// bx rh_fn
|
||||
//1:
|
||||
|
||||
// uint8_t step = eg_step_table[(eg_counter >> slot->eg_shift) & 7];
|
||||
|
||||
ldrb rl_tmp1, [rl_slot, #SLOTB_EG_SHIFT]
|
||||
movs rl_tmp2, rl_eg_counter
|
||||
lsrs rl_tmp2, rl_tmp1
|
||||
lsls rl_tmp2, #29
|
||||
lsrs rl_tmp2, #29
|
||||
ldr rl_tmp1, [sp, #FRAMEW_EG_STEP_TABLE]
|
||||
ldrb rl_tmp1, [rl_tmp1, rl_tmp2]
|
||||
|
||||
// slot->eg_out += (~slot->eg_out * step) >> 3;
|
||||
mvns rl_tmp2, rl_tmp0
|
||||
muls rl_tmp2, rl_tmp1
|
||||
asrs rl_tmp2, #3
|
||||
adds rl_tmp0, rl_tmp2
|
||||
strh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
|
||||
update_eg_out_tll_lsl3
|
||||
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
cmp rl_tmp0, #0
|
||||
beq enter_decay_state
|
||||
bx rh_fn
|
||||
|
||||
enter_decay_state:
|
||||
// int p_rate = slot->patch->DR;
|
||||
ldr rl_tmp0, [rl_slot, #SLOTW_PATCH]
|
||||
ldrb rl_tmp0, [rl_tmp0, #PATCH_DR]
|
||||
cmp rl_tmp0, #0
|
||||
// set all eg_state, shift, rate_h, rate_l all to zero (if rl_tmp0 is zero)
|
||||
str rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
beq 1f
|
||||
update_eg_vars_for_non_zero_rate
|
||||
1:
|
||||
movs rl_tmp0, #DECAY
|
||||
strb rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
// do last sample
|
||||
mov rh_sample_out_end, rl_sample_out
|
||||
|
||||
ldr rl_tmp0, =check_decay
|
||||
mov rh_begin_loop, rl_tmp0
|
||||
bx rh_fn
|
||||
|
||||
.thumb_func
|
||||
check_decay:
|
||||
ldr rl_tmp0, [sp, #FRAMEW_SAMPLE_OUT_END_BACK]
|
||||
cmp rl_sample_out, rl_tmp0
|
||||
beq check_sustain_release // done is too far away
|
||||
mov rh_sample_out_end, rl_tmp0
|
||||
ldrb rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
cmp rl_tmp0, #DECAY
|
||||
bne check_sustain_release
|
||||
decay:
|
||||
ldr rl_tmp0, =check_sustain_release
|
||||
mov rh_end_loop, rl_tmp0
|
||||
|
||||
set_eg_shift_mask
|
||||
set_step_table decay_rate_def_table
|
||||
|
||||
adds rl_tmp0, rl_eg_counter, #1
|
||||
tst rl_tmp0, rl_eg_shift_mask
|
||||
beq decay_loop_enter
|
||||
|
||||
// check ((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL))
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
ldr rl_tmp1, [rl_slot, #SLOTW_PATCH]
|
||||
ldrb rl_tmp1, [rl_tmp1, #PATCH_SL]
|
||||
cmp rl_tmp1, #15
|
||||
beq decay_loop_enter
|
||||
|
||||
lsrs rl_tmp2, rl_tmp0, #4
|
||||
cmp rl_tmp2, rl_tmp1
|
||||
bne decay_loop_enter
|
||||
// early enter sustain state
|
||||
mov rl_eg_counter, rl_tmp0
|
||||
b enter_sustain_state
|
||||
|
||||
decay_loop_enter:
|
||||
ldr rl_tmp0, =decay_loop
|
||||
mov rh_begin_loop, rl_tmp0
|
||||
.thumb_func
|
||||
decay_loop:
|
||||
adds rl_eg_counter, #1
|
||||
tst rl_eg_counter, rl_eg_shift_mask
|
||||
beq 1f
|
||||
bx rh_fn
|
||||
1:
|
||||
//slot->eg_out = static_cast<int16_t>(std::min(EG_MUTE, slot->eg_out + (int) eg_step_table[(eg_counter >> slot->eg_shift) & 7]));
|
||||
ldr rl_tmp0, [sp, #FRAMEW_EG_STEP_TABLE]
|
||||
ldrb rl_tmp1, [rl_slot, #SLOTB_EG_SHIFT]
|
||||
movs rl_tmp2, rl_eg_counter
|
||||
lsrs rl_tmp2, rl_tmp1
|
||||
lsls rl_tmp2, #29
|
||||
lsrs rl_tmp2, #29
|
||||
ldrb rl_tmp1, [rl_tmp0, rl_tmp2]
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
adds rl_tmp0, rl_tmp1
|
||||
// todo do we actually need this min check
|
||||
lsrs rl_tmp1, rl_tmp0, #9
|
||||
beq 1f
|
||||
ldr rl_tmp0, =0x1ff
|
||||
1:
|
||||
strh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
|
||||
// uses rl_tmp0
|
||||
update_eg_out_tll_lsl3
|
||||
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
ldr rl_tmp1, [rl_slot, #SLOTW_PATCH]
|
||||
ldrb rl_tmp1, [rl_tmp1, #PATCH_SL]
|
||||
cmp rl_tmp1, #15
|
||||
beq 1f
|
||||
|
||||
lsrs rl_tmp2, rl_tmp0, #4
|
||||
cmp rl_tmp2, rl_tmp1
|
||||
beq enter_sustain_state
|
||||
|
||||
1:
|
||||
// check for EG_MUTE (0x1ff)
|
||||
adds rl_tmp1, rl_tmp0, #1
|
||||
lsrs rl_tmp1, #9
|
||||
bne 1f
|
||||
bx rh_fn
|
||||
|
||||
1:
|
||||
// we decayed to zero, but still need to run loop contents once more
|
||||
mov rh_sample_out_end, rl_sample_out
|
||||
bx rh_fn
|
||||
|
||||
enter_sustain_state:
|
||||
// int p_rate = slot->patch->EG ? 0 : slot->patch->RR
|
||||
ldr rl_tmp2, [rl_slot, #SLOTW_PATCH]
|
||||
ldrb rl_tmp1, [rl_tmp2, #PATCH_EG]
|
||||
movs rl_tmp0, #0
|
||||
cmp rl_tmp1, #0
|
||||
bne 1f
|
||||
ldrb rl_tmp0, [rl_tmp2, #PATCH_RR]
|
||||
1:
|
||||
// set all eg_state, shift, rate_h, rate_l all to zero (if rl_tmp0 is zero)
|
||||
str rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
cmp rl_tmp0, #0
|
||||
beq 1f
|
||||
update_eg_vars_for_non_zero_rate
|
||||
1:
|
||||
movs rl_tmp0, #SUSTAIN
|
||||
strb rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
// do last sample
|
||||
mov rh_sample_out_end, rl_sample_out
|
||||
|
||||
ldr rl_tmp0, =check_sustain_release
|
||||
mov rh_begin_loop, rl_tmp0
|
||||
bx rh_fn
|
||||
|
||||
.thumb_func
|
||||
check_sustain_release:
|
||||
ldr rl_tmp0, [sp, #FRAMEW_SAMPLE_OUT_END_BACK]
|
||||
cmp rl_sample_out, rl_tmp0
|
||||
beq done
|
||||
mov rh_sample_out_end, rl_tmp0
|
||||
ldrb rl_tmp0, [rl_slot, #SLOTB_EG_STATE]
|
||||
cmp rl_tmp0, #SUSTAIN
|
||||
blt done
|
||||
ldr rl_tmp0, =done
|
||||
mov rh_end_loop, rl_tmp0
|
||||
set_eg_shift_mask
|
||||
set_step_table decay_rate_def_table
|
||||
|
||||
sustain_release_loop_enter:
|
||||
ldr rl_tmp0, =sustain_release_loop
|
||||
mov rh_begin_loop, rl_tmp0
|
||||
.thumb_func
|
||||
sustain_release_loop:
|
||||
adds rl_eg_counter, #1
|
||||
tst rl_eg_counter, rl_eg_shift_mask
|
||||
beq 1f
|
||||
bx rh_fn
|
||||
1:
|
||||
//slot->eg_out = static_cast<int16_t>(std::min(EG_MUTE, slot->eg_out + (int) eg_step_table[(eg_counter >> slot->eg_shift) & 7]));
|
||||
ldr rl_tmp0, [sp, #FRAMEW_EG_STEP_TABLE]
|
||||
ldrb rl_tmp1, [rl_slot, #SLOTB_EG_SHIFT]
|
||||
movs rl_tmp2, rl_eg_counter
|
||||
lsrs rl_tmp2, rl_tmp1
|
||||
lsls rl_tmp2, #29
|
||||
lsrs rl_tmp2, #29
|
||||
ldrb rl_tmp1, [rl_tmp0, rl_tmp2]
|
||||
ldrh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
adds rl_tmp0, rl_tmp1
|
||||
|
||||
strh rl_tmp0, [rl_slot, #SLOTH_EG_OUT]
|
||||
adds rl_tmp2, rl_tmp0, #1
|
||||
lsrs rl_tmp1, rl_tmp2, #9
|
||||
bne 1f
|
||||
update_eg_out_tll_lsl3
|
||||
bx rh_fn
|
||||
1:
|
||||
// we decayed to zero, but still need to run loop contents
|
||||
mov rh_sample_out_end, rl_sample_out
|
||||
bx rh_fn
|
||||
.thumb_func
|
||||
done:
|
||||
add sp, #FRAME_SIZE
|
||||
pop {r2-r7}
|
||||
mov r12, r3
|
||||
mov r8, r4
|
||||
mov r9, r5
|
||||
mov r10, r6
|
||||
mov r11, r7
|
||||
|
||||
// figure out how many samples we did
|
||||
lsrs r2, #3
|
||||
bne 1f
|
||||
// is a mod fn
|
||||
ldr r0, [rl_slot, #SLOTW_MOD_BUFFER]
|
||||
subs r1, r0
|
||||
lsrs r0, r1, #1
|
||||
pop {r4-r7, pc}
|
||||
1:
|
||||
// is an arg fn
|
||||
ldr r0, [rl_slot, #SLOTW_BUFFER]
|
||||
subs r1, r1
|
||||
lsrs r1, r0, #2
|
||||
pop {r4-r7, pc}
|
||||
|
||||
|
||||
#if 0
|
||||
movs rl_tmp0, #1
|
||||
lsls rl_tmp0, #12
|
||||
subs rl_tmp0, #2
|
||||
lsls rl_tmp1, rl_eg_counter, #1
|
||||
cmp rl_tmp0, rl_tmp1
|
||||
bne 1f
|
||||
bkpt #0
|
||||
1:
|
||||
|
||||
#endif
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
add_library(pcsound STATIC
|
||||
pcsound.c pcsound.h
|
||||
pcsound_bsd.c
|
||||
pcsound_sdl.c
|
||||
pcsound_linux.c
|
||||
pcsound_win32.c
|
||||
pcsound_internal.h)
|
||||
target_include_directories(pcsound
|
||||
INTERFACE "."
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(pcsound SDL2::mixer)
|
||||
if (PICO_SDK)
|
||||
add_library(pcsound INTERFACE)
|
||||
else()
|
||||
add_library(pcsound STATIC
|
||||
pcsound.c pcsound.h
|
||||
pcsound_bsd.c
|
||||
pcsound_sdl.c
|
||||
pcsound_linux.c
|
||||
pcsound_win32.c
|
||||
pcsound_internal.h)
|
||||
target_include_directories(pcsound
|
||||
INTERFACE "."
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(pcsound SDL2::mixer)
|
||||
endif()
|
||||
62
pico_extras_import.cmake
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# This is a copy of <PICO_EXTRAS_PATH>/external/pico_extras_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate pico-extras
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH))
|
||||
set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH})
|
||||
message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT))
|
||||
set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT})
|
||||
message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (NOT PICO_EXTRAS_PATH)
|
||||
if (PICO_EXTRAS_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_EXTRAS_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
PICO_EXTRAS
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-extras
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT PICO_EXTRAS)
|
||||
message("Downloading PICO EXTRAS")
|
||||
FetchContent_Populate(PICO_EXTRAS)
|
||||
set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras")
|
||||
set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras)
|
||||
message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS")
|
||||
set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable")
|
||||
set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS")
|
||||
|
||||
get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_EXTRAS_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE)
|
||||
|
||||
add_subdirectory(${PICO_EXTRAS_PATH} pico_extras)
|
||||
64
pico_sdk_import.cmake
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
# todo document
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY git@asic-git.pitowers.org:projectmu/pico_sdk.git
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading PICO SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||
1
src/.gitignore
vendored
|
|
@ -17,6 +17,7 @@ chocolate-setup
|
|||
*.desktop
|
||||
*.txt
|
||||
!CMakeLists.txt
|
||||
!license.txt
|
||||
*.appdata.xml
|
||||
tags
|
||||
TAGS
|
||||
|
|
|
|||
|
|
@ -1,39 +1,78 @@
|
|||
foreach(SUBDIR doom heretic hexen strife setup)
|
||||
if (PICO_SDK)
|
||||
set(BINARIES doom)
|
||||
else()
|
||||
# comment out heretic/hexen/strife for now, as whilst they may build, how well they work is unclear
|
||||
# set(BINARIES doom heretic hexen strife setup)
|
||||
set(BINARIES doom setup)
|
||||
endif()
|
||||
|
||||
add_subdirectory(adpcm-xq)
|
||||
|
||||
foreach(SUBDIR ${BINARIES})
|
||||
add_subdirectory("${SUBDIR}")
|
||||
endforeach()
|
||||
|
||||
# Common source files used by absolutely everything:
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
|
||||
set(COMMON_SOURCE_FILES
|
||||
if (NOT PICO_SDK)
|
||||
set(I_PLATFORM sdl)
|
||||
else()
|
||||
set(I_PLATFORM pico)
|
||||
endif()
|
||||
|
||||
add_library(common INTERFACE)
|
||||
target_sources(common INTERFACE
|
||||
i_main.c
|
||||
i_system.c i_system.h
|
||||
i_system.h
|
||||
m_argv.c m_argv.h
|
||||
m_misc.c m_misc.h)
|
||||
|
||||
# Dedicated server (chocolate-server):
|
||||
target_compile_definitions(common INTERFACE
|
||||
#HACK_FINALE_E1M1=1
|
||||
#HACK_FINALE_SHAREWARE=1
|
||||
# DEBUG_MOBJ=1
|
||||
)
|
||||
|
||||
set(DEDSERV_FILES
|
||||
d_dedicated.c
|
||||
d_mode.c d_mode.h
|
||||
i_timer.c i_timer.h
|
||||
net_common.c net_common.h
|
||||
net_dedicated.c net_dedicated.h
|
||||
net_io.c net_io.h
|
||||
net_packet.c net_packet.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_query.c net_query.h
|
||||
net_server.c net_server.h
|
||||
net_structrw.c net_structrw.h
|
||||
z_native.c z_zone.h)
|
||||
if (NOT PICO_SDK)
|
||||
add_library(common_sdl INTERFACE)
|
||||
target_sources(common_sdl INTERFACE
|
||||
i_system.c
|
||||
)
|
||||
else()
|
||||
add_subdirectory(pico)
|
||||
endif()
|
||||
|
||||
add_executable("${PROGRAM_PREFIX}server" WIN32 ${COMMON_SOURCE_FILES} ${DEDSERV_FILES})
|
||||
target_include_directories("${PROGRAM_PREFIX}server"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}server" SDL2::SDL2main SDL2::net)
|
||||
target_link_libraries(common INTERFACE common_${I_PLATFORM})
|
||||
|
||||
# Source files used by the game binaries (chocolate-doom, etc.)
|
||||
# todo chocolate-server also doesn't currently build
|
||||
if (NOT PICO_SDK)
|
||||
# Dedicated server (chocolate-server):
|
||||
|
||||
set(GAME_SOURCE_FILES
|
||||
set(DEDSERV_FILES
|
||||
d_dedicated.c
|
||||
d_mode.c d_mode.h
|
||||
i_timer.c i_timer.h
|
||||
net_common.c net_common.h
|
||||
net_dedicated.c net_dedicated.h
|
||||
net_io.c net_io.h
|
||||
net_packet.c net_packet.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_query.c net_query.h
|
||||
net_server.c net_server.h
|
||||
net_structrw.c net_structrw.h
|
||||
z_native.c z_zone.h)
|
||||
|
||||
add_executable("${PROGRAM_PREFIX}server" WIN32 ${COMMON_SOURCE_FILES} ${DEDSERV_FILES})
|
||||
target_include_directories("${PROGRAM_PREFIX}server"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}server" common SDL2::SDL2main SDL2::net)
|
||||
endif()
|
||||
|
||||
# Source files used by the game BINARIES (chocolate-doom, etc.)
|
||||
|
||||
add_library(game INTERFACE)
|
||||
target_sources(game INTERFACE
|
||||
aes_prng.c aes_prng.h
|
||||
d_event.c d_event.h
|
||||
doomkeys.h
|
||||
|
|
@ -44,22 +83,17 @@ set(GAME_SOURCE_FILES
|
|||
d_ticcmd.h
|
||||
deh_str.c deh_str.h
|
||||
gusconf.c gusconf.h
|
||||
i_cdmus.c i_cdmus.h
|
||||
i_endoom.c i_endoom.h
|
||||
i_glob.c i_glob.h
|
||||
i_input.c i_input.h
|
||||
i_joystick.c i_joystick.h
|
||||
i_swap.h
|
||||
i_midipipe.c i_midipipe.h
|
||||
i_musicpack.c
|
||||
i_oplmusic.c
|
||||
i_pcsound.c
|
||||
i_sdlmusic.c
|
||||
i_sdlsound.c
|
||||
i_sound.c i_sound.h
|
||||
i_timer.c i_timer.h
|
||||
i_video.c i_video.h
|
||||
i_videohr.c i_videohr.h
|
||||
i_cdmus.h
|
||||
i_endoom.h
|
||||
i_glob.h
|
||||
i_input.h
|
||||
i_joystick.h
|
||||
i_swap.h
|
||||
i_midipipe.h
|
||||
i_sound.h
|
||||
i_timer.h
|
||||
i_video.h
|
||||
i_videohr.h
|
||||
midifile.c midifile.h
|
||||
mus2mid.c mus2mid.h
|
||||
m_bbox.c m_bbox.h
|
||||
|
|
@ -67,18 +101,8 @@ set(GAME_SOURCE_FILES
|
|||
m_config.c m_config.h
|
||||
m_controls.c m_controls.h
|
||||
m_fixed.c m_fixed.h
|
||||
|
||||
net_client.c net_client.h
|
||||
net_common.c net_common.h
|
||||
net_dedicated.c net_dedicated.h
|
||||
net_defs.h
|
||||
net_gui.c net_gui.h
|
||||
net_io.c net_io.h
|
||||
net_loop.c net_loop.h
|
||||
net_packet.c net_packet.h
|
||||
net_query.c net_query.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_server.c net_server.h
|
||||
net_structrw.c net_structrw.h
|
||||
sha1.c sha1.h
|
||||
memio.c memio.h
|
||||
tables.c tables.h
|
||||
|
|
@ -89,134 +113,553 @@ set(GAME_SOURCE_FILES
|
|||
w_main.c w_main.h
|
||||
w_wad.c w_wad.h
|
||||
w_file.c w_file.h
|
||||
w_file_stdc.c
|
||||
w_file_posix.c
|
||||
w_file_win32.c
|
||||
w_merge.c w_merge.h
|
||||
z_zone.c z_zone.h)
|
||||
w_file_posix.c
|
||||
w_file_memory.c
|
||||
|
||||
w_merge.c w_merge.h
|
||||
z_zone.c z_zone.h
|
||||
|
||||
i_oplmusic.c
|
||||
i_sound.c
|
||||
|
||||
)
|
||||
|
||||
if (NOT PICO_SDK)
|
||||
add_library(game_sdl INTERFACE)
|
||||
target_sources(game_sdl INTERFACE
|
||||
# todo maybe a separate network library
|
||||
net_common.c net_common.h
|
||||
net_dedicated.c net_dedicated.h
|
||||
net_defs.h
|
||||
net_gui.c net_gui.h
|
||||
net_io.c net_io.h
|
||||
net_loop.c net_loop.h
|
||||
net_packet.c net_packet.h
|
||||
net_query.c net_query.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_server.c net_server.h
|
||||
net_structrw.c net_structrw.h
|
||||
|
||||
w_file_stdc.c
|
||||
w_file_win32.c
|
||||
|
||||
i_cdmus.c
|
||||
i_endoom.c
|
||||
i_glob.c
|
||||
i_input.c
|
||||
i_joystick.c
|
||||
|
||||
i_midipipe.c
|
||||
i_musicpack.c
|
||||
i_pcsound.c
|
||||
i_sdlmusic.c
|
||||
i_sdlsound.c
|
||||
i_timer.c
|
||||
i_video.c
|
||||
i_videohr.c
|
||||
)
|
||||
else()
|
||||
add_library(game_pico INTERFACE)
|
||||
endif()
|
||||
|
||||
target_link_libraries(game INTERFACE game_${I_PLATFORM})
|
||||
if(MSVC)
|
||||
list(APPEND GAME_SOURCE_FILES
|
||||
target_sources(game INTERFACE
|
||||
"../win32/win_opendir.c" "../win32/win_opendir.h")
|
||||
endif()
|
||||
|
||||
set(DEHACKED_SOURCE_FILES
|
||||
target_link_libraries(game INTERFACE common opl)
|
||||
|
||||
add_library(game_deh INTERFACE)
|
||||
target_sources(game_deh INTERFACE
|
||||
deh_defs.h
|
||||
deh_io.c deh_io.h
|
||||
deh_main.c deh_main.h
|
||||
deh_mapping.c deh_mapping.h
|
||||
deh_text.c)
|
||||
|
||||
# Some games support dehacked patches, some don't:
|
||||
target_link_libraries(game_deh INTERFACE game)
|
||||
|
||||
set(SOURCE_FILES ${COMMON_SOURCE_FILES} ${GAME_SOURCE_FILES})
|
||||
set(SOURCE_FILES_WITH_DEH ${SOURCE_FILES} ${DEHACKED_SOURCE_FILES})
|
||||
if (NOT PICO_SDK)
|
||||
set(EXTRA_LIBS SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net textscreen pcsound)
|
||||
if(SAMPLERATE_FOUND)
|
||||
list(APPEND EXTRA_LIBS samplerate::samplerate)
|
||||
endif()
|
||||
if(PNG_FOUND)
|
||||
list(APPEND EXTRA_LIBS PNG::PNG)
|
||||
endif()
|
||||
|
||||
set(EXTRA_LIBS SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net textscreen pcsound opl)
|
||||
if(SAMPLERATE_FOUND)
|
||||
list(APPEND EXTRA_LIBS samplerate::samplerate)
|
||||
endif()
|
||||
if(PNG_FOUND)
|
||||
list(APPEND EXTRA_LIBS PNG::PNG)
|
||||
if ("doom" IN_LIST BINARIES)
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}doom" WIN32 "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}doom")
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}doom" PRIVATE game_deh)
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}doom"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}doom" PRIVATE doom ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}doom" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if ("heretic" IN_LIST BINARIES)
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}heretic" WIN32 "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}heretic" )
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}heretic" PRIVATE game_deh)
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}heretic"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}heretic" PRIVATE heretic ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}heretic" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if ("hexen" IN_LIST BINARIES)
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}hexen" WIN32 "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}hexen")
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}hexen" PRIVATE game)
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}hexen"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}hexen" PRIVATE hexen ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}hexen" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if ("strife" IN_LIST BINARIES)
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}strife" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}strife" ${SOURCE_FILES_WITH_DEH})
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}strife" PRIVATE game_deh)
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}strife"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}strife" PRIVATE strife ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}strife" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Source files needed for chocolate-setup:
|
||||
|
||||
if ("setup" IN_LIST BINARIES)
|
||||
set(SETUP_FILES
|
||||
deh_str.c deh_str.h
|
||||
d_mode.c d_mode.h
|
||||
d_iwad.c d_iwad.h
|
||||
i_timer.c i_timer.h
|
||||
m_config.c m_config.h
|
||||
m_controls.c m_controls.h
|
||||
net_io.c net_io.h
|
||||
net_packet.c net_packet.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_query.c net_query.h
|
||||
net_structrw.c net_structrw.h
|
||||
z_native.c z_zone.h)
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}setup" WIN32 ${SETUP_FILES} "${CMAKE_CURRENT_BINARY_DIR}/setup-res.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}setup" ${SETUP_FILES} )
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}setup" PRIVATE setup common)
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}setup"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
if (NOT PICO_SDK)
|
||||
target_link_libraries("${PROGRAM_PREFIX}setup" PRIVATE SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net)
|
||||
endif()
|
||||
target_link_libraries("${PROGRAM_PREFIX}setup" PRIVATE textscreen)
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}setup" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(midiread midifile.c z_native.c i_system.c m_argv.c m_misc.c)
|
||||
target_compile_definitions(midiread PRIVATE "-DTEST")
|
||||
target_include_directories(midiread PRIVATE "." "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
|
||||
if (NOT PICO_SDK)
|
||||
target_link_libraries(midiread SDL2::SDL2main SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
add_executable(mus2mid mus2mid.c memio.c z_native.c i_system.c m_argv.c m_misc.c)
|
||||
target_compile_definitions(mus2mid PRIVATE "-DSTANDALONE")
|
||||
target_include_directories(mus2mid PRIVATE "." "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
if (NOT PICO_SDK)
|
||||
target_link_libraries(mus2mid SDL2::SDL2main SDL2::SDL2)
|
||||
endif()
|
||||
endif() # NOT PICO_SDK
|
||||
add_library(small_doom_common INTERFACE)
|
||||
|
||||
target_link_libraries(small_doom_common INTERFACE adpcm-lib) # handy to have about
|
||||
|
||||
target_compile_definitions(small_doom_common INTERFACE
|
||||
SHRINK_MOBJ=1 # may break saved games
|
||||
|
||||
DOOM_ONLY=1 # don't support hexxen etc.
|
||||
DOOM_SMALL=1
|
||||
DOOM_CONST=1 # thread const thru lots of places
|
||||
|
||||
SOUND_LOW_PASS=1
|
||||
NUM_SOUND_CHANNELS=8 # sounds ok (actually that is how many are used by default)
|
||||
|
||||
# functionality
|
||||
NO_USE_CHECKSUM=1
|
||||
NO_USE_RELOAD=1
|
||||
USE_SINGLE_IWAD=1
|
||||
NO_USE_WIPE=1 #memory if nothing else - we can fix this (you can do it)
|
||||
NO_USE_JOYSTICK=1
|
||||
NO_USE_DEH=1
|
||||
NO_USE_MUSIC_PACKS=1
|
||||
|
||||
USE_FLAT_MAX_256=1
|
||||
USE_MEMMAP_ONLY=1
|
||||
|
||||
USE_LIGHTMAP_INDEXES=1 # saves about 9K
|
||||
|
||||
#FRACBITS=15 # ha ha doesn't seem to be passed thru everywhere!
|
||||
#NO_USE_ENDDOOM=1
|
||||
#NO_DRAW_VISPLANES=1
|
||||
|
||||
USE_ERASE_FRAME=1 #
|
||||
NO_DRAW_MID=1
|
||||
NO_DRAW_TOP=1
|
||||
NO_DRAW_BOTTOM=1
|
||||
NO_DRAW_MASKED=1
|
||||
NO_DRAW_SKY=1
|
||||
#NO_DRAW_SPANS=1
|
||||
NO_DRAW_SPRITES=1
|
||||
NO_DRAW_PSPRITES=1
|
||||
|
||||
# NO_VISPLANES=1
|
||||
NO_VISPLANE_GUTS=1
|
||||
NO_VISPLANE_CACHES=1 # todo these might be fine as they can be temporary
|
||||
NO_DRAWSEGS=1
|
||||
NO_VISSPRITES=1
|
||||
|
||||
|
||||
NO_MASKED_FLOOR_CLIP=1 # not needed with PD_ rendering and with floor clip it we have occasional visual glitches with sprites split across visplanes
|
||||
|
||||
PD_DRAW_COLUMNS=1
|
||||
PD_DRAW_MARKERS=1
|
||||
PD_DRAW_PLANES=1
|
||||
|
||||
PD_SCALE_SORT=1
|
||||
|
||||
PD_CLIP_WALLS=1
|
||||
PD_QUANTIZE=1
|
||||
|
||||
PD_SANITY=1
|
||||
|
||||
#MU_STATS=1
|
||||
PD_COLUMNS=1
|
||||
PICO_DOOM=1
|
||||
NO_USE_DS_COLORMAP=1
|
||||
NO_USE_DC_COLORMAP=1
|
||||
|
||||
#PRINT_COLORMAPS=1
|
||||
#PRINT_PALETTE=1
|
||||
USE_READONLY_MMAP=1
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# MUSIC RELATED
|
||||
# -----------------------------------------------------------------
|
||||
NO_USE_TIMIDITY=1
|
||||
NO_USE_GUS=1
|
||||
NO_USE_LIBSAMPLERATE=1
|
||||
|
||||
# slightly slower but only uses 1K of sin tables vs 9K
|
||||
EMU8950_NO_WAVE_TABLE_MAP=1
|
||||
|
||||
EMU8950_NO_TLL=1 # don't use lookup table for total level
|
||||
EMU8950_NO_FLOAT=1 # double check there is no float
|
||||
EMU8950_NO_TIMER=1 # disable timer which isn't used
|
||||
EMU8950_NO_TEST_FLAG=1 # disable test flags (which aren't used)
|
||||
EMU8950_SIMPLER_NOISE=1 # only generate noise bit when needed
|
||||
EMU8950_SHORT_NOISE_UPDATE_CHECK=1 # only update short noise if it is used
|
||||
# actually this doesn't make things faster
|
||||
#EMU8950_LINEAR_NEG_NOT_NOT=1 # negative values rounded towrds -infinity not 0 without this; does it matter?
|
||||
|
||||
EMU8950_LINEAR_SKIP=1 # skip silent slots
|
||||
EMU8950_LINEAR_END_OF_NOTE_OPTIMIZATION # early out envelope when DECAY/SUSTAIN/RELEASE envelope reaches mute
|
||||
EMU8950_NO_PERCUSSION_MODE=1 # rhythm only mode (doom doesn't use percussion; whhhaaaah!?)
|
||||
EMU8950_LINEAR=1 # reorganize to do linear runs of channels
|
||||
|
||||
# things we really don't care about but need for diff-ing with non linear version
|
||||
# BEWARE - need to turn this off for comparison with on device version which doesn't do nitpicks (though could i guess)
|
||||
|
||||
EMU8950_ASM=1
|
||||
# EMU8950_NIT_PICKS=1
|
||||
# DUMPO=1
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# FLASH SIZE
|
||||
# -----------------------------------------------------------------
|
||||
NO_USE_STATE_MISC #doesn't appear to be used anyway - perhaps in doom only
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# RAM SIZE
|
||||
# -----------------------------------------------------------------
|
||||
USE_RAW_MAPNODE=1
|
||||
USE_RAW_MAPVERTEX=1
|
||||
USE_RAW_MAPSEG=1
|
||||
USE_RAW_MAPLINEDEF=1
|
||||
# USE_RAW_MAPSUBSECTOR=1 # obsolete... now WHD
|
||||
# USE_RAW_MAPSIDEDEF=1 obsolete
|
||||
USE_RAW_MAPTHING=1
|
||||
|
||||
USE_INDEX_LINEBUFFER=1
|
||||
NO_USE_ZLIGHT=1
|
||||
NO_Z_ZONE_ID=1 # seems unused
|
||||
|
||||
Z_MALOOC_EXTRA_DATA=1
|
||||
USE_THINKER_POOL=1
|
||||
NO_INTERCEPTS_OVERRUN=1
|
||||
# INCLUDE_SOUND_C_IN_S_SOUND=1 # avoid issues with non static const array
|
||||
# -----------------------------------------------------------------
|
||||
# IMMUTABLE
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
TEMP_IMMUTABLE_DISABLED=1
|
||||
USE_CONST_SFX=1
|
||||
USE_CONST_MUSIC=1
|
||||
|
||||
NO_DEMO_RECORDING=1
|
||||
PICO_NO_TIMING_DEMO=1
|
||||
NO_USE_EXIT=1 # not sure whether we have an exit... if so what does it need to do?
|
||||
|
||||
PICO_DEBUG_PIN_BASE=18
|
||||
PICO_DEBUG_PIN_COUNT=2
|
||||
)
|
||||
|
||||
target_compile_options(small_doom_common INTERFACE
|
||||
-Wall
|
||||
-Wno-unused-function
|
||||
-Wno-unused-but-set-variable
|
||||
-Wno-unused-variable
|
||||
)
|
||||
|
||||
if (TARGET chocolate-doom)
|
||||
target_compile_definitions(chocolate-doom PRIVATE
|
||||
USE_FLAT_MAX_256=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}doom" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
function(add_doom_tiny SUFFIX RENDER_LIB)
|
||||
add_executable(doom_tiny${SUFFIX})
|
||||
target_link_libraries(doom_tiny${SUFFIX} PRIVATE game)
|
||||
|
||||
target_sources(doom_tiny${SUFFIX} PRIVATE
|
||||
tiny_huff.c
|
||||
musx_decoder.c
|
||||
image_decoder.c
|
||||
)
|
||||
target_include_directories(doom_tiny${SUFFIX} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(doom_tiny${SUFFIX} PRIVATE doom ${EXTRA_LIBS})
|
||||
|
||||
target_link_libraries(doom_tiny${SUFFIX} PRIVATE small_doom_common)
|
||||
|
||||
if (PICO_SDK)
|
||||
pico_set_float_implementation(doom_tiny${SUFFIX} none)
|
||||
pico_set_double_implementation(doom_tiny${SUFFIX} none)
|
||||
pico_add_extra_outputs(doom_tiny${SUFFIX})
|
||||
endif()
|
||||
|
||||
target_compile_options(doom_tiny${SUFFIX} PRIVATE
|
||||
-Wno-format-truncation)
|
||||
target_compile_definitions(doom_tiny${SUFFIX} PRIVATE
|
||||
DOOM_TINY=1
|
||||
EMU8950_SLOT_RENDER=1
|
||||
|
||||
SHRINK_MOBJ=1 # may break saved games
|
||||
NO_RDRAW=1
|
||||
|
||||
#NO_DRAW_MENU=1
|
||||
# SDK minimilization
|
||||
PICO_TIME_DEFAULT_ALARM_POOL_DISABLED=1
|
||||
PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS=1
|
||||
PICO_DISABLE_SHARED_IRQ_HANDLERS=1
|
||||
PICO_USE_OPTIMISTIC_SBRK=1
|
||||
|
||||
# MUSIC
|
||||
USE_EMU8950_OPL=1
|
||||
USE_DIRECT_MIDI_LUMP=1
|
||||
|
||||
NO_USE_NET=1 # standard networking
|
||||
USE_PICO_NET=1
|
||||
# for vgaboard
|
||||
PICO_DEFAULT_I2C=1
|
||||
PICO_DEFAULT_I2C_SDA_PIN=18
|
||||
PICO_DEFAULT_I2C_SCL_PIN=19
|
||||
|
||||
#NO_USE_LOAD=1
|
||||
#NO_USE_SAVE=1
|
||||
$<$<BOOL:${PICO_ON_DEVICE}>:NO_FILE_ACCESS=1>
|
||||
|
||||
SAVE_COMPRESSED=1
|
||||
LOAD_COMPRESSED=1
|
||||
|
||||
NO_USE_ARGS=1
|
||||
NO_USE_SAVE_CONFIG=1
|
||||
NO_USE_FLOAT=1
|
||||
|
||||
USE_VANILLA_KEYBOARD_MAPPING_ONLY=1
|
||||
|
||||
#FORCE_NODRAW=1
|
||||
|
||||
NO_USE_LOADING_DISK=1
|
||||
|
||||
# -- these aren't used any more, we adapt based on DOOM_TINY
|
||||
#NO_USE_ST=1 # for now skip all the status crap
|
||||
#NO_USE_DEFAULT_ST_LIB=1
|
||||
|
||||
#NO_PAGE_DRAWER=1 # splash screens
|
||||
#NO_HU_DRAWER=1
|
||||
|
||||
USE_WHD=1
|
||||
|
||||
|
||||
NO_Z_MALLOC_USER_PTR=1 # onyl needed for freeing textures i think
|
||||
|
||||
FIXED_SCREENWIDTH=1
|
||||
FLOOR_CEILING_CLIP_8BIT=1
|
||||
|
||||
# NO_USE_WI=1
|
||||
USE_MUSX=1
|
||||
MUSX_COMPRESSED=1
|
||||
|
||||
NO_SCREENSHOT=1
|
||||
|
||||
NO_USE_BOUND_CONFIG=1
|
||||
|
||||
USE_FPS=1
|
||||
|
||||
# PICO_DOOM_INFO=1
|
||||
)
|
||||
target_compile_definitions(doom_tiny${SUFFIX} PRIVATE
|
||||
#FORCE_NODRAW=1
|
||||
USE_MEMORY_WAD=1
|
||||
PICO_DEBUG_MALLOC=1
|
||||
EMU8950_NO_RATECONV=1
|
||||
|
||||
PICO_CORE1_STACK_SIZE=0x4f8
|
||||
|
||||
NO_IERROR=1
|
||||
)
|
||||
if (PICO_ON_DEVICE)
|
||||
target_compile_options(doom_tiny${SUFFIX} PRIVATE -fno-common -fdata-sections -Wl,--sort-section=alignment)
|
||||
target_compile_definitions(doom_tiny${SUFFIX} PRIVATE
|
||||
NO_ZONE_DEBUG=1
|
||||
)
|
||||
#target_link_libraries(doom_tiny${SUFFIX} PRIVATE hardware_flash)
|
||||
endif()
|
||||
if (FORCE_DEBUG)
|
||||
target_compile_options(doom_tiny${SUFFIX} PRIVATE -g)
|
||||
endif()
|
||||
target_link_libraries(doom_tiny${SUFFIX} PRIVATE ${RENDER_LIB})
|
||||
set(PICO_HACK 0)
|
||||
set(STAMP_HACK 0)
|
||||
if (PICO_HACK)
|
||||
target_compile_definitions(doom_tiny${SUFFIX} PRIVATE
|
||||
PICO_AUDIO_I2S_DATA_PIN=20
|
||||
PICO_AUDIO_I2S_CLOCK_PIN_BASE=21
|
||||
NO_USE_UART=1
|
||||
)
|
||||
pico_enable_stdio_uart(doom_tiny${SUFFIX} 0)
|
||||
elseif(STAMP_HACK)
|
||||
target_compile_definitions(doom_tiny${SUFFIX} PRIVATE
|
||||
PICO_AUDIO_I2S_DATA_PIN=22
|
||||
PICO_AUDIO_I2S_CLOCK_PIN_BASE=23
|
||||
NO_USE_UART=1
|
||||
INCREASE_I2S_DRIVE_STRENGTH=1
|
||||
)
|
||||
pico_enable_stdio_uart(doom_tiny${SUFFIX} 0)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(whd_gen)
|
||||
|
||||
add_library(render_newhope INTERFACE)
|
||||
target_sources(render_newhope INTERFACE
|
||||
pd_render.cpp
|
||||
)
|
||||
target_compile_definitions(render_newhope INTERFACE
|
||||
PICODOOM_RENDER_NEWHOPE=1
|
||||
MERGE_DISTSCALE0_INTO_VIEWCOSSINANGLE=1
|
||||
NO_USE_ZLIGHT=1
|
||||
PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=4
|
||||
PICO_SCANVIDEO_LINKED_SCANLINE_BUFFERS=1
|
||||
PICO_SCANVIDEO_SCANLINE_RELEASE_FUNCTION=1
|
||||
)
|
||||
if (PICO_ON_DEVICE)
|
||||
target_compile_definitions(render_newhope INTERFACE
|
||||
PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=164
|
||||
)
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}doom" ${SOURCE_FILES_WITH_DEH})
|
||||
# we cannot rewrite the buffers in host mode so we need them to be big enough for text mode
|
||||
target_compile_definitions(render_newhope INTERFACE
|
||||
PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=324
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}doom"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}doom" doom ${EXTRA_LIBS})
|
||||
if (PICO_SDK)
|
||||
add_doom_tiny("" render_newhope)
|
||||
add_library(tiny_settings INTERFACE)
|
||||
target_compile_definitions(tiny_settings INTERFACE
|
||||
WHD_SUPER_TINY=1
|
||||
DEMO1_ONLY=1
|
||||
NO_USE_FINALE_CAST=1
|
||||
NO_USE_FINALE_BUNNY=1
|
||||
)
|
||||
target_compile_definitions(doom_tiny PRIVATE
|
||||
TINY_WAD_ADDR=0x10040000
|
||||
)
|
||||
target_link_libraries(doom_tiny PRIVATE tiny_settings)
|
||||
add_doom_tiny(_nost render_newhope)
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}doom" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
target_compile_definitions(doom_tiny_nost PRIVATE
|
||||
# NO_USE_FINALE_CAST=1 # note this is only used by doom II but doesnt take that much more space
|
||||
TINY_WAD_ADDR=0x10048000
|
||||
)
|
||||
if (PICO_ON_DEVICE)
|
||||
suppress_tinyusb_warnings()
|
||||
add_doom_tiny(_usb render_newhope)
|
||||
target_link_libraries(doom_tiny_usb PRIVATE tiny_settings pico_cd)
|
||||
target_compile_definitions(doom_tiny_usb PRIVATE
|
||||
USB_SUPPORT=1
|
||||
TINY_WAD_ADDR=0x10042000 # ugh tinyusb is >8K currently!!
|
||||
)
|
||||
add_doom_tiny(_nost_usb render_newhope)
|
||||
target_link_libraries(doom_tiny_nost_usb PRIVATE pico_cd)
|
||||
target_compile_definitions(doom_tiny_nost_usb PRIVATE
|
||||
USB_SUPPORT=1
|
||||
TINY_WAD_ADDR=0x10048000
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}heretic" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}heretic" ${SOURCE_FILES_WITH_DEH})
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}heretic"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}heretic" heretic ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}heretic" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}hexen" WIN32 ${SOURCE_FILES} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}hexen" ${SOURCE_FILES})
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}hexen"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}hexen" hexen ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}hexen" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}strife" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}strife" ${SOURCE_FILES_WITH_DEH})
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}strife"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}strife" strife ${EXTRA_LIBS})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}strife" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
|
||||
# Source files needed for chocolate-setup:
|
||||
|
||||
set(SETUP_FILES
|
||||
deh_str.c deh_str.h
|
||||
d_mode.c d_mode.h
|
||||
d_iwad.c d_iwad.h
|
||||
i_timer.c i_timer.h
|
||||
m_config.c m_config.h
|
||||
m_controls.c m_controls.h
|
||||
net_io.c net_io.h
|
||||
net_packet.c net_packet.h
|
||||
net_sdl.c net_sdl.h
|
||||
net_query.c net_query.h
|
||||
net_structrw.c net_structrw.h
|
||||
z_native.c z_zone.h)
|
||||
|
||||
if(WIN32)
|
||||
add_executable("${PROGRAM_PREFIX}setup" WIN32 ${SETUP_FILES} ${COMMON_SOURCE_FILES} "${CMAKE_CURRENT_BINARY_DIR}/setup-res.rc")
|
||||
else()
|
||||
add_executable("${PROGRAM_PREFIX}setup" ${SETUP_FILES} ${COMMON_SOURCE_FILES})
|
||||
endif()
|
||||
|
||||
target_include_directories("${PROGRAM_PREFIX}setup"
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries("${PROGRAM_PREFIX}setup" SDL2::SDL2main SDL2::SDL2 SDL2::mixer SDL2::net setup textscreen)
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties("${PROGRAM_PREFIX}setup" PROPERTIES
|
||||
LINK_FLAGS "/MANIFEST:NO")
|
||||
endif()
|
||||
|
||||
add_executable(midiread midifile.c z_native.c i_system.c m_argv.c m_misc.c)
|
||||
target_compile_definitions(midiread PRIVATE "-DTEST")
|
||||
target_include_directories(midiread PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(midiread SDL2::SDL2main SDL2::SDL2)
|
||||
|
||||
add_executable(mus2mid mus2mid.c memio.c z_native.c i_system.c m_argv.c m_misc.c)
|
||||
target_compile_definitions(mus2mid PRIVATE "-DSTANDALONE")
|
||||
target_include_directories(mus2mid PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(mus2mid SDL2::SDL2main SDL2::SDL2)
|
||||
|
|
|
|||
10
src/adpcm-xq/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
add_library(adpcm-lib INTERFACE)
|
||||
target_sources(adpcm-lib INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR}/adpcm-lib.c)
|
||||
|
||||
target_include_directories(adpcm-lib INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
#if (NOT PICO_ON_DEVICE)
|
||||
# add_executable(adpcm-xq adpcm-xq.c)
|
||||
# target_link_libraries(adpcm-xq adpcm-lib)
|
||||
#endif()
|
||||
400
src/adpcm-xq/adpcm-lib.c
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** ADPCM-XQ **** //
|
||||
// Xtreme Quality ADPCM Encoder/Decoder //
|
||||
// Copyright (c) 2015 David Bryant. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "adpcm-lib.h"
|
||||
|
||||
/* This module encodes and decodes 4-bit ADPCM (DVI/IMA varient). ADPCM data is divided
|
||||
* into independently decodable blocks that can be relatively small. The most common
|
||||
* configuration is to store 505 samples into a 256 byte block, although other sizes are
|
||||
* permitted as long as the number of samples is one greater than a multiple of 8. When
|
||||
* multiple channels are present, they are interleaved in the data with an 8-sample
|
||||
* interval.
|
||||
*/
|
||||
|
||||
/********************************* 4-bit ADPCM encoder ********************************/
|
||||
|
||||
#define CLIP(data, min, max) \
|
||||
if ((data) > (max)) data = max; \
|
||||
else if ((data) < (min)) data = min;
|
||||
|
||||
/* step table */
|
||||
static const uint16_t step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||
32767
|
||||
};
|
||||
|
||||
/* step index tables */
|
||||
static const int index_table[] = {
|
||||
/* adpcm data size is 4 */
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
struct adpcm_channel {
|
||||
int32_t pcmdata; // current PCM value
|
||||
int32_t error, weight, history [2]; // for noise shaping
|
||||
int8_t index; // current index into step size table
|
||||
};
|
||||
|
||||
struct adpcm_context {
|
||||
struct adpcm_channel channels [2];
|
||||
int num_channels, lookahead, noise_shaping;
|
||||
};
|
||||
|
||||
/* Create ADPCM encoder context with given number of channels.
|
||||
* The returned pointer is used for subsequent calls. Note that
|
||||
* even though an ADPCM encoder could be set up to encode frames
|
||||
* independently, we use a context so that we can use previous
|
||||
* data to improve quality (this encoder might not be optimal
|
||||
* for encoding independent frames).
|
||||
*/
|
||||
|
||||
void *adpcm_create_context (int num_channels, int lookahead, int noise_shaping, int32_t initial_deltas [2])
|
||||
{
|
||||
struct adpcm_context *pcnxt = malloc (sizeof (struct adpcm_context));
|
||||
int ch, i;
|
||||
|
||||
memset (pcnxt, 0, sizeof (struct adpcm_context));
|
||||
pcnxt->noise_shaping = noise_shaping;
|
||||
pcnxt->num_channels = num_channels;
|
||||
pcnxt->lookahead = lookahead;
|
||||
|
||||
// given the supplied initial deltas, search for and store the closest index
|
||||
|
||||
for (ch = 0; ch < num_channels; ++ch)
|
||||
for (i = 0; i <= 88; i++)
|
||||
if (i == 88 || initial_deltas [ch] < ((int32_t) step_table [i] + step_table [i+1]) / 2) {
|
||||
pcnxt->channels [ch].index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
return pcnxt;
|
||||
}
|
||||
|
||||
/* Free the ADPCM encoder context.
|
||||
*/
|
||||
|
||||
void adpcm_free_context (void *p)
|
||||
{
|
||||
struct adpcm_context *pcnxt = (struct adpcm_context *) p;
|
||||
|
||||
free (pcnxt);
|
||||
}
|
||||
|
||||
static void set_decode_parameters (struct adpcm_context *pcnxt, int32_t *init_pcmdata, int8_t *init_index)
|
||||
{
|
||||
int ch;
|
||||
|
||||
for (ch = 0; ch < pcnxt->num_channels; ch++) {
|
||||
pcnxt->channels[ch].pcmdata = init_pcmdata[ch];
|
||||
pcnxt->channels[ch].index = init_index[ch];
|
||||
}
|
||||
}
|
||||
|
||||
static void get_decode_parameters (struct adpcm_context *pcnxt, int32_t *init_pcmdata, int8_t *init_index)
|
||||
{
|
||||
int ch;
|
||||
|
||||
for (ch = 0; ch < pcnxt->num_channels; ch++) {
|
||||
init_pcmdata[ch] = pcnxt->channels[ch].pcmdata;
|
||||
init_index[ch] = pcnxt->channels[ch].index;
|
||||
}
|
||||
}
|
||||
|
||||
static double minimum_error (const struct adpcm_channel *pchan, int nch, int32_t csample, const int16_t *sample, int depth, int *best_nibble)
|
||||
{
|
||||
int32_t delta = csample - pchan->pcmdata;
|
||||
struct adpcm_channel chan = *pchan;
|
||||
int step = step_table[chan.index];
|
||||
int trial_delta = (step >> 3);
|
||||
int nibble, nibble2;
|
||||
double min_error;
|
||||
|
||||
if (delta < 0) {
|
||||
int mag = (-delta << 2) / step;
|
||||
nibble = 0x8 | (mag > 7 ? 7 : mag);
|
||||
}
|
||||
else {
|
||||
int mag = (delta << 2) / step;
|
||||
nibble = mag > 7 ? 7 : mag;
|
||||
}
|
||||
|
||||
if (nibble & 1) trial_delta += (step >> 2);
|
||||
if (nibble & 2) trial_delta += (step >> 1);
|
||||
if (nibble & 4) trial_delta += step;
|
||||
if (nibble & 8) trial_delta = -trial_delta;
|
||||
|
||||
chan.pcmdata += trial_delta;
|
||||
CLIP(chan.pcmdata, -32768, 32767);
|
||||
if (best_nibble) *best_nibble = nibble;
|
||||
min_error = (double) (chan.pcmdata - csample) * (chan.pcmdata - csample);
|
||||
|
||||
if (depth) {
|
||||
chan.index += index_table[nibble & 0x07];
|
||||
CLIP(chan.index, 0, 88);
|
||||
min_error += minimum_error (&chan, nch, sample [nch], sample + nch, depth - 1, NULL);
|
||||
}
|
||||
else
|
||||
return min_error;
|
||||
|
||||
for (nibble2 = 0; nibble2 <= 0xF; ++nibble2) {
|
||||
double error;
|
||||
|
||||
if (nibble2 == nibble)
|
||||
continue;
|
||||
|
||||
chan = *pchan;
|
||||
trial_delta = (step >> 3);
|
||||
|
||||
if (nibble2 & 1) trial_delta += (step >> 2);
|
||||
if (nibble2 & 2) trial_delta += (step >> 1);
|
||||
if (nibble2 & 4) trial_delta += step;
|
||||
if (nibble2 & 8) trial_delta = -trial_delta;
|
||||
|
||||
chan.pcmdata += trial_delta;
|
||||
CLIP(chan.pcmdata, -32768, 32767);
|
||||
|
||||
error = (double) (chan.pcmdata - csample) * (chan.pcmdata - csample);
|
||||
|
||||
if (error < min_error) {
|
||||
chan.index += index_table[nibble2 & 0x07];
|
||||
CLIP(chan.index, 0, 88);
|
||||
error += minimum_error (&chan, nch, sample [nch], sample + nch, depth - 1, NULL);
|
||||
|
||||
if (error < min_error) {
|
||||
if (best_nibble) *best_nibble = nibble2;
|
||||
min_error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return min_error;
|
||||
}
|
||||
|
||||
static uint8_t encode_sample (struct adpcm_context *pcnxt, int ch, const int16_t *sample, int num_samples)
|
||||
{
|
||||
struct adpcm_channel *pchan = pcnxt->channels + ch;
|
||||
int32_t csample = *sample;
|
||||
int depth = num_samples - 1, nibble;
|
||||
int step = step_table[pchan->index];
|
||||
int trial_delta = (step >> 3);
|
||||
|
||||
if (pcnxt->noise_shaping == NOISE_SHAPING_DYNAMIC) {
|
||||
int32_t sam = (3 * pchan->history [0] - pchan->history [1]) >> 1;
|
||||
int32_t temp = csample - (((pchan->weight * sam) + 512) >> 10);
|
||||
int32_t shaping_weight;
|
||||
|
||||
if (sam && temp) pchan->weight -= (((sam ^ temp) >> 29) & 4) - 2;
|
||||
pchan->history [1] = pchan->history [0];
|
||||
pchan->history [0] = csample;
|
||||
|
||||
shaping_weight = (pchan->weight < 256) ? 1024 : 1536 - (pchan->weight * 2);
|
||||
temp = -((shaping_weight * pchan->error + 512) >> 10);
|
||||
|
||||
if (shaping_weight < 0 && temp) {
|
||||
if (temp == pchan->error)
|
||||
temp = (temp < 0) ? temp + 1 : temp - 1;
|
||||
|
||||
pchan->error = -csample;
|
||||
csample += temp;
|
||||
}
|
||||
else
|
||||
pchan->error = -(csample += temp);
|
||||
}
|
||||
else if (pcnxt->noise_shaping == NOISE_SHAPING_STATIC)
|
||||
pchan->error = -(csample -= pchan->error);
|
||||
|
||||
if (depth > pcnxt->lookahead)
|
||||
depth = pcnxt->lookahead;
|
||||
|
||||
minimum_error (pchan, pcnxt->num_channels, csample, sample, depth, &nibble);
|
||||
|
||||
if (nibble & 1) trial_delta += (step >> 2);
|
||||
if (nibble & 2) trial_delta += (step >> 1);
|
||||
if (nibble & 4) trial_delta += step;
|
||||
if (nibble & 8) trial_delta = -trial_delta;
|
||||
|
||||
pchan->pcmdata += trial_delta;
|
||||
pchan->index += index_table[nibble & 0x07];
|
||||
CLIP(pchan->index, 0, 88);
|
||||
CLIP(pchan->pcmdata, -32768, 32767);
|
||||
|
||||
if (pcnxt->noise_shaping)
|
||||
pchan->error += pchan->pcmdata;
|
||||
|
||||
return nibble;
|
||||
}
|
||||
|
||||
static void encode_chunks (struct adpcm_context *pcnxt, uint8_t **outbuf, size_t *outbufsize, const int16_t **inbuf, int inbufcount)
|
||||
{
|
||||
const int16_t *pcmbuf;
|
||||
int chunks, ch, i;
|
||||
|
||||
chunks = (inbufcount - 1) / 8;
|
||||
*outbufsize += (chunks * 4) * pcnxt->num_channels;
|
||||
|
||||
while (chunks--)
|
||||
{
|
||||
for (ch = 0; ch < pcnxt->num_channels; ch++)
|
||||
{
|
||||
pcmbuf = *inbuf + ch;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
**outbuf = encode_sample (pcnxt, ch, pcmbuf, chunks * 8 + (3 - i) * 2 + 2);
|
||||
pcmbuf += pcnxt->num_channels;
|
||||
**outbuf |= encode_sample (pcnxt, ch, pcmbuf, chunks * 8 + (3 - i) * 2 + 1) << 4;
|
||||
pcmbuf += pcnxt->num_channels;
|
||||
(*outbuf)++;
|
||||
}
|
||||
}
|
||||
|
||||
*inbuf += 8 * pcnxt->num_channels;
|
||||
}
|
||||
}
|
||||
|
||||
/* Encode a block of 16-bit PCM data into 4-bit ADPCM.
|
||||
*
|
||||
* Parameters:
|
||||
* p the context returned by adpcm_begin()
|
||||
* outbuf destination buffer
|
||||
* outbufsize pointer to variable where the number of bytes written
|
||||
* will be stored
|
||||
* inbuf source PCM samples
|
||||
* inbufcount number of composite PCM samples provided (note: this is
|
||||
* the total number of 16-bit samples divided by the number
|
||||
* of channels)
|
||||
*
|
||||
* Returns 1 (for success as there is no error checking)
|
||||
*/
|
||||
|
||||
int adpcm_encode_block (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount)
|
||||
{
|
||||
struct adpcm_context *pcnxt = (struct adpcm_context *) p;
|
||||
int32_t init_pcmdata[2];
|
||||
int8_t init_index[2];
|
||||
int ch;
|
||||
|
||||
*outbufsize = 0;
|
||||
|
||||
if (!inbufcount)
|
||||
return 1;
|
||||
|
||||
get_decode_parameters(pcnxt, init_pcmdata, init_index);
|
||||
|
||||
for (ch = 0; ch < pcnxt->num_channels; ch++) {
|
||||
init_pcmdata[ch] = *inbuf++;
|
||||
outbuf[0] = init_pcmdata[ch];
|
||||
outbuf[1] = init_pcmdata[ch] >> 8;
|
||||
outbuf[2] = init_index[ch];
|
||||
outbuf[3] = 0;
|
||||
|
||||
outbuf += 4;
|
||||
*outbufsize += 4;
|
||||
}
|
||||
|
||||
set_decode_parameters(pcnxt, init_pcmdata, init_index);
|
||||
encode_chunks (pcnxt, &outbuf, outbufsize, &inbuf, inbufcount);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/********************************* 4-bit ADPCM decoder ********************************/
|
||||
|
||||
/* Decode the block of ADPCM data into PCM. This requires no context because ADPCM blocks
|
||||
* are indeppendently decodable. This assumes that a single entire block is always decoded;
|
||||
* it must be called multiple times for multiple blocks and cannot resume in the middle of a
|
||||
* block.
|
||||
*
|
||||
* Parameters:
|
||||
* outbuf destination for interleaved PCM samples
|
||||
* inbuf source ADPCM block
|
||||
* inbufsize size of source ADPCM block
|
||||
* channels number of channels in block (must be determined from other context)
|
||||
*
|
||||
* Returns number of converted composite samples (total samples divided by number of channels)
|
||||
*/
|
||||
|
||||
int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels)
|
||||
{
|
||||
int ch, samples = 1, chunks;
|
||||
int32_t pcmdata[2];
|
||||
int8_t index[2];
|
||||
|
||||
if (inbufsize < (uint32_t) channels * 4)
|
||||
return 0;
|
||||
|
||||
for (ch = 0; ch < channels; ch++) {
|
||||
*outbuf++ = pcmdata[ch] = (int16_t) (inbuf [0] | (inbuf [1] << 8));
|
||||
index[ch] = inbuf [2];
|
||||
|
||||
if (index [ch] < 0 || index [ch] > 88 || inbuf [3]) // sanitize the input a little...
|
||||
return 0;
|
||||
|
||||
inbufsize -= 4;
|
||||
inbuf += 4;
|
||||
}
|
||||
|
||||
chunks = inbufsize / (channels * 4);
|
||||
samples += chunks * 8;
|
||||
|
||||
while (chunks--) {
|
||||
int ch, i;
|
||||
|
||||
for (ch = 0; ch < channels; ++ch) {
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
int step = step_table [index [ch]], delta = step >> 3;
|
||||
|
||||
if (*inbuf & 1) delta += (step >> 2);
|
||||
if (*inbuf & 2) delta += (step >> 1);
|
||||
if (*inbuf & 4) delta += step;
|
||||
if (*inbuf & 8) delta = -delta;
|
||||
|
||||
pcmdata[ch] += delta;
|
||||
index[ch] += index_table [*inbuf & 0x7];
|
||||
CLIP(index[ch], 0, 88);
|
||||
CLIP(pcmdata[ch], -32768, 32767);
|
||||
outbuf [i * 2 * channels] = pcmdata[ch];
|
||||
|
||||
step = step_table [index [ch]], delta = step >> 3;
|
||||
|
||||
if (*inbuf & 0x10) delta += (step >> 2);
|
||||
if (*inbuf & 0x20) delta += (step >> 1);
|
||||
if (*inbuf & 0x40) delta += step;
|
||||
if (*inbuf & 0x80) delta = -delta;
|
||||
|
||||
pcmdata[ch] += delta;
|
||||
index[ch] += index_table [(*inbuf >> 4) & 0x7];
|
||||
CLIP(index[ch], 0, 88);
|
||||
CLIP(pcmdata[ch], -32768, 32767);
|
||||
outbuf [(i * 2 + 1) * channels] = pcmdata[ch];
|
||||
|
||||
inbuf++;
|
||||
}
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
|
||||
outbuf += channels * 7;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
35
src/adpcm-xq/adpcm-lib.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** ADPCM-XQ **** //
|
||||
// Xtreme Quality ADPCM Encoder/Decoder //
|
||||
// Copyright (c) 2015 David Bryant. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ADPCMLIB_H_
|
||||
#define ADPCMLIB_H_
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int8 int8_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
void *adpcm_create_context (int num_channels, int lookahead, int noise_shaping, int32_t initial_deltas [2]);
|
||||
int adpcm_encode_block (void *p, uint8_t *outbuf, size_t *outbufsize, const int16_t *inbuf, int inbufcount);
|
||||
int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inbufsize, int channels);
|
||||
void adpcm_free_context (void *p);
|
||||
|
||||
#define NOISE_SHAPING_OFF 0 // flat noise (no shaping)
|
||||
#define NOISE_SHAPING_STATIC 1 // first-order highpass shaping
|
||||
#define NOISE_SHAPING_DYNAMIC 2 // dynamically tilted noise based on signal
|
||||
|
||||
#endif /* ADPCMLIB_H_ */
|
||||
|
||||
789
src/adpcm-xq/adpcm-xq.c
Normal file
|
|
@ -0,0 +1,789 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// **** ADPCM-XQ **** //
|
||||
// Xtreme Quality ADPCM Encoder/Decoder //
|
||||
// Copyright (c) 2015 David Bryant. //
|
||||
// All Rights Reserved. //
|
||||
// Distributed under the BSD Software License (see license.txt) //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "adpcm-lib.h"
|
||||
|
||||
static const char *sign_on = "\n"
|
||||
" ADPCM-XQ Xtreme Quality IMA-ADPCM WAV Encoder / Decoder Version 0.3\n"
|
||||
" Copyright (c) 2018 David Bryant. All Rights Reserved.\n\n";
|
||||
|
||||
static const char *usage =
|
||||
" Usage: ADPCM-XQ [-options] infile.wav outfile.wav\n\n"
|
||||
" Operation: conversion is performed based on the type of the infile\n"
|
||||
" (either encode 16-bit PCM to 4-bit IMA-ADPCM or decode back)\n\n"
|
||||
" Options: -[0-8] = encode lookahead samples (default = 3)\n"
|
||||
" -bn = override auto block size, 2^n bytes (n = 8-15)\n"
|
||||
" -d = decode only (fail on WAV file already PCM)\n"
|
||||
" -e = encode only (fail on WAV file already ADPCM)\n"
|
||||
" -f = encode flat noise (no dynamic noise shaping)\n"
|
||||
" -h = display this help message\n"
|
||||
" -q = quiet mode (display errors only)\n"
|
||||
" -r = raw output (no WAV header written)\n"
|
||||
" -v = verbose (display lots of info)\n"
|
||||
" -y = overwrite outfile if it exists\n\n"
|
||||
" Web: Visit www.github.com/dbry/adpcm-xq for latest version and info\n\n";
|
||||
|
||||
#define ADPCM_FLAG_NOISE_SHAPING 0x1
|
||||
#define ADPCM_FLAG_RAW_OUTPUT 0x2
|
||||
|
||||
static int adpcm_converter (char *infilename, char *outfilename, int flags, int blocksize_pow2, int lookahead);
|
||||
static int verbosity = 0, decode_only = 0, encode_only = 0;
|
||||
|
||||
int main (argc, argv) int argc; char **argv;
|
||||
{
|
||||
int lookahead = 3, flags = ADPCM_FLAG_NOISE_SHAPING, blocksize_pow2 = 0, overwrite = 0, asked_help = 0;
|
||||
char *infilename = NULL, *outfilename = NULL;
|
||||
FILE *outfile;
|
||||
|
||||
// if the name of the executable ends in "encoder" or "decoder", just do that function
|
||||
encode_only = argc && strstr (argv [0], "encoder") && strlen (strstr (argv [0], "encoder")) == strlen ("encoder");
|
||||
decode_only = argc && strstr (argv [0], "decoder") && strlen (strstr (argv [0], "decoder")) == strlen ("decoder");
|
||||
|
||||
// loop through command-line arguments
|
||||
|
||||
while (--argc) {
|
||||
#if defined (_WIN32)
|
||||
if ((**++argv == '-' || **argv == '/') && (*argv)[1])
|
||||
#else
|
||||
if ((**++argv == '-') && (*argv)[1])
|
||||
#endif
|
||||
while (*++*argv)
|
||||
switch (**argv) {
|
||||
|
||||
case '0': case '1': case '2':
|
||||
case '3': case '4': case '5':
|
||||
case '6': case '7': case '8':
|
||||
lookahead = **argv - '0';
|
||||
break;
|
||||
|
||||
case 'B': case 'b':
|
||||
blocksize_pow2 = strtol (++*argv, argv, 10);
|
||||
|
||||
if (blocksize_pow2 < 8 || blocksize_pow2 > 15) {
|
||||
fprintf (stderr, "\nblock size power must be 8 to 15!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
--*argv;
|
||||
break;
|
||||
|
||||
case 'D': case 'd':
|
||||
decode_only = 1;
|
||||
break;
|
||||
|
||||
case 'E': case 'e':
|
||||
encode_only = 1;
|
||||
break;
|
||||
|
||||
case 'F': case 'f':
|
||||
flags &= ~ADPCM_FLAG_NOISE_SHAPING;
|
||||
break;
|
||||
|
||||
case 'H': case 'h':
|
||||
asked_help = 0;
|
||||
break;
|
||||
|
||||
case 'Q': case 'q':
|
||||
verbosity = -1;
|
||||
break;
|
||||
|
||||
case 'R': case 'r':
|
||||
flags |= ADPCM_FLAG_RAW_OUTPUT;
|
||||
break;
|
||||
|
||||
case 'V': case 'v':
|
||||
verbosity = 1;
|
||||
break;
|
||||
|
||||
case 'Y': case 'y':
|
||||
overwrite = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf (stderr, "\nillegal option: %c !\n", **argv);
|
||||
return 1;
|
||||
}
|
||||
else if (!infilename) {
|
||||
infilename = malloc (strlen (*argv) + 10);
|
||||
strcpy (infilename, *argv);
|
||||
}
|
||||
else if (!outfilename) {
|
||||
outfilename = malloc (strlen (*argv) + 10);
|
||||
strcpy (outfilename, *argv);
|
||||
}
|
||||
else {
|
||||
fprintf (stderr, "\nextra unknown argument: %s !\n", *argv);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbosity >= 0)
|
||||
fprintf (stderr, "%s", sign_on);
|
||||
|
||||
if (!outfilename || asked_help) {
|
||||
printf ("%s", usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp (infilename, outfilename)) {
|
||||
fprintf (stderr, "can't overwrite input file (specify different/new output file name)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!overwrite && (outfile = fopen (outfilename, "r"))) {
|
||||
fclose (outfile);
|
||||
fprintf (stderr, "output file \"%s\" exists (use -y to overwrite)\n", outfilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return adpcm_converter (infilename, outfilename, flags, blocksize_pow2, lookahead);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
char formType [4];
|
||||
} RiffChunkHeader;
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
} ChunkHeader;
|
||||
|
||||
#define ChunkHeaderFormat "4L"
|
||||
|
||||
typedef struct {
|
||||
uint16_t FormatTag, NumChannels;
|
||||
uint32_t SampleRate, BytesPerSecond;
|
||||
uint16_t BlockAlign, BitsPerSample;
|
||||
uint16_t cbSize;
|
||||
union {
|
||||
uint16_t ValidBitsPerSample;
|
||||
uint16_t SamplesPerBlock;
|
||||
uint16_t Reserved;
|
||||
} Samples;
|
||||
int32_t ChannelMask;
|
||||
uint16_t SubFormat;
|
||||
char GUID [14];
|
||||
} WaveHeader;
|
||||
|
||||
#define WaveHeaderFormat "SSLLSSSSLS"
|
||||
|
||||
typedef struct {
|
||||
char ckID [4];
|
||||
uint32_t ckSize;
|
||||
uint32_t TotalSamples;
|
||||
} FactHeader;
|
||||
|
||||
#define FactHeaderFormat "4LL"
|
||||
|
||||
#define WAVE_FORMAT_PCM 0x1
|
||||
#define WAVE_FORMAT_IMA_ADPCM 0x11
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xfffe
|
||||
|
||||
static int write_pcm_wav_header (FILE *outfile, int num_channels, size_t num_samples, int sample_rate);
|
||||
static int write_adpcm_wav_header (FILE *outfile, int num_channels, size_t num_samples, int sample_rate, int samples_per_block);
|
||||
static int adpcm_decode_data (FILE *infile, FILE *outfile, int num_channels, size_t num_samples, int block_size);
|
||||
static int adpcm_encode_data (FILE *infile, FILE *outfile, int num_channels, size_t num_samples, int samples_per_block, int lookahead, int noise_shaping);
|
||||
static void little_endian_to_native (void *data, char *format);
|
||||
static void native_to_little_endian (void *data, char *format);
|
||||
|
||||
static int adpcm_converter (char *infilename, char *outfilename, int flags, int blocksize_pow2, int lookahead)
|
||||
{
|
||||
int format = 0, res = 0, bits_per_sample, sample_rate, num_channels;
|
||||
uint32_t fact_samples = 0;
|
||||
size_t num_samples = 0;
|
||||
FILE *infile, *outfile;
|
||||
RiffChunkHeader riff_chunk_header;
|
||||
ChunkHeader chunk_header;
|
||||
WaveHeader WaveHeader;
|
||||
|
||||
if (!(infile = fopen (infilename, "rb"))) {
|
||||
fprintf (stderr, "can't open file \"%s\" for reading!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read initial RIFF form header
|
||||
|
||||
if (!fread (&riff_chunk_header, sizeof (RiffChunkHeader), 1, infile) ||
|
||||
strncmp (riff_chunk_header.ckID, "RIFF", 4) ||
|
||||
strncmp (riff_chunk_header.formType, "WAVE", 4)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// loop through all elements of the RIFF wav header (until the data chuck)
|
||||
|
||||
while (1) {
|
||||
|
||||
if (!fread (&chunk_header, sizeof (ChunkHeader), 1, infile)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
little_endian_to_native (&chunk_header, ChunkHeaderFormat);
|
||||
|
||||
// if it's the format chunk, we want to get some info out of there and
|
||||
// make sure it's a .wav file we can handle
|
||||
|
||||
if (!strncmp (chunk_header.ckID, "fmt ", 4)) {
|
||||
int supported = 1;
|
||||
|
||||
if (chunk_header.ckSize < 16 || chunk_header.ckSize > sizeof (WaveHeader) ||
|
||||
!fread (&WaveHeader, chunk_header.ckSize, 1, infile)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
little_endian_to_native (&WaveHeader, WaveHeaderFormat);
|
||||
|
||||
format = (WaveHeader.FormatTag == WAVE_FORMAT_EXTENSIBLE && chunk_header.ckSize == 40) ?
|
||||
WaveHeader.SubFormat : WaveHeader.FormatTag;
|
||||
|
||||
bits_per_sample = (chunk_header.ckSize == 40 && WaveHeader.Samples.ValidBitsPerSample) ?
|
||||
WaveHeader.Samples.ValidBitsPerSample : WaveHeader.BitsPerSample;
|
||||
|
||||
if (WaveHeader.NumChannels < 1 || WaveHeader.NumChannels > 2)
|
||||
supported = 0;
|
||||
else if (format == WAVE_FORMAT_PCM) {
|
||||
if (decode_only) {
|
||||
fprintf (stderr, "\"%s\" is PCM .WAV file, invalid in decode-only mode!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bits_per_sample < 9 || bits_per_sample > 16)
|
||||
supported = 0;
|
||||
|
||||
if (WaveHeader.BlockAlign != WaveHeader.NumChannels * 2)
|
||||
supported = 0;
|
||||
}
|
||||
else if (format == WAVE_FORMAT_IMA_ADPCM) {
|
||||
if (encode_only) {
|
||||
fprintf (stderr, "\"%s\" is ADPCM .WAV file, invalid in encode-only mode!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bits_per_sample != 4)
|
||||
supported = 0;
|
||||
|
||||
if (WaveHeader.Samples.SamplesPerBlock != (WaveHeader.BlockAlign - WaveHeader.NumChannels * 4) * (WaveHeader.NumChannels ^ 3) + 1) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
supported = 0;
|
||||
|
||||
if (!supported) {
|
||||
fprintf (stderr, "\"%s\" is an unsupported .WAV format!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity > 0) {
|
||||
fprintf (stderr, "format tag size = %d\n", chunk_header.ckSize);
|
||||
fprintf (stderr, "FormatTag = 0x%x, NumChannels = %d, BitsPerSample = %d\n",
|
||||
WaveHeader.FormatTag, WaveHeader.NumChannels, WaveHeader.BitsPerSample);
|
||||
fprintf (stderr, "BlockAlign = %d, SampleRate = %d, BytesPerSecond = %d\n",
|
||||
WaveHeader.BlockAlign, WaveHeader.SampleRate, WaveHeader.BytesPerSecond);
|
||||
|
||||
if (chunk_header.ckSize > 16) {
|
||||
if (format == WAVE_FORMAT_PCM)
|
||||
fprintf (stderr, "cbSize = %d, ValidBitsPerSample = %d\n", WaveHeader.cbSize,
|
||||
WaveHeader.Samples.ValidBitsPerSample);
|
||||
else if (format == WAVE_FORMAT_IMA_ADPCM)
|
||||
fprintf (stderr, "cbSize = %d, SamplesPerBlock = %d\n", WaveHeader.cbSize,
|
||||
WaveHeader.Samples.SamplesPerBlock);
|
||||
}
|
||||
|
||||
if (chunk_header.ckSize > 20)
|
||||
fprintf (stderr, "ChannelMask = %x, SubFormat = %d\n",
|
||||
WaveHeader.ChannelMask, WaveHeader.SubFormat);
|
||||
}
|
||||
}
|
||||
else if (!strncmp (chunk_header.ckID, "fact", 4)) {
|
||||
|
||||
if (chunk_header.ckSize < 4 || !fread (&fact_samples, sizeof (fact_samples), 1, infile)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (chunk_header.ckSize > 4) {
|
||||
int bytes_to_skip = chunk_header.ckSize - 4;
|
||||
char dummy;
|
||||
|
||||
while (bytes_to_skip--)
|
||||
if (!fread (&dummy, 1, 1, infile)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strncmp (chunk_header.ckID, "data", 4)) {
|
||||
|
||||
// on the data chunk, get size and exit parsing loop
|
||||
|
||||
if (!WaveHeader.NumChannels) { // make sure we saw a "fmt" chunk...
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!chunk_header.ckSize) {
|
||||
fprintf (stderr, "this .WAV file has no audio samples, probably is corrupt!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (format == WAVE_FORMAT_PCM) {
|
||||
if (chunk_header.ckSize % WaveHeader.BlockAlign) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_samples = chunk_header.ckSize / WaveHeader.BlockAlign;
|
||||
}
|
||||
else {
|
||||
int complete_blocks = chunk_header.ckSize / WaveHeader.BlockAlign;
|
||||
int leftover_bytes = chunk_header.ckSize % WaveHeader.BlockAlign;
|
||||
int samples_last_block;
|
||||
|
||||
num_samples = complete_blocks * WaveHeader.Samples.SamplesPerBlock;
|
||||
|
||||
if (leftover_bytes) {
|
||||
if (leftover_bytes % (WaveHeader.NumChannels * 4)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
if (verbosity > 0) fprintf (stderr, "data chunk has %d bytes left over for final ADPCM block\n", leftover_bytes);
|
||||
samples_last_block = (leftover_bytes - (WaveHeader.NumChannels * 4)) * (WaveHeader.NumChannels ^ 3) + 1;
|
||||
num_samples += samples_last_block;
|
||||
}
|
||||
else
|
||||
samples_last_block = WaveHeader.Samples.SamplesPerBlock;
|
||||
|
||||
if (fact_samples) {
|
||||
if (fact_samples < num_samples && fact_samples > num_samples - samples_last_block) {
|
||||
if (verbosity > 0) fprintf (stderr, "total samples reduced %lu by FACT chunk\n", (unsigned long) (num_samples - fact_samples));
|
||||
num_samples = fact_samples;
|
||||
}
|
||||
else if (WaveHeader.NumChannels == 2 && (fact_samples >>= 1) < num_samples && fact_samples > num_samples - samples_last_block) {
|
||||
if (verbosity > 0) fprintf (stderr, "num samples reduced %lu by [incorrect] FACT chunk\n", (unsigned long) (num_samples - fact_samples));
|
||||
num_samples = fact_samples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!num_samples) {
|
||||
fprintf (stderr, "this .WAV file has no audio samples, probably is corrupt!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity > 0)
|
||||
fprintf (stderr, "num samples = %lu\n", (unsigned long) num_samples);
|
||||
|
||||
num_channels = WaveHeader.NumChannels;
|
||||
sample_rate = WaveHeader.SampleRate;
|
||||
break;
|
||||
}
|
||||
else { // just ignore unknown chunks
|
||||
int bytes_to_eat = (chunk_header.ckSize + 1) & ~1L;
|
||||
char dummy;
|
||||
|
||||
if (verbosity > 0)
|
||||
fprintf (stderr, "extra unknown chunk \"%c%c%c%c\" of %d bytes\n",
|
||||
chunk_header.ckID [0], chunk_header.ckID [1], chunk_header.ckID [2],
|
||||
chunk_header.ckID [3], chunk_header.ckSize);
|
||||
|
||||
while (bytes_to_eat--)
|
||||
if (!fread (&dummy, 1, 1, infile)) {
|
||||
fprintf (stderr, "\"%s\" is not a valid .WAV file!\n", infilename);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(outfile = fopen (outfilename, "wb"))) {
|
||||
fprintf (stderr, "can't open file \"%s\" for writing!\n", outfilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (format == WAVE_FORMAT_PCM) {
|
||||
int block_size, samples_per_block;
|
||||
|
||||
if (blocksize_pow2)
|
||||
block_size = 1 << blocksize_pow2;
|
||||
else
|
||||
block_size = 256 * num_channels * (sample_rate < 11000 ? 1 : sample_rate / 11000);
|
||||
|
||||
samples_per_block = (block_size - num_channels * 4) * (num_channels ^ 3) + 1;
|
||||
|
||||
if (verbosity > 0)
|
||||
fprintf (stderr, "each %d byte ADPCM block will contain %d samples * %d channels\n",
|
||||
block_size, samples_per_block, num_channels);
|
||||
|
||||
if (!(flags & ADPCM_FLAG_RAW_OUTPUT) && !write_adpcm_wav_header (outfile, num_channels, num_samples, sample_rate, samples_per_block)) {
|
||||
fprintf (stderr, "can't write header to file \"%s\" !\n", outfilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity >= 0) fprintf (stderr, "encoding PCM file \"%s\" to%sADPCM file \"%s\"...\n",
|
||||
infilename, (flags & ADPCM_FLAG_RAW_OUTPUT) ? " raw " : " ", outfilename);
|
||||
|
||||
res = adpcm_encode_data (infile, outfile, num_channels, num_samples, samples_per_block, lookahead,
|
||||
(flags & ADPCM_FLAG_NOISE_SHAPING) ? (sample_rate > 64000 ? NOISE_SHAPING_STATIC : NOISE_SHAPING_DYNAMIC) : NOISE_SHAPING_OFF);
|
||||
}
|
||||
else if (format == WAVE_FORMAT_IMA_ADPCM) {
|
||||
if (!(flags & ADPCM_FLAG_RAW_OUTPUT) && !write_pcm_wav_header (outfile, num_channels, num_samples, sample_rate)) {
|
||||
fprintf (stderr, "can't write header to file \"%s\" !\n", outfilename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity >= 0) fprintf (stderr, "decoding ADPCM file \"%s\" to%sPCM file \"%s\"...\n",
|
||||
infilename, (flags & ADPCM_FLAG_RAW_OUTPUT) ? " raw " : " ", outfilename);
|
||||
|
||||
res = adpcm_decode_data (infile, outfile, num_channels, num_samples, WaveHeader.BlockAlign);
|
||||
}
|
||||
|
||||
fclose (outfile);
|
||||
fclose (infile);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int write_pcm_wav_header (FILE *outfile, int num_channels, size_t num_samples, int sample_rate)
|
||||
{
|
||||
RiffChunkHeader riffhdr;
|
||||
ChunkHeader datahdr, fmthdr;
|
||||
WaveHeader wavhdr;
|
||||
|
||||
int wavhdrsize = 16;
|
||||
int bytes_per_sample = 2;
|
||||
size_t total_data_bytes = num_samples * bytes_per_sample * num_channels;
|
||||
|
||||
memset (&wavhdr, 0, sizeof (wavhdr));
|
||||
|
||||
wavhdr.FormatTag = WAVE_FORMAT_PCM;
|
||||
wavhdr.NumChannels = num_channels;
|
||||
wavhdr.SampleRate = sample_rate;
|
||||
wavhdr.BytesPerSecond = sample_rate * num_channels * bytes_per_sample;
|
||||
wavhdr.BlockAlign = bytes_per_sample * num_channels;
|
||||
wavhdr.BitsPerSample = 16;
|
||||
|
||||
strncpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID));
|
||||
strncpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType));
|
||||
riffhdr.ckSize = sizeof (riffhdr) + wavhdrsize + sizeof (datahdr) + total_data_bytes;
|
||||
strncpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID));
|
||||
fmthdr.ckSize = wavhdrsize;
|
||||
|
||||
strncpy (datahdr.ckID, "data", sizeof (datahdr.ckID));
|
||||
datahdr.ckSize = total_data_bytes;
|
||||
|
||||
// write the RIFF chunks up to just before the data starts
|
||||
|
||||
native_to_little_endian (&riffhdr, ChunkHeaderFormat);
|
||||
native_to_little_endian (&fmthdr, ChunkHeaderFormat);
|
||||
native_to_little_endian (&wavhdr, WaveHeaderFormat);
|
||||
native_to_little_endian (&datahdr, ChunkHeaderFormat);
|
||||
|
||||
return fwrite (&riffhdr, sizeof (riffhdr), 1, outfile) &&
|
||||
fwrite (&fmthdr, sizeof (fmthdr), 1, outfile) &&
|
||||
fwrite (&wavhdr, wavhdrsize, 1, outfile) &&
|
||||
fwrite (&datahdr, sizeof (datahdr), 1, outfile);
|
||||
}
|
||||
|
||||
static int write_adpcm_wav_header (FILE *outfile, int num_channels, size_t num_samples, int sample_rate, int samples_per_block)
|
||||
{
|
||||
RiffChunkHeader riffhdr;
|
||||
ChunkHeader datahdr, fmthdr;
|
||||
WaveHeader wavhdr;
|
||||
FactHeader facthdr;
|
||||
|
||||
int wavhdrsize = 20;
|
||||
int block_size = (samples_per_block - 1) / (num_channels ^ 3) + (num_channels * 4);
|
||||
size_t num_blocks = num_samples / samples_per_block;
|
||||
int leftover_samples = num_samples % samples_per_block;
|
||||
size_t total_data_bytes = num_blocks * block_size;
|
||||
|
||||
if (leftover_samples) {
|
||||
int last_block_samples = ((leftover_samples + 6) & ~7) + 1;
|
||||
int last_block_size = (last_block_samples - 1) / (num_channels ^ 3) + (num_channels * 4);
|
||||
total_data_bytes += last_block_size;
|
||||
}
|
||||
|
||||
memset (&wavhdr, 0, sizeof (wavhdr));
|
||||
|
||||
wavhdr.FormatTag = WAVE_FORMAT_IMA_ADPCM;
|
||||
wavhdr.NumChannels = num_channels;
|
||||
wavhdr.SampleRate = sample_rate;
|
||||
wavhdr.BytesPerSecond = sample_rate * block_size / samples_per_block;
|
||||
wavhdr.BlockAlign = block_size;
|
||||
wavhdr.BitsPerSample = 4;
|
||||
wavhdr.cbSize = 2;
|
||||
wavhdr.Samples.SamplesPerBlock = samples_per_block;
|
||||
|
||||
strncpy (riffhdr.ckID, "RIFF", sizeof (riffhdr.ckID));
|
||||
strncpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType));
|
||||
riffhdr.ckSize = sizeof (riffhdr) + wavhdrsize + sizeof (facthdr) + sizeof (datahdr) + total_data_bytes;
|
||||
strncpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID));
|
||||
fmthdr.ckSize = wavhdrsize;
|
||||
strncpy (facthdr.ckID, "fact", sizeof (facthdr.ckID));
|
||||
facthdr.TotalSamples = num_samples;
|
||||
facthdr.ckSize = 4;
|
||||
|
||||
strncpy (datahdr.ckID, "data", sizeof (datahdr.ckID));
|
||||
datahdr.ckSize = total_data_bytes;
|
||||
|
||||
// write the RIFF chunks up to just before the data starts
|
||||
|
||||
native_to_little_endian (&riffhdr, ChunkHeaderFormat);
|
||||
native_to_little_endian (&fmthdr, ChunkHeaderFormat);
|
||||
native_to_little_endian (&wavhdr, WaveHeaderFormat);
|
||||
native_to_little_endian (&facthdr, FactHeaderFormat);
|
||||
native_to_little_endian (&datahdr, ChunkHeaderFormat);
|
||||
|
||||
return fwrite (&riffhdr, sizeof (riffhdr), 1, outfile) &&
|
||||
fwrite (&fmthdr, sizeof (fmthdr), 1, outfile) &&
|
||||
fwrite (&wavhdr, wavhdrsize, 1, outfile) &&
|
||||
fwrite (&facthdr, sizeof (facthdr), 1, outfile) &&
|
||||
fwrite (&datahdr, sizeof (datahdr), 1, outfile);
|
||||
}
|
||||
|
||||
static int adpcm_decode_data (FILE *infile, FILE *outfile, int num_channels, size_t num_samples, int block_size)
|
||||
{
|
||||
int samples_per_block = (block_size - num_channels * 4) * (num_channels ^ 3) + 1, percent;
|
||||
void *pcm_block = malloc (samples_per_block * num_channels * 2);
|
||||
void *adpcm_block = malloc (block_size);
|
||||
size_t progress_divider = 0;
|
||||
|
||||
if (!pcm_block || !adpcm_block) {
|
||||
fprintf (stderr, "could not allocate memory for buffers!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity >= 0 && num_samples > 1000) {
|
||||
progress_divider = (num_samples + 50) / 100;
|
||||
fprintf (stderr, "\rprogress: %d%% ", percent = 0);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
while (num_samples) {
|
||||
int this_block_adpcm_samples = samples_per_block;
|
||||
int this_block_pcm_samples = samples_per_block;
|
||||
|
||||
if (this_block_adpcm_samples > num_samples) {
|
||||
this_block_adpcm_samples = ((num_samples + 6) & ~7) + 1;
|
||||
block_size = (this_block_adpcm_samples - 1) / (num_channels ^ 3) + (num_channels * 4);
|
||||
this_block_pcm_samples = num_samples;
|
||||
}
|
||||
|
||||
if (!fread (adpcm_block, block_size, 1, infile)) {
|
||||
fprintf (stderr, "could not read all audio data from input file!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adpcm_decode_block (pcm_block, adpcm_block, block_size, num_channels) != this_block_adpcm_samples) {
|
||||
fprintf (stderr, "adpcm_decode_block() did not return expected value!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fwrite (pcm_block, this_block_pcm_samples * num_channels * 2, 1, outfile)) {
|
||||
fprintf (stderr, "could not write all audio data to output file!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_samples -= this_block_pcm_samples;
|
||||
|
||||
if (progress_divider) {
|
||||
int new_percent = 100 - num_samples / progress_divider;
|
||||
|
||||
if (new_percent != percent) {
|
||||
fprintf (stderr, "\rprogress: %d%% ", percent = new_percent);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verbosity >= 0)
|
||||
fprintf (stderr, "\r...completed successfully\n");
|
||||
|
||||
free (adpcm_block);
|
||||
free (pcm_block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adpcm_encode_data (FILE *infile, FILE *outfile, int num_channels, size_t num_samples, int samples_per_block, int lookahead, int noise_shaping)
|
||||
{
|
||||
int block_size = (samples_per_block - 1) / (num_channels ^ 3) + (num_channels * 4), percent;
|
||||
int16_t *pcm_block = malloc (samples_per_block * num_channels * 2);
|
||||
void *adpcm_block = malloc (block_size);
|
||||
size_t progress_divider = 0;
|
||||
void *adpcm_cnxt = NULL;
|
||||
|
||||
if (!pcm_block || !adpcm_block) {
|
||||
fprintf (stderr, "could not allocate memory for buffers!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (verbosity >= 0 && num_samples > 1000) {
|
||||
progress_divider = (num_samples + 50) / 100;
|
||||
fprintf (stderr, "\rprogress: %d%% ", percent = 0);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
while (num_samples) {
|
||||
int this_block_adpcm_samples = samples_per_block;
|
||||
int this_block_pcm_samples = samples_per_block;
|
||||
size_t num_bytes;
|
||||
|
||||
if (this_block_pcm_samples > num_samples) {
|
||||
this_block_adpcm_samples = ((num_samples + 6) & ~7) + 1;
|
||||
block_size = (this_block_adpcm_samples - 1) / (num_channels ^ 3) + (num_channels * 4);
|
||||
this_block_pcm_samples = num_samples;
|
||||
}
|
||||
|
||||
if (!fread (pcm_block, this_block_pcm_samples * num_channels * 2, 1, infile)) {
|
||||
fprintf (stderr, "\rcould not read all audio data from input file!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if this is the last block and it's not full, duplicate the last sample(s) so we don't
|
||||
// create problems for the lookahead
|
||||
|
||||
if (this_block_adpcm_samples > this_block_pcm_samples) {
|
||||
int16_t *dst = pcm_block + this_block_pcm_samples * num_channels, *src = dst - num_channels;
|
||||
int dups = (this_block_adpcm_samples - this_block_pcm_samples) * num_channels;
|
||||
|
||||
while (dups--)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
// if this is the first block, compute a decaying average (in reverse) so that we can let the
|
||||
// encoder know what kind of initial deltas to expect (helps initializing index)
|
||||
|
||||
if (!adpcm_cnxt) {
|
||||
int32_t average_deltas [2];
|
||||
int i;
|
||||
|
||||
average_deltas [0] = average_deltas [1] = 0;
|
||||
|
||||
for (i = this_block_adpcm_samples * num_channels; i -= num_channels;) {
|
||||
average_deltas [0] -= average_deltas [0] >> 3;
|
||||
average_deltas [0] += abs ((int32_t) pcm_block [i] - pcm_block [i - num_channels]);
|
||||
|
||||
if (num_channels == 2) {
|
||||
average_deltas [1] -= average_deltas [1] >> 3;
|
||||
average_deltas [1] += abs ((int32_t) pcm_block [i-1] - pcm_block [i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
average_deltas [0] >>= 3;
|
||||
average_deltas [1] >>= 3;
|
||||
|
||||
adpcm_cnxt = adpcm_create_context (num_channels, lookahead, noise_shaping, average_deltas);
|
||||
}
|
||||
|
||||
adpcm_encode_block (adpcm_cnxt, adpcm_block, &num_bytes, pcm_block, this_block_adpcm_samples);
|
||||
|
||||
if (num_bytes != block_size) {
|
||||
fprintf (stderr, "\radpcm_encode_block() did not return expected value (expected %d, got %d)!\n", block_size, (int) num_bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fwrite (adpcm_block, block_size, 1, outfile)) {
|
||||
fprintf (stderr, "\rcould not write all audio data to output file!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_samples -= this_block_pcm_samples;
|
||||
|
||||
if (progress_divider) {
|
||||
int new_percent = 100 - num_samples / progress_divider;
|
||||
|
||||
if (new_percent != percent) {
|
||||
fprintf (stderr, "\rprogress: %d%% ", percent = new_percent);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verbosity >= 0)
|
||||
fprintf (stderr, "\r...completed successfully\n");
|
||||
|
||||
if (adpcm_cnxt)
|
||||
adpcm_free_context (adpcm_cnxt);
|
||||
|
||||
free (adpcm_block);
|
||||
free (pcm_block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void little_endian_to_native (void *data, char *format)
|
||||
{
|
||||
unsigned char *cp = (unsigned char *) data;
|
||||
int32_t temp;
|
||||
|
||||
while (*format) {
|
||||
switch (*format) {
|
||||
case 'L':
|
||||
temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24);
|
||||
* (int32_t *) cp = temp;
|
||||
cp += 4;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
temp = cp [0] + (cp [1] << 8);
|
||||
* (short *) cp = (short) temp;
|
||||
cp += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isdigit ((unsigned char) *format))
|
||||
cp += *format - '0';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_to_little_endian (void *data, char *format)
|
||||
{
|
||||
unsigned char *cp = (unsigned char *) data;
|
||||
int32_t temp;
|
||||
|
||||
while (*format) {
|
||||
switch (*format) {
|
||||
case 'L':
|
||||
temp = * (int32_t *) cp;
|
||||
*cp++ = (unsigned char) temp;
|
||||
*cp++ = (unsigned char) (temp >> 8);
|
||||
*cp++ = (unsigned char) (temp >> 16);
|
||||
*cp++ = (unsigned char) (temp >> 24);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
temp = * (short *) cp;
|
||||
*cp++ = (unsigned char) temp;
|
||||
*cp++ = (unsigned char) (temp >> 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isdigit ((unsigned char) *format))
|
||||
cp += *format - '0';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
src/adpcm-xq/license.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) David Bryant
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Conifer Software nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -22,7 +23,11 @@
|
|||
#include <stdlib.h>
|
||||
#include "d_event.h"
|
||||
|
||||
#if !DOOM_SMALL
|
||||
#define MAXEVENTS 64
|
||||
#else
|
||||
#define MAXEVENTS 8
|
||||
#endif
|
||||
|
||||
static event_t events[MAXEVENTS];
|
||||
static int eventhead;
|
||||
|
|
|
|||
40
src/d_iwad.c
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -38,16 +39,20 @@ static const iwad_t iwads[] =
|
|||
{ "tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution" },
|
||||
{ "doom.wad", doom, retail, "Doom" },
|
||||
{ "doom1.wad", doom, shareware, "Doom Shareware" },
|
||||
#if !DOOM_ONLY
|
||||
{ "chex.wad", pack_chex, retail, "Chex Quest" },
|
||||
{ "hacx.wad", pack_hacx, commercial, "Hacx" },
|
||||
#endif
|
||||
{ "freedm.wad", doom2, commercial, "FreeDM" },
|
||||
{ "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" },
|
||||
{ "freedoom1.wad", doom, retail, "Freedoom: Phase 1" },
|
||||
#if !DOOM_ONLY
|
||||
{ "heretic.wad", heretic, retail, "Heretic" },
|
||||
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
|
||||
{ "hexen.wad", hexen, commercial, "Hexen" },
|
||||
//{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME
|
||||
{ "strife1.wad", strife, commercial, "Strife" },
|
||||
#endif
|
||||
};
|
||||
|
||||
// Array of locations to search for IWAD files
|
||||
|
|
@ -442,6 +447,7 @@ static void CheckDOSDefaults(void)
|
|||
|
||||
#endif
|
||||
|
||||
#if !DOOM_TINY
|
||||
// Returns true if the specified path is a path to a file
|
||||
// of the specified name.
|
||||
|
||||
|
|
@ -529,7 +535,7 @@ static GameMission_t IdentifyIWADByName(const char *name, int mask)
|
|||
GameMission_t mission;
|
||||
|
||||
name = M_BaseName(name);
|
||||
mission = none;
|
||||
mission = mission_none;
|
||||
|
||||
for (i=0; i<arrlen(iwads); ++i)
|
||||
{
|
||||
|
|
@ -696,9 +702,11 @@ static void BuildIWADDirList(void)
|
|||
// Look in the current directory. Doom always does this.
|
||||
AddIWADDir(".");
|
||||
|
||||
#if !NO_USE_ARGS
|
||||
// Next check the directory where the executable is located. This might
|
||||
// be different from the current directory.
|
||||
AddIWADDir(M_DirName(myargv[0]));
|
||||
#endif
|
||||
|
||||
// Add DOOMWADDIR if it is in the environment
|
||||
env = getenv("DOOMWADDIR");
|
||||
|
|
@ -835,7 +843,10 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
|
|||
//
|
||||
// @arg <file>
|
||||
//
|
||||
|
||||
#if USE_MEMORY_WAD
|
||||
return "<memory>.wad";
|
||||
#else
|
||||
#if !NO_USE_ARGS
|
||||
iwadparm = M_CheckParmWithArgs("-iwad", 1);
|
||||
|
||||
if (iwadparm)
|
||||
|
|
@ -854,6 +865,7 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
|
|||
*mission = IdentifyIWADByName(result, mask);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Search through the list and look for an IWAD
|
||||
|
||||
|
|
@ -866,6 +878,7 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
|
|||
result = SearchDirectoryForIWAD(iwad_dirs[i], mask, mission);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -950,6 +963,9 @@ const char *D_SuggestIWADName(GameMission_t mission, GameMode_t mode)
|
|||
return "unknown.wad";
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -957,7 +973,7 @@ const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
|||
for (i = 0; i < arrlen(iwads); ++i)
|
||||
{
|
||||
if (iwads[i].mission == mission
|
||||
&& (mode == indetermined || iwads[i].mode == mode))
|
||||
&& (mode == indetermined || iwads[i].mode == mode))
|
||||
{
|
||||
return iwads[i].description;
|
||||
}
|
||||
|
|
@ -966,3 +982,21 @@ const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
|||
return "Unknown game?";
|
||||
}
|
||||
|
||||
#if DOOM_TINY
|
||||
GameMission_t IdentifyIWADByName(const char *name) {
|
||||
size_t i;
|
||||
GameMission_t mission;
|
||||
mission = mission_none;
|
||||
|
||||
for (i=0; i<arrlen(iwads); ++i)
|
||||
{
|
||||
if (!strcasecmp(name, iwads[i].name))
|
||||
{
|
||||
mission = iwads[i].mission;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mission;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
12
src/d_iwad.h
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
|
||||
#include "d_mode.h"
|
||||
|
||||
#if !DOOM_ONLY
|
||||
#define IWAD_MASK_DOOM ((1 << doom) \
|
||||
| (1 << doom2) \
|
||||
| (1 << pack_tnt) \
|
||||
|
|
@ -30,6 +32,12 @@
|
|||
#define IWAD_MASK_HERETIC (1 << heretic)
|
||||
#define IWAD_MASK_HEXEN (1 << hexen)
|
||||
#define IWAD_MASK_STRIFE (1 << strife)
|
||||
#else
|
||||
#define IWAD_MASK_DOOM ((1 << doom) \
|
||||
| (1 << doom2) \
|
||||
| (1 << pack_tnt) \
|
||||
| (1 << pack_plut))
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -47,6 +55,8 @@ const char *D_SaveGameIWADName(GameMission_t gamemission);
|
|||
const char *D_SuggestIWADName(GameMission_t mission, GameMode_t mode);
|
||||
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode);
|
||||
void D_CheckCorrectIWAD(GameMission_t mission);
|
||||
|
||||
#if DOOM_TINY
|
||||
GameMission_t IdentifyIWADByName(const char *name);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
594
src/d_loop.c
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -38,13 +39,24 @@
|
|||
#include "net_sdl.h"
|
||||
#include "net_loop.h"
|
||||
|
||||
// The complete set of data for a particular tic.
|
||||
#if PICO_BUILD
|
||||
|
||||
#include "pico/time.h"
|
||||
#include "hardware/gpio.h"
|
||||
#if USE_PICO_NET
|
||||
#include "piconet.h"
|
||||
#include "doom/m_menu.h"
|
||||
#endif
|
||||
CU_REGISTER_DEBUG_PINS(tics)
|
||||
//CU_SELECT_DEBUG_PINS(tics)
|
||||
#endif
|
||||
|
||||
|
||||
#if DOOM_TINY
|
||||
static_assert(sizeof(ticcmd_t) == 8, "");
|
||||
static_assert(sizeof(ticcmd_set_t) == 8 * NET_MAXPLAYERS, "");
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ticcmd_t cmds[NET_MAXPLAYERS];
|
||||
boolean ingame[NET_MAXPLAYERS];
|
||||
} ticcmd_set_t;
|
||||
|
||||
// Maximum time that we wait in TryRunTics() for netgame data to be
|
||||
// received before we bail out and render a frame anyway.
|
||||
|
|
@ -61,7 +73,7 @@ typedef struct
|
|||
// from all players.
|
||||
//
|
||||
|
||||
static ticcmd_set_t ticdata[BACKUPTICS];
|
||||
ticcmd_set_t ticdata[BACKUPTICS];
|
||||
|
||||
// The index of the next tic to be made (with a call to BuildTiccmd).
|
||||
|
||||
|
|
@ -82,24 +94,33 @@ boolean singletics = false;
|
|||
|
||||
// Index of the local player.
|
||||
|
||||
static int localplayer;
|
||||
#if !USE_PICO_NET
|
||||
static isb_int8_t localplayer;
|
||||
#else
|
||||
#include "doom/doomstat.h"
|
||||
#define localplayer consoleplayer
|
||||
#endif
|
||||
|
||||
// Used for original sync code.
|
||||
|
||||
static int skiptics = 0;
|
||||
static int skiptics = 0;
|
||||
|
||||
// Reduce the bandwidth needed by sampling game input less and transmitting
|
||||
// less. If ticdup is 2, sample half normal, 3 = one third normal, etc.
|
||||
|
||||
int ticdup;
|
||||
isb_int8_t ticdup;
|
||||
|
||||
// Amount to offset the timer for game sync.
|
||||
|
||||
fixed_t offsetms;
|
||||
fixed_t offsetms;
|
||||
|
||||
// Use new client syncronisation code
|
||||
|
||||
static boolean new_sync = true;
|
||||
#if !USE_PICO_NET
|
||||
static boolean new_sync = true;
|
||||
#else
|
||||
#define new_sync true
|
||||
#endif
|
||||
|
||||
// Callback functions for loop code.
|
||||
|
||||
|
|
@ -120,74 +141,85 @@ static int player_class;
|
|||
|
||||
// 35 fps clock adjusted by offsetms milliseconds
|
||||
|
||||
static int GetAdjustedTime(void)
|
||||
{
|
||||
static int GetAdjustedTime(void) {
|
||||
int time_ms;
|
||||
|
||||
time_ms = I_GetTimeMS();
|
||||
|
||||
if (new_sync)
|
||||
{
|
||||
// Use the adjustments from net_client.c only if we are
|
||||
// using the new sync mode.
|
||||
if (new_sync) {
|
||||
// Use the adjustments from net_client.c only if we are
|
||||
// using the new sync mode.
|
||||
|
||||
time_ms += (offsetms / FRACUNIT);
|
||||
}
|
||||
|
||||
return (time_ms * TICRATE) / 1000;
|
||||
#if DOOM_TINY && TICRATE == 35
|
||||
// using 64 bit so we can run demo for > 17 hours
|
||||
uint64_t v = 150323855ull * (uint32_t)time_ms;
|
||||
return (int)(v >> 32);
|
||||
#else
|
||||
return time_ms * TICRATE / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
static boolean BuildNewTic(void)
|
||||
{
|
||||
int gameticdiv;
|
||||
static boolean BuildNewTic(void) {
|
||||
int gameticdiv;
|
||||
ticcmd_t cmd;
|
||||
|
||||
gameticdiv = gametic/ticdup;
|
||||
gameticdiv = gametic / ticdup;
|
||||
|
||||
I_StartTic ();
|
||||
I_StartTic();
|
||||
loop_interface->ProcessEvents();
|
||||
|
||||
// Always run the menu
|
||||
|
||||
loop_interface->RunMenu();
|
||||
|
||||
if (drone)
|
||||
{
|
||||
if (drone) {
|
||||
// In drone mode, do not generate any ticcmds.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_sync)
|
||||
{
|
||||
// If playing single player, do not allow tics to buffer
|
||||
// up very far
|
||||
if (new_sync) {
|
||||
// If playing single player, do not allow tics to buffer
|
||||
// up very far
|
||||
|
||||
if (!net_client_connected && maketic - gameticdiv > 2)
|
||||
return false;
|
||||
if (!net_client_connected && maketic - gameticdiv > 2)
|
||||
return false;
|
||||
|
||||
// Never go more than ~200ms ahead
|
||||
// Never go more than ~200ms ahead
|
||||
|
||||
if (maketic - gameticdiv > 8)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maketic - gameticdiv >= 5)
|
||||
return false;
|
||||
if (maketic - gameticdiv >= MIN(8, BACKUPTICS))
|
||||
return false;
|
||||
} else {
|
||||
if (maketic - gameticdiv >= 5)
|
||||
return false;
|
||||
}
|
||||
|
||||
//printf ("mk:%i ",maketic);
|
||||
memset(&cmd, 0, sizeof(ticcmd_t));
|
||||
loop_interface->BuildTiccmd(&cmd, maketic);
|
||||
|
||||
#if !NO_USE_NET
|
||||
if (net_client_connected)
|
||||
{
|
||||
NET_CL_SendTiccmd(&cmd, maketic);
|
||||
}
|
||||
#endif
|
||||
|
||||
ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd;
|
||||
#if !DOOM_TINY
|
||||
ticdata[maketic % BACKUPTICS].ingame[localplayer] = true;
|
||||
#else
|
||||
ticdata[maketic % BACKUPTICS].cmds[localplayer].ingame = true;
|
||||
#endif
|
||||
#if USE_PICO_NET
|
||||
if (net_client_connected) {
|
||||
// piconet uses ticdata directly, so we need it to be initialized above
|
||||
piconet_new_local_tic(maketic);
|
||||
}
|
||||
#endif
|
||||
|
||||
++maketic;
|
||||
|
||||
|
|
@ -199,13 +231,12 @@ static boolean BuildNewTic(void)
|
|||
// Builds ticcmds for console player,
|
||||
// sends out a packet
|
||||
//
|
||||
int lasttime;
|
||||
int lasttime;
|
||||
|
||||
void NetUpdate (void)
|
||||
{
|
||||
void NetUpdate(void) {
|
||||
int nowtime;
|
||||
int newtics;
|
||||
int i;
|
||||
int i;
|
||||
|
||||
// If we are running with singletics (timing a demo), this
|
||||
// is all done separately.
|
||||
|
|
@ -215,8 +246,10 @@ void NetUpdate (void)
|
|||
|
||||
// Run network subsystems
|
||||
|
||||
#if !NO_USE_NET
|
||||
NET_CL_Run();
|
||||
NET_SV_Run();
|
||||
#endif
|
||||
|
||||
// check time
|
||||
nowtime = GetAdjustedTime() / ticdup;
|
||||
|
|
@ -224,40 +257,35 @@ void NetUpdate (void)
|
|||
|
||||
lasttime = nowtime;
|
||||
|
||||
if (skiptics <= newtics)
|
||||
{
|
||||
if (skiptics <= newtics) {
|
||||
newtics -= skiptics;
|
||||
skiptics = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
skiptics -= newtics;
|
||||
newtics = 0;
|
||||
}
|
||||
|
||||
// build new ticcmds for console player
|
||||
|
||||
for (i=0 ; i<newtics ; i++)
|
||||
{
|
||||
if (!BuildNewTic())
|
||||
{
|
||||
for (i = 0; i < newtics; i++) {
|
||||
if (!BuildNewTic()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void D_Disconnected(void)
|
||||
{
|
||||
static void D_Disconnected(void) {
|
||||
#if !USE_PICO_NET
|
||||
// In drone mode, the game cannot continue once disconnected.
|
||||
|
||||
if (drone)
|
||||
{
|
||||
if (drone) {
|
||||
I_Error("Disconnected from server in drone mode.");
|
||||
}
|
||||
|
||||
// disconnected from server
|
||||
|
||||
printf("Disconnected from server.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -265,28 +293,30 @@ static void D_Disconnected(void)
|
|||
// available.
|
||||
//
|
||||
|
||||
void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
|
||||
{
|
||||
void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask) {
|
||||
int i;
|
||||
|
||||
// Disconnected from server?
|
||||
|
||||
if (ticcmds == NULL && players_mask == NULL)
|
||||
{
|
||||
if (ticcmds == NULL && players_mask == NULL) {
|
||||
D_Disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
||||
{
|
||||
if (!drone && i == localplayer)
|
||||
{
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||
if (!drone && i == localplayer) {
|
||||
// This is us. Don't overwrite it.
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
|
||||
#if !USE_PICO_NET
|
||||
#if !DOOM_TINY
|
||||
ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
|
||||
#else
|
||||
ticdata[recvtic % BACKUPTICS].cmds[i].ingame = players_mask[i];
|
||||
#endif
|
||||
#else
|
||||
(void)players_mask;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -299,14 +329,14 @@ void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
|
|||
// Called after the screen is set but before the game starts running.
|
||||
//
|
||||
|
||||
void D_StartGameLoop(void)
|
||||
{
|
||||
void D_StartGameLoop(void) {
|
||||
lasttime = GetAdjustedTime() / ticdup;
|
||||
}
|
||||
|
||||
//
|
||||
// Block until the game start message is received from the server.
|
||||
//
|
||||
#if !NO_USE_NET
|
||||
|
||||
static void BlockUntilStart(net_gamesettings_t *settings,
|
||||
netgame_startup_callback_t callback)
|
||||
|
|
@ -331,94 +361,6 @@ static void BlockUntilStart(net_gamesettings_t *settings,
|
|||
}
|
||||
}
|
||||
|
||||
void D_StartNetGame(net_gamesettings_t *settings,
|
||||
netgame_startup_callback_t callback)
|
||||
{
|
||||
int i;
|
||||
|
||||
offsetms = 0;
|
||||
recvtic = 0;
|
||||
|
||||
settings->consoleplayer = 0;
|
||||
settings->num_players = 1;
|
||||
settings->player_classes[0] = player_class;
|
||||
|
||||
//!
|
||||
// @category net
|
||||
//
|
||||
// Use original network client sync code rather than the improved
|
||||
// sync code.
|
||||
//
|
||||
settings->new_sync = !M_ParmExists("-oldsync");
|
||||
|
||||
//!
|
||||
// @category net
|
||||
// @arg <n>
|
||||
//
|
||||
// Send n extra tics in every packet as insurance against dropped
|
||||
// packets.
|
||||
//
|
||||
|
||||
i = M_CheckParmWithArgs("-extratics", 1);
|
||||
|
||||
if (i > 0)
|
||||
settings->extratics = atoi(myargv[i+1]);
|
||||
else
|
||||
settings->extratics = 1;
|
||||
|
||||
//!
|
||||
// @category net
|
||||
// @arg <n>
|
||||
//
|
||||
// Reduce the resolution of the game by a factor of n, reducing
|
||||
// the amount of network bandwidth needed.
|
||||
//
|
||||
|
||||
i = M_CheckParmWithArgs("-dup", 1);
|
||||
|
||||
if (i > 0)
|
||||
settings->ticdup = atoi(myargv[i+1]);
|
||||
else
|
||||
settings->ticdup = 1;
|
||||
|
||||
if (net_client_connected)
|
||||
{
|
||||
// Send our game settings and block until game start is received
|
||||
// from the server.
|
||||
|
||||
NET_CL_StartGame(settings);
|
||||
BlockUntilStart(settings, callback);
|
||||
|
||||
// Read the game settings that were received.
|
||||
|
||||
NET_CL_GetSettings(settings);
|
||||
}
|
||||
|
||||
if (drone)
|
||||
{
|
||||
settings->consoleplayer = 0;
|
||||
}
|
||||
|
||||
// Set the local player and playeringame[] values.
|
||||
|
||||
localplayer = settings->consoleplayer;
|
||||
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
||||
{
|
||||
local_playeringame[i] = i < settings->num_players;
|
||||
}
|
||||
|
||||
// Copy settings to global variables.
|
||||
|
||||
ticdup = settings->ticdup;
|
||||
new_sync = settings->new_sync;
|
||||
|
||||
// TODO: Message disabled until we fix new_sync.
|
||||
//if (!new_sync)
|
||||
//{
|
||||
// printf("Syncing netgames like Vanilla Doom.\n");
|
||||
//}
|
||||
}
|
||||
|
||||
boolean D_InitNetGame(net_connect_data_t *connect_data)
|
||||
{
|
||||
|
|
@ -528,17 +470,19 @@ void D_QuitNetGame (void)
|
|||
NET_SV_Shutdown();
|
||||
NET_CL_Disconnect();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int GetLowTic(void)
|
||||
{
|
||||
static int GetLowTic(void) {
|
||||
int lowtic;
|
||||
|
||||
lowtic = maketic;
|
||||
|
||||
if (net_client_connected)
|
||||
{
|
||||
if (drone || recvtic < lowtic)
|
||||
{
|
||||
if (net_client_connected) {
|
||||
#if USE_PICO_NET
|
||||
recvtic = piconet_maybe_recv_tic(recvtic);
|
||||
if (!net_client_connected) piconet_stop();
|
||||
#endif
|
||||
if (drone || recvtic < lowtic) {
|
||||
lowtic = recvtic;
|
||||
}
|
||||
}
|
||||
|
|
@ -550,8 +494,7 @@ static int frameon;
|
|||
static int frameskip[4];
|
||||
static int oldnettics;
|
||||
|
||||
static void OldNetSync(void)
|
||||
{
|
||||
static void OldNetSync(void) {
|
||||
unsigned int i;
|
||||
int keyplayer = -1;
|
||||
|
||||
|
|
@ -560,30 +503,23 @@ static void OldNetSync(void)
|
|||
// ideally maketic should be 1 - 3 tics above lowtic
|
||||
// if we are consistantly slower, speed up time
|
||||
|
||||
for (i=0 ; i<NET_MAXPLAYERS ; i++)
|
||||
{
|
||||
if (local_playeringame[i])
|
||||
{
|
||||
for (i = 0; i < NET_MAXPLAYERS; i++) {
|
||||
if (local_playeringame[i]) {
|
||||
keyplayer = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyplayer < 0)
|
||||
{
|
||||
if (keyplayer < 0) {
|
||||
// If there are no players, we can never advance anyway
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (localplayer == keyplayer)
|
||||
{
|
||||
if (localplayer == keyplayer) {
|
||||
// the key player does not adapt
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maketic <= recvtic)
|
||||
{
|
||||
} else {
|
||||
if (maketic <= recvtic) {
|
||||
lasttime--;
|
||||
// printf ("-");
|
||||
}
|
||||
|
|
@ -591,8 +527,7 @@ static void OldNetSync(void)
|
|||
frameskip[frameon & 3] = oldnettics > recvtic;
|
||||
oldnettics = maketic;
|
||||
|
||||
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
|
||||
{
|
||||
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) {
|
||||
skiptics = 1;
|
||||
// printf ("+");
|
||||
}
|
||||
|
|
@ -601,18 +536,15 @@ static void OldNetSync(void)
|
|||
|
||||
// Returns true if there are players in the game:
|
||||
|
||||
static boolean PlayersInGame(void)
|
||||
{
|
||||
static boolean PlayersInGame(void) {
|
||||
boolean result = false;
|
||||
unsigned int i;
|
||||
|
||||
// If we are connected to a server, check if there are any players
|
||||
// in the game.
|
||||
|
||||
if (net_client_connected)
|
||||
{
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
||||
{
|
||||
if (net_client_connected) {
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||
result = result || local_playeringame[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -620,8 +552,7 @@ static boolean PlayersInGame(void)
|
|||
// Whether single or multi-player, unless we are running as a drone,
|
||||
// we are in the game.
|
||||
|
||||
if (!drone)
|
||||
{
|
||||
if (!drone) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
|
|
@ -631,13 +562,11 @@ static boolean PlayersInGame(void)
|
|||
// When using ticdup, certain values must be cleared out when running
|
||||
// the duplicate ticcmds.
|
||||
|
||||
static void TicdupSquash(ticcmd_set_t *set)
|
||||
{
|
||||
static void TicdupSquash(ticcmd_set_t *set) {
|
||||
ticcmd_t *cmd;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NET_MAXPLAYERS ; ++i)
|
||||
{
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||
cmd = &set->cmds[i];
|
||||
cmd->chatchar = 0;
|
||||
if (cmd->buttons & BT_SPECIAL)
|
||||
|
|
@ -648,15 +577,16 @@ static void TicdupSquash(ticcmd_set_t *set)
|
|||
// When running in single player mode, clear all the ingame[] array
|
||||
// except the local player.
|
||||
|
||||
static void SinglePlayerClear(ticcmd_set_t *set)
|
||||
{
|
||||
static void SinglePlayerClear(ticcmd_set_t *set) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
||||
{
|
||||
if (i != localplayer)
|
||||
{
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||
if (i != localplayer) {
|
||||
#if !DOOM_TINY
|
||||
set->ingame[i] = false;
|
||||
#else
|
||||
set->cmds[i].ingame = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -665,16 +595,18 @@ static void SinglePlayerClear(ticcmd_set_t *set)
|
|||
// TryRunTics
|
||||
//
|
||||
|
||||
void TryRunTics (void)
|
||||
{
|
||||
int i;
|
||||
int lowtic;
|
||||
int entertic;
|
||||
void TryRunTics(void) {
|
||||
int i;
|
||||
int lowtic;
|
||||
int entertic;
|
||||
static int oldentertics;
|
||||
int realtics;
|
||||
int availabletics;
|
||||
int counts;
|
||||
int availabletics;
|
||||
int counts;
|
||||
|
||||
#if PICO_BUILD
|
||||
DEBUG_PINS_SET(tics, 2);
|
||||
#endif
|
||||
// get real tics
|
||||
entertic = I_GetTime() / ticdup;
|
||||
realtics = entertic - oldentertics;
|
||||
|
|
@ -683,30 +615,24 @@ void TryRunTics (void)
|
|||
// in singletics mode, run a single tic every time this function
|
||||
// is called.
|
||||
|
||||
if (singletics)
|
||||
{
|
||||
if (singletics) {
|
||||
BuildNewTic();
|
||||
}
|
||||
else
|
||||
{
|
||||
NetUpdate ();
|
||||
} else {
|
||||
NetUpdate();
|
||||
}
|
||||
|
||||
lowtic = GetLowTic();
|
||||
|
||||
availabletics = lowtic - gametic/ticdup;
|
||||
availabletics = lowtic - gametic / ticdup;
|
||||
|
||||
// decide how many tics to run
|
||||
|
||||
if (new_sync)
|
||||
{
|
||||
counts = availabletics;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new_sync) {
|
||||
counts = availabletics;
|
||||
} else {
|
||||
// decide how many tics to run
|
||||
if (realtics < availabletics-1)
|
||||
counts = realtics+1;
|
||||
if (realtics < availabletics - 1)
|
||||
counts = realtics + 1;
|
||||
else if (realtics < availabletics)
|
||||
counts = realtics;
|
||||
else
|
||||
|
|
@ -715,33 +641,29 @@ void TryRunTics (void)
|
|||
if (counts < 1)
|
||||
counts = 1;
|
||||
|
||||
if (net_client_connected)
|
||||
{
|
||||
if (net_client_connected) {
|
||||
OldNetSync();
|
||||
}
|
||||
}
|
||||
|
||||
if (counts < 1)
|
||||
counts = 1;
|
||||
counts = 1;
|
||||
|
||||
// wait for new tics if needed
|
||||
while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
|
||||
{
|
||||
NetUpdate ();
|
||||
while (!PlayersInGame() || lowtic < gametic / ticdup + counts) {
|
||||
NetUpdate();
|
||||
|
||||
lowtic = GetLowTic();
|
||||
|
||||
if (lowtic < gametic/ticdup)
|
||||
I_Error ("TryRunTics: lowtic < gametic");
|
||||
if (lowtic < gametic / ticdup)
|
||||
I_Error("TryRunTics: lowtic < gametic");
|
||||
|
||||
// Still no tics to run? Sleep until some are available.
|
||||
if (lowtic < gametic/ticdup + counts)
|
||||
{
|
||||
if (lowtic < gametic / ticdup + counts) {
|
||||
// If we're in a netgame, we might spin forever waiting for
|
||||
// new network data to be received. So don't stay in here
|
||||
// forever - give the menu a chance to work.
|
||||
if (I_GetTime() / ticdup - entertic >= MAX_NETGAME_STALL_TICS)
|
||||
{
|
||||
if (I_GetTime() / ticdup - entertic >= MAX_NETGAME_STALL_TICS) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -750,43 +672,86 @@ void TryRunTics (void)
|
|||
}
|
||||
|
||||
// run the count * ticdup dics
|
||||
while (counts--)
|
||||
{
|
||||
while (counts--) {
|
||||
#if PICO_BUILD
|
||||
DEBUG_PINS_SET(tics, 1);
|
||||
#endif
|
||||
ticcmd_set_t *set;
|
||||
|
||||
if (!PlayersInGame())
|
||||
{
|
||||
if (!PlayersInGame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
set = &ticdata[(gametic / ticdup) % BACKUPTICS];
|
||||
|
||||
if (!net_client_connected)
|
||||
{
|
||||
#if DEBUG_CONSISTENCY
|
||||
if (netgame) printf("apply tic %d\n", gametic);
|
||||
#endif
|
||||
if (!net_client_connected) {
|
||||
SinglePlayerClear(set);
|
||||
}
|
||||
|
||||
for (i=0 ; i<ticdup ; i++)
|
||||
{
|
||||
if (gametic/ticdup > lowtic)
|
||||
I_Error ("gametic>lowtic");
|
||||
for (i = 0; i < ticdup; i++) {
|
||||
if (gametic / ticdup > lowtic)
|
||||
I_Error("gametic>lowtic");
|
||||
|
||||
#if !DOOM_TINY
|
||||
memcpy(local_playeringame, set->ingame, sizeof(local_playeringame));
|
||||
#else
|
||||
int lplayer_count = 0;
|
||||
for(int j=0;j<NET_MAXPLAYERS;j++) {
|
||||
local_playeringame[j] = set->cmds[j].ingame;
|
||||
lplayer_count += local_playeringame[j];
|
||||
}
|
||||
if (net_client_connected && lplayer_count < 2) {
|
||||
net_client_connected = false;
|
||||
piconet_stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
loop_interface->RunTic(set->cmds, set->ingame);
|
||||
gametic++;
|
||||
//#define DUMP_TICS PICO_BUILD
|
||||
#if DUMP_TICS
|
||||
#define MAX_TIMES 64
|
||||
static uint16_t times[MAX_TIMES];
|
||||
uint32_t us = time_us_32();
|
||||
#endif
|
||||
loop_interface->RunTic(set->cmds, local_playeringame);
|
||||
#if DUMP_TICS
|
||||
static int ticks;
|
||||
static uint32_t t0;
|
||||
times[ticks & (MAX_TIMES - 1)] = time_us_32() - us;
|
||||
ticks++;
|
||||
if (0 == (ticks & (MAX_TIMES - 1))) {
|
||||
int min = 100000000;
|
||||
int max = 0;
|
||||
int total = 0;
|
||||
for (int t = 0; t < MAX_TIMES; t++) {
|
||||
total += times[t];
|
||||
if (times[t] > max) max = times[t];
|
||||
if (times[t] < min) min = times[t];
|
||||
}
|
||||
printf("TICKS %d %d/block min/max/avg %d/%d/%d\n", ticks, (int) (time_us_32() - t0), min, max,
|
||||
total / MAX_TIMES);
|
||||
t0 = time_us_32();
|
||||
}
|
||||
#endif
|
||||
gametic++;
|
||||
|
||||
// modify command for duplicated tics
|
||||
// modify command for duplicated tics
|
||||
|
||||
TicdupSquash(set);
|
||||
}
|
||||
}
|
||||
|
||||
NetUpdate (); // check for new console commands
|
||||
NetUpdate(); // check for new console commands
|
||||
#if PICO_BUILD
|
||||
DEBUG_PINS_CLR(tics, 1);
|
||||
#endif
|
||||
}
|
||||
#if PICO_BUILD
|
||||
DEBUG_PINS_CLR(tics, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
void D_RegisterLoopCallbacks(loop_interface_t *i)
|
||||
{
|
||||
void D_RegisterLoopCallbacks(loop_interface_t *i) {
|
||||
loop_interface = i;
|
||||
}
|
||||
|
||||
|
|
@ -794,8 +759,7 @@ void D_RegisterLoopCallbacks(loop_interface_t *i)
|
|||
#include "m_misc.h"
|
||||
#include "w_wad.h"
|
||||
|
||||
static boolean StrictDemos(void)
|
||||
{
|
||||
static boolean StrictDemos(void) {
|
||||
//!
|
||||
// @category demo
|
||||
//
|
||||
|
|
@ -812,10 +776,8 @@ static boolean StrictDemos(void)
|
|||
// this extension (no extensions are allowed if -strictdemos is given
|
||||
// on the command line). A warning is shown on the console using the
|
||||
// provided string describing the non-vanilla expansion.
|
||||
boolean D_NonVanillaRecord(boolean conditional, const char *feature)
|
||||
{
|
||||
if (!conditional || StrictDemos())
|
||||
{
|
||||
boolean D_NonVanillaRecord(boolean conditional, const char *feature) {
|
||||
if (!conditional || StrictDemos()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -828,8 +790,8 @@ boolean D_NonVanillaRecord(boolean conditional, const char *feature)
|
|||
|
||||
// Returns true if the given lump number corresponds to data from a .lmp
|
||||
// file, as opposed to a WAD.
|
||||
static boolean IsDemoFile(int lumpnum)
|
||||
{
|
||||
static boolean IsDemoFile(int lumpnum) {
|
||||
#if !USE_MEMMAP_ONLY
|
||||
char *lower;
|
||||
boolean result;
|
||||
|
||||
|
|
@ -839,6 +801,9 @@ static boolean IsDemoFile(int lumpnum)
|
|||
free(lower);
|
||||
|
||||
return result;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// If the provided conditional value is true, we're trying to play back
|
||||
|
|
@ -850,15 +815,12 @@ static boolean IsDemoFile(int lumpnum)
|
|||
// demo that comes from a .lmp file, not a .wad file.
|
||||
// - Before proceeding, a warning is shown to the user on the console.
|
||||
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
||||
const char *feature)
|
||||
{
|
||||
if (!conditional || StrictDemos())
|
||||
{
|
||||
const char *feature) {
|
||||
if (!conditional || StrictDemos()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsDemoFile(lumpnum))
|
||||
{
|
||||
if (!IsDemoFile(lumpnum)) {
|
||||
printf("Warning: WAD contains demo with a non-vanilla extension "
|
||||
"(%s)\n", feature);
|
||||
return false;
|
||||
|
|
@ -871,3 +833,105 @@ boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
|||
return true;
|
||||
}
|
||||
|
||||
void D_StartNetGame(net_gamesettings_t *settings,
|
||||
netgame_startup_callback_t callback) {
|
||||
int i;
|
||||
|
||||
offsetms = 0;
|
||||
recvtic = 0;
|
||||
|
||||
settings->consoleplayer = 0;
|
||||
settings->num_players = 1;
|
||||
settings->player_classes[0] = player_class;
|
||||
|
||||
#if !NO_USE_NET
|
||||
//!
|
||||
// @category net
|
||||
//
|
||||
// Use original network client sync code rather than the improved
|
||||
// sync code.
|
||||
//
|
||||
settings->new_sync = !M_ParmExists("-oldsync");
|
||||
#endif
|
||||
|
||||
//!
|
||||
// @category net
|
||||
// @arg <n>
|
||||
//
|
||||
// Send n extra tics in every packet as insurance against dropped
|
||||
// packets.
|
||||
//
|
||||
|
||||
#if !NO_USE_ARGS
|
||||
i = M_CheckParmWithArgs("-extratics", 1);
|
||||
|
||||
if (i > 0)
|
||||
settings->extratics = atoi(myargv[i+1]);
|
||||
else
|
||||
#endif
|
||||
settings->extratics = 1;
|
||||
|
||||
//!
|
||||
// @category net
|
||||
// @arg <n>
|
||||
//
|
||||
// Reduce the resolution of the game by a factor of n, reducing
|
||||
// the amount of network bandwidth needed.
|
||||
//
|
||||
|
||||
#if !NO_USE_ARGS
|
||||
i = M_CheckParmWithArgs("-dup", 1);
|
||||
|
||||
if (i > 0)
|
||||
settings->ticdup = atoi(myargv[i+1]);
|
||||
else
|
||||
#endif
|
||||
settings->ticdup = 1;
|
||||
|
||||
#if !NO_USE_NET
|
||||
if (net_client_connected)
|
||||
{
|
||||
// Send our game settings and block until game start is received
|
||||
// from the server.
|
||||
|
||||
NET_CL_StartGame(settings);
|
||||
BlockUntilStart(settings, callback);
|
||||
|
||||
// Read the game settings that were received.
|
||||
|
||||
NET_CL_GetSettings(settings);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (drone) {
|
||||
settings->consoleplayer = 0;
|
||||
}
|
||||
|
||||
// Set the local player and playeringame[] values.
|
||||
|
||||
localplayer = settings->consoleplayer;
|
||||
|
||||
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||
local_playeringame[i] = i < settings->num_players;
|
||||
}
|
||||
|
||||
// Copy settings to global variables.
|
||||
|
||||
ticdup = settings->ticdup;
|
||||
#if !NO_USE_NET
|
||||
new_sync = settings->new_sync;
|
||||
#endif
|
||||
|
||||
// TODO: Message disabled until we fix new_sync.
|
||||
//if (!new_sync)
|
||||
//{
|
||||
// printf("Syncing netgames like Vanilla Doom.\n");
|
||||
//}
|
||||
}
|
||||
|
||||
#if USE_PICO_NET
|
||||
void D_StartPicoNetGame() {
|
||||
gametic = maketic = recvtic = 0;
|
||||
net_client_connected = true;
|
||||
}
|
||||
#endif
|
||||
17
src/d_loop.h
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -75,7 +76,8 @@ void D_StartNetGame(net_gamesettings_t *settings,
|
|||
netgame_startup_callback_t callback);
|
||||
|
||||
extern boolean singletics;
|
||||
extern int gametic, ticdup;
|
||||
extern int gametic;
|
||||
extern isb_int8_t ticdup;
|
||||
|
||||
// Check if it is permitted to record a demo with a non-vanilla feature.
|
||||
boolean D_NonVanillaRecord(boolean conditional, const char *feature);
|
||||
|
|
@ -84,5 +86,18 @@ boolean D_NonVanillaRecord(boolean conditional, const char *feature);
|
|||
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
||||
const char *feature);
|
||||
|
||||
// The complete set of data for a particular tic.
|
||||
typedef struct {
|
||||
ticcmd_t cmds[NET_MAXPLAYERS];
|
||||
#if !DOOM_TINY
|
||||
boolean ingame[NET_MAXPLAYERS];
|
||||
#endif
|
||||
} ticcmd_set_t;
|
||||
|
||||
#if USE_PICO_NET
|
||||
// setup game loop for piconet play
|
||||
void D_StartPicoNetGame();
|
||||
extern ticcmd_set_t ticdata[BACKUPTICS]; // we share the ticdata with piconet
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
35
src/d_mode.c
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -23,26 +24,30 @@
|
|||
// Valid game mode/mission combinations, with the number of
|
||||
// episodes/maps for each.
|
||||
|
||||
static struct
|
||||
static const struct
|
||||
{
|
||||
GameMission_t mission;
|
||||
GameMode_t mode;
|
||||
int episode;
|
||||
int map;
|
||||
isb_int8_t episode;
|
||||
isb_int8_t map;
|
||||
} valid_modes[] = {
|
||||
#if !DOOM_ONLY
|
||||
{ pack_chex, retail, 1, 5 },
|
||||
#endif
|
||||
{ doom, shareware, 1, 9 },
|
||||
{ doom, registered, 3, 9 },
|
||||
{ doom, retail, 4, 9 },
|
||||
{ doom2, commercial, 1, 32 },
|
||||
{ pack_tnt, commercial, 1, 32 },
|
||||
{ pack_plut, commercial, 1, 32 },
|
||||
#if !DOOM_ONLY
|
||||
{ pack_hacx, commercial, 1, 32 },
|
||||
{ heretic, shareware, 1, 9 },
|
||||
{ heretic, registered, 3, 9 },
|
||||
{ heretic, retail, 5, 9 },
|
||||
{ hexen, commercial, 1, 60 },
|
||||
{ strife, commercial, 1, 34 },
|
||||
#endif
|
||||
};
|
||||
|
||||
// Check that a gamemode+gamemission received over the network is valid.
|
||||
|
|
@ -69,7 +74,7 @@ boolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,
|
|||
|
||||
// Hacks for Heretic secret episodes
|
||||
|
||||
if (mission == heretic)
|
||||
if (gamemission_is_heretic(mission))
|
||||
{
|
||||
if (mode == retail && episode == 6)
|
||||
{
|
||||
|
|
@ -120,20 +125,26 @@ static struct {
|
|||
GameMission_t mission;
|
||||
GameVersion_t version;
|
||||
} valid_versions[] = {
|
||||
#if !DOOM_TINY
|
||||
{ doom, exe_doom_1_2 },
|
||||
{ doom, exe_doom_1_666 },
|
||||
{ doom, exe_doom_1_7 },
|
||||
{ doom, exe_doom_1_8 },
|
||||
#endif
|
||||
{ doom, exe_doom_1_9 },
|
||||
#if !DOOM_ONLY
|
||||
{ doom, exe_hacx },
|
||||
#endif
|
||||
{ doom, exe_ultimate },
|
||||
{ doom, exe_final },
|
||||
{ doom, exe_final2 },
|
||||
#if !DOOM_ONLY
|
||||
{ doom, exe_chex },
|
||||
{ heretic, exe_heretic_1_3 },
|
||||
{ hexen, exe_hexen_1_1 },
|
||||
{ strife, exe_strife_1_2 },
|
||||
{ strife, exe_strife_1_31 },
|
||||
#endif
|
||||
};
|
||||
|
||||
boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version)
|
||||
|
|
@ -143,7 +154,7 @@ boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version)
|
|||
// All Doom variants can use the Doom versions.
|
||||
|
||||
if (mission == doom2 || mission == pack_plut || mission == pack_tnt
|
||||
|| mission == pack_hacx || mission == pack_chex)
|
||||
|| gamemission_is_hacx(mission) || gamemission_is_chex(mission))
|
||||
{
|
||||
mission = doom;
|
||||
}
|
||||
|
|
@ -167,17 +178,21 @@ boolean D_IsEpisodeMap(GameMission_t mission)
|
|||
switch (mission)
|
||||
{
|
||||
case doom:
|
||||
#if !DOOM_ONLY
|
||||
case heretic:
|
||||
case pack_chex:
|
||||
#endif
|
||||
return true;
|
||||
|
||||
case none:
|
||||
case hexen:
|
||||
case mission_none:
|
||||
case doom2:
|
||||
case pack_hacx:
|
||||
case pack_tnt:
|
||||
case pack_plut:
|
||||
#if !DOOM_ONLY
|
||||
case hexen:
|
||||
case pack_hacx:
|
||||
case strife:
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
@ -187,7 +202,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
|||
{
|
||||
switch (mission)
|
||||
{
|
||||
case none:
|
||||
case mission_none:
|
||||
default:
|
||||
return "none";
|
||||
case doom:
|
||||
|
|
@ -198,6 +213,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
|||
return "tnt";
|
||||
case pack_plut:
|
||||
return "plutonia";
|
||||
#if !DOOM_ONLY
|
||||
case pack_hacx:
|
||||
return "hacx";
|
||||
case pack_chex:
|
||||
|
|
@ -208,6 +224,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
|||
return "hexen";
|
||||
case strife:
|
||||
return "strife";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
23
src/d_mode.h
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -30,14 +31,25 @@ typedef enum
|
|||
doom2, // Doom 2
|
||||
pack_tnt, // Final Doom: TNT: Evilution
|
||||
pack_plut, // Final Doom: The Plutonia Experiment
|
||||
#if !DOOM_ONLY
|
||||
pack_chex, // Chex Quest (modded doom)
|
||||
pack_hacx, // Hacx (modded doom2)
|
||||
heretic, // Heretic
|
||||
hexen, // Hexen
|
||||
strife, // Strife
|
||||
#endif
|
||||
|
||||
none
|
||||
mission_none
|
||||
} GameMission_t;
|
||||
#if !DOOM_ONLY
|
||||
#define gamemission_is_chex(m) ((m) == pack_chex)
|
||||
#define gamemission_is_hacx(m) ((m) == pack_hacx)
|
||||
#define gamemission_is_heretic(m) ((m) == heretic)
|
||||
#else
|
||||
#define gamemission_is_chex(m) 0
|
||||
#define gamemission_is_hacx(m) 0
|
||||
#define gamemission_is_heretic(m) 0
|
||||
#endif
|
||||
|
||||
// The "mode" allows more accurate specification of the game mode we are
|
||||
// in: eg. shareware vs. registered. So doom1.wad and doom.wad are the
|
||||
|
|
@ -61,10 +73,13 @@ typedef enum
|
|||
exe_doom_1_7, // Doom 1.7/1.7a: "
|
||||
exe_doom_1_8, // Doom 1.8: "
|
||||
exe_doom_1_9, // Doom 1.9: "
|
||||
#if !DOOM_ONLY
|
||||
exe_hacx, // Hacx
|
||||
#endif
|
||||
exe_ultimate, // Ultimate Doom (retail)
|
||||
exe_final, // Final Doom
|
||||
exe_final2, // Final Doom (alternate exe)
|
||||
#if !DOOM_ONLY
|
||||
exe_chex, // Chex Quest executable (based on Final Doom)
|
||||
|
||||
exe_heretic_1_3, // Heretic 1.3
|
||||
|
|
@ -72,7 +87,13 @@ typedef enum
|
|||
exe_hexen_1_1, // Hexen 1.1
|
||||
exe_strife_1_2, // Strife v1.2
|
||||
exe_strife_1_31 // Strife v1.31
|
||||
#endif
|
||||
} GameVersion_t;
|
||||
#if !DOOM_ONLY
|
||||
#define gameversion_is_chex(v) ((v) == exe_chex)
|
||||
#else
|
||||
#define gameversion_is_chex(v) 0
|
||||
#endif
|
||||
|
||||
// What IWAD variant are we using?
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 1993-2008 Raven Software
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -39,18 +40,24 @@ typedef struct
|
|||
// villsa [STRIFE] according to the asm,
|
||||
// consistancy is a short, not a byte
|
||||
byte consistancy; // checks for net game
|
||||
#if !DOOM_ONLY
|
||||
|
||||
// villsa - Strife specific:
|
||||
|
||||
byte buttons2;
|
||||
int inventory;
|
||||
|
||||
|
||||
// Heretic/Hexen specific:
|
||||
|
||||
byte lookfly; // look/fly up/down/centering
|
||||
byte arti; // artitype_t to use
|
||||
} ticcmd_t;
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
#if DOOM_TINY
|
||||
boolean ingame; // saves space elsewhere by moving from separate array (and the ^ is 7 bytes)
|
||||
#endif
|
||||
} ticcmd_t;
|
||||
#if DOOM_TINY
|
||||
#include <assert.h>
|
||||
static_assert(sizeof(ticcmd_t) == 8, "");
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
13
src/deh_io.c
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
#include "deh_defs.h"
|
||||
#include "deh_io.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
typedef enum
|
||||
{
|
||||
DEH_INPUT_FILE,
|
||||
|
|
@ -66,12 +68,12 @@ static deh_context_t *DEH_NewContext(void)
|
|||
{
|
||||
deh_context_t *context;
|
||||
|
||||
context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
|
||||
context = Z_Malloc(sizeof(*context), PU_STATIC, 0);
|
||||
|
||||
// Initial read buffer size of 128 bytes
|
||||
|
||||
context->readbuffer_size = 128;
|
||||
context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
|
||||
context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, 0);
|
||||
context->linenum = 0;
|
||||
context->last_was_newline = true;
|
||||
|
||||
|
|
@ -120,7 +122,11 @@ deh_context_t *DEH_OpenLump(int lumpnum)
|
|||
context->input_buffer_pos = 0;
|
||||
|
||||
context->filename = malloc(9);
|
||||
#if !USE_MEMMAP_ONLY
|
||||
M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9);
|
||||
#else
|
||||
M_StringCopy(context->filename, "<unknown>", 9);
|
||||
#endif
|
||||
|
||||
return context;
|
||||
}
|
||||
|
|
@ -213,7 +219,7 @@ static void IncreaseReadBuffer(deh_context_t *context)
|
|||
int newbuffer_size;
|
||||
|
||||
newbuffer_size = context->readbuffer_size * 2;
|
||||
newbuffer = Z_Malloc(newbuffer_size, PU_STATIC, NULL);
|
||||
newbuffer = Z_Malloc(newbuffer_size, PU_STATIC, 0);
|
||||
|
||||
memcpy(newbuffer, context->readbuffer, context->readbuffer_size);
|
||||
|
||||
|
|
@ -335,3 +341,4 @@ boolean DEH_HadError(deh_context_t *context)
|
|||
return context->had_error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
#include "deh_io.h"
|
||||
#include "deh_main.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
extern deh_section_t *deh_section_types[];
|
||||
extern const char *deh_signatures[];
|
||||
|
||||
|
|
@ -487,6 +489,7 @@ int DEH_LoadLumpByName(const char *name, boolean allow_long, boolean allow_error
|
|||
// Check the command line for -deh argument, and others.
|
||||
void DEH_ParseCommandLine(void)
|
||||
{
|
||||
#if !NO_USE_ARGS
|
||||
char *filename;
|
||||
int p;
|
||||
|
||||
|
|
@ -511,5 +514,7 @@ void DEH_ParseCommandLine(void)
|
|||
++p;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
|
||||
#include "deh_mapping.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context,
|
||||
deh_mapping_t *mapping,
|
||||
char *name)
|
||||
|
|
@ -198,3 +200,4 @@ void DEH_StructSHA1Sum(sha1_context_t *context, deh_mapping_t *mapping,
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
|
||||
#include "z_zone.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
typedef struct
|
||||
{
|
||||
char *from_text;
|
||||
|
|
@ -103,7 +105,7 @@ static void InitHashTable(void)
|
|||
hash_table_entries = 0;
|
||||
hash_table_length = 16;
|
||||
hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length,
|
||||
PU_STATIC, NULL);
|
||||
PU_STATIC, 0);
|
||||
memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length);
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +126,7 @@ static void IncreaseHashtable(void)
|
|||
|
||||
hash_table_length *= 2;
|
||||
hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length,
|
||||
PU_STATIC, NULL);
|
||||
PU_STATIC, 0);
|
||||
memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length);
|
||||
|
||||
// go through the old table and insert all the old entries
|
||||
|
|
@ -184,7 +186,7 @@ void DEH_AddStringReplacement(const char *from_text, const char *to_text)
|
|||
Z_Free(sub->to_text);
|
||||
|
||||
len = strlen(to_text) + 1;
|
||||
sub->to_text = Z_Malloc(len, PU_STATIC, NULL);
|
||||
sub->to_text = Z_Malloc(len, PU_STATIC, 0);
|
||||
memcpy(sub->to_text, to_text, len);
|
||||
}
|
||||
else
|
||||
|
|
@ -194,11 +196,11 @@ void DEH_AddStringReplacement(const char *from_text, const char *to_text)
|
|||
|
||||
// We need to create our own duplicates of the provided strings.
|
||||
len = strlen(from_text) + 1;
|
||||
sub->from_text = Z_Malloc(len, PU_STATIC, NULL);
|
||||
sub->from_text = Z_Malloc(len, PU_STATIC, 0);
|
||||
memcpy(sub->from_text, from_text, len);
|
||||
|
||||
len = strlen(to_text) + 1;
|
||||
sub->to_text = Z_Malloc(len, PU_STATIC, NULL);
|
||||
sub->to_text = Z_Malloc(len, PU_STATIC, 0);
|
||||
memcpy(sub->to_text, to_text, len);
|
||||
|
||||
DEH_AddToHashtable(sub);
|
||||
|
|
@ -431,3 +433,4 @@ void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -24,19 +25,18 @@
|
|||
|
||||
// Used to do dehacked text substitutions throughout the program
|
||||
|
||||
#if !NO_USE_DEH
|
||||
const char *DEH_String(const char *s) PRINTF_ARG_ATTR(1);
|
||||
void DEH_printf(const char *fmt, ...) PRINTF_ATTR(1, 2);
|
||||
void DEH_fprintf(FILE *fstream, const char *fmt, ...) PRINTF_ATTR(2, 3);
|
||||
void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...) PRINTF_ATTR(3, 4);
|
||||
void DEH_AddStringReplacement(const char *from_text, const char *to_text);
|
||||
|
||||
|
||||
#if 0
|
||||
#else
|
||||
// Static macro versions of the functions above
|
||||
|
||||
#define DEH_String(x) (x)
|
||||
#define DEH_printf printf
|
||||
#define DEH_fprintf fprintf
|
||||
#define DEH_fprintf(x,...) stderr_print(__VA_ARGS__)
|
||||
#define DEH_snprintf snprintf
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
#include "deh_io.h"
|
||||
#include "deh_main.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
// Given a string length, find the maximum length of a
|
||||
// string that can replace it.
|
||||
|
||||
|
|
@ -111,3 +113,4 @@ deh_section_t deh_section_text =
|
|||
NULL,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
add_library(doom STATIC
|
||||
add_library(doom INTERFACE)
|
||||
cmake_policy(SET CMP0076 NEW)
|
||||
target_sources(doom INTERFACE
|
||||
am_map.c am_map.h
|
||||
deh_ammo.c
|
||||
deh_bexstr.c
|
||||
|
|
@ -21,8 +23,10 @@ add_library(doom STATIC
|
|||
dstrings.c dstrings.h
|
||||
d_textur.h
|
||||
d_think.h
|
||||
f_finale.c f_finale.h
|
||||
f_wipe.c f_wipe.h
|
||||
f_finale.h
|
||||
f_finale.c
|
||||
f_wipe.c
|
||||
f_wipe.h
|
||||
g_game.c g_game.h
|
||||
hu_lib.c hu_lib.h
|
||||
hu_stuff.c hu_stuff.h
|
||||
|
|
@ -50,6 +54,7 @@ add_library(doom STATIC
|
|||
p_tick.c p_tick.h
|
||||
p_user.c
|
||||
r_bsp.c r_bsp.h
|
||||
r_data_whd.c
|
||||
r_data.c r_data.h
|
||||
r_defs.h
|
||||
r_draw.c r_draw.h
|
||||
|
|
@ -67,5 +72,9 @@ add_library(doom STATIC
|
|||
st_stuff.c st_stuff.h
|
||||
wi_stuff.c wi_stuff.h)
|
||||
|
||||
target_include_directories(doom PRIVATE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../")
|
||||
target_link_libraries(doom SDL2::SDL2 SDL2::mixer SDL2::net)
|
||||
target_include_directories(doom INTERFACE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../")
|
||||
if (NOT PICO_SDK)
|
||||
target_link_libraries(doom INTERFACE SDL2::SDL2 SDL2::mixer SDL2::net)
|
||||
else()
|
||||
target_link_libraries(doom INTERFACE pico_stdlib pico_multicore)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -143,7 +144,7 @@ typedef struct
|
|||
// starting from the middle.
|
||||
//
|
||||
#define R ((8*PLAYERRADIUS)/7)
|
||||
mline_t player_arrow[] = {
|
||||
static const mline_t player_arrow[] = {
|
||||
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
||||
{ { R, 0 }, { R-R/2, R/4 } }, // ----->
|
||||
{ { R, 0 }, { R-R/2, -R/4 } },
|
||||
|
|
@ -155,7 +156,7 @@ mline_t player_arrow[] = {
|
|||
#undef R
|
||||
|
||||
#define R ((8*PLAYERRADIUS)/7)
|
||||
mline_t cheat_player_arrow[] = {
|
||||
static const mline_t cheat_player_arrow[] = {
|
||||
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
||||
{ { R, 0 }, { R-R/2, R/6 } }, // ----->
|
||||
{ { R, 0 }, { R-R/2, -R/6 } },
|
||||
|
|
@ -175,16 +176,16 @@ mline_t cheat_player_arrow[] = {
|
|||
};
|
||||
#undef R
|
||||
|
||||
#define R (FRACUNIT)
|
||||
mline_t triangle_guy[] = {
|
||||
{ { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } },
|
||||
{ { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(R ) } },
|
||||
{ { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }
|
||||
};
|
||||
#undef R
|
||||
//#define R (FRACUNIT)
|
||||
//static const mline_t triangle_guy[] = {
|
||||
// { { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)(.867*R ), (fixed_t)(-.5*R) } },
|
||||
// { { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(R ) } },
|
||||
// { { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }
|
||||
//};
|
||||
//#undef R
|
||||
|
||||
#define R (FRACUNIT)
|
||||
mline_t thintriangle_guy[] = {
|
||||
static const mline_t thintriangle_guy[] = {
|
||||
{ { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)(R ), (fixed_t)(0 ) } },
|
||||
{ { (fixed_t)(R ), (fixed_t)(0 ) }, { (fixed_t)(-.5*R), (fixed_t)(.7*R ) } },
|
||||
{ { (fixed_t)(-.5*R), (fixed_t)(.7*R ) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } }
|
||||
|
|
@ -194,14 +195,14 @@ mline_t thintriangle_guy[] = {
|
|||
|
||||
|
||||
|
||||
static int cheating = 0;
|
||||
static int grid = 0;
|
||||
static isb_int8_t cheating = 0;
|
||||
static isb_int8_t grid = 0;
|
||||
|
||||
static int leveljuststarted = 1; // kluge until AM_LevelInit() is called
|
||||
|
||||
boolean automapactive = false;
|
||||
static int finit_width = SCREENWIDTH;
|
||||
static int finit_height = SCREENHEIGHT - ST_HEIGHT;
|
||||
#define finit_width SCREENWIDTH
|
||||
#define finit_height (SCREENHEIGHT - ST_HEIGHT)
|
||||
|
||||
// location of window on screen
|
||||
static int f_x;
|
||||
|
|
@ -211,7 +212,11 @@ static int f_y;
|
|||
static int f_w;
|
||||
static int f_h;
|
||||
|
||||
#if DOOM_TINY
|
||||
#define lightlev 0 // unused anyway
|
||||
#else
|
||||
static int lightlev; // used for funky strobing effect
|
||||
#endif
|
||||
static pixel_t* fb; // pseudo-frame buffer
|
||||
static int amclock;
|
||||
|
||||
|
|
@ -259,11 +264,13 @@ static fixed_t scale_ftom;
|
|||
|
||||
static player_t *plr; // the player represented by an arrow
|
||||
|
||||
static patch_t *marknums[10]; // numbers used for marking by the automap
|
||||
#if !USE_WHD
|
||||
static vpatch_handle_large_t marknums[10]; // numbers used for marking by the automap
|
||||
#endif
|
||||
static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
|
||||
static int markpointnum = 0; // next point to be assigned
|
||||
|
||||
static int followplayer = 1; // specifies whether to follow the player around
|
||||
static isb_int8_t followplayer = 1; // specifies whether to follow the player around
|
||||
|
||||
cheatseq_t cheat_amap = CHEAT("iddt", 0);
|
||||
|
||||
|
|
@ -328,8 +335,8 @@ void AM_restoreScaleAndLoc(void)
|
|||
m_x = old_m_x;
|
||||
m_y = old_m_y;
|
||||
} else {
|
||||
m_x = plr->mo->x - m_w/2;
|
||||
m_y = plr->mo->y - m_h/2;
|
||||
m_x = plr->mo->xy.x - m_w/2;
|
||||
m_y = plr->mo->xy.y - m_h/2;
|
||||
}
|
||||
m_x2 = m_x + m_w;
|
||||
m_y2 = m_y + m_h;
|
||||
|
|
@ -365,17 +372,22 @@ void AM_findMinMaxBoundaries(void)
|
|||
|
||||
for (i=0;i<numvertexes;i++)
|
||||
{
|
||||
if (vertexes[i].x < min_x)
|
||||
min_x = vertexes[i].x;
|
||||
else if (vertexes[i].x > max_x)
|
||||
max_x = vertexes[i].x;
|
||||
if (vertex_x_raw(&vertexes[i]) < min_x)
|
||||
min_x = vertex_x_raw(&vertexes[i]);
|
||||
else if (vertex_x_raw(&vertexes[i]) > max_x)
|
||||
max_x = vertex_x_raw(&vertexes[i]);
|
||||
|
||||
if (vertexes[i].y < min_y)
|
||||
min_y = vertexes[i].y;
|
||||
else if (vertexes[i].y > max_y)
|
||||
max_y = vertexes[i].y;
|
||||
if (vertex_y_raw(&vertexes[i]) < min_y)
|
||||
min_y = vertex_y_raw(&vertexes[i]);
|
||||
else if (vertex_y_raw(&vertexes[i]) > max_y)
|
||||
max_y = vertex_y_raw(&vertexes[i]);
|
||||
}
|
||||
|
||||
|
||||
min_x = vertex_raw_to_fixed(min_x);
|
||||
min_y = vertex_raw_to_fixed(min_y);
|
||||
max_x = vertex_raw_to_fixed(max_x);
|
||||
max_y = vertex_raw_to_fixed(max_y);
|
||||
|
||||
max_w = max_x - min_x;
|
||||
max_h = max_y - min_y;
|
||||
|
||||
|
|
@ -429,11 +441,12 @@ void AM_initVariables(void)
|
|||
static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 };
|
||||
|
||||
automapactive = true;
|
||||
fb = I_VideoBuffer;
|
||||
|
||||
f_oldloc.x = INT_MAX;
|
||||
amclock = 0;
|
||||
#if !DOOM_TINY
|
||||
lightlev = 0;
|
||||
#endif
|
||||
|
||||
m_paninc.x = m_paninc.y = 0;
|
||||
ftom_zoommul = FRACUNIT;
|
||||
|
|
@ -461,8 +474,8 @@ void AM_initVariables(void)
|
|||
}
|
||||
}
|
||||
|
||||
m_x = plr->mo->x - m_w/2;
|
||||
m_y = plr->mo->y - m_h/2;
|
||||
m_x = plr->mo->xy.x - m_w/2;
|
||||
m_y = plr->mo->xy.y - m_h/2;
|
||||
AM_changeWindowLoc();
|
||||
|
||||
// for saving & restoring
|
||||
|
|
@ -486,14 +499,17 @@ void AM_loadPics(void)
|
|||
|
||||
for (i=0;i<10;i++)
|
||||
{
|
||||
#if !USE_WHD
|
||||
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
||||
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AM_unloadPics(void)
|
||||
{
|
||||
#if !DOOM_TINY
|
||||
int i;
|
||||
char namebuf[9];
|
||||
|
||||
|
|
@ -502,6 +518,7 @@ void AM_unloadPics(void)
|
|||
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
||||
W_ReleaseLumpName(namebuf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AM_clearMarks(void)
|
||||
|
|
@ -555,7 +572,7 @@ void AM_Stop (void)
|
|||
//
|
||||
void AM_Start (void)
|
||||
{
|
||||
static int lastlevel = -1, lastepisode = -1;
|
||||
static isb_int8_t lastlevel = -1, lastepisode = -1;
|
||||
|
||||
if (!stopped) AM_Stop();
|
||||
stopped = false;
|
||||
|
|
@ -605,6 +622,7 @@ AM_Responder
|
|||
|
||||
rc = false;
|
||||
|
||||
#if !NO_USE_JOYSTICK
|
||||
if (ev->type == ev_joystick && joybautomap >= 0
|
||||
&& (ev->data1 & (1 << joybautomap)) != 0)
|
||||
{
|
||||
|
|
@ -624,6 +642,7 @@ AM_Responder
|
|||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!automapactive)
|
||||
{
|
||||
|
|
@ -784,14 +803,14 @@ void AM_changeWindowScale(void)
|
|||
void AM_doFollowPlayer(void)
|
||||
{
|
||||
|
||||
if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
|
||||
if (f_oldloc.x != plr->mo->xy.x || f_oldloc.y != plr->mo->xy.y)
|
||||
{
|
||||
m_x = FTOM(MTOF(plr->mo->x)) - m_w/2;
|
||||
m_y = FTOM(MTOF(plr->mo->y)) - m_h/2;
|
||||
m_x = FTOM(MTOF(plr->mo->xy.x)) - m_w/2;
|
||||
m_y = FTOM(MTOF(plr->mo->xy.y)) - m_h/2;
|
||||
m_x2 = m_x + m_w;
|
||||
m_y2 = m_y + m_h;
|
||||
f_oldloc.x = plr->mo->x;
|
||||
f_oldloc.y = plr->mo->y;
|
||||
f_oldloc.x = plr->mo->xy.x;
|
||||
f_oldloc.y = plr->mo->xy.y;
|
||||
|
||||
// m_x = FTOM(MTOF(plr->mo->x - m_w/2));
|
||||
// m_y = FTOM(MTOF(plr->mo->y - m_h/2));
|
||||
|
|
@ -805,6 +824,7 @@ void AM_doFollowPlayer(void)
|
|||
//
|
||||
//
|
||||
//
|
||||
#if !DOOM_TINY
|
||||
void AM_updateLightLev(void)
|
||||
{
|
||||
static int nexttic = 0;
|
||||
|
|
@ -821,7 +841,7 @@ void AM_updateLightLev(void)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
// Updates on Game Tick
|
||||
|
|
@ -856,6 +876,9 @@ void AM_Ticker (void)
|
|||
//
|
||||
void AM_clearFB(int color)
|
||||
{
|
||||
#if PICODOOM_RENDER_NEWHOPE
|
||||
assert(f_h <= MAIN_VIEWHEIGHT);
|
||||
#endif
|
||||
memset(fb, color, f_w*f_h*sizeof(*fb));
|
||||
}
|
||||
|
||||
|
|
@ -1017,7 +1040,8 @@ AM_drawFline
|
|||
register int ax;
|
||||
register int ay;
|
||||
register int d;
|
||||
|
||||
|
||||
#if !DOOM_TINY
|
||||
static int fuck = 0;
|
||||
|
||||
// For debugging only
|
||||
|
|
@ -1029,6 +1053,7 @@ AM_drawFline
|
|||
DEH_fprintf(stderr, "fuck %d \r", fuck++);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc)
|
||||
|
||||
|
|
@ -1148,37 +1173,38 @@ void AM_drawWalls(void)
|
|||
int i;
|
||||
static mline_t l;
|
||||
|
||||
for (i=0;i<numlines;i++)
|
||||
line_t *li = lines;
|
||||
for (i=0;i<numlines;i++,li += line_next_step(li))
|
||||
{
|
||||
l.a.x = lines[i].v1->x;
|
||||
l.a.y = lines[i].v1->y;
|
||||
l.b.x = lines[i].v2->x;
|
||||
l.b.y = lines[i].v2->y;
|
||||
if (cheating || (lines[i].flags & ML_MAPPED))
|
||||
l.a.x = vertex_x(line_v1(li));
|
||||
l.a.y = vertex_y(line_v1(li));
|
||||
l.b.x = vertex_x(line_v2(li));
|
||||
l.b.y = vertex_y(line_v2(li));
|
||||
if (cheating || line_is_mapped(li))
|
||||
{
|
||||
if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
|
||||
if ((line_flags(li) & LINE_NEVERSEE) && !cheating)
|
||||
continue;
|
||||
if (!lines[i].backsector)
|
||||
if (!line_backsector(li))
|
||||
{
|
||||
AM_drawMline(&l, WALLCOLORS+lightlev);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lines[i].special == 39)
|
||||
if (line_special(li) == 39)
|
||||
{ // teleporters
|
||||
AM_drawMline(&l, WALLCOLORS+WALLRANGE/2);
|
||||
}
|
||||
else if (lines[i].flags & ML_SECRET) // secret door
|
||||
else if (line_flags(li) & ML_SECRET) // secret door
|
||||
{
|
||||
if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev);
|
||||
else AM_drawMline(&l, WALLCOLORS+lightlev);
|
||||
}
|
||||
else if (lines[i].backsector->floorheight
|
||||
!= lines[i].frontsector->floorheight) {
|
||||
else if (line_backsector(li)->rawfloorheight
|
||||
!= line_frontsector(li)->rawfloorheight) {
|
||||
AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
|
||||
}
|
||||
else if (lines[i].backsector->ceilingheight
|
||||
!= lines[i].frontsector->ceilingheight) {
|
||||
else if (line_backsector(li)->rawceilingheight
|
||||
!= line_frontsector(li)->rawceilingheight) {
|
||||
AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
|
||||
}
|
||||
else if (cheating) {
|
||||
|
|
@ -1188,7 +1214,7 @@ void AM_drawWalls(void)
|
|||
}
|
||||
else if (plr->powers[pw_allmap])
|
||||
{
|
||||
if (!(lines[i].flags & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);
|
||||
if (!(line_flags(li) & LINE_NEVERSEE)) AM_drawMline(&l, GRAYS+3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1207,19 +1233,19 @@ AM_rotate
|
|||
fixed_t tmpx;
|
||||
|
||||
tmpx =
|
||||
FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])
|
||||
- FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);
|
||||
FixedMul(*x,finecosine(a>>ANGLETOFINESHIFT))
|
||||
- FixedMul(*y,finesine(a>>ANGLETOFINESHIFT));
|
||||
|
||||
*y =
|
||||
FixedMul(*x,finesine[a>>ANGLETOFINESHIFT])
|
||||
+ FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);
|
||||
FixedMul(*x,finesine(a>>ANGLETOFINESHIFT))
|
||||
+ FixedMul(*y,finecosine(a>>ANGLETOFINESHIFT));
|
||||
|
||||
*x = tmpx;
|
||||
}
|
||||
|
||||
void
|
||||
AM_drawLineCharacter
|
||||
( mline_t* lineguy,
|
||||
( const mline_t* lineguy,
|
||||
int lineguylines,
|
||||
fixed_t scale,
|
||||
angle_t angle,
|
||||
|
|
@ -1279,11 +1305,11 @@ void AM_drawPlayers(void)
|
|||
if (cheating)
|
||||
AM_drawLineCharacter
|
||||
(cheat_player_arrow, arrlen(cheat_player_arrow), 0,
|
||||
plr->mo->angle, WHITE, plr->mo->x, plr->mo->y);
|
||||
mobj_angle(plr->mo), WHITE, plr->mo->xy.x, plr->mo->xy.y);
|
||||
else
|
||||
AM_drawLineCharacter
|
||||
(player_arrow, arrlen(player_arrow), 0, plr->mo->angle,
|
||||
WHITE, plr->mo->x, plr->mo->y);
|
||||
(player_arrow, arrlen(player_arrow), 0, mobj_angle(plr->mo),
|
||||
WHITE, plr->mo->xy.x, plr->mo->xy.y);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1304,8 +1330,8 @@ void AM_drawPlayers(void)
|
|||
color = their_colors[their_color];
|
||||
|
||||
AM_drawLineCharacter
|
||||
(player_arrow, arrlen(player_arrow), 0, p->mo->angle,
|
||||
color, p->mo->x, p->mo->y);
|
||||
(player_arrow, arrlen(player_arrow), 0, mobj_angle(p->mo),
|
||||
color, p->mo->xy.x, p->mo->xy.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1320,13 +1346,13 @@ AM_drawThings
|
|||
|
||||
for (i=0;i<numsectors;i++)
|
||||
{
|
||||
t = sectors[i].thinglist;
|
||||
t = shortptr_to_mobj(sectors[i].thinglist);
|
||||
while (t)
|
||||
{
|
||||
AM_drawLineCharacter
|
||||
(thintriangle_guy, arrlen(thintriangle_guy),
|
||||
16<<FRACBITS, t->angle, colors+lightlev, t->x, t->y);
|
||||
t = t->snext;
|
||||
16<<FRACBITS, mobj_angle(t), colors+lightlev, t->xy.x, t->xy.y);
|
||||
t = mobj_snext(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1345,8 +1371,14 @@ void AM_drawMarks(void)
|
|||
h = 6; // because something's wrong with the wad, i guess
|
||||
fx = CXMTOF(markpoints[i].x);
|
||||
fy = CYMTOF(markpoints[i].y);
|
||||
vpatch_handle_large_t patch;
|
||||
#if USE_WHD
|
||||
patch = VPATCH_AMMNUM0 + i;
|
||||
#else
|
||||
patch = marknums[i];
|
||||
#endif
|
||||
if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
|
||||
V_DrawPatch(fx, fy, marknums[i]);
|
||||
V_DrawPatch(fx, fy, patch);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1361,6 +1393,7 @@ void AM_drawCrosshair(int color)
|
|||
void AM_Drawer (void)
|
||||
{
|
||||
if (!automapactive) return;
|
||||
fb = I_VideoBuffer;
|
||||
|
||||
AM_clearFB(BACKGROUND);
|
||||
if (grid)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -34,7 +35,7 @@
|
|||
// atkstate, i.e. attack/fire/hit frame
|
||||
// flashstate, muzzle flash
|
||||
//
|
||||
weaponinfo_t weaponinfo[NUMWEAPONS] =
|
||||
should_be_const weaponinfo_t weaponinfo[NUMWEAPONS] =
|
||||
{
|
||||
{
|
||||
// fist
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -28,14 +29,14 @@
|
|||
typedef struct
|
||||
{
|
||||
ammotype_t ammo;
|
||||
int upstate;
|
||||
int downstate;
|
||||
int readystate;
|
||||
int atkstate;
|
||||
int flashstate;
|
||||
isb_int16_t upstate;
|
||||
isb_int16_t downstate;
|
||||
isb_int16_t readystate;
|
||||
isb_int16_t atkstate;
|
||||
isb_int16_t flashstate;
|
||||
|
||||
} weaponinfo_t;
|
||||
|
||||
extern weaponinfo_t weaponinfo[NUMWEAPONS];
|
||||
extern should_be_const weaponinfo_t weaponinfo[NUMWEAPONS];
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -44,7 +45,8 @@ void D_StartTitle (void);
|
|||
//
|
||||
|
||||
extern gameaction_t gameaction;
|
||||
|
||||
extern const char* pagename;
|
||||
extern boolean advancedemo;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -68,7 +69,6 @@ static void PlayerQuitGame(player_t *player)
|
|||
|
||||
static void RunTic(ticcmd_t *cmds, boolean *ingame)
|
||||
{
|
||||
extern boolean advancedemo;
|
||||
unsigned int i;
|
||||
|
||||
// Check for player quits.
|
||||
|
|
@ -143,7 +143,11 @@ static void SaveGameSettings(net_gamesettings_t *settings)
|
|||
settings->episode = startepisode;
|
||||
settings->map = startmap;
|
||||
settings->skill = startskill;
|
||||
#if !DOOM_TINY
|
||||
settings->loadgame = startloadgame;
|
||||
#else
|
||||
settings->loadgame = -1;
|
||||
#endif
|
||||
settings->gameversion = gameversion;
|
||||
settings->nomonsters = nomonsters;
|
||||
settings->fast_monsters = fastparm;
|
||||
|
|
@ -166,6 +170,7 @@ static void InitConnectData(net_connect_data_t *connect_data)
|
|||
// Run as the left screen in three screen mode.
|
||||
//
|
||||
|
||||
#if !DOOM_TINY
|
||||
if (M_CheckParm("-left") > 0)
|
||||
{
|
||||
viewangleoffset = ANG90;
|
||||
|
|
@ -183,16 +188,17 @@ static void InitConnectData(net_connect_data_t *connect_data)
|
|||
viewangleoffset = ANG270;
|
||||
connect_data->drone = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
//
|
||||
// Connect data
|
||||
//
|
||||
|
||||
// Game type fields:
|
||||
|
||||
connect_data->gamemode = gamemode;
|
||||
connect_data->_gamemode = gamemode;
|
||||
connect_data->gamemission = gamemission;
|
||||
|
||||
#if !DOOM_TINY
|
||||
// Are we recording a demo? Possibly set lowres turn mode
|
||||
|
||||
connect_data->lowres_turn = (M_ParmExists("-record")
|
||||
|
|
@ -200,9 +206,14 @@ static void InitConnectData(net_connect_data_t *connect_data)
|
|||
|| M_ParmExists("-shorttics");
|
||||
|
||||
// Read checksums of our WAD directory and dehacked information
|
||||
#endif
|
||||
|
||||
#if !NO_USE_CHECKSUM
|
||||
W_Checksum(connect_data->wad_sha1sum);
|
||||
DEH_Checksum(connect_data->deh_sha1sum);
|
||||
#else
|
||||
// leave as garbage for now
|
||||
#endif
|
||||
|
||||
// Are we playing with the Freedoom IWAD?
|
||||
|
||||
|
|
@ -214,6 +225,7 @@ void D_ConnectNetGame(void)
|
|||
net_connect_data_t connect_data;
|
||||
|
||||
InitConnectData(&connect_data);
|
||||
#if !NO_USE_NET
|
||||
netgame = D_InitNetGame(&connect_data);
|
||||
|
||||
//!
|
||||
|
|
@ -224,10 +236,13 @@ void D_ConnectNetGame(void)
|
|||
// demos.
|
||||
//
|
||||
|
||||
#if !NO_USE_ARGS
|
||||
if (M_CheckParm("-solo-net") > 0)
|
||||
{
|
||||
netgame = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -249,6 +264,7 @@ void D_CheckNetGame (void)
|
|||
D_StartNetGame(&settings, NULL);
|
||||
LoadGameSettings(&settings);
|
||||
|
||||
#if !DOOM_TINY
|
||||
DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
|
||||
startskill, deathmatch, startmap, startepisode);
|
||||
|
||||
|
|
@ -274,5 +290,6 @@ void D_CheckNetGame (void)
|
|||
printf(".\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -111,13 +112,13 @@ typedef struct player_s
|
|||
// Is wp_nochange if not changing.
|
||||
weapontype_t pendingweapon;
|
||||
|
||||
int weaponowned[NUMWEAPONS];
|
||||
int ammo[NUMAMMO];
|
||||
int maxammo[NUMAMMO];
|
||||
isb_int8_t weaponowned[NUMWEAPONS];
|
||||
int ammo[NUMAMMO]; // int because pointed to by st_ stuff
|
||||
int maxammo[NUMAMMO]; // int because pointed to by st_ stuff
|
||||
|
||||
// True if button down last tic.
|
||||
int attackdown;
|
||||
int usedown;
|
||||
isb_int8_t attackdown;
|
||||
isb_int8_t usedown;
|
||||
|
||||
// Bit flags, for cheats and debug.
|
||||
// See cheat_t, above.
|
||||
|
|
@ -142,21 +143,21 @@ typedef struct player_s
|
|||
mobj_t* attacker;
|
||||
|
||||
// So gun flashes light up areas.
|
||||
int extralight;
|
||||
isb_int8_t extralight;
|
||||
|
||||
// Current PLAYPAL, ???
|
||||
// can be set to REDCOLORMAP for pain, etc.
|
||||
int fixedcolormap;
|
||||
isb_int8_t fixedcolormap;
|
||||
|
||||
// Player skin colorshift,
|
||||
// 0-3 for which color to draw player.
|
||||
int colormap;
|
||||
|
||||
// Overlay view sprites (gun, etc).
|
||||
pspdef_t psprites[NUMPSPRITES];
|
||||
isb_int8_t colormap;
|
||||
|
||||
// True if secret level has been done.
|
||||
boolean didsecret;
|
||||
boolean didsecret;
|
||||
|
||||
// Overlay view sprites (gun, etc).
|
||||
pspdef_t psprites[NUMPSPRITES];
|
||||
|
||||
} player_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -51,17 +52,64 @@ typedef union
|
|||
// Historically, "think_t" is yet another
|
||||
// function pointer to a routine to handle
|
||||
// an actor.
|
||||
typedef actionf_t think_t;
|
||||
typedef enum {
|
||||
ThinkF_NULL = 0,
|
||||
ThinkF_T_MoveCeiling,
|
||||
ThinkF_T_VerticalDoor,
|
||||
ThinkF_T_PlatRaise,
|
||||
ThinkF_T_FireFlicker,
|
||||
ThinkF_T_LightFlash,
|
||||
ThinkF_T_StrobeFlash,
|
||||
ThinkF_T_MoveFloor,
|
||||
ThinkF_T_Glow,
|
||||
ThinkF_P_MobjThinker,
|
||||
ThinkF_INVALID, // only set during save game load, should be overwritten
|
||||
ThinkF_REMOVED,
|
||||
NUM_THINKF
|
||||
} think_t_orig;
|
||||
#include <assert.h>
|
||||
static_assert(NUM_THINKF < 256, "");
|
||||
typedef uint8_t think_t;
|
||||
|
||||
|
||||
// Doubly linked list of actors.
|
||||
// linked list of actors.
|
||||
typedef struct thinker_s
|
||||
{
|
||||
struct thinker_s* prev;
|
||||
struct thinker_s* next;
|
||||
// todo graham this can be an array index into an active thinker array
|
||||
shortptr_t /*struct thinker_s*/ sp_next;
|
||||
think_t function;
|
||||
|
||||
} thinker_t;
|
||||
#if DOOM_SMALL
|
||||
uint8_t pool_info; // 0xff if not in memory pool
|
||||
#endif
|
||||
} __attribute__((aligned(4))) thinker_t; // must be aligned for shortptr
|
||||
|
||||
#if DOOM_SMALL
|
||||
#if PICO_ON_DEVICE
|
||||
static_assert(sizeof(thinker_t) == 4, ""); // note z_zone requires this too to zero out
|
||||
#endif
|
||||
#endif
|
||||
#define thinker_next(t) ((thinker_t *)shortptr_to_ptr((t)->sp_next))
|
||||
static inline shortptr_t thinker_to_shortptr(thinker_t *thinker) {
|
||||
return ptr_to_shortptr(thinker);
|
||||
}
|
||||
|
||||
#if !USE_THINKER_POOL
|
||||
#include "z_zone.h"
|
||||
static inline void *Z_ThinkMalloc(int size, int tag, void *user) {
|
||||
thinker_t *t = Z_Malloc(size, tag, user);
|
||||
memset(t, 0, size);
|
||||
return t;
|
||||
}
|
||||
#define Z_ThinkFree Z_Free
|
||||
#else
|
||||
#include "z_zone.h"
|
||||
thinker_t *Z_ThinkMallocImpl(int size);
|
||||
void Z_ThinkFree(thinker_t *thinker);
|
||||
static inline void *Z_ThinkMalloc(int size, int tag, void *user) {
|
||||
assert(!user);
|
||||
assert(tag == PU_LEVEL);
|
||||
return Z_ThinkMallocImpl(size);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
#include "deh_main.h"
|
||||
#include "p_local.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
static void *DEH_AmmoStart(deh_context_t *context, char *line)
|
||||
{
|
||||
int ammo_number = 0;
|
||||
|
|
@ -101,3 +103,4 @@ deh_section_t deh_section_ammo =
|
|||
DEH_AmmoSHA1Hash,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2014 Fabian Greffrath
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
|
||||
#include "dstrings.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
typedef struct {
|
||||
const char *macro;
|
||||
const char *string;
|
||||
|
|
@ -374,3 +376,4 @@ deh_section_t deh_section_bexstr =
|
|||
NULL,
|
||||
NULL,
|
||||
};
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
#include "am_map.h"
|
||||
#include "st_stuff.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
|
|
@ -144,3 +146,4 @@ deh_section_t deh_section_cheat =
|
|||
NULL,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -19,6 +20,8 @@
|
|||
#include "deh_defs.h"
|
||||
#include "deh_main.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
|
||||
const char *deh_signatures[] =
|
||||
{
|
||||
"Patch File for DeHackEd v2.3",
|
||||
|
|
@ -66,3 +69,4 @@ deh_section_t *deh_section_types[] =
|
|||
NULL
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
#include "deh_main.h"
|
||||
#include "deh_mapping.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
DEH_BEGIN_MAPPING(state_mapping, state_t)
|
||||
DEH_MAPPING("Sprite number", sprite)
|
||||
DEH_MAPPING("Sprite subnumber", frame)
|
||||
|
|
@ -159,3 +161,4 @@ deh_section_t deh_section_frame =
|
|||
DEH_FrameSHA1Sum,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
#include "deh_main.h"
|
||||
#include "deh_misc.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
// Dehacked: "Initial Health"
|
||||
// This is the initial health a player has when starting anew.
|
||||
// See G_PlayerReborn in g_game.c
|
||||
|
|
@ -226,3 +228,4 @@ deh_section_t deh_section_misc =
|
|||
DEH_MiscSHA1Sum,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -35,6 +36,8 @@
|
|||
#define DEH_DEFAULT_BFG_CELLS_PER_SHOT 40
|
||||
#define DEH_DEFAULT_SPECIES_INFIGHTING 0
|
||||
|
||||
#if !NO_USE_DEH
|
||||
|
||||
extern int deh_initial_health;
|
||||
extern int deh_initial_bullets;
|
||||
extern int deh_max_health;
|
||||
|
|
@ -52,7 +55,7 @@ extern int deh_idkfa_armor_class;
|
|||
extern int deh_bfg_cells_per_shot;
|
||||
extern int deh_species_infighting;
|
||||
|
||||
#if 0
|
||||
#else
|
||||
|
||||
// To compile without dehacked, it's possible to use these:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
#include "deh_io.h"
|
||||
#include "deh_main.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
static actionf_t codeptrs[NUMSTATES];
|
||||
|
||||
static int CodePointerIndex(actionf_t *ptr)
|
||||
|
|
@ -140,3 +142,4 @@ deh_section_t deh_section_pointer =
|
|||
DEH_PointerSHA1Sum,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
#include "deh_mapping.h"
|
||||
#include "sounds.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t)
|
||||
DEH_UNSUPPORTED_MAPPING("Offset")
|
||||
DEH_UNSUPPORTED_MAPPING("Zero/One")
|
||||
|
|
@ -100,3 +102,4 @@ deh_section_t deh_section_sound =
|
|||
NULL,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -26,6 +27,7 @@
|
|||
|
||||
#include "info.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
|
||||
DEH_MAPPING("ID #", doomednum)
|
||||
DEH_MAPPING("Initial frame", spawnstate)
|
||||
|
|
@ -129,3 +131,4 @@ deh_section_t deh_section_thing =
|
|||
DEH_ThingSHA1Sum,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
//
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
#include "deh_main.h"
|
||||
#include "deh_mapping.h"
|
||||
|
||||
#if !NO_USE_DEH
|
||||
DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
|
||||
DEH_MAPPING("Ammo type", ammo)
|
||||
DEH_MAPPING("Deselect frame", upstate)
|
||||
|
|
@ -99,3 +101,4 @@ deh_section_t deh_section_weapon =
|
|||
DEH_WeaponSHA1Sum,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -131,9 +132,14 @@ typedef PACKED_STRUCT (
|
|||
// Set if already seen, thus drawn in automap.
|
||||
#define ML_MAPPED 256
|
||||
|
||||
|
||||
|
||||
|
||||
#if USE_WHD && WHD_SUPER_TINY
|
||||
#define ML_NO_PREDICT_SIDE 256 // can share with ML_MAPPED
|
||||
#define ML_NO_PREDICT_V1 512
|
||||
#define ML_NO_PREDICT_V2 1024
|
||||
#define ML_HAS_SPECIAL 2048
|
||||
#define ML_HAS_TAG 4096
|
||||
#define ML_SIDE_MASK 0xe000u
|
||||
#endif
|
||||
// Sector definition, from editing.
|
||||
typedef PACKED_STRUCT (
|
||||
{
|
||||
|
|
@ -206,8 +212,14 @@ typedef PACKED_STRUCT (
|
|||
short options;
|
||||
}) mapthing_t;
|
||||
|
||||
|
||||
|
||||
#if !WHD_SUPER_TINY
|
||||
#define TAG_666 666
|
||||
#define TAG_667 667
|
||||
#else
|
||||
// we only have one byte tags
|
||||
#define TAG_666 254
|
||||
#define TAG_667 255
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __DOOMDATA__
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -39,7 +40,9 @@
|
|||
|
||||
// If rangecheck is undefined,
|
||||
// most parameter validation debugging code will not be compiled
|
||||
#ifndef NDEBUG
|
||||
#define RANGECHECK
|
||||
#endif
|
||||
|
||||
// The maximum number of players, multiplayer/networking.
|
||||
#define MAXPLAYERS 4
|
||||
|
|
@ -66,7 +69,15 @@ typedef enum
|
|||
ga_completed,
|
||||
ga_victory,
|
||||
ga_worlddone,
|
||||
ga_screenshot
|
||||
#if !NO_SCREENSHOT
|
||||
ga_screenshot,
|
||||
#endif
|
||||
#if USE_PICO_NET
|
||||
ga_newgamenet,
|
||||
#endif
|
||||
#if DOOM_TINY
|
||||
ga_deferredquit,
|
||||
#endif
|
||||
} gameaction_t;
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -22,14 +23,18 @@
|
|||
|
||||
|
||||
// Game Mode - identify IWAD as shareware, retail etc.
|
||||
#if !DEMO1_ONLY
|
||||
GameMode_t gamemode = indetermined;
|
||||
#endif
|
||||
GameMission_t gamemission = doom;
|
||||
GameVersion_t gameversion = exe_final2;
|
||||
GameVariant_t gamevariant = vanilla;
|
||||
const char *gamedescription;
|
||||
|
||||
#if !DOOM_TINY
|
||||
// Set if homebrew PWAD stuff has been added.
|
||||
boolean modifiedgame;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -53,7 +54,11 @@ extern boolean devparm; // DEBUG: launched with -devparm
|
|||
// -----------------------------------------------------
|
||||
// Game Mode - identify IWAD as shareware, retail etc.
|
||||
//
|
||||
#if !DEMO1_ONLY
|
||||
extern GameMode_t gamemode;
|
||||
#else
|
||||
#define gamemode shareware
|
||||
#endif
|
||||
extern GameMission_t gamemission;
|
||||
extern GameVersion_t gameversion;
|
||||
extern GameVariant_t gamevariant;
|
||||
|
|
@ -65,11 +70,15 @@ extern const char *gamedescription;
|
|||
// as the same most of the time.
|
||||
|
||||
#define logical_gamemission \
|
||||
(gamemission == pack_chex ? doom : \
|
||||
gamemission == pack_hacx ? doom2 : gamemission)
|
||||
(gamemission_is_chex(gamemission) ? doom : \
|
||||
gamemission_is_hacx(gamemission) ? doom2 : gamemission)
|
||||
|
||||
// Set if homebrew PWAD stuff has been added.
|
||||
#if !DOOM_TINY
|
||||
extern boolean modifiedgame;
|
||||
#else
|
||||
#define modifiedgame 0
|
||||
#endif
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
|
|
@ -78,20 +87,20 @@ extern boolean modifiedgame;
|
|||
|
||||
// Defaults for menu, methinks.
|
||||
extern skill_t startskill;
|
||||
extern int startepisode;
|
||||
extern int startmap;
|
||||
extern isb_int8_t startepisode;
|
||||
extern isb_int8_t startmap;
|
||||
|
||||
// Savegame slot to load on startup. This is the value provided to
|
||||
// the -loadgame option. If this has not been provided, this is -1.
|
||||
|
||||
extern int startloadgame;
|
||||
extern isb_int8_t startloadgame;
|
||||
|
||||
extern boolean autostart;
|
||||
|
||||
// Selected by user.
|
||||
extern skill_t gameskill;
|
||||
extern int gameepisode;
|
||||
extern int gamemap;
|
||||
extern isb_int8_t gameepisode;
|
||||
extern isb_int8_t gamemap;
|
||||
|
||||
// If non-zero, exit the level after this number of minutes
|
||||
extern int timelimit;
|
||||
|
|
@ -100,10 +109,14 @@ extern int timelimit;
|
|||
extern boolean respawnmonsters;
|
||||
|
||||
// Netgame? Only true if >1 player.
|
||||
#if !NO_USE_NET || USE_PICO_NET
|
||||
extern boolean netgame;
|
||||
#else
|
||||
#define netgame false
|
||||
#endif
|
||||
|
||||
// 0=Cooperative; 1=Deathmatch; 2=Altdeath
|
||||
extern int deathmatch;
|
||||
extern isb_int8_t deathmatch;
|
||||
|
||||
// -------------------------
|
||||
// Internal parameters for sound rendering.
|
||||
|
|
@ -145,11 +158,20 @@ extern boolean paused; // Game Pause?
|
|||
|
||||
extern boolean viewactive;
|
||||
|
||||
#if !FORCE_NODRAW
|
||||
extern boolean nodrawers;
|
||||
#else
|
||||
#define nodrawers true
|
||||
#endif
|
||||
|
||||
|
||||
#if !DOOM_TINY
|
||||
extern boolean testcontrols;
|
||||
extern int testcontrols_mousespeed;
|
||||
#else
|
||||
#define testcontrols false
|
||||
#define testcontrols_mousespeed false
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
@ -159,8 +181,8 @@ extern int testcontrols_mousespeed;
|
|||
extern int viewangleoffset;
|
||||
|
||||
// Player taking events, and displaying.
|
||||
extern int consoleplayer;
|
||||
extern int displayplayer;
|
||||
extern isb_int8_t consoleplayer;
|
||||
extern isb_int8_t displayplayer;
|
||||
|
||||
|
||||
// -------------------------------------
|
||||
|
|
@ -185,7 +207,11 @@ extern boolean usergame;
|
|||
|
||||
//?
|
||||
extern boolean demoplayback;
|
||||
#if !NO_DEMO_RECORDING
|
||||
extern boolean demorecording;
|
||||
#else
|
||||
#define demorecording 0
|
||||
#endif
|
||||
|
||||
// Round angleturn in ticcmds to the nearest 256. This is used when
|
||||
// recording Vanilla demos in netgames.
|
||||
|
|
@ -243,8 +269,10 @@ extern wbstartstruct_t wminfo;
|
|||
// Internal parameters, used for engine.
|
||||
//
|
||||
|
||||
#if !NO_FILE_ACCESS
|
||||
// File handling stuff.
|
||||
extern char *savegamedir;
|
||||
#endif
|
||||
|
||||
// if true, load all graphics at level load
|
||||
extern boolean precache;
|
||||
|
|
@ -254,16 +282,16 @@ extern boolean precache;
|
|||
// to force a wipe on the next draw
|
||||
extern gamestate_t wipegamestate;
|
||||
|
||||
extern int mouseSensitivity;
|
||||
extern isb_int8_t mouseSensitivity;
|
||||
|
||||
extern int bodyqueslot;
|
||||
extern isb_uint8_t bodyqueslot;
|
||||
|
||||
|
||||
|
||||
// Needed to store the number of the dummy sky flat.
|
||||
// Used for rendering,
|
||||
// as well as tracking projectiles etc.
|
||||
extern int skyflatnum;
|
||||
extern flatnum_t skyflatnum;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
|
||||
#include "dstrings.h"
|
||||
|
||||
const char *doom1_endmsg[] =
|
||||
const constcharstar doom1_endmsg[NUM_QUITMESSAGES] =
|
||||
{
|
||||
"are you sure you want to\nquit this great game?",
|
||||
"please don't leave, there's more\ndemons to toast!",
|
||||
|
|
@ -32,7 +33,7 @@ const char *doom1_endmsg[] =
|
|||
"go ahead and leave. see if i care.",
|
||||
};
|
||||
|
||||
const char *doom2_endmsg[] =
|
||||
const constcharstar doom2_endmsg[NUM_QUITMESSAGES] =
|
||||
{
|
||||
// QuitDOOM II messages
|
||||
"are you sure you want to\nquit this great game?",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -34,8 +35,9 @@
|
|||
// 8 per each game type
|
||||
#define NUM_QUITMESSAGES 8
|
||||
|
||||
extern const char *doom1_endmsg[];
|
||||
extern const char *doom2_endmsg[];
|
||||
#include "doomtype.h"
|
||||
extern const constcharstar doom1_endmsg[NUM_QUITMESSAGES];
|
||||
extern const constcharstar doom2_endmsg[NUM_QUITMESSAGES];
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -35,19 +36,13 @@
|
|||
#include "sounds.h"
|
||||
|
||||
#include "doomstat.h"
|
||||
#include "r_data.h"
|
||||
#include "r_state.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
F_STAGE_TEXT,
|
||||
F_STAGE_ARTSCREEN,
|
||||
F_STAGE_CAST,
|
||||
} finalestage_t;
|
||||
|
||||
// ?
|
||||
//#include "doomstat.h"
|
||||
//#include "r_local.h"
|
||||
//#include "f_finale.h"
|
||||
#include "f_finale.h"
|
||||
|
||||
// Stage of animation:
|
||||
finalestage_t finalestage;
|
||||
|
|
@ -67,7 +62,16 @@ typedef struct
|
|||
|
||||
static textscreen_t textscreens[] =
|
||||
{
|
||||
#if !HACK_FINALE_E1M1
|
||||
{ doom, 1, 8, "FLOOR4_8", E1TEXT},
|
||||
#else
|
||||
#if HACK_FINALE_SHAREWARE
|
||||
{ doom, 3, 1, "MFLR8_4", E3TEXT}, // for shareware
|
||||
#else
|
||||
{ doom, 1, 1, "FLOOR4_8", E1TEXT}, // for ultimate
|
||||
#endif
|
||||
#endif
|
||||
#if !DEMO1_ONLY
|
||||
{ doom, 2, 8, "SFLR6_1", E2TEXT},
|
||||
{ doom, 3, 8, "MFLR8_4", E3TEXT},
|
||||
{ doom, 4, 8, "MFLR8_3", E4TEXT},
|
||||
|
|
@ -92,6 +96,7 @@ static textscreen_t textscreens[] =
|
|||
{ pack_plut, 1, 30, "RROCK17", P4TEXT},
|
||||
{ pack_plut, 1, 15, "RROCK13", P5TEXT},
|
||||
{ pack_plut, 1, 31, "RROCK19", P6TEXT},
|
||||
#endif
|
||||
};
|
||||
|
||||
const char *finaletext;
|
||||
|
|
@ -100,8 +105,10 @@ const char *finaleflat;
|
|||
void F_StartCast (void);
|
||||
void F_CastTicker (void);
|
||||
boolean F_CastResponder (event_t *ev);
|
||||
void F_CastDrawer (void);
|
||||
|
||||
#if DOOM_TINY
|
||||
#include "doom/m_menu.h"
|
||||
#endif
|
||||
//
|
||||
// F_StartFinale
|
||||
//
|
||||
|
|
@ -114,6 +121,9 @@ void F_StartFinale (void)
|
|||
viewactive = false;
|
||||
automapactive = false;
|
||||
|
||||
#if DOOM_TINY
|
||||
M_ClearMenus(); // don't want menus in the finale... this way we can prevent help appearing which we don't draw correctly
|
||||
#endif
|
||||
if (logical_gamemission == doom)
|
||||
{
|
||||
S_ChangeMusic(mus_victor, true);
|
||||
|
|
@ -131,7 +141,7 @@ void F_StartFinale (void)
|
|||
|
||||
// Hack for Chex Quest
|
||||
|
||||
if (gameversion == exe_chex && screen->mission == doom)
|
||||
if (gameversion_is_chex(gameversion) && screen->mission == doom)
|
||||
{
|
||||
screen->level = 5;
|
||||
}
|
||||
|
|
@ -141,6 +151,9 @@ void F_StartFinale (void)
|
|||
&& gamemap == screen->level)
|
||||
{
|
||||
finaletext = screen->text;
|
||||
#if 0 && HACK_FINALE_E1M1
|
||||
finaletext = "MUCH SHORTER!";
|
||||
#endif
|
||||
finaleflat = screen->background;
|
||||
}
|
||||
}
|
||||
|
|
@ -159,9 +172,10 @@ void F_StartFinale (void)
|
|||
|
||||
boolean F_Responder (event_t *event)
|
||||
{
|
||||
#if !NO_USE_FINALE_CAST
|
||||
if (finalestage == F_STAGE_CAST)
|
||||
return F_CastResponder (event);
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -171,47 +185,50 @@ boolean F_Responder (event_t *event)
|
|||
//
|
||||
void F_Ticker (void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
size_t i;
|
||||
|
||||
// check for skipping
|
||||
if ( (gamemode == commercial)
|
||||
&& ( finalecount > 50) )
|
||||
{
|
||||
// go on to the next level
|
||||
for (i=0 ; i<MAXPLAYERS ; i++)
|
||||
if (players[i].cmd.buttons)
|
||||
break;
|
||||
|
||||
if (i < MAXPLAYERS)
|
||||
{
|
||||
if (gamemap == 30)
|
||||
F_StartCast ();
|
||||
else
|
||||
gameaction = ga_worlddone;
|
||||
}
|
||||
if ((gamemode == commercial)
|
||||
&& (finalecount > 50)) {
|
||||
// go on to the next level
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (players[i].cmd.buttons)
|
||||
break;
|
||||
|
||||
if (i < MAXPLAYERS) {
|
||||
#if !NO_USE_FINALE_CAST
|
||||
if (gamemap == 30)
|
||||
F_StartCast();
|
||||
else
|
||||
#endif
|
||||
gameaction = ga_worlddone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// advance animation
|
||||
finalecount++;
|
||||
|
||||
if (finalestage == F_STAGE_CAST)
|
||||
{
|
||||
F_CastTicker ();
|
||||
return;
|
||||
|
||||
#if !NO_USE_FINALE_CAST
|
||||
if (finalestage == F_STAGE_CAST) {
|
||||
F_CastTicker();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( gamemode == commercial)
|
||||
return;
|
||||
|
||||
#endif
|
||||
|
||||
if (gamemode == commercial)
|
||||
return;
|
||||
|
||||
if (finalestage == F_STAGE_TEXT
|
||||
&& finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
|
||||
{
|
||||
finalecount = 0;
|
||||
finalestage = F_STAGE_ARTSCREEN;
|
||||
wipegamestate = -1; // force a wipe
|
||||
if (gameepisode == 3)
|
||||
S_StartMusic (mus_bunny);
|
||||
&& finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT) {
|
||||
finalecount = 0;
|
||||
finalestage = F_STAGE_ARTSCREEN;
|
||||
wipegamestate = -1; // force a wipe
|
||||
if (gameepisode == 3)
|
||||
S_StartMusic(mus_bunny);
|
||||
}
|
||||
#if PICO_ON_DEVICE
|
||||
I_UpdateSound();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -221,12 +238,12 @@ void F_Ticker (void)
|
|||
//
|
||||
|
||||
#include "hu_stuff.h"
|
||||
extern patch_t *hu_font[HU_FONTSIZE];
|
||||
extern vpatch_sequence_t hu_font;
|
||||
|
||||
|
||||
void F_TextWrite (void)
|
||||
{
|
||||
byte* src;
|
||||
should_be_const byte* src;
|
||||
pixel_t* dest;
|
||||
|
||||
int x,y,w;
|
||||
|
|
@ -235,7 +252,8 @@ void F_TextWrite (void)
|
|||
int c;
|
||||
int cx;
|
||||
int cy;
|
||||
|
||||
|
||||
#if !DOOM_TINY
|
||||
// erase the entire screen to a tiled background
|
||||
src = W_CacheLumpName ( finaleflat , PU_CACHE);
|
||||
dest = I_VideoBuffer;
|
||||
|
|
@ -255,6 +273,7 @@ void F_TextWrite (void)
|
|||
}
|
||||
|
||||
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
||||
#endif
|
||||
|
||||
// draw some of the text onto the screen
|
||||
cx = 10;
|
||||
|
|
@ -275,7 +294,7 @@ void F_TextWrite (void)
|
|||
cy += 11;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
c = toupper(c) - HU_FONTSTART;
|
||||
if (c < 0 || c> HU_FONTSIZE)
|
||||
{
|
||||
|
|
@ -283,15 +302,22 @@ void F_TextWrite (void)
|
|||
continue;
|
||||
}
|
||||
|
||||
w = SHORT (hu_font[c]->width);
|
||||
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||
if (cx+w > SCREENWIDTH)
|
||||
break;
|
||||
V_DrawPatch(cx, cy, hu_font[c]);
|
||||
#if DOOM_TINY
|
||||
// since we don't clear the screen, only draw the last few character (otherwise we have too many patches)
|
||||
// note we should only need to draw one, but we seem to skip some and I can't be bothered to figure out why
|
||||
if (count < 3) V_DrawPatch(cx, cy, vpatch_n(hu_font,c));
|
||||
#else
|
||||
V_DrawPatch(cx, cy, vpatch_n(hu_font,c));
|
||||
#endif
|
||||
cx+=w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if !NO_USE_FINALE_CAST
|
||||
//
|
||||
// Final DOOM 2 animation
|
||||
// Casting by id Software.
|
||||
|
|
@ -327,7 +353,7 @@ castinfo_t castorder[] = {
|
|||
|
||||
int castnum;
|
||||
int casttics;
|
||||
state_t* caststate;
|
||||
should_be_const state_t* caststate;
|
||||
boolean castdeath;
|
||||
int castframes;
|
||||
int castonmelee;
|
||||
|
|
@ -339,10 +365,11 @@ boolean castattacking;
|
|||
//
|
||||
void F_StartCast (void)
|
||||
{
|
||||
if (finalestage == F_STAGE_CAST) return; // seems to be a bug in general, but seems easier for us to start the cast wipe multiple times
|
||||
wipegamestate = -1; // force a screen wipe
|
||||
castnum = 0;
|
||||
caststate = &states[mobjinfo[castorder[castnum].type].seestate];
|
||||
casttics = caststate->tics;
|
||||
casttics = state_tics(caststate);
|
||||
castdeath = false;
|
||||
finalestage = F_STAGE_CAST;
|
||||
castframes = 0;
|
||||
|
|
@ -363,7 +390,7 @@ void F_CastTicker (void)
|
|||
if (--casttics > 0)
|
||||
return; // not time to change state yet
|
||||
|
||||
if (caststate->tics == -1 || caststate->nextstate == S_NULL)
|
||||
if (state_tics(caststate) == -1 || caststate->nextstate == S_NULL)
|
||||
{
|
||||
// switch from deathstate to next monster
|
||||
castnum++;
|
||||
|
|
@ -452,7 +479,7 @@ void F_CastTicker (void)
|
|||
}
|
||||
}
|
||||
|
||||
casttics = caststate->tics;
|
||||
casttics = state_tics(caststate);
|
||||
if (casttics == -1)
|
||||
casttics = 15;
|
||||
}
|
||||
|
|
@ -473,7 +500,7 @@ boolean F_CastResponder (event_t* ev)
|
|||
// go into death frame
|
||||
castdeath = true;
|
||||
caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
|
||||
casttics = caststate->tics;
|
||||
casttics = state_tics(caststate);
|
||||
castframes = 0;
|
||||
castattacking = false;
|
||||
if (mobjinfo[castorder[castnum].type].deathsound)
|
||||
|
|
@ -507,7 +534,7 @@ void F_CastPrint (const char *text)
|
|||
continue;
|
||||
}
|
||||
|
||||
w = SHORT (hu_font[c]->width);
|
||||
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||
width += w;
|
||||
}
|
||||
|
||||
|
|
@ -526,8 +553,8 @@ void F_CastPrint (const char *text)
|
|||
continue;
|
||||
}
|
||||
|
||||
w = SHORT (hu_font[c]->width);
|
||||
V_DrawPatch(cx, 180, hu_font[c]);
|
||||
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||
V_DrawPatch(cx, 180, vpatch_n(hu_font,c));
|
||||
cx+=w;
|
||||
}
|
||||
|
||||
|
|
@ -538,32 +565,44 @@ void F_CastPrint (const char *text)
|
|||
// F_CastDrawer
|
||||
//
|
||||
|
||||
int F_CastSprite(void) {
|
||||
spriteframeref_t sprframe = sprite_frame(caststate->sprite, caststate->frame & FF_FRAMEMASK);
|
||||
int lump;
|
||||
boolean flip;
|
||||
if (spriteframe_rotates(sprframe)) {
|
||||
lump = spriteframe_rotated_pic(sprframe, 0);
|
||||
flip = spriteframe_rotated_flipped(sprframe, 0);
|
||||
} else {
|
||||
// use single rotation for all views
|
||||
lump = spriteframe_unrotated_pic(sprframe);
|
||||
flip = spriteframe_unrotated_flipped(sprframe);
|
||||
}
|
||||
return flip ? -1 - lump : lump;
|
||||
}
|
||||
|
||||
void F_CastDrawer (void)
|
||||
{
|
||||
spritedef_t* sprdef;
|
||||
spriteframe_t* sprframe;
|
||||
int lump;
|
||||
boolean flip;
|
||||
patch_t* patch;
|
||||
|
||||
#if !DOOM_TINY
|
||||
int lump = F_CastSprite();
|
||||
boolean flip = lump < 0;
|
||||
if (flip) lump = -1 - lump;
|
||||
// erase the entire screen to a background
|
||||
V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE));
|
||||
|
||||
F_CastPrint (DEH_String(castorder[castnum].name));
|
||||
|
||||
// draw the current frame in the middle of the screen
|
||||
sprdef = &sprites[caststate->sprite];
|
||||
sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
|
||||
lump = sprframe->lump[0];
|
||||
flip = (boolean)sprframe->flip[0];
|
||||
|
||||
patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE);
|
||||
if (flip)
|
||||
V_DrawPatchFlipped(SCREENWIDTH/2, 170, patch);
|
||||
else
|
||||
V_DrawPatch(SCREENWIDTH/2, 170, patch);
|
||||
}
|
||||
|
||||
should_be_const patch_t *patch = W_CacheLumpNum (lump + firstspritelump, PU_CACHE);
|
||||
if (flip)
|
||||
V_DrawPatchFlipped(SCREENWIDTH/2, 170, patch);
|
||||
else
|
||||
V_DrawPatch(SCREENWIDTH/2, 170, patch);
|
||||
#else
|
||||
F_CastPrint (DEH_String(castorder[castnum].name));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// F_DrawPatchCol
|
||||
|
|
@ -571,7 +610,7 @@ void F_CastDrawer (void)
|
|||
void
|
||||
F_DrawPatchCol
|
||||
( int x,
|
||||
patch_t* patch,
|
||||
should_be_const patch_t* patch,
|
||||
int col )
|
||||
{
|
||||
column_t* column;
|
||||
|
|
@ -580,7 +619,7 @@ F_DrawPatchCol
|
|||
pixel_t* desttop;
|
||||
int count;
|
||||
|
||||
column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
|
||||
column = (column_t *)((byte *)patch + patch_columnofs(patch, col));
|
||||
desttop = I_VideoBuffer + x;
|
||||
|
||||
// step through the posts in a column
|
||||
|
|
@ -599,31 +638,73 @@ F_DrawPatchCol
|
|||
}
|
||||
}
|
||||
|
||||
#if !NO_USE_FINALE_BUNNY
|
||||
int F_BunnyScrollPos(void) {
|
||||
int scrolled = (SCREENWIDTH - ((signed int) finalecount-230)/2);
|
||||
if (scrolled > SCREENWIDTH)
|
||||
scrolled = SCREENWIDTH;
|
||||
if (scrolled < 0)
|
||||
scrolled = 0;
|
||||
return scrolled;
|
||||
}
|
||||
|
||||
void F_BunnyDrawPatches(void) {
|
||||
char name[10];
|
||||
int stage;
|
||||
static int laststage;
|
||||
if (finalecount < 1130)
|
||||
return;
|
||||
if (finalecount < 1180)
|
||||
{
|
||||
#if !DOOM_TINY
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
W_CacheLumpName ("END0",PU_CACHE));
|
||||
#else
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
VPATCH_NAME(END0));
|
||||
#endif
|
||||
laststage = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
stage = (finalecount-1180) / 5;
|
||||
if (stage > 6)
|
||||
stage = 6;
|
||||
if (stage > laststage)
|
||||
{
|
||||
S_StartSound (NULL, sfx_pistol);
|
||||
laststage = stage;
|
||||
}
|
||||
|
||||
#if !DOOM_TINY
|
||||
DEH_snprintf(name, 10, "END%i", stage);
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
W_CacheLumpName (name,PU_CACHE));
|
||||
#else
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
VPATCH_END0 + stage);
|
||||
#endif
|
||||
}
|
||||
#if !DOOM_TINY
|
||||
//
|
||||
// F_BunnyScroll
|
||||
//
|
||||
void F_BunnyScroll (void)
|
||||
{
|
||||
signed int scrolled;
|
||||
signed int scrolled = F_BunnyScrollPos();
|
||||
int x;
|
||||
patch_t* p1;
|
||||
patch_t* p2;
|
||||
char name[10];
|
||||
int stage;
|
||||
static int laststage;
|
||||
|
||||
should_be_const patch_t* p1;
|
||||
should_be_const patch_t* p2;
|
||||
|
||||
p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
|
||||
p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
|
||||
|
||||
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
||||
|
||||
scrolled = (SCREENWIDTH - ((signed int) finalecount-230)/2);
|
||||
if (scrolled > SCREENWIDTH)
|
||||
scrolled = SCREENWIDTH;
|
||||
if (scrolled < 0)
|
||||
scrolled = 0;
|
||||
|
||||
|
||||
for ( x=0 ; x<SCREENWIDTH ; x++)
|
||||
{
|
||||
if (x+scrolled < SCREENWIDTH)
|
||||
|
|
@ -631,69 +712,56 @@ void F_BunnyScroll (void)
|
|||
else
|
||||
F_DrawPatchCol (x, p2, x+scrolled - SCREENWIDTH);
|
||||
}
|
||||
|
||||
if (finalecount < 1130)
|
||||
return;
|
||||
if (finalecount < 1180)
|
||||
F_BunnyDrawPatches();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const char *F_ArtScreenLumpName(void) {
|
||||
const char* lumpname;
|
||||
switch (gameepisode)
|
||||
{
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
W_CacheLumpName(DEH_String("END0"), PU_CACHE));
|
||||
laststage = 0;
|
||||
return;
|
||||
case 1:
|
||||
if (gameversion >= exe_ultimate)
|
||||
{
|
||||
lumpname = "CREDIT";
|
||||
}
|
||||
else
|
||||
{
|
||||
lumpname = "HELP2";
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
lumpname = "VICTORY2";
|
||||
break;
|
||||
case 3:
|
||||
return NULL; // bunny
|
||||
case 4:
|
||||
lumpname = "ENDPIC";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
lumpname = "CREDIT";
|
||||
}
|
||||
|
||||
stage = (finalecount-1180) / 5;
|
||||
if (stage > 6)
|
||||
stage = 6;
|
||||
if (stage > laststage)
|
||||
{
|
||||
S_StartSound (NULL, sfx_pistol);
|
||||
laststage = stage;
|
||||
}
|
||||
|
||||
DEH_snprintf(name, 10, "END%i", stage);
|
||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
||||
(SCREENHEIGHT - 8 * 8) / 2,
|
||||
W_CacheLumpName (name,PU_CACHE));
|
||||
|
||||
return DEH_String(lumpname);
|
||||
}
|
||||
|
||||
static void F_ArtScreenDrawer(void)
|
||||
{
|
||||
const char *lumpname;
|
||||
|
||||
if (gameepisode == 3)
|
||||
#if !DOOM_TINY
|
||||
const char *lumpname = F_ArtScreenLumpName();
|
||||
|
||||
if (!lumpname)
|
||||
{
|
||||
#if !NO_USE_FINALE_BUNNY
|
||||
F_BunnyScroll();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
} else
|
||||
{
|
||||
switch (gameepisode)
|
||||
{
|
||||
case 1:
|
||||
if (gameversion >= exe_ultimate)
|
||||
{
|
||||
lumpname = "CREDIT";
|
||||
}
|
||||
else
|
||||
{
|
||||
lumpname = "HELP2";
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
lumpname = "VICTORY2";
|
||||
break;
|
||||
case 4:
|
||||
lumpname = "ENDPIC";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
lumpname = DEH_String(lumpname);
|
||||
|
||||
V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -704,8 +772,10 @@ void F_Drawer (void)
|
|||
switch (finalestage)
|
||||
{
|
||||
case F_STAGE_CAST:
|
||||
#if !DOOM_TINY && !NO_USE_FINALE_CAST
|
||||
F_CastDrawer();
|
||||
break;
|
||||
#endif
|
||||
case F_STAGE_TEXT:
|
||||
F_TextWrite();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -39,7 +40,25 @@ void F_Drawer (void);
|
|||
|
||||
void F_StartFinale (void);
|
||||
|
||||
const char *F_ArtScreenLumpName(void);
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
F_STAGE_TEXT,
|
||||
F_STAGE_ARTSCREEN,
|
||||
F_STAGE_CAST,
|
||||
} finalestage_t;
|
||||
|
||||
// Stage of animation:
|
||||
extern finalestage_t finalestage;
|
||||
extern const char *finaleflat;
|
||||
|
||||
#if DOOM_TINY
|
||||
int F_BunnyScrollPos(void);
|
||||
void F_BunnyDrawPatches(void);
|
||||
#endif
|
||||
// return the sprite to to draw or -1-x to draw x flipped
|
||||
int F_CastSprite(void);
|
||||
void F_CastDrawer (void);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -234,8 +235,10 @@ wipe_StartScreen
|
|||
int width,
|
||||
int height )
|
||||
{
|
||||
wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, NULL);
|
||||
#if !NO_USE_WIPE
|
||||
wipe_scr_start = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, 0);
|
||||
I_ReadScreen(wipe_scr_start);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -246,9 +249,11 @@ wipe_EndScreen
|
|||
int width,
|
||||
int height )
|
||||
{
|
||||
wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end), PU_STATIC, NULL);
|
||||
#if !NO_USE_WIPE
|
||||
wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end), PU_STATIC, 0);
|
||||
I_ReadScreen(wipe_scr_end);
|
||||
V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr.
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -261,6 +266,7 @@ wipe_ScreenWipe
|
|||
int height,
|
||||
int ticks )
|
||||
{
|
||||
#if !NO_USE_WIPE
|
||||
int rc;
|
||||
static int (*wipes[])(int, int, int) =
|
||||
{
|
||||
|
|
@ -290,5 +296,8 @@ wipe_ScreenWipe
|
|||
}
|
||||
|
||||
return !go;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -60,4 +61,16 @@ wipe_ScreenWipe
|
|||
int height,
|
||||
int ticks );
|
||||
|
||||
#if DOOM_TINY
|
||||
typedef enum {
|
||||
WIPESTATE_NONE,
|
||||
WIPESTATE_SKIP1,
|
||||
WIPESTATE_REDRAW1,
|
||||
WIPESTATE_SKIP2,
|
||||
WIPESTATE_REDRAW2,
|
||||
WIPESTATE_SKIP3,
|
||||
} wipestate_t;
|
||||
extern wipestate_t wipestate;
|
||||
extern volatile uint8_t wipe_min;
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -35,7 +36,7 @@ void G_InitNew (skill_t skill, int episode, int map);
|
|||
// Can be called by the startup code or M_Responder.
|
||||
// A normal game starts at map 1,
|
||||
// but a warp test can start elsewhere
|
||||
void G_DeferedInitNew (skill_t skill, int episode, int map);
|
||||
void G_DeferedInitNew (skill_t skill, int episode, int map, boolean net);
|
||||
|
||||
void G_DeferedPlayDemo (const char* demo);
|
||||
|
||||
|
|
@ -48,10 +49,12 @@ void G_DoLoadGame (void);
|
|||
// Called by M_Responder.
|
||||
void G_SaveGame (int slot, char* description);
|
||||
|
||||
#if !NO_DEMO_RECORDING
|
||||
// Only called by startup code.
|
||||
void G_RecordDemo (char* name);
|
||||
|
||||
void G_BeginRecording (void);
|
||||
#endif
|
||||
|
||||
void G_PlayDemo (char* name);
|
||||
void G_TimeDemo (char* name);
|
||||
|
|
@ -74,7 +77,11 @@ void G_ScreenShot (void);
|
|||
void G_DrawMouseSpeedBox(void);
|
||||
int G_VanillaVersionCode(void);
|
||||
|
||||
extern int vanilla_savegame_limit;
|
||||
extern int vanilla_demo_limit;
|
||||
extern isb_int8_t vanilla_savegame_limit;
|
||||
extern isb_int8_t vanilla_demo_limit;
|
||||
|
||||
#if PICO_ON_DEVICE
|
||||
extern uint8_t g_load_slot;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
#include "v_video.h"
|
||||
#include "i_swap.h"
|
||||
|
||||
#include "w_wad.h"
|
||||
#include "hu_lib.h"
|
||||
#include "r_local.h"
|
||||
#include "r_draw.h"
|
||||
|
|
@ -49,7 +51,7 @@ HUlib_initTextLine
|
|||
( hu_textline_t* t,
|
||||
int x,
|
||||
int y,
|
||||
patch_t** f,
|
||||
vpatch_sequence_t f,
|
||||
int sc )
|
||||
{
|
||||
t->x = x;
|
||||
|
|
@ -110,10 +112,10 @@ HUlib_drawTextLine
|
|||
&& c >= l->sc
|
||||
&& c <= '_')
|
||||
{
|
||||
w = SHORT(l->f[c - l->sc]->width);
|
||||
w = vpatch_width(resolve_vpatch_handle(vpatch_n(l->f,c - l->sc)));
|
||||
if (x+w > SCREENWIDTH)
|
||||
break;
|
||||
V_DrawPatchDirect(x, l->y, l->f[c - l->sc]);
|
||||
V_DrawPatchDirect(x, l->y, vpatch_n(l->f, c - l->sc));
|
||||
x += w;
|
||||
}
|
||||
else
|
||||
|
|
@ -126,9 +128,9 @@ HUlib_drawTextLine
|
|||
|
||||
// draw the cursor if requested
|
||||
if (drawcursor
|
||||
&& x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH)
|
||||
&& x + vpatch_width(resolve_vpatch_handle(vpatch_n(l->f,'_' - l->sc))) <= SCREENWIDTH)
|
||||
{
|
||||
V_DrawPatchDirect(x, l->y, l->f['_' - l->sc]);
|
||||
V_DrawPatchDirect(x, l->y, vpatch_n(l->f, '_' - l->sc));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,10 +146,11 @@ void HUlib_eraseTextLine(hu_textline_t* l)
|
|||
// and the text must either need updating or refreshing
|
||||
// (because of a recent change back from the automap)
|
||||
|
||||
#if !NO_RDRAW
|
||||
if (!automapactive &&
|
||||
viewwindowx && l->needsupdate)
|
||||
{
|
||||
lh = SHORT(l->f[0]->height) + 1;
|
||||
lh = patch_height(l->f[0]) + 1;
|
||||
for (y=l->y,yoffset=y*SCREENWIDTH ; y<l->y+lh ; y++,yoffset+=SCREENWIDTH)
|
||||
{
|
||||
if (y < viewwindowy || y >= viewwindowy + viewheight)
|
||||
|
|
@ -160,6 +163,7 @@ void HUlib_eraseTextLine(hu_textline_t* l)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (l->needsupdate) l->needsupdate--;
|
||||
|
||||
|
|
@ -171,7 +175,7 @@ HUlib_initSText
|
|||
int x,
|
||||
int y,
|
||||
int h,
|
||||
patch_t** font,
|
||||
vpatch_sequence_t font,
|
||||
int startchar,
|
||||
boolean* on )
|
||||
{
|
||||
|
|
@ -182,9 +186,10 @@ HUlib_initSText
|
|||
s->on = on;
|
||||
s->laston = true;
|
||||
s->cl = 0;
|
||||
int pheight = vpatch_height(resolve_vpatch_handle(vpatch_n(font, 0)));
|
||||
for (i=0;i<h;i++)
|
||||
HUlib_initTextLine(&s->l[i],
|
||||
x, y - i*(SHORT(font[0]->height)+1),
|
||||
x, y - i*(pheight+1),
|
||||
font, startchar);
|
||||
|
||||
}
|
||||
|
|
@ -263,7 +268,7 @@ HUlib_initIText
|
|||
( hu_itext_t* it,
|
||||
int x,
|
||||
int y,
|
||||
patch_t** font,
|
||||
vpatch_sequence_t font,
|
||||
int startchar,
|
||||
boolean* on )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -39,7 +40,7 @@ typedef struct
|
|||
int x;
|
||||
int y;
|
||||
|
||||
patch_t** f; // font
|
||||
vpatch_sequence_t f; // font
|
||||
int sc; // start character
|
||||
char l[HU_MAXLINELENGTH+1]; // line of text
|
||||
int len; // current line length
|
||||
|
|
@ -97,7 +98,7 @@ void HUlib_init(void);
|
|||
// clear a line of text
|
||||
void HUlib_clearTextLine(hu_textline_t *t);
|
||||
|
||||
void HUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc);
|
||||
void HUlib_initTextLine(hu_textline_t *t, int x, int y, vpatch_sequence_t f, int sc);
|
||||
|
||||
// returns success
|
||||
boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch);
|
||||
|
|
@ -123,7 +124,7 @@ HUlib_initSText
|
|||
int x,
|
||||
int y,
|
||||
int h,
|
||||
patch_t** font,
|
||||
vpatch_sequence_t font,
|
||||
int startchar,
|
||||
boolean* on );
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ HUlib_initIText
|
|||
( hu_itext_t* it,
|
||||
int x,
|
||||
int y,
|
||||
patch_t** font,
|
||||
vpatch_sequence_t font,
|
||||
int startchar,
|
||||
boolean* on );
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
// Data.
|
||||
#include "dstrings.h"
|
||||
#include "sounds.h"
|
||||
#include "r_data.h"
|
||||
|
||||
//
|
||||
// Locally used constants, shortcuts.
|
||||
|
|
@ -52,17 +54,19 @@
|
|||
#define HU_TITLE_CHEX (mapnames_chex[(gameepisode-1)*9+gamemap-1])
|
||||
#define HU_TITLEHEIGHT 1
|
||||
#define HU_TITLEX 0
|
||||
#define HU_TITLEY (167 - SHORT(hu_font[0]->height))
|
||||
#define HU_TITLEY (167 - vpatch_height(resolve_vpatch_handle(vpatch_n(hu_font,0))))
|
||||
|
||||
#define HU_INPUTTOGGLE 't'
|
||||
#define HU_INPUTX HU_MSGX
|
||||
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0]->height) +1))
|
||||
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(vpatch_height(resolve_vpatch_handle(vpatch_n(hu_font,0))) +1))
|
||||
#define HU_INPUTWIDTH 64
|
||||
#define HU_INPUTHEIGHT 1
|
||||
|
||||
|
||||
|
||||
char *chat_macros[10] =
|
||||
#if !DOOM_TINY
|
||||
const char *chat_macros[10] =
|
||||
#else
|
||||
const constcharstar chat_macros[10] =
|
||||
#endif
|
||||
{
|
||||
HUSTR_CHATMACRO0,
|
||||
HUSTR_CHATMACRO1,
|
||||
|
|
@ -76,7 +80,7 @@ char *chat_macros[10] =
|
|||
HUSTR_CHATMACRO9
|
||||
};
|
||||
|
||||
const char *player_names[] =
|
||||
const constcharstar player_names[] =
|
||||
{
|
||||
HUSTR_PLRGREEN,
|
||||
HUSTR_PLRINDIGO,
|
||||
|
|
@ -86,7 +90,12 @@ const char *player_names[] =
|
|||
|
||||
char chat_char; // remove later.
|
||||
static player_t* plr;
|
||||
patch_t* hu_font[HU_FONTSIZE];
|
||||
#if !USE_WHD
|
||||
vpatch_handle_large_t hu_fontx[HU_FONTSIZE];
|
||||
vpatch_sequence_t hu_font = hu_fontx;
|
||||
#else
|
||||
vpatch_sequence_t hu_font;
|
||||
#endif
|
||||
static hu_textline_t w_title;
|
||||
boolean chat_on;
|
||||
static hu_itext_t w_chat;
|
||||
|
|
@ -101,7 +110,7 @@ static boolean message_nottobefuckedwith;
|
|||
static hu_stext_t w_message;
|
||||
static int message_counter;
|
||||
|
||||
extern int showMessages;
|
||||
extern isb_int8_t showMessages;
|
||||
|
||||
static boolean headsupactive = false;
|
||||
|
||||
|
|
@ -110,7 +119,7 @@ static boolean headsupactive = false;
|
|||
// The actual names can be found in DStrings.h.
|
||||
//
|
||||
|
||||
const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
||||
const constcharstar mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
||||
{
|
||||
|
||||
HUSTR_E1M1,
|
||||
|
|
@ -123,6 +132,7 @@ const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
|||
HUSTR_E1M8,
|
||||
HUSTR_E1M9,
|
||||
|
||||
#if !DEMO1_ONLY
|
||||
HUSTR_E2M1,
|
||||
HUSTR_E2M2,
|
||||
HUSTR_E2M3,
|
||||
|
|
@ -162,6 +172,7 @@ const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
|||
"NEWLEVEL",
|
||||
"NEWLEVEL",
|
||||
"NEWLEVEL"
|
||||
#endif
|
||||
};
|
||||
|
||||
const char *mapnames_chex[] = // Chex Quest names.
|
||||
|
|
@ -224,7 +235,7 @@ const char *mapnames_chex[] = // Chex Quest names.
|
|||
// the layout in the Vanilla executable, where it is possible to
|
||||
// overflow the end of one array into the next.
|
||||
|
||||
const char *mapnames_commercial[] =
|
||||
const char * const mapnames_commercial[] =
|
||||
{
|
||||
// DOOM 2 map names.
|
||||
|
||||
|
|
@ -352,12 +363,19 @@ void HU_Init(void)
|
|||
char buffer[9];
|
||||
|
||||
// load the heads-up font
|
||||
#if !USE_WHD
|
||||
j = HU_FONTSTART;
|
||||
for (i=0;i<HU_FONTSIZE;i++)
|
||||
{
|
||||
DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
|
||||
hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
|
||||
}
|
||||
#else
|
||||
static_assert(HU_FONTSTART == 33, "");
|
||||
static_assert(HU_FONTSIZE == 95 - 33 + 1, "");
|
||||
hu_font = VPATCH_STCFN033;
|
||||
// todo what about STCFN121 !!?
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -398,6 +416,7 @@ void HU_Start(void)
|
|||
case doom:
|
||||
s = HU_TITLE;
|
||||
break;
|
||||
#if !DEMO1_ONLY
|
||||
case doom2:
|
||||
s = HU_TITLE2;
|
||||
// Pre-Final Doom compatibility: map33-map35 names don't spill over
|
||||
|
|
@ -411,16 +430,19 @@ void HU_Start(void)
|
|||
break;
|
||||
case pack_tnt:
|
||||
s = HU_TITLET;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
s = "Unknown level";
|
||||
break;
|
||||
}
|
||||
|
||||
#if !DOOM_ONLY
|
||||
if (logical_gamemission == doom && gameversion == exe_chex)
|
||||
{
|
||||
s = HU_TITLE_CHEX;
|
||||
}
|
||||
#endif
|
||||
|
||||
// dehacked substitution to get modified level name
|
||||
|
||||
|
|
@ -445,12 +467,12 @@ void HU_Start(void)
|
|||
|
||||
void HU_Drawer(void)
|
||||
{
|
||||
|
||||
#if !NO_HU_DRAWER
|
||||
HUlib_drawSText(&w_message);
|
||||
HUlib_drawIText(&w_chat);
|
||||
if (automapactive)
|
||||
HUlib_drawTextLine(&w_title, false);
|
||||
|
||||
HUlib_drawTextLine(&w_title, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void HU_Erase(void)
|
||||
|
|
@ -521,9 +543,9 @@ void HU_Ticker(void)
|
|||
message_on = true;
|
||||
message_counter = HU_MSGTIMEOUT;
|
||||
if ( gamemode == commercial )
|
||||
S_StartSound(0, sfx_radio);
|
||||
S_StartUnpositionedSound( sfx_radio);
|
||||
else
|
||||
S_StartSound(0, sfx_tink);
|
||||
S_StartUnpositionedSound( sfx_tink);
|
||||
}
|
||||
HUlib_resetIText(&w_inputbuffer[i]);
|
||||
}
|
||||
|
|
@ -535,6 +557,8 @@ void HU_Ticker(void)
|
|||
|
||||
}
|
||||
|
||||
#if !NO_USE_NET
|
||||
|
||||
#define QUEUESIZE 128
|
||||
|
||||
static char chatchars[QUEUESIZE];
|
||||
|
|
@ -716,3 +740,4 @@ boolean HU_Responder(event_t *ev)
|
|||
|
||||
return eatkey;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -53,7 +54,11 @@ void HU_Drawer(void);
|
|||
char HU_dequeueChatChar(void);
|
||||
void HU_Erase(void);
|
||||
|
||||
extern char *chat_macros[10];
|
||||
#if !DOOM_TINY
|
||||
extern const char *chat_macros[10];
|
||||
#else
|
||||
extern const constcharstar chat_macros[10];
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
2085
src/doom/info.c
|
|
@ -1,6 +1,7 @@
|
|||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2021-2022 Graham Sanderson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
|
|
@ -21,6 +22,8 @@
|
|||
#ifndef __INFO__
|
||||
#define __INFO__
|
||||
|
||||
#include "doomdef.h"
|
||||
|
||||
// Needed for action function pointer handling.
|
||||
#include "d_think.h"
|
||||
|
||||
|
|
@ -166,7 +169,11 @@ typedef enum
|
|||
SPR_TLP2,
|
||||
NUMSPRITES
|
||||
|
||||
} spritenum_t;
|
||||
} spritenum_t_orig;
|
||||
|
||||
#include <assert.h>
|
||||
static_assert(NUMSPRITES <= 256, "");
|
||||
typedef uint8_t spritenum_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -1138,22 +1145,52 @@ typedef enum
|
|||
S_TECH2LAMP3,
|
||||
S_TECH2LAMP4,
|
||||
NUMSTATES
|
||||
} statenum_t;
|
||||
} statenum_t_orig;
|
||||
|
||||
static_assert(NUMSTATES < 1024, ""); // just so we know
|
||||
typedef uint16_t statenum_t;
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#if !DOOM_SMALL
|
||||
spritenum_t sprite;
|
||||
int frame;
|
||||
int tics;
|
||||
// void (*action) ();
|
||||
actionf_t action;
|
||||
statenum_t nextstate;
|
||||
#ifndef NO_USE_STATE_MISC
|
||||
int misc1;
|
||||
int misc2;
|
||||
#endif
|
||||
#else
|
||||
uint8_t sprite;
|
||||
#if !DOOM_SMALL
|
||||
uint16_t frame; // todo highbit is a flag
|
||||
#else
|
||||
uint8_t frame;
|
||||
#endif
|
||||
int8_t xtics;
|
||||
// void (*action) ();
|
||||
actionf_t action; // could be trivially small
|
||||
statenum_t nextstate;
|
||||
#if !NO_USE_STATE_MISC
|
||||
int misc1;
|
||||
int misc2;
|
||||
#endif
|
||||
#endif
|
||||
} state_t;
|
||||
#if !DOOM_CONST
|
||||
#define state_tics(s) ((s)->tics)
|
||||
#else
|
||||
int state_tics(const state_t *s);
|
||||
extern boolean nightmare_speeds;
|
||||
#endif
|
||||
|
||||
extern should_be_const state_t states[NUMSTATES];
|
||||
|
||||
extern state_t states[NUMSTATES];
|
||||
extern const char *sprnames[];
|
||||
|
||||
typedef enum {
|
||||
|
|
@ -1296,36 +1333,40 @@ typedef enum {
|
|||
MT_MISC86,
|
||||
NUMMOBJTYPES
|
||||
|
||||
} mobjtype_t;
|
||||
} mobjtype_t_orig;
|
||||
static_assert(NUMMOBJTYPES<256, "");
|
||||
typedef uint8_t mobjtype_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int doomednum;
|
||||
int spawnstate;
|
||||
int spawnhealth;
|
||||
int seestate;
|
||||
int seesound;
|
||||
int reactiontime;
|
||||
int attacksound;
|
||||
int painstate;
|
||||
int painchance;
|
||||
int painsound;
|
||||
int meleestate;
|
||||
int missilestate;
|
||||
int deathstate;
|
||||
int xdeathstate;
|
||||
int deathsound;
|
||||
isb_int16_t doomednum;
|
||||
statenum_t spawnstate;
|
||||
|
||||
isb_int16_t spawnhealth;
|
||||
statenum_t seestate;
|
||||
|
||||
isb_uint8_t seesound;
|
||||
isb_int8_t reactiontime;
|
||||
isb_uint8_t attacksound;
|
||||
statenum_t painstate;
|
||||
isb_int16_t painchance;
|
||||
isb_uint8_t painsound;
|
||||
statenum_t meleestate;
|
||||
statenum_t missilestate;
|
||||
statenum_t deathstate;
|
||||
statenum_t xdeathstate;
|
||||
isb_uint8_t deathsound;
|
||||
int speed;
|
||||
int radius;
|
||||
int height;
|
||||
int radius; // todo wasteful
|
||||
int height; // todo wasteful
|
||||
int mass;
|
||||
int damage;
|
||||
int activesound;
|
||||
isb_int8_t damage;
|
||||
isb_uint8_t activesound;
|
||||
int flags;
|
||||
int raisestate;
|
||||
statenum_t raisestate;
|
||||
|
||||
} mobjinfo_t;
|
||||
|
||||
extern mobjinfo_t mobjinfo[NUMMOBJTYPES];
|
||||
extern should_be_const mobjinfo_t mobjinfo[NUMMOBJTYPES];
|
||||
|
||||
#endif
|
||||
|
|
|
|||