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
|
autom4te.cache
|
||||||
autotools
|
autotools
|
||||||
bin
|
bin
|
||||||
config.h
|
/config.h
|
||||||
config.hin
|
config.hin
|
||||||
config.log
|
config.log
|
||||||
config.status
|
config.status
|
||||||
|
|
@ -52,3 +52,8 @@ GPATH
|
||||||
GRTAGS
|
GRTAGS
|
||||||
GTAGS
|
GTAGS
|
||||||
/HTML/
|
/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")
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.7.2)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project("Chocolate Doom" VERSION 3.0.0 LANGUAGES C)
|
|
||||||
|
# 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
|
# Autotools variables
|
||||||
set(top_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
|
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"
|
add_definitions("/D_CRT_SECURE_NO_WARNINGS" "/D_CRT_SECURE_NO_DEPRECATE"
|
||||||
"/D_CRT_NONSTDC_NO_DEPRECATE")
|
"/D_CRT_NONSTDC_NO_DEPRECATE")
|
||||||
else()
|
else()
|
||||||
add_compile_options("-Wall" "-Wdeclaration-after-statement"
|
#add_compile_options("-Wall" "-Wdeclaration-after-statement" "-Wredundant-decls")
|
||||||
"-Wredundant-decls")
|
add_compile_options("-Wall" "-Wredundant-decls")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(SDL2 2.0.1)
|
# Note PICO_SDK path is set by the SDK initialization if it occurs above
|
||||||
find_package(SDL2_mixer 2.0.0)
|
if (NOT PICO_SDK)
|
||||||
find_package(SDL2_net 2.0.0)
|
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.
|
# Check for libsamplerate.
|
||||||
find_package(samplerate)
|
find_package(samplerate)
|
||||||
if(SAMPLERATE_FOUND)
|
if(SAMPLERATE_FOUND)
|
||||||
set(HAVE_LIBSAMPLERATE TRUE)
|
set(HAVE_LIBSAMPLERATE TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for libpng.
|
# Check for libpng.
|
||||||
find_package(PNG)
|
find_package(PNG)
|
||||||
if(PNG_FOUND)
|
if(PNG_FOUND)
|
||||||
set(HAVE_LIBPNG TRUE)
|
set(HAVE_LIBPNG TRUE)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(HAVE_MMAP 1)
|
||||||
find_package(m)
|
find_package(m)
|
||||||
|
|
||||||
include(CheckSymbolExists)
|
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
|
This is a port of Doom for RP2040 devices, derived from [Chocolate Doom](https://github.com/chocolate-doom/chocolate-doom).
|
||||||
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
|
Significant changes have been made to support running on the RP2040 device, but particularly to support running the
|
||||||
now includes ports of Heretic and Hexen, and Strife.
|
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.
|
Note that a hopefully-fully-functional `chocolate-doom` executable is buildable from this RP2040 code base as a
|
||||||
* Portability to as many different operating systems as possible.
|
means of
|
||||||
* Accurate reproduction of the original DOS versions of the games,
|
verification that everything still works, but whilst they can still be built, Hexen, Strife and Heretic are almost
|
||||||
including bugs.
|
certainly broken, so are not built by default.
|
||||||
* 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
|
This chocolate-doom commit that the code is branched off can be found in the `upstream` branch.
|
||||||
can be found in the PHILOSOPHY file distributed with the source code.
|
|
||||||
|
|
||||||
## 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
|
## Code State
|
||||||
INSTALL file.
|
|
||||||
|
|
||||||
## 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
|
## Artifacts
|
||||||
(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.
|
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
|
## Goals
|
||||||
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
|
The main goals for this port were:
|
||||||
|
|
||||||
With Vanilla Doom there is no way to include sprites in PWAD files.
|
1. Everything should match the original game experience, i.e. all the graphics at classic 320x200 resolution, stereo
|
||||||
Chocolate Doom’s ‘-file’ command line option behaves exactly the same
|
sound,
|
||||||
as Vanilla Doom, and trying to play TCs by adding the WAD files using
|
OPL2 music, save/load, demo playback, cheats, network multiplayer... basically it should feel like the original game.
|
||||||
‘-file’ will not work.
|
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
|
## Results
|
||||||
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:
|
[](https://youtu.be/eDVazQVycP4)
|
||||||
|
|
||||||
```
|
Features:
|
||||||
chocolate-doom -merge thetc.wad
|
|
||||||
|
* 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`
|
||||||
|
|
||||||
```
|
Note this is the way you build the `whd_gen` tool too.
|
||||||
chocolate-doom -merge batman.wad -deh batman.deh vbatman.deh (Batman Doom)
|
|
||||||
chocolate-doom -merge aoddoom1.wad -deh aoddoom1.deh (Army of Darkness Doom)
|
## 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
|
As before, use `make` or `make <target>` to build.
|
||||||
playback. See the README.Music file for more details.
|
|
||||||
|
|
||||||
* More information, including information about how to play various
|
The RP2040 version has four targets, each of which create a similarly named `UF2` file (e.g. `doom_tiny_nh.uf2`).
|
||||||
classic TCs, is available on the Chocolate Doom website:
|
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
|
* **doom_tiny_nh** This is a "super tiny" version with no USB keyboard support. You can use
|
||||||
emulate and reproduce the DOS executables, some behavior can be very
|
[SDL Event Forwarder](https://github.com/kilograham/sdl_event_forwarder) to tunnel keyboard input from your host
|
||||||
difficult to reproduce. Because of the nature of the project, you
|
computer over UART. The WHX file must be loaded at `0x10040000`.
|
||||||
may also encounter Vanilla Doom bugs; these are intentionally
|
* **doom_tiny_nh_cd** This is a "super tiny" version with additional USB keyboard support. Because of the extra USB
|
||||||
present; see the NOT-BUGS file for more information.
|
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
|
See `whd_gen` further below for generating `WHX` or `WHD` files.
|
||||||
guidelines - see the file named HACKING included with the source
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
* Chocolate Doom is distributed under the GNU GPL. See the COPYING
|
#### USB keyboard support
|
||||||
file for more information.
|
|
||||||
|
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_LIBSAMPLERATE
|
||||||
#cmakedefine HAVE_LIBPNG
|
#cmakedefine HAVE_LIBPNG
|
||||||
#cmakedefine HAVE_DIRENT_H
|
#cmakedefine HAVE_DIRENT_H
|
||||||
|
#cmakedefine HAVE_MMAP
|
||||||
#cmakedefine01 HAVE_DECL_STRCASECMP
|
#cmakedefine01 HAVE_DECL_STRCASECMP
|
||||||
#cmakedefine01 HAVE_DECL_STRNCASECMP
|
#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
|
if (PICO_SDK)
|
||||||
opl_internal.h
|
add_library(opl INTERFACE)
|
||||||
opl.c opl.h
|
target_sources(opl INTERFACE
|
||||||
opl_linux.c
|
${CMAKE_CURRENT_LIST_DIR}/opl_api.c
|
||||||
opl_obsd.c
|
${CMAKE_CURRENT_LIST_DIR}/emu8950.c
|
||||||
opl_queue.c opl_queue.h
|
${CMAKE_CURRENT_LIST_DIR}/slot_render.cpp
|
||||||
opl_sdl.c
|
${CMAKE_CURRENT_LIST_DIR}/opl_pico.c)
|
||||||
opl_timer.c opl_timer.h
|
target_compile_options(opl INTERFACE -fms-extensions) # want OPL_SLOT_RENDER to be unnamed within OPL_SLOT
|
||||||
opl_win32.c
|
if (APPLE)
|
||||||
ioperm_sys.c ioperm_sys.h
|
target_compile_options(opl INTERFACE -Wno-microsoft-anon-tag)
|
||||||
opl3.c opl3.h)
|
endif()
|
||||||
target_include_directories(opl
|
target_compile_definitions(opl INTERFACE
|
||||||
INTERFACE "."
|
EMU8950_NO_RATECONV
|
||||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
)
|
||||||
target_link_libraries(opl SDL2::mixer)
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -20,435 +21,12 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "SDL.h"
|
|
||||||
|
|
||||||
#include "opl.h"
|
#include "opl.h"
|
||||||
#include "opl_internal.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 *driver;
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -504,20 +82,3 @@ void OPL_Delay(uint64_t us)
|
||||||
SDL_DestroyMutex(delay_data.mutex);
|
SDL_DestroyMutex(delay_data.mutex);
|
||||||
SDL_DestroyCond(delay_data.cond);
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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
|
// Adjust callback times by the specified factor. For example, a value of
|
||||||
// 0.5 will halve all remaining times.
|
// 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.
|
// Clear all OPL callbacks that have been set.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT)
|
// 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
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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_channel opl3_channel;
|
||||||
typedef struct _opl3_chip opl3_chip;
|
typedef struct _opl3_chip opl3_chip;
|
||||||
|
|
||||||
|
|
||||||
struct _opl3_slot {
|
struct _opl3_slot {
|
||||||
opl3_channel *channel;
|
opl3_channel *channel;
|
||||||
opl3_chip *chip;
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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_lock_func)(void);
|
||||||
typedef void (*opl_unlock_func)(void);
|
typedef void (*opl_unlock_func)(void);
|
||||||
typedef void (*opl_set_paused_func)(int paused);
|
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
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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)
|
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));
|
port_base, strerror(errno));
|
||||||
|
|
||||||
if (errno == EPERM)
|
if (errno == EPERM)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
stderr_print(
|
||||||
"\tYou may need to run the program as root in order\n"
|
"\tYou may need to run the program as root in order\n"
|
||||||
"\tto acquire I/O port permissions for OPL MIDI playback.\n");
|
"\tto acquire I/O port permissions for OPL MIDI playback.\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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)
|
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");
|
"check that you are running as root.\n");
|
||||||
return 0;
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "doomtype.h"
|
||||||
#include "opl_queue.h"
|
#include "opl_queue.h"
|
||||||
|
|
||||||
#define MAX_OPL_QUEUE 64
|
#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)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,11 +204,12 @@ uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
void OPL_Queue_AdjustCallbacks(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;
|
int64_t offset;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
float factor = old_tempo / new_tempo;
|
||||||
for (i = 0; i < queue->num_entries; ++i)
|
for (i = 0; i < queue->num_entries; ++i)
|
||||||
{
|
{
|
||||||
offset = queue->entries[i].time - time;
|
offset = queue->entries[i].time - time;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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,
|
int OPL_Queue_Pop(opl_callback_queue_t *queue,
|
||||||
opl_callback_t *callback, void **data);
|
opl_callback_t *callback, void **data);
|
||||||
uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue);
|
uint64_t OPL_Queue_Peek(opl_callback_queue_t *queue);
|
||||||
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue,
|
void OPL_Queue_AdjustCallbacks(opl_callback_queue_t *queue, uint64_t time,
|
||||||
uint64_t time, float factor);
|
unsigned int old_tempo, unsigned int new_tempo);
|
||||||
|
|
||||||
#endif /* #ifndef OPL_QUEUE_H */
|
#endif /* #ifndef OPL_QUEUE_H */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -24,8 +25,15 @@
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "SDL_mixer.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"
|
#include "opl3.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "opl.h"
|
#include "opl.h"
|
||||||
#include "opl_internal.h"
|
#include "opl_internal.h"
|
||||||
|
|
@ -70,8 +78,16 @@ static uint64_t pause_offset;
|
||||||
|
|
||||||
// OPL software emulator structure.
|
// 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 opl3_chip opl_chip;
|
||||||
static int opl_opl3mode;
|
static int opl_opl3mode;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Temporary mixing buffer used by the mixing callback.
|
// 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
|
// OPL output is generated into temporary buffer and then mixed
|
||||||
// (to avoid overflows etc.)
|
// (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);
|
OPL3_GenerateStream(&opl_chip, (Bit16s *) mix_buffer, nsamples);
|
||||||
|
#endif
|
||||||
SDL_MixAudioFormat(buffer, mix_buffer, AUDIO_S16SYS, nsamples * 4,
|
SDL_MixAudioFormat(buffer, mix_buffer, AUDIO_S16SYS, nsamples * 4,
|
||||||
SDL_MIX_MAXVOLUME);
|
SDL_MIX_MAXVOLUME);
|
||||||
}
|
}
|
||||||
|
|
@ -288,13 +317,13 @@ static int OPL_SDL_Init(unsigned int port_base)
|
||||||
{
|
{
|
||||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Mix_OpenAudio(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize()) < 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);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -328,7 +357,7 @@ static int OPL_SDL_Init(unsigned int port_base)
|
||||||
|
|
||||||
if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
|
if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
stderr_print(
|
||||||
"OPL_SDL only supports native signed 16-bit LSB, "
|
"OPL_SDL only supports native signed 16-bit LSB, "
|
||||||
"stereo format!\n");
|
"stereo format!\n");
|
||||||
|
|
||||||
|
|
@ -341,8 +370,14 @@ static int OPL_SDL_Init(unsigned int port_base)
|
||||||
|
|
||||||
// Create the emulator structure:
|
// 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);
|
OPL3_Reset(&opl_chip, mixing_freq);
|
||||||
opl_opl3mode = 0;
|
opl_opl3mode = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
callback_mutex = SDL_CreateMutex();
|
callback_mutex = SDL_CreateMutex();
|
||||||
callback_queue_mutex = SDL_CreateMutex();
|
callback_queue_mutex = SDL_CreateMutex();
|
||||||
|
|
@ -432,10 +467,17 @@ static void WriteRegister(unsigned int reg_num, unsigned int value)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPL_REG_NEW:
|
case OPL_REG_NEW:
|
||||||
|
#if !USE_WOODY_OPL && !USE_EMU8950_OPL
|
||||||
opl_opl3mode = value & 0x01;
|
opl_opl3mode = value & 0x01;
|
||||||
|
#endif
|
||||||
default:
|
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);
|
OPL3_WriteRegBuffered(&opl_chip, reg_num, value);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -487,10 +529,10 @@ static void OPL_SDL_SetPaused(int paused)
|
||||||
opl_sdl_paused = 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);
|
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);
|
SDL_UnlockMutex(callback_queue_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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);
|
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);
|
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);
|
SDL_UnlockMutex(callback_queue_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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_Lock(void);
|
||||||
void OPL_Timer_Unlock(void);
|
void OPL_Timer_Unlock(void);
|
||||||
void OPL_Timer_SetPaused(int paused);
|
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 */
|
#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
|
if (PICO_SDK)
|
||||||
pcsound.c pcsound.h
|
add_library(pcsound INTERFACE)
|
||||||
pcsound_bsd.c
|
else()
|
||||||
pcsound_sdl.c
|
add_library(pcsound STATIC
|
||||||
pcsound_linux.c
|
pcsound.c pcsound.h
|
||||||
pcsound_win32.c
|
pcsound_bsd.c
|
||||||
pcsound_internal.h)
|
pcsound_sdl.c
|
||||||
target_include_directories(pcsound
|
pcsound_linux.c
|
||||||
INTERFACE "."
|
pcsound_win32.c
|
||||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
pcsound_internal.h)
|
||||||
target_link_libraries(pcsound SDL2::mixer)
|
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
|
*.desktop
|
||||||
*.txt
|
*.txt
|
||||||
!CMakeLists.txt
|
!CMakeLists.txt
|
||||||
|
!license.txt
|
||||||
*.appdata.xml
|
*.appdata.xml
|
||||||
tags
|
tags
|
||||||
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}")
|
add_subdirectory("${SUBDIR}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
# Common source files used by absolutely everything:
|
# 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_main.c
|
||||||
i_system.c i_system.h
|
i_system.h
|
||||||
m_argv.c m_argv.h
|
m_argv.c m_argv.h
|
||||||
m_misc.c m_misc.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
|
if (NOT PICO_SDK)
|
||||||
d_dedicated.c
|
add_library(common_sdl INTERFACE)
|
||||||
d_mode.c d_mode.h
|
target_sources(common_sdl INTERFACE
|
||||||
i_timer.c i_timer.h
|
i_system.c
|
||||||
net_common.c net_common.h
|
)
|
||||||
net_dedicated.c net_dedicated.h
|
else()
|
||||||
net_io.c net_io.h
|
add_subdirectory(pico)
|
||||||
net_packet.c net_packet.h
|
endif()
|
||||||
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_link_libraries(common INTERFACE common_${I_PLATFORM})
|
||||||
target_include_directories("${PROGRAM_PREFIX}server"
|
|
||||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
|
||||||
target_link_libraries("${PROGRAM_PREFIX}server" SDL2::SDL2main SDL2::net)
|
|
||||||
|
|
||||||
# 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
|
aes_prng.c aes_prng.h
|
||||||
d_event.c d_event.h
|
d_event.c d_event.h
|
||||||
doomkeys.h
|
doomkeys.h
|
||||||
|
|
@ -44,22 +83,17 @@ set(GAME_SOURCE_FILES
|
||||||
d_ticcmd.h
|
d_ticcmd.h
|
||||||
deh_str.c deh_str.h
|
deh_str.c deh_str.h
|
||||||
gusconf.c gusconf.h
|
gusconf.c gusconf.h
|
||||||
i_cdmus.c i_cdmus.h
|
i_cdmus.h
|
||||||
i_endoom.c i_endoom.h
|
i_endoom.h
|
||||||
i_glob.c i_glob.h
|
i_glob.h
|
||||||
i_input.c i_input.h
|
i_input.h
|
||||||
i_joystick.c i_joystick.h
|
i_joystick.h
|
||||||
i_swap.h
|
i_swap.h
|
||||||
i_midipipe.c i_midipipe.h
|
i_midipipe.h
|
||||||
i_musicpack.c
|
i_sound.h
|
||||||
i_oplmusic.c
|
i_timer.h
|
||||||
i_pcsound.c
|
i_video.h
|
||||||
i_sdlmusic.c
|
i_videohr.h
|
||||||
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
|
|
||||||
midifile.c midifile.h
|
midifile.c midifile.h
|
||||||
mus2mid.c mus2mid.h
|
mus2mid.c mus2mid.h
|
||||||
m_bbox.c m_bbox.h
|
m_bbox.c m_bbox.h
|
||||||
|
|
@ -67,18 +101,8 @@ set(GAME_SOURCE_FILES
|
||||||
m_config.c m_config.h
|
m_config.c m_config.h
|
||||||
m_controls.c m_controls.h
|
m_controls.c m_controls.h
|
||||||
m_fixed.c m_fixed.h
|
m_fixed.c m_fixed.h
|
||||||
|
|
||||||
net_client.c net_client.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
|
sha1.c sha1.h
|
||||||
memio.c memio.h
|
memio.c memio.h
|
||||||
tables.c tables.h
|
tables.c tables.h
|
||||||
|
|
@ -89,134 +113,553 @@ set(GAME_SOURCE_FILES
|
||||||
w_main.c w_main.h
|
w_main.c w_main.h
|
||||||
w_wad.c w_wad.h
|
w_wad.c w_wad.h
|
||||||
w_file.c w_file.h
|
w_file.c w_file.h
|
||||||
w_file_stdc.c
|
w_file_posix.c
|
||||||
w_file_posix.c
|
w_file_memory.c
|
||||||
w_file_win32.c
|
|
||||||
w_merge.c w_merge.h
|
|
||||||
z_zone.c z_zone.h)
|
|
||||||
|
|
||||||
|
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)
|
if(MSVC)
|
||||||
list(APPEND GAME_SOURCE_FILES
|
target_sources(game INTERFACE
|
||||||
"../win32/win_opendir.c" "../win32/win_opendir.h")
|
"../win32/win_opendir.c" "../win32/win_opendir.h")
|
||||||
endif()
|
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_defs.h
|
||||||
deh_io.c deh_io.h
|
deh_io.c deh_io.h
|
||||||
deh_main.c deh_main.h
|
deh_main.c deh_main.h
|
||||||
deh_mapping.c deh_mapping.h
|
deh_mapping.c deh_mapping.h
|
||||||
deh_text.c)
|
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})
|
if (NOT PICO_SDK)
|
||||||
set(SOURCE_FILES_WITH_DEH ${SOURCE_FILES} ${DEHACKED_SOURCE_FILES})
|
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 ("doom" IN_LIST BINARIES)
|
||||||
if(SAMPLERATE_FOUND)
|
if(WIN32)
|
||||||
list(APPEND EXTRA_LIBS samplerate::samplerate)
|
add_executable("${PROGRAM_PREFIX}doom" WIN32 "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
||||||
endif()
|
else()
|
||||||
if(PNG_FOUND)
|
add_executable("${PROGRAM_PREFIX}doom")
|
||||||
list(APPEND EXTRA_LIBS PNG::PNG)
|
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()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
function(add_doom_tiny SUFFIX RENDER_LIB)
|
||||||
add_executable("${PROGRAM_PREFIX}doom" WIN32 ${SOURCE_FILES_WITH_DEH} "${CMAKE_CURRENT_BINARY_DIR}/resource.rc")
|
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()
|
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()
|
endif()
|
||||||
|
|
||||||
target_include_directories("${PROGRAM_PREFIX}doom"
|
if (PICO_SDK)
|
||||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
add_doom_tiny("" render_newhope)
|
||||||
target_link_libraries("${PROGRAM_PREFIX}doom" doom ${EXTRA_LIBS})
|
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)
|
target_compile_definitions(doom_tiny_nost PRIVATE
|
||||||
set_target_properties("${PROGRAM_PREFIX}doom" PROPERTIES
|
# NO_USE_FINALE_CAST=1 # note this is only used by doom II but doesnt take that much more space
|
||||||
LINK_FLAGS "/MANIFEST:NO")
|
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()
|
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) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -22,7 +23,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "d_event.h"
|
#include "d_event.h"
|
||||||
|
|
||||||
|
#if !DOOM_SMALL
|
||||||
#define MAXEVENTS 64
|
#define MAXEVENTS 64
|
||||||
|
#else
|
||||||
|
#define MAXEVENTS 8
|
||||||
|
#endif
|
||||||
|
|
||||||
static event_t events[MAXEVENTS];
|
static event_t events[MAXEVENTS];
|
||||||
static int eventhead;
|
static int eventhead;
|
||||||
|
|
|
||||||
40
src/d_iwad.c
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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" },
|
{ "tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution" },
|
||||||
{ "doom.wad", doom, retail, "Doom" },
|
{ "doom.wad", doom, retail, "Doom" },
|
||||||
{ "doom1.wad", doom, shareware, "Doom Shareware" },
|
{ "doom1.wad", doom, shareware, "Doom Shareware" },
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ "chex.wad", pack_chex, retail, "Chex Quest" },
|
{ "chex.wad", pack_chex, retail, "Chex Quest" },
|
||||||
{ "hacx.wad", pack_hacx, commercial, "Hacx" },
|
{ "hacx.wad", pack_hacx, commercial, "Hacx" },
|
||||||
|
#endif
|
||||||
{ "freedm.wad", doom2, commercial, "FreeDM" },
|
{ "freedm.wad", doom2, commercial, "FreeDM" },
|
||||||
{ "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" },
|
{ "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" },
|
||||||
{ "freedoom1.wad", doom, retail, "Freedoom: Phase 1" },
|
{ "freedoom1.wad", doom, retail, "Freedoom: Phase 1" },
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ "heretic.wad", heretic, retail, "Heretic" },
|
{ "heretic.wad", heretic, retail, "Heretic" },
|
||||||
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
|
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
|
||||||
{ "hexen.wad", hexen, commercial, "Hexen" },
|
{ "hexen.wad", hexen, commercial, "Hexen" },
|
||||||
//{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME
|
//{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME
|
||||||
{ "strife1.wad", strife, commercial, "Strife" },
|
{ "strife1.wad", strife, commercial, "Strife" },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Array of locations to search for IWAD files
|
// Array of locations to search for IWAD files
|
||||||
|
|
@ -442,6 +447,7 @@ static void CheckDOSDefaults(void)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
// Returns true if the specified path is a path to a file
|
// Returns true if the specified path is a path to a file
|
||||||
// of the specified name.
|
// of the specified name.
|
||||||
|
|
||||||
|
|
@ -529,7 +535,7 @@ static GameMission_t IdentifyIWADByName(const char *name, int mask)
|
||||||
GameMission_t mission;
|
GameMission_t mission;
|
||||||
|
|
||||||
name = M_BaseName(name);
|
name = M_BaseName(name);
|
||||||
mission = none;
|
mission = mission_none;
|
||||||
|
|
||||||
for (i=0; i<arrlen(iwads); ++i)
|
for (i=0; i<arrlen(iwads); ++i)
|
||||||
{
|
{
|
||||||
|
|
@ -696,9 +702,11 @@ static void BuildIWADDirList(void)
|
||||||
// Look in the current directory. Doom always does this.
|
// Look in the current directory. Doom always does this.
|
||||||
AddIWADDir(".");
|
AddIWADDir(".");
|
||||||
|
|
||||||
|
#if !NO_USE_ARGS
|
||||||
// Next check the directory where the executable is located. This might
|
// Next check the directory where the executable is located. This might
|
||||||
// be different from the current directory.
|
// be different from the current directory.
|
||||||
AddIWADDir(M_DirName(myargv[0]));
|
AddIWADDir(M_DirName(myargv[0]));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add DOOMWADDIR if it is in the environment
|
// Add DOOMWADDIR if it is in the environment
|
||||||
env = getenv("DOOMWADDIR");
|
env = getenv("DOOMWADDIR");
|
||||||
|
|
@ -835,7 +843,10 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
|
||||||
//
|
//
|
||||||
// @arg <file>
|
// @arg <file>
|
||||||
//
|
//
|
||||||
|
#if USE_MEMORY_WAD
|
||||||
|
return "<memory>.wad";
|
||||||
|
#else
|
||||||
|
#if !NO_USE_ARGS
|
||||||
iwadparm = M_CheckParmWithArgs("-iwad", 1);
|
iwadparm = M_CheckParmWithArgs("-iwad", 1);
|
||||||
|
|
||||||
if (iwadparm)
|
if (iwadparm)
|
||||||
|
|
@ -854,6 +865,7 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
|
||||||
*mission = IdentifyIWADByName(result, mask);
|
*mission = IdentifyIWADByName(result, mask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
// Search through the list and look for an IWAD
|
// 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);
|
result = SearchDirectoryForIWAD(iwad_dirs[i], mask, mission);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -950,6 +963,9 @@ const char *D_SuggestIWADName(GameMission_t mission, GameMode_t mode)
|
||||||
return "unknown.wad";
|
return "unknown.wad";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -957,7 +973,7 @@ const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
||||||
for (i = 0; i < arrlen(iwads); ++i)
|
for (i = 0; i < arrlen(iwads); ++i)
|
||||||
{
|
{
|
||||||
if (iwads[i].mission == mission
|
if (iwads[i].mission == mission
|
||||||
&& (mode == indetermined || iwads[i].mode == mode))
|
&& (mode == indetermined || iwads[i].mode == mode))
|
||||||
{
|
{
|
||||||
return iwads[i].description;
|
return iwads[i].description;
|
||||||
}
|
}
|
||||||
|
|
@ -966,3 +982,21 @@ const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
|
||||||
return "Unknown game?";
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -21,6 +22,7 @@
|
||||||
|
|
||||||
#include "d_mode.h"
|
#include "d_mode.h"
|
||||||
|
|
||||||
|
#if !DOOM_ONLY
|
||||||
#define IWAD_MASK_DOOM ((1 << doom) \
|
#define IWAD_MASK_DOOM ((1 << doom) \
|
||||||
| (1 << doom2) \
|
| (1 << doom2) \
|
||||||
| (1 << pack_tnt) \
|
| (1 << pack_tnt) \
|
||||||
|
|
@ -30,6 +32,12 @@
|
||||||
#define IWAD_MASK_HERETIC (1 << heretic)
|
#define IWAD_MASK_HERETIC (1 << heretic)
|
||||||
#define IWAD_MASK_HEXEN (1 << hexen)
|
#define IWAD_MASK_HEXEN (1 << hexen)
|
||||||
#define IWAD_MASK_STRIFE (1 << strife)
|
#define IWAD_MASK_STRIFE (1 << strife)
|
||||||
|
#else
|
||||||
|
#define IWAD_MASK_DOOM ((1 << doom) \
|
||||||
|
| (1 << doom2) \
|
||||||
|
| (1 << pack_tnt) \
|
||||||
|
| (1 << pack_plut))
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct
|
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_SuggestIWADName(GameMission_t mission, GameMode_t mode);
|
||||||
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode);
|
const char *D_SuggestGameName(GameMission_t mission, GameMode_t mode);
|
||||||
void D_CheckCorrectIWAD(GameMission_t mission);
|
void D_CheckCorrectIWAD(GameMission_t mission);
|
||||||
|
#if DOOM_TINY
|
||||||
|
GameMission_t IdentifyIWADByName(const char *name);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
594
src/d_loop.c
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -38,13 +39,24 @@
|
||||||
#include "net_sdl.h"
|
#include "net_sdl.h"
|
||||||
#include "net_loop.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
|
// Maximum time that we wait in TryRunTics() for netgame data to be
|
||||||
// received before we bail out and render a frame anyway.
|
// received before we bail out and render a frame anyway.
|
||||||
|
|
@ -61,7 +73,7 @@ typedef struct
|
||||||
// from all players.
|
// 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).
|
// 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.
|
// 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.
|
// Used for original sync code.
|
||||||
|
|
||||||
static int skiptics = 0;
|
static int skiptics = 0;
|
||||||
|
|
||||||
// Reduce the bandwidth needed by sampling game input less and transmitting
|
// Reduce the bandwidth needed by sampling game input less and transmitting
|
||||||
// less. If ticdup is 2, sample half normal, 3 = one third normal, etc.
|
// 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.
|
// Amount to offset the timer for game sync.
|
||||||
|
|
||||||
fixed_t offsetms;
|
fixed_t offsetms;
|
||||||
|
|
||||||
// Use new client syncronisation code
|
// 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.
|
// Callback functions for loop code.
|
||||||
|
|
||||||
|
|
@ -120,74 +141,85 @@ static int player_class;
|
||||||
|
|
||||||
// 35 fps clock adjusted by offsetms milliseconds
|
// 35 fps clock adjusted by offsetms milliseconds
|
||||||
|
|
||||||
static int GetAdjustedTime(void)
|
static int GetAdjustedTime(void) {
|
||||||
{
|
|
||||||
int time_ms;
|
int time_ms;
|
||||||
|
|
||||||
time_ms = I_GetTimeMS();
|
time_ms = I_GetTimeMS();
|
||||||
|
|
||||||
if (new_sync)
|
if (new_sync) {
|
||||||
{
|
// Use the adjustments from net_client.c only if we are
|
||||||
// Use the adjustments from net_client.c only if we are
|
// using the new sync mode.
|
||||||
// using the new sync mode.
|
|
||||||
|
|
||||||
time_ms += (offsetms / FRACUNIT);
|
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)
|
static boolean BuildNewTic(void) {
|
||||||
{
|
int gameticdiv;
|
||||||
int gameticdiv;
|
|
||||||
ticcmd_t cmd;
|
ticcmd_t cmd;
|
||||||
|
|
||||||
gameticdiv = gametic/ticdup;
|
gameticdiv = gametic / ticdup;
|
||||||
|
|
||||||
I_StartTic ();
|
I_StartTic();
|
||||||
loop_interface->ProcessEvents();
|
loop_interface->ProcessEvents();
|
||||||
|
|
||||||
// Always run the menu
|
// Always run the menu
|
||||||
|
|
||||||
loop_interface->RunMenu();
|
loop_interface->RunMenu();
|
||||||
|
|
||||||
if (drone)
|
if (drone) {
|
||||||
{
|
|
||||||
// In drone mode, do not generate any ticcmds.
|
// In drone mode, do not generate any ticcmds.
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_sync)
|
if (new_sync) {
|
||||||
{
|
// If playing single player, do not allow tics to buffer
|
||||||
// If playing single player, do not allow tics to buffer
|
// up very far
|
||||||
// up very far
|
|
||||||
|
|
||||||
if (!net_client_connected && maketic - gameticdiv > 2)
|
if (!net_client_connected && maketic - gameticdiv > 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Never go more than ~200ms ahead
|
// Never go more than ~200ms ahead
|
||||||
|
|
||||||
if (maketic - gameticdiv > 8)
|
if (maketic - gameticdiv >= MIN(8, BACKUPTICS))
|
||||||
return false;
|
return false;
|
||||||
}
|
} else {
|
||||||
else
|
if (maketic - gameticdiv >= 5)
|
||||||
{
|
return false;
|
||||||
if (maketic - gameticdiv >= 5)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf ("mk:%i ",maketic);
|
//printf ("mk:%i ",maketic);
|
||||||
memset(&cmd, 0, sizeof(ticcmd_t));
|
memset(&cmd, 0, sizeof(ticcmd_t));
|
||||||
loop_interface->BuildTiccmd(&cmd, maketic);
|
loop_interface->BuildTiccmd(&cmd, maketic);
|
||||||
|
|
||||||
|
#if !NO_USE_NET
|
||||||
if (net_client_connected)
|
if (net_client_connected)
|
||||||
{
|
{
|
||||||
NET_CL_SendTiccmd(&cmd, maketic);
|
NET_CL_SendTiccmd(&cmd, maketic);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd;
|
ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd;
|
||||||
|
#if !DOOM_TINY
|
||||||
ticdata[maketic % BACKUPTICS].ingame[localplayer] = true;
|
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;
|
++maketic;
|
||||||
|
|
||||||
|
|
@ -199,13 +231,12 @@ static boolean BuildNewTic(void)
|
||||||
// Builds ticcmds for console player,
|
// Builds ticcmds for console player,
|
||||||
// sends out a packet
|
// sends out a packet
|
||||||
//
|
//
|
||||||
int lasttime;
|
int lasttime;
|
||||||
|
|
||||||
void NetUpdate (void)
|
void NetUpdate(void) {
|
||||||
{
|
|
||||||
int nowtime;
|
int nowtime;
|
||||||
int newtics;
|
int newtics;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// If we are running with singletics (timing a demo), this
|
// If we are running with singletics (timing a demo), this
|
||||||
// is all done separately.
|
// is all done separately.
|
||||||
|
|
@ -215,8 +246,10 @@ void NetUpdate (void)
|
||||||
|
|
||||||
// Run network subsystems
|
// Run network subsystems
|
||||||
|
|
||||||
|
#if !NO_USE_NET
|
||||||
NET_CL_Run();
|
NET_CL_Run();
|
||||||
NET_SV_Run();
|
NET_SV_Run();
|
||||||
|
#endif
|
||||||
|
|
||||||
// check time
|
// check time
|
||||||
nowtime = GetAdjustedTime() / ticdup;
|
nowtime = GetAdjustedTime() / ticdup;
|
||||||
|
|
@ -224,40 +257,35 @@ void NetUpdate (void)
|
||||||
|
|
||||||
lasttime = nowtime;
|
lasttime = nowtime;
|
||||||
|
|
||||||
if (skiptics <= newtics)
|
if (skiptics <= newtics) {
|
||||||
{
|
|
||||||
newtics -= skiptics;
|
newtics -= skiptics;
|
||||||
skiptics = 0;
|
skiptics = 0;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
skiptics -= newtics;
|
skiptics -= newtics;
|
||||||
newtics = 0;
|
newtics = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build new ticcmds for console player
|
// build new ticcmds for console player
|
||||||
|
|
||||||
for (i=0 ; i<newtics ; i++)
|
for (i = 0; i < newtics; i++) {
|
||||||
{
|
if (!BuildNewTic()) {
|
||||||
if (!BuildNewTic())
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void D_Disconnected(void)
|
static void D_Disconnected(void) {
|
||||||
{
|
#if !USE_PICO_NET
|
||||||
// In drone mode, the game cannot continue once disconnected.
|
// In drone mode, the game cannot continue once disconnected.
|
||||||
|
|
||||||
if (drone)
|
if (drone) {
|
||||||
{
|
|
||||||
I_Error("Disconnected from server in drone mode.");
|
I_Error("Disconnected from server in drone mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// disconnected from server
|
// disconnected from server
|
||||||
|
|
||||||
printf("Disconnected from server.\n");
|
printf("Disconnected from server.\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -265,28 +293,30 @@ static void D_Disconnected(void)
|
||||||
// available.
|
// available.
|
||||||
//
|
//
|
||||||
|
|
||||||
void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
|
void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask) {
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// Disconnected from server?
|
// Disconnected from server?
|
||||||
|
|
||||||
if (ticcmds == NULL && players_mask == NULL)
|
if (ticcmds == NULL && players_mask == NULL) {
|
||||||
{
|
|
||||||
D_Disconnected();
|
D_Disconnected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||||
{
|
if (!drone && i == localplayer) {
|
||||||
if (!drone && i == localplayer)
|
|
||||||
{
|
|
||||||
// This is us. Don't overwrite it.
|
// This is us. Don't overwrite it.
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
|
ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
|
||||||
|
#if !USE_PICO_NET
|
||||||
|
#if !DOOM_TINY
|
||||||
ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
|
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.
|
// Called after the screen is set but before the game starts running.
|
||||||
//
|
//
|
||||||
|
|
||||||
void D_StartGameLoop(void)
|
void D_StartGameLoop(void) {
|
||||||
{
|
|
||||||
lasttime = GetAdjustedTime() / ticdup;
|
lasttime = GetAdjustedTime() / ticdup;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block until the game start message is received from the server.
|
// Block until the game start message is received from the server.
|
||||||
//
|
//
|
||||||
|
#if !NO_USE_NET
|
||||||
|
|
||||||
static void BlockUntilStart(net_gamesettings_t *settings,
|
static void BlockUntilStart(net_gamesettings_t *settings,
|
||||||
netgame_startup_callback_t callback)
|
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)
|
boolean D_InitNetGame(net_connect_data_t *connect_data)
|
||||||
{
|
{
|
||||||
|
|
@ -528,17 +470,19 @@ void D_QuitNetGame (void)
|
||||||
NET_SV_Shutdown();
|
NET_SV_Shutdown();
|
||||||
NET_CL_Disconnect();
|
NET_CL_Disconnect();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int GetLowTic(void)
|
static int GetLowTic(void) {
|
||||||
{
|
|
||||||
int lowtic;
|
int lowtic;
|
||||||
|
|
||||||
lowtic = maketic;
|
lowtic = maketic;
|
||||||
|
|
||||||
if (net_client_connected)
|
if (net_client_connected) {
|
||||||
{
|
#if USE_PICO_NET
|
||||||
if (drone || recvtic < lowtic)
|
recvtic = piconet_maybe_recv_tic(recvtic);
|
||||||
{
|
if (!net_client_connected) piconet_stop();
|
||||||
|
#endif
|
||||||
|
if (drone || recvtic < lowtic) {
|
||||||
lowtic = recvtic;
|
lowtic = recvtic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -550,8 +494,7 @@ static int frameon;
|
||||||
static int frameskip[4];
|
static int frameskip[4];
|
||||||
static int oldnettics;
|
static int oldnettics;
|
||||||
|
|
||||||
static void OldNetSync(void)
|
static void OldNetSync(void) {
|
||||||
{
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int keyplayer = -1;
|
int keyplayer = -1;
|
||||||
|
|
||||||
|
|
@ -560,30 +503,23 @@ static void OldNetSync(void)
|
||||||
// ideally maketic should be 1 - 3 tics above lowtic
|
// ideally maketic should be 1 - 3 tics above lowtic
|
||||||
// if we are consistantly slower, speed up time
|
// if we are consistantly slower, speed up time
|
||||||
|
|
||||||
for (i=0 ; i<NET_MAXPLAYERS ; i++)
|
for (i = 0; i < NET_MAXPLAYERS; i++) {
|
||||||
{
|
if (local_playeringame[i]) {
|
||||||
if (local_playeringame[i])
|
|
||||||
{
|
|
||||||
keyplayer = i;
|
keyplayer = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyplayer < 0)
|
if (keyplayer < 0) {
|
||||||
{
|
|
||||||
// If there are no players, we can never advance anyway
|
// If there are no players, we can never advance anyway
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localplayer == keyplayer)
|
if (localplayer == keyplayer) {
|
||||||
{
|
|
||||||
// the key player does not adapt
|
// the key player does not adapt
|
||||||
}
|
} else {
|
||||||
else
|
if (maketic <= recvtic) {
|
||||||
{
|
|
||||||
if (maketic <= recvtic)
|
|
||||||
{
|
|
||||||
lasttime--;
|
lasttime--;
|
||||||
// printf ("-");
|
// printf ("-");
|
||||||
}
|
}
|
||||||
|
|
@ -591,8 +527,7 @@ static void OldNetSync(void)
|
||||||
frameskip[frameon & 3] = oldnettics > recvtic;
|
frameskip[frameon & 3] = oldnettics > recvtic;
|
||||||
oldnettics = maketic;
|
oldnettics = maketic;
|
||||||
|
|
||||||
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
|
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) {
|
||||||
{
|
|
||||||
skiptics = 1;
|
skiptics = 1;
|
||||||
// printf ("+");
|
// printf ("+");
|
||||||
}
|
}
|
||||||
|
|
@ -601,18 +536,15 @@ static void OldNetSync(void)
|
||||||
|
|
||||||
// Returns true if there are players in the game:
|
// Returns true if there are players in the game:
|
||||||
|
|
||||||
static boolean PlayersInGame(void)
|
static boolean PlayersInGame(void) {
|
||||||
{
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
// If we are connected to a server, check if there are any players
|
// If we are connected to a server, check if there are any players
|
||||||
// in the game.
|
// in the game.
|
||||||
|
|
||||||
if (net_client_connected)
|
if (net_client_connected) {
|
||||||
{
|
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
|
||||||
{
|
|
||||||
result = result || local_playeringame[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,
|
// Whether single or multi-player, unless we are running as a drone,
|
||||||
// we are in the game.
|
// we are in the game.
|
||||||
|
|
||||||
if (!drone)
|
if (!drone) {
|
||||||
{
|
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,13 +562,11 @@ static boolean PlayersInGame(void)
|
||||||
// When using ticdup, certain values must be cleared out when running
|
// When using ticdup, certain values must be cleared out when running
|
||||||
// the duplicate ticcmds.
|
// the duplicate ticcmds.
|
||||||
|
|
||||||
static void TicdupSquash(ticcmd_set_t *set)
|
static void TicdupSquash(ticcmd_set_t *set) {
|
||||||
{
|
|
||||||
ticcmd_t *cmd;
|
ticcmd_t *cmd;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < NET_MAXPLAYERS ; ++i)
|
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||||
{
|
|
||||||
cmd = &set->cmds[i];
|
cmd = &set->cmds[i];
|
||||||
cmd->chatchar = 0;
|
cmd->chatchar = 0;
|
||||||
if (cmd->buttons & BT_SPECIAL)
|
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
|
// When running in single player mode, clear all the ingame[] array
|
||||||
// except the local player.
|
// except the local player.
|
||||||
|
|
||||||
static void SinglePlayerClear(ticcmd_set_t *set)
|
static void SinglePlayerClear(ticcmd_set_t *set) {
|
||||||
{
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < NET_MAXPLAYERS; ++i)
|
for (i = 0; i < NET_MAXPLAYERS; ++i) {
|
||||||
{
|
if (i != localplayer) {
|
||||||
if (i != localplayer)
|
#if !DOOM_TINY
|
||||||
{
|
|
||||||
set->ingame[i] = false;
|
set->ingame[i] = false;
|
||||||
|
#else
|
||||||
|
set->cmds[i].ingame = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -665,16 +595,18 @@ static void SinglePlayerClear(ticcmd_set_t *set)
|
||||||
// TryRunTics
|
// TryRunTics
|
||||||
//
|
//
|
||||||
|
|
||||||
void TryRunTics (void)
|
void TryRunTics(void) {
|
||||||
{
|
int i;
|
||||||
int i;
|
int lowtic;
|
||||||
int lowtic;
|
int entertic;
|
||||||
int entertic;
|
|
||||||
static int oldentertics;
|
static int oldentertics;
|
||||||
int realtics;
|
int realtics;
|
||||||
int availabletics;
|
int availabletics;
|
||||||
int counts;
|
int counts;
|
||||||
|
|
||||||
|
#if PICO_BUILD
|
||||||
|
DEBUG_PINS_SET(tics, 2);
|
||||||
|
#endif
|
||||||
// get real tics
|
// get real tics
|
||||||
entertic = I_GetTime() / ticdup;
|
entertic = I_GetTime() / ticdup;
|
||||||
realtics = entertic - oldentertics;
|
realtics = entertic - oldentertics;
|
||||||
|
|
@ -683,30 +615,24 @@ void TryRunTics (void)
|
||||||
// in singletics mode, run a single tic every time this function
|
// in singletics mode, run a single tic every time this function
|
||||||
// is called.
|
// is called.
|
||||||
|
|
||||||
if (singletics)
|
if (singletics) {
|
||||||
{
|
|
||||||
BuildNewTic();
|
BuildNewTic();
|
||||||
}
|
} else {
|
||||||
else
|
NetUpdate();
|
||||||
{
|
|
||||||
NetUpdate ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lowtic = GetLowTic();
|
lowtic = GetLowTic();
|
||||||
|
|
||||||
availabletics = lowtic - gametic/ticdup;
|
availabletics = lowtic - gametic / ticdup;
|
||||||
|
|
||||||
// decide how many tics to run
|
// decide how many tics to run
|
||||||
|
|
||||||
if (new_sync)
|
if (new_sync) {
|
||||||
{
|
counts = availabletics;
|
||||||
counts = availabletics;
|
} else {
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// decide how many tics to run
|
// decide how many tics to run
|
||||||
if (realtics < availabletics-1)
|
if (realtics < availabletics - 1)
|
||||||
counts = realtics+1;
|
counts = realtics + 1;
|
||||||
else if (realtics < availabletics)
|
else if (realtics < availabletics)
|
||||||
counts = realtics;
|
counts = realtics;
|
||||||
else
|
else
|
||||||
|
|
@ -715,33 +641,29 @@ void TryRunTics (void)
|
||||||
if (counts < 1)
|
if (counts < 1)
|
||||||
counts = 1;
|
counts = 1;
|
||||||
|
|
||||||
if (net_client_connected)
|
if (net_client_connected) {
|
||||||
{
|
|
||||||
OldNetSync();
|
OldNetSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (counts < 1)
|
if (counts < 1)
|
||||||
counts = 1;
|
counts = 1;
|
||||||
|
|
||||||
// wait for new tics if needed
|
// wait for new tics if needed
|
||||||
while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
|
while (!PlayersInGame() || lowtic < gametic / ticdup + counts) {
|
||||||
{
|
NetUpdate();
|
||||||
NetUpdate ();
|
|
||||||
|
|
||||||
lowtic = GetLowTic();
|
lowtic = GetLowTic();
|
||||||
|
|
||||||
if (lowtic < gametic/ticdup)
|
if (lowtic < gametic / ticdup)
|
||||||
I_Error ("TryRunTics: lowtic < gametic");
|
I_Error("TryRunTics: lowtic < gametic");
|
||||||
|
|
||||||
// Still no tics to run? Sleep until some are available.
|
// 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
|
// If we're in a netgame, we might spin forever waiting for
|
||||||
// new network data to be received. So don't stay in here
|
// new network data to be received. So don't stay in here
|
||||||
// forever - give the menu a chance to work.
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -750,43 +672,86 @@ void TryRunTics (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the count * ticdup dics
|
// run the count * ticdup dics
|
||||||
while (counts--)
|
while (counts--) {
|
||||||
{
|
#if PICO_BUILD
|
||||||
|
DEBUG_PINS_SET(tics, 1);
|
||||||
|
#endif
|
||||||
ticcmd_set_t *set;
|
ticcmd_set_t *set;
|
||||||
|
|
||||||
if (!PlayersInGame())
|
if (!PlayersInGame()) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set = &ticdata[(gametic / ticdup) % BACKUPTICS];
|
set = &ticdata[(gametic / ticdup) % BACKUPTICS];
|
||||||
|
#if DEBUG_CONSISTENCY
|
||||||
if (!net_client_connected)
|
if (netgame) printf("apply tic %d\n", gametic);
|
||||||
{
|
#endif
|
||||||
|
if (!net_client_connected) {
|
||||||
SinglePlayerClear(set);
|
SinglePlayerClear(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0 ; i<ticdup ; i++)
|
for (i = 0; i < ticdup; i++) {
|
||||||
{
|
if (gametic / ticdup > lowtic)
|
||||||
if (gametic/ticdup > lowtic)
|
I_Error("gametic>lowtic");
|
||||||
I_Error ("gametic>lowtic");
|
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
memcpy(local_playeringame, set->ingame, sizeof(local_playeringame));
|
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);
|
//#define DUMP_TICS PICO_BUILD
|
||||||
gametic++;
|
#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);
|
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;
|
loop_interface = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -794,8 +759,7 @@ void D_RegisterLoopCallbacks(loop_interface_t *i)
|
||||||
#include "m_misc.h"
|
#include "m_misc.h"
|
||||||
#include "w_wad.h"
|
#include "w_wad.h"
|
||||||
|
|
||||||
static boolean StrictDemos(void)
|
static boolean StrictDemos(void) {
|
||||||
{
|
|
||||||
//!
|
//!
|
||||||
// @category demo
|
// @category demo
|
||||||
//
|
//
|
||||||
|
|
@ -812,10 +776,8 @@ static boolean StrictDemos(void)
|
||||||
// this extension (no extensions are allowed if -strictdemos is given
|
// this extension (no extensions are allowed if -strictdemos is given
|
||||||
// on the command line). A warning is shown on the console using the
|
// on the command line). A warning is shown on the console using the
|
||||||
// provided string describing the non-vanilla expansion.
|
// provided string describing the non-vanilla expansion.
|
||||||
boolean D_NonVanillaRecord(boolean conditional, const char *feature)
|
boolean D_NonVanillaRecord(boolean conditional, const char *feature) {
|
||||||
{
|
if (!conditional || StrictDemos()) {
|
||||||
if (!conditional || StrictDemos())
|
|
||||||
{
|
|
||||||
return false;
|
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
|
// Returns true if the given lump number corresponds to data from a .lmp
|
||||||
// file, as opposed to a WAD.
|
// file, as opposed to a WAD.
|
||||||
static boolean IsDemoFile(int lumpnum)
|
static boolean IsDemoFile(int lumpnum) {
|
||||||
{
|
#if !USE_MEMMAP_ONLY
|
||||||
char *lower;
|
char *lower;
|
||||||
boolean result;
|
boolean result;
|
||||||
|
|
||||||
|
|
@ -839,6 +801,9 @@ static boolean IsDemoFile(int lumpnum)
|
||||||
free(lower);
|
free(lower);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the provided conditional value is true, we're trying to play back
|
// 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.
|
// demo that comes from a .lmp file, not a .wad file.
|
||||||
// - Before proceeding, a warning is shown to the user on the console.
|
// - Before proceeding, a warning is shown to the user on the console.
|
||||||
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
||||||
const char *feature)
|
const char *feature) {
|
||||||
{
|
if (!conditional || StrictDemos()) {
|
||||||
if (!conditional || StrictDemos())
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsDemoFile(lumpnum))
|
if (!IsDemoFile(lumpnum)) {
|
||||||
{
|
|
||||||
printf("Warning: WAD contains demo with a non-vanilla extension "
|
printf("Warning: WAD contains demo with a non-vanilla extension "
|
||||||
"(%s)\n", feature);
|
"(%s)\n", feature);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -871,3 +833,105 @@ boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
||||||
return true;
|
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) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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);
|
netgame_startup_callback_t callback);
|
||||||
|
|
||||||
extern boolean singletics;
|
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.
|
// Check if it is permitted to record a demo with a non-vanilla feature.
|
||||||
boolean D_NonVanillaRecord(boolean conditional, const char *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,
|
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
|
||||||
const char *feature);
|
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
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
35
src/d_mode.c
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -23,26 +24,30 @@
|
||||||
// Valid game mode/mission combinations, with the number of
|
// Valid game mode/mission combinations, with the number of
|
||||||
// episodes/maps for each.
|
// episodes/maps for each.
|
||||||
|
|
||||||
static struct
|
static const struct
|
||||||
{
|
{
|
||||||
GameMission_t mission;
|
GameMission_t mission;
|
||||||
GameMode_t mode;
|
GameMode_t mode;
|
||||||
int episode;
|
isb_int8_t episode;
|
||||||
int map;
|
isb_int8_t map;
|
||||||
} valid_modes[] = {
|
} valid_modes[] = {
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ pack_chex, retail, 1, 5 },
|
{ pack_chex, retail, 1, 5 },
|
||||||
|
#endif
|
||||||
{ doom, shareware, 1, 9 },
|
{ doom, shareware, 1, 9 },
|
||||||
{ doom, registered, 3, 9 },
|
{ doom, registered, 3, 9 },
|
||||||
{ doom, retail, 4, 9 },
|
{ doom, retail, 4, 9 },
|
||||||
{ doom2, commercial, 1, 32 },
|
{ doom2, commercial, 1, 32 },
|
||||||
{ pack_tnt, commercial, 1, 32 },
|
{ pack_tnt, commercial, 1, 32 },
|
||||||
{ pack_plut, commercial, 1, 32 },
|
{ pack_plut, commercial, 1, 32 },
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ pack_hacx, commercial, 1, 32 },
|
{ pack_hacx, commercial, 1, 32 },
|
||||||
{ heretic, shareware, 1, 9 },
|
{ heretic, shareware, 1, 9 },
|
||||||
{ heretic, registered, 3, 9 },
|
{ heretic, registered, 3, 9 },
|
||||||
{ heretic, retail, 5, 9 },
|
{ heretic, retail, 5, 9 },
|
||||||
{ hexen, commercial, 1, 60 },
|
{ hexen, commercial, 1, 60 },
|
||||||
{ strife, commercial, 1, 34 },
|
{ strife, commercial, 1, 34 },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check that a gamemode+gamemission received over the network is valid.
|
// 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
|
// Hacks for Heretic secret episodes
|
||||||
|
|
||||||
if (mission == heretic)
|
if (gamemission_is_heretic(mission))
|
||||||
{
|
{
|
||||||
if (mode == retail && episode == 6)
|
if (mode == retail && episode == 6)
|
||||||
{
|
{
|
||||||
|
|
@ -120,20 +125,26 @@ static struct {
|
||||||
GameMission_t mission;
|
GameMission_t mission;
|
||||||
GameVersion_t version;
|
GameVersion_t version;
|
||||||
} valid_versions[] = {
|
} valid_versions[] = {
|
||||||
|
#if !DOOM_TINY
|
||||||
{ doom, exe_doom_1_2 },
|
{ doom, exe_doom_1_2 },
|
||||||
{ doom, exe_doom_1_666 },
|
{ doom, exe_doom_1_666 },
|
||||||
{ doom, exe_doom_1_7 },
|
{ doom, exe_doom_1_7 },
|
||||||
{ doom, exe_doom_1_8 },
|
{ doom, exe_doom_1_8 },
|
||||||
|
#endif
|
||||||
{ doom, exe_doom_1_9 },
|
{ doom, exe_doom_1_9 },
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ doom, exe_hacx },
|
{ doom, exe_hacx },
|
||||||
|
#endif
|
||||||
{ doom, exe_ultimate },
|
{ doom, exe_ultimate },
|
||||||
{ doom, exe_final },
|
{ doom, exe_final },
|
||||||
{ doom, exe_final2 },
|
{ doom, exe_final2 },
|
||||||
|
#if !DOOM_ONLY
|
||||||
{ doom, exe_chex },
|
{ doom, exe_chex },
|
||||||
{ heretic, exe_heretic_1_3 },
|
{ heretic, exe_heretic_1_3 },
|
||||||
{ hexen, exe_hexen_1_1 },
|
{ hexen, exe_hexen_1_1 },
|
||||||
{ strife, exe_strife_1_2 },
|
{ strife, exe_strife_1_2 },
|
||||||
{ strife, exe_strife_1_31 },
|
{ strife, exe_strife_1_31 },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version)
|
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.
|
// All Doom variants can use the Doom versions.
|
||||||
|
|
||||||
if (mission == doom2 || mission == pack_plut || mission == pack_tnt
|
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;
|
mission = doom;
|
||||||
}
|
}
|
||||||
|
|
@ -167,17 +178,21 @@ boolean D_IsEpisodeMap(GameMission_t mission)
|
||||||
switch (mission)
|
switch (mission)
|
||||||
{
|
{
|
||||||
case doom:
|
case doom:
|
||||||
|
#if !DOOM_ONLY
|
||||||
case heretic:
|
case heretic:
|
||||||
case pack_chex:
|
case pack_chex:
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case none:
|
case mission_none:
|
||||||
case hexen:
|
|
||||||
case doom2:
|
case doom2:
|
||||||
case pack_hacx:
|
|
||||||
case pack_tnt:
|
case pack_tnt:
|
||||||
case pack_plut:
|
case pack_plut:
|
||||||
|
#if !DOOM_ONLY
|
||||||
|
case hexen:
|
||||||
|
case pack_hacx:
|
||||||
case strife:
|
case strife:
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -187,7 +202,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
||||||
{
|
{
|
||||||
switch (mission)
|
switch (mission)
|
||||||
{
|
{
|
||||||
case none:
|
case mission_none:
|
||||||
default:
|
default:
|
||||||
return "none";
|
return "none";
|
||||||
case doom:
|
case doom:
|
||||||
|
|
@ -198,6 +213,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
||||||
return "tnt";
|
return "tnt";
|
||||||
case pack_plut:
|
case pack_plut:
|
||||||
return "plutonia";
|
return "plutonia";
|
||||||
|
#if !DOOM_ONLY
|
||||||
case pack_hacx:
|
case pack_hacx:
|
||||||
return "hacx";
|
return "hacx";
|
||||||
case pack_chex:
|
case pack_chex:
|
||||||
|
|
@ -208,6 +224,7 @@ const char *D_GameMissionString(GameMission_t mission)
|
||||||
return "hexen";
|
return "hexen";
|
||||||
case strife:
|
case strife:
|
||||||
return "strife";
|
return "strife";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
23
src/d_mode.h
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -30,14 +31,25 @@ typedef enum
|
||||||
doom2, // Doom 2
|
doom2, // Doom 2
|
||||||
pack_tnt, // Final Doom: TNT: Evilution
|
pack_tnt, // Final Doom: TNT: Evilution
|
||||||
pack_plut, // Final Doom: The Plutonia Experiment
|
pack_plut, // Final Doom: The Plutonia Experiment
|
||||||
|
#if !DOOM_ONLY
|
||||||
pack_chex, // Chex Quest (modded doom)
|
pack_chex, // Chex Quest (modded doom)
|
||||||
pack_hacx, // Hacx (modded doom2)
|
pack_hacx, // Hacx (modded doom2)
|
||||||
heretic, // Heretic
|
heretic, // Heretic
|
||||||
hexen, // Hexen
|
hexen, // Hexen
|
||||||
strife, // Strife
|
strife, // Strife
|
||||||
|
#endif
|
||||||
|
|
||||||
none
|
mission_none
|
||||||
} GameMission_t;
|
} 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
|
// 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
|
// 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_7, // Doom 1.7/1.7a: "
|
||||||
exe_doom_1_8, // Doom 1.8: "
|
exe_doom_1_8, // Doom 1.8: "
|
||||||
exe_doom_1_9, // Doom 1.9: "
|
exe_doom_1_9, // Doom 1.9: "
|
||||||
|
#if !DOOM_ONLY
|
||||||
exe_hacx, // Hacx
|
exe_hacx, // Hacx
|
||||||
|
#endif
|
||||||
exe_ultimate, // Ultimate Doom (retail)
|
exe_ultimate, // Ultimate Doom (retail)
|
||||||
exe_final, // Final Doom
|
exe_final, // Final Doom
|
||||||
exe_final2, // Final Doom (alternate exe)
|
exe_final2, // Final Doom (alternate exe)
|
||||||
|
#if !DOOM_ONLY
|
||||||
exe_chex, // Chex Quest executable (based on Final Doom)
|
exe_chex, // Chex Quest executable (based on Final Doom)
|
||||||
|
|
||||||
exe_heretic_1_3, // Heretic 1.3
|
exe_heretic_1_3, // Heretic 1.3
|
||||||
|
|
@ -72,7 +87,13 @@ typedef enum
|
||||||
exe_hexen_1_1, // Hexen 1.1
|
exe_hexen_1_1, // Hexen 1.1
|
||||||
exe_strife_1_2, // Strife v1.2
|
exe_strife_1_2, // Strife v1.2
|
||||||
exe_strife_1_31 // Strife v1.31
|
exe_strife_1_31 // Strife v1.31
|
||||||
|
#endif
|
||||||
} GameVersion_t;
|
} 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?
|
// What IWAD variant are we using?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 1993-2008 Raven Software
|
// Copyright(C) 1993-2008 Raven Software
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -39,18 +40,24 @@ typedef struct
|
||||||
// villsa [STRIFE] according to the asm,
|
// villsa [STRIFE] according to the asm,
|
||||||
// consistancy is a short, not a byte
|
// consistancy is a short, not a byte
|
||||||
byte consistancy; // checks for net game
|
byte consistancy; // checks for net game
|
||||||
|
#if !DOOM_ONLY
|
||||||
|
|
||||||
// villsa - Strife specific:
|
// villsa - Strife specific:
|
||||||
|
|
||||||
byte buttons2;
|
byte buttons2;
|
||||||
int inventory;
|
int inventory;
|
||||||
|
|
||||||
// Heretic/Hexen specific:
|
// Heretic/Hexen specific:
|
||||||
|
|
||||||
byte lookfly; // look/fly up/down/centering
|
byte lookfly; // look/fly up/down/centering
|
||||||
byte arti; // artitype_t to use
|
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
|
#endif
|
||||||
|
|
|
||||||
13
src/deh_io.c
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -28,6 +29,7 @@
|
||||||
#include "deh_defs.h"
|
#include "deh_defs.h"
|
||||||
#include "deh_io.h"
|
#include "deh_io.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
DEH_INPUT_FILE,
|
DEH_INPUT_FILE,
|
||||||
|
|
@ -66,12 +68,12 @@ static deh_context_t *DEH_NewContext(void)
|
||||||
{
|
{
|
||||||
deh_context_t *context;
|
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
|
// Initial read buffer size of 128 bytes
|
||||||
|
|
||||||
context->readbuffer_size = 128;
|
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->linenum = 0;
|
||||||
context->last_was_newline = true;
|
context->last_was_newline = true;
|
||||||
|
|
||||||
|
|
@ -120,7 +122,11 @@ deh_context_t *DEH_OpenLump(int lumpnum)
|
||||||
context->input_buffer_pos = 0;
|
context->input_buffer_pos = 0;
|
||||||
|
|
||||||
context->filename = malloc(9);
|
context->filename = malloc(9);
|
||||||
|
#if !USE_MEMMAP_ONLY
|
||||||
M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9);
|
M_StringCopy(context->filename, lumpinfo[lumpnum]->name, 9);
|
||||||
|
#else
|
||||||
|
M_StringCopy(context->filename, "<unknown>", 9);
|
||||||
|
#endif
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -213,7 +219,7 @@ static void IncreaseReadBuffer(deh_context_t *context)
|
||||||
int newbuffer_size;
|
int newbuffer_size;
|
||||||
|
|
||||||
newbuffer_size = context->readbuffer_size * 2;
|
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);
|
memcpy(newbuffer, context->readbuffer, context->readbuffer_size);
|
||||||
|
|
||||||
|
|
@ -335,3 +341,4 @@ boolean DEH_HadError(deh_context_t *context)
|
||||||
return context->had_error;
|
return context->had_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -31,6 +32,7 @@
|
||||||
#include "deh_io.h"
|
#include "deh_io.h"
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
extern deh_section_t *deh_section_types[];
|
extern deh_section_t *deh_section_types[];
|
||||||
extern const char *deh_signatures[];
|
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.
|
// Check the command line for -deh argument, and others.
|
||||||
void DEH_ParseCommandLine(void)
|
void DEH_ParseCommandLine(void)
|
||||||
{
|
{
|
||||||
|
#if !NO_USE_ARGS
|
||||||
char *filename;
|
char *filename;
|
||||||
int p;
|
int p;
|
||||||
|
|
||||||
|
|
@ -511,5 +514,7 @@ void DEH_ParseCommandLine(void)
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
|
|
||||||
#include "deh_mapping.h"
|
#include "deh_mapping.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context,
|
static deh_mapping_entry_t *GetMappingEntryByName(deh_context_t *context,
|
||||||
deh_mapping_t *mapping,
|
deh_mapping_t *mapping,
|
||||||
char *name)
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
|
|
||||||
#include "z_zone.h"
|
#include "z_zone.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char *from_text;
|
char *from_text;
|
||||||
|
|
@ -103,7 +105,7 @@ static void InitHashTable(void)
|
||||||
hash_table_entries = 0;
|
hash_table_entries = 0;
|
||||||
hash_table_length = 16;
|
hash_table_length = 16;
|
||||||
hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length,
|
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);
|
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_length *= 2;
|
||||||
hash_table = Z_Malloc(sizeof(deh_substitution_t *) * hash_table_length,
|
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);
|
memset(hash_table, 0, sizeof(deh_substitution_t *) * hash_table_length);
|
||||||
|
|
||||||
// go through the old table and insert all the old entries
|
// 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);
|
Z_Free(sub->to_text);
|
||||||
|
|
||||||
len = strlen(to_text) + 1;
|
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);
|
memcpy(sub->to_text, to_text, len);
|
||||||
}
|
}
|
||||||
else
|
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.
|
// We need to create our own duplicates of the provided strings.
|
||||||
len = strlen(from_text) + 1;
|
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);
|
memcpy(sub->from_text, from_text, len);
|
||||||
|
|
||||||
len = strlen(to_text) + 1;
|
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);
|
memcpy(sub->to_text, to_text, len);
|
||||||
|
|
||||||
DEH_AddToHashtable(sub);
|
DEH_AddToHashtable(sub);
|
||||||
|
|
@ -431,3 +433,4 @@ void DEH_snprintf(char *buffer, size_t len, const char *fmt, ...)
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -24,19 +25,18 @@
|
||||||
|
|
||||||
// Used to do dehacked text substitutions throughout the program
|
// Used to do dehacked text substitutions throughout the program
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
const char *DEH_String(const char *s) PRINTF_ARG_ATTR(1);
|
const char *DEH_String(const char *s) PRINTF_ARG_ATTR(1);
|
||||||
void DEH_printf(const char *fmt, ...) PRINTF_ATTR(1, 2);
|
void DEH_printf(const char *fmt, ...) PRINTF_ATTR(1, 2);
|
||||||
void DEH_fprintf(FILE *fstream, const char *fmt, ...) PRINTF_ATTR(2, 3);
|
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_snprintf(char *buffer, size_t len, const char *fmt, ...) PRINTF_ATTR(3, 4);
|
||||||
void DEH_AddStringReplacement(const char *from_text, const char *to_text);
|
void DEH_AddStringReplacement(const char *from_text, const char *to_text);
|
||||||
|
#else
|
||||||
|
|
||||||
#if 0
|
|
||||||
// Static macro versions of the functions above
|
// Static macro versions of the functions above
|
||||||
|
|
||||||
#define DEH_String(x) (x)
|
#define DEH_String(x) (x)
|
||||||
#define DEH_printf printf
|
#define DEH_printf printf
|
||||||
#define DEH_fprintf fprintf
|
#define DEH_fprintf(x,...) stderr_print(__VA_ARGS__)
|
||||||
#define DEH_snprintf snprintf
|
#define DEH_snprintf snprintf
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
#include "deh_io.h"
|
#include "deh_io.h"
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
// Given a string length, find the maximum length of a
|
// Given a string length, find the maximum length of a
|
||||||
// string that can replace it.
|
// string that can replace it.
|
||||||
|
|
||||||
|
|
@ -111,3 +113,4 @@ deh_section_t deh_section_text =
|
||||||
NULL,
|
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
|
am_map.c am_map.h
|
||||||
deh_ammo.c
|
deh_ammo.c
|
||||||
deh_bexstr.c
|
deh_bexstr.c
|
||||||
|
|
@ -21,8 +23,10 @@ add_library(doom STATIC
|
||||||
dstrings.c dstrings.h
|
dstrings.c dstrings.h
|
||||||
d_textur.h
|
d_textur.h
|
||||||
d_think.h
|
d_think.h
|
||||||
f_finale.c f_finale.h
|
f_finale.h
|
||||||
f_wipe.c f_wipe.h
|
f_finale.c
|
||||||
|
f_wipe.c
|
||||||
|
f_wipe.h
|
||||||
g_game.c g_game.h
|
g_game.c g_game.h
|
||||||
hu_lib.c hu_lib.h
|
hu_lib.c hu_lib.h
|
||||||
hu_stuff.c hu_stuff.h
|
hu_stuff.c hu_stuff.h
|
||||||
|
|
@ -50,6 +54,7 @@ add_library(doom STATIC
|
||||||
p_tick.c p_tick.h
|
p_tick.c p_tick.h
|
||||||
p_user.c
|
p_user.c
|
||||||
r_bsp.c r_bsp.h
|
r_bsp.c r_bsp.h
|
||||||
|
r_data_whd.c
|
||||||
r_data.c r_data.h
|
r_data.c r_data.h
|
||||||
r_defs.h
|
r_defs.h
|
||||||
r_draw.c r_draw.h
|
r_draw.c r_draw.h
|
||||||
|
|
@ -67,5 +72,9 @@ add_library(doom STATIC
|
||||||
st_stuff.c st_stuff.h
|
st_stuff.c st_stuff.h
|
||||||
wi_stuff.c wi_stuff.h)
|
wi_stuff.c wi_stuff.h)
|
||||||
|
|
||||||
target_include_directories(doom PRIVATE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../")
|
target_include_directories(doom INTERFACE "../" "${CMAKE_CURRENT_BINARY_DIR}/../../")
|
||||||
target_link_libraries(doom SDL2::SDL2 SDL2::mixer SDL2::net)
|
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) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -143,7 +144,7 @@ typedef struct
|
||||||
// starting from the middle.
|
// starting from the middle.
|
||||||
//
|
//
|
||||||
#define R ((8*PLAYERRADIUS)/7)
|
#define R ((8*PLAYERRADIUS)/7)
|
||||||
mline_t player_arrow[] = {
|
static const mline_t player_arrow[] = {
|
||||||
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
||||||
{ { R, 0 }, { R-R/2, R/4 } }, // ----->
|
{ { R, 0 }, { R-R/2, R/4 } }, // ----->
|
||||||
{ { R, 0 }, { R-R/2, -R/4 } },
|
{ { R, 0 }, { R-R/2, -R/4 } },
|
||||||
|
|
@ -155,7 +156,7 @@ mline_t player_arrow[] = {
|
||||||
#undef R
|
#undef R
|
||||||
|
|
||||||
#define R ((8*PLAYERRADIUS)/7)
|
#define R ((8*PLAYERRADIUS)/7)
|
||||||
mline_t cheat_player_arrow[] = {
|
static const mline_t cheat_player_arrow[] = {
|
||||||
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
{ { -R+R/8, 0 }, { R, 0 } }, // -----
|
||||||
{ { R, 0 }, { R-R/2, R/6 } }, // ----->
|
{ { R, 0 }, { R-R/2, R/6 } }, // ----->
|
||||||
{ { 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
|
#undef R
|
||||||
|
|
||||||
#define R (FRACUNIT)
|
//#define R (FRACUNIT)
|
||||||
mline_t triangle_guy[] = {
|
//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)(.867*R ), (fixed_t)(-.5*R) } },
|
||||||
{ { (fixed_t)(.867*R ), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)(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) } }
|
// { { (fixed_t)(0 ), (fixed_t)(R ) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } }
|
||||||
};
|
//};
|
||||||
#undef R
|
//#undef R
|
||||||
|
|
||||||
#define R (FRACUNIT)
|
#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)(-.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)(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) } }
|
{ { (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 isb_int8_t cheating = 0;
|
||||||
static int grid = 0;
|
static isb_int8_t grid = 0;
|
||||||
|
|
||||||
static int leveljuststarted = 1; // kluge until AM_LevelInit() is called
|
static int leveljuststarted = 1; // kluge until AM_LevelInit() is called
|
||||||
|
|
||||||
boolean automapactive = false;
|
boolean automapactive = false;
|
||||||
static int finit_width = SCREENWIDTH;
|
#define finit_width SCREENWIDTH
|
||||||
static int finit_height = SCREENHEIGHT - ST_HEIGHT;
|
#define finit_height (SCREENHEIGHT - ST_HEIGHT)
|
||||||
|
|
||||||
// location of window on screen
|
// location of window on screen
|
||||||
static int f_x;
|
static int f_x;
|
||||||
|
|
@ -211,7 +212,11 @@ static int f_y;
|
||||||
static int f_w;
|
static int f_w;
|
||||||
static int f_h;
|
static int f_h;
|
||||||
|
|
||||||
|
#if DOOM_TINY
|
||||||
|
#define lightlev 0 // unused anyway
|
||||||
|
#else
|
||||||
static int lightlev; // used for funky strobing effect
|
static int lightlev; // used for funky strobing effect
|
||||||
|
#endif
|
||||||
static pixel_t* fb; // pseudo-frame buffer
|
static pixel_t* fb; // pseudo-frame buffer
|
||||||
static int amclock;
|
static int amclock;
|
||||||
|
|
||||||
|
|
@ -259,11 +264,13 @@ static fixed_t scale_ftom;
|
||||||
|
|
||||||
static player_t *plr; // the player represented by an arrow
|
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 mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
|
||||||
static int markpointnum = 0; // next point to be assigned
|
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);
|
cheatseq_t cheat_amap = CHEAT("iddt", 0);
|
||||||
|
|
||||||
|
|
@ -328,8 +335,8 @@ void AM_restoreScaleAndLoc(void)
|
||||||
m_x = old_m_x;
|
m_x = old_m_x;
|
||||||
m_y = old_m_y;
|
m_y = old_m_y;
|
||||||
} else {
|
} else {
|
||||||
m_x = plr->mo->x - m_w/2;
|
m_x = plr->mo->xy.x - m_w/2;
|
||||||
m_y = plr->mo->y - m_h/2;
|
m_y = plr->mo->xy.y - m_h/2;
|
||||||
}
|
}
|
||||||
m_x2 = m_x + m_w;
|
m_x2 = m_x + m_w;
|
||||||
m_y2 = m_y + m_h;
|
m_y2 = m_y + m_h;
|
||||||
|
|
@ -365,17 +372,22 @@ void AM_findMinMaxBoundaries(void)
|
||||||
|
|
||||||
for (i=0;i<numvertexes;i++)
|
for (i=0;i<numvertexes;i++)
|
||||||
{
|
{
|
||||||
if (vertexes[i].x < min_x)
|
if (vertex_x_raw(&vertexes[i]) < min_x)
|
||||||
min_x = vertexes[i].x;
|
min_x = vertex_x_raw(&vertexes[i]);
|
||||||
else if (vertexes[i].x > max_x)
|
else if (vertex_x_raw(&vertexes[i]) > max_x)
|
||||||
max_x = vertexes[i].x;
|
max_x = vertex_x_raw(&vertexes[i]);
|
||||||
|
|
||||||
if (vertexes[i].y < min_y)
|
if (vertex_y_raw(&vertexes[i]) < min_y)
|
||||||
min_y = vertexes[i].y;
|
min_y = vertex_y_raw(&vertexes[i]);
|
||||||
else if (vertexes[i].y > max_y)
|
else if (vertex_y_raw(&vertexes[i]) > max_y)
|
||||||
max_y = vertexes[i].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_w = max_x - min_x;
|
||||||
max_h = max_y - min_y;
|
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 };
|
static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 };
|
||||||
|
|
||||||
automapactive = true;
|
automapactive = true;
|
||||||
fb = I_VideoBuffer;
|
|
||||||
|
|
||||||
f_oldloc.x = INT_MAX;
|
f_oldloc.x = INT_MAX;
|
||||||
amclock = 0;
|
amclock = 0;
|
||||||
|
#if !DOOM_TINY
|
||||||
lightlev = 0;
|
lightlev = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
m_paninc.x = m_paninc.y = 0;
|
m_paninc.x = m_paninc.y = 0;
|
||||||
ftom_zoommul = FRACUNIT;
|
ftom_zoommul = FRACUNIT;
|
||||||
|
|
@ -461,8 +474,8 @@ void AM_initVariables(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_x = plr->mo->x - m_w/2;
|
m_x = plr->mo->xy.x - m_w/2;
|
||||||
m_y = plr->mo->y - m_h/2;
|
m_y = plr->mo->xy.y - m_h/2;
|
||||||
AM_changeWindowLoc();
|
AM_changeWindowLoc();
|
||||||
|
|
||||||
// for saving & restoring
|
// for saving & restoring
|
||||||
|
|
@ -486,14 +499,17 @@ void AM_loadPics(void)
|
||||||
|
|
||||||
for (i=0;i<10;i++)
|
for (i=0;i<10;i++)
|
||||||
{
|
{
|
||||||
|
#if !USE_WHD
|
||||||
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
||||||
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
|
marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AM_unloadPics(void)
|
void AM_unloadPics(void)
|
||||||
{
|
{
|
||||||
|
#if !DOOM_TINY
|
||||||
int i;
|
int i;
|
||||||
char namebuf[9];
|
char namebuf[9];
|
||||||
|
|
||||||
|
|
@ -502,6 +518,7 @@ void AM_unloadPics(void)
|
||||||
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
DEH_snprintf(namebuf, 9, "AMMNUM%d", i);
|
||||||
W_ReleaseLumpName(namebuf);
|
W_ReleaseLumpName(namebuf);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AM_clearMarks(void)
|
void AM_clearMarks(void)
|
||||||
|
|
@ -555,7 +572,7 @@ void AM_Stop (void)
|
||||||
//
|
//
|
||||||
void AM_Start (void)
|
void AM_Start (void)
|
||||||
{
|
{
|
||||||
static int lastlevel = -1, lastepisode = -1;
|
static isb_int8_t lastlevel = -1, lastepisode = -1;
|
||||||
|
|
||||||
if (!stopped) AM_Stop();
|
if (!stopped) AM_Stop();
|
||||||
stopped = false;
|
stopped = false;
|
||||||
|
|
@ -605,6 +622,7 @@ AM_Responder
|
||||||
|
|
||||||
rc = false;
|
rc = false;
|
||||||
|
|
||||||
|
#if !NO_USE_JOYSTICK
|
||||||
if (ev->type == ev_joystick && joybautomap >= 0
|
if (ev->type == ev_joystick && joybautomap >= 0
|
||||||
&& (ev->data1 & (1 << joybautomap)) != 0)
|
&& (ev->data1 & (1 << joybautomap)) != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -624,6 +642,7 @@ AM_Responder
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!automapactive)
|
if (!automapactive)
|
||||||
{
|
{
|
||||||
|
|
@ -784,14 +803,14 @@ void AM_changeWindowScale(void)
|
||||||
void AM_doFollowPlayer(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_x = FTOM(MTOF(plr->mo->xy.x)) - m_w/2;
|
||||||
m_y = FTOM(MTOF(plr->mo->y)) - m_h/2;
|
m_y = FTOM(MTOF(plr->mo->xy.y)) - m_h/2;
|
||||||
m_x2 = m_x + m_w;
|
m_x2 = m_x + m_w;
|
||||||
m_y2 = m_y + m_h;
|
m_y2 = m_y + m_h;
|
||||||
f_oldloc.x = plr->mo->x;
|
f_oldloc.x = plr->mo->xy.x;
|
||||||
f_oldloc.y = plr->mo->y;
|
f_oldloc.y = plr->mo->xy.y;
|
||||||
|
|
||||||
// m_x = FTOM(MTOF(plr->mo->x - m_w/2));
|
// m_x = FTOM(MTOF(plr->mo->x - m_w/2));
|
||||||
// m_y = FTOM(MTOF(plr->mo->y - m_h/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)
|
void AM_updateLightLev(void)
|
||||||
{
|
{
|
||||||
static int nexttic = 0;
|
static int nexttic = 0;
|
||||||
|
|
@ -821,7 +841,7 @@ void AM_updateLightLev(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Updates on Game Tick
|
// Updates on Game Tick
|
||||||
|
|
@ -856,6 +876,9 @@ void AM_Ticker (void)
|
||||||
//
|
//
|
||||||
void AM_clearFB(int color)
|
void AM_clearFB(int color)
|
||||||
{
|
{
|
||||||
|
#if PICODOOM_RENDER_NEWHOPE
|
||||||
|
assert(f_h <= MAIN_VIEWHEIGHT);
|
||||||
|
#endif
|
||||||
memset(fb, color, f_w*f_h*sizeof(*fb));
|
memset(fb, color, f_w*f_h*sizeof(*fb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1017,7 +1040,8 @@ AM_drawFline
|
||||||
register int ax;
|
register int ax;
|
||||||
register int ay;
|
register int ay;
|
||||||
register int d;
|
register int d;
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
static int fuck = 0;
|
static int fuck = 0;
|
||||||
|
|
||||||
// For debugging only
|
// For debugging only
|
||||||
|
|
@ -1029,6 +1053,7 @@ AM_drawFline
|
||||||
DEH_fprintf(stderr, "fuck %d \r", fuck++);
|
DEH_fprintf(stderr, "fuck %d \r", fuck++);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc)
|
#define PUTDOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc)
|
||||||
|
|
||||||
|
|
@ -1148,37 +1173,38 @@ void AM_drawWalls(void)
|
||||||
int i;
|
int i;
|
||||||
static mline_t l;
|
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.x = vertex_x(line_v1(li));
|
||||||
l.a.y = lines[i].v1->y;
|
l.a.y = vertex_y(line_v1(li));
|
||||||
l.b.x = lines[i].v2->x;
|
l.b.x = vertex_x(line_v2(li));
|
||||||
l.b.y = lines[i].v2->y;
|
l.b.y = vertex_y(line_v2(li));
|
||||||
if (cheating || (lines[i].flags & ML_MAPPED))
|
if (cheating || line_is_mapped(li))
|
||||||
{
|
{
|
||||||
if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
|
if ((line_flags(li) & LINE_NEVERSEE) && !cheating)
|
||||||
continue;
|
continue;
|
||||||
if (!lines[i].backsector)
|
if (!line_backsector(li))
|
||||||
{
|
{
|
||||||
AM_drawMline(&l, WALLCOLORS+lightlev);
|
AM_drawMline(&l, WALLCOLORS+lightlev);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (lines[i].special == 39)
|
if (line_special(li) == 39)
|
||||||
{ // teleporters
|
{ // teleporters
|
||||||
AM_drawMline(&l, WALLCOLORS+WALLRANGE/2);
|
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);
|
if (cheating) AM_drawMline(&l, SECRETWALLCOLORS + lightlev);
|
||||||
else AM_drawMline(&l, WALLCOLORS+lightlev);
|
else AM_drawMline(&l, WALLCOLORS+lightlev);
|
||||||
}
|
}
|
||||||
else if (lines[i].backsector->floorheight
|
else if (line_backsector(li)->rawfloorheight
|
||||||
!= lines[i].frontsector->floorheight) {
|
!= line_frontsector(li)->rawfloorheight) {
|
||||||
AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
|
AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
|
||||||
}
|
}
|
||||||
else if (lines[i].backsector->ceilingheight
|
else if (line_backsector(li)->rawceilingheight
|
||||||
!= lines[i].frontsector->ceilingheight) {
|
!= line_frontsector(li)->rawceilingheight) {
|
||||||
AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
|
AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
|
||||||
}
|
}
|
||||||
else if (cheating) {
|
else if (cheating) {
|
||||||
|
|
@ -1188,7 +1214,7 @@ void AM_drawWalls(void)
|
||||||
}
|
}
|
||||||
else if (plr->powers[pw_allmap])
|
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;
|
fixed_t tmpx;
|
||||||
|
|
||||||
tmpx =
|
tmpx =
|
||||||
FixedMul(*x,finecosine[a>>ANGLETOFINESHIFT])
|
FixedMul(*x,finecosine(a>>ANGLETOFINESHIFT))
|
||||||
- FixedMul(*y,finesine[a>>ANGLETOFINESHIFT]);
|
- FixedMul(*y,finesine(a>>ANGLETOFINESHIFT));
|
||||||
|
|
||||||
*y =
|
*y =
|
||||||
FixedMul(*x,finesine[a>>ANGLETOFINESHIFT])
|
FixedMul(*x,finesine(a>>ANGLETOFINESHIFT))
|
||||||
+ FixedMul(*y,finecosine[a>>ANGLETOFINESHIFT]);
|
+ FixedMul(*y,finecosine(a>>ANGLETOFINESHIFT));
|
||||||
|
|
||||||
*x = tmpx;
|
*x = tmpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AM_drawLineCharacter
|
AM_drawLineCharacter
|
||||||
( mline_t* lineguy,
|
( const mline_t* lineguy,
|
||||||
int lineguylines,
|
int lineguylines,
|
||||||
fixed_t scale,
|
fixed_t scale,
|
||||||
angle_t angle,
|
angle_t angle,
|
||||||
|
|
@ -1279,11 +1305,11 @@ void AM_drawPlayers(void)
|
||||||
if (cheating)
|
if (cheating)
|
||||||
AM_drawLineCharacter
|
AM_drawLineCharacter
|
||||||
(cheat_player_arrow, arrlen(cheat_player_arrow), 0,
|
(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
|
else
|
||||||
AM_drawLineCharacter
|
AM_drawLineCharacter
|
||||||
(player_arrow, arrlen(player_arrow), 0, plr->mo->angle,
|
(player_arrow, arrlen(player_arrow), 0, mobj_angle(plr->mo),
|
||||||
WHITE, plr->mo->x, plr->mo->y);
|
WHITE, plr->mo->xy.x, plr->mo->xy.y);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1304,8 +1330,8 @@ void AM_drawPlayers(void)
|
||||||
color = their_colors[their_color];
|
color = their_colors[their_color];
|
||||||
|
|
||||||
AM_drawLineCharacter
|
AM_drawLineCharacter
|
||||||
(player_arrow, arrlen(player_arrow), 0, p->mo->angle,
|
(player_arrow, arrlen(player_arrow), 0, mobj_angle(p->mo),
|
||||||
color, p->mo->x, p->mo->y);
|
color, p->mo->xy.x, p->mo->xy.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1320,13 +1346,13 @@ AM_drawThings
|
||||||
|
|
||||||
for (i=0;i<numsectors;i++)
|
for (i=0;i<numsectors;i++)
|
||||||
{
|
{
|
||||||
t = sectors[i].thinglist;
|
t = shortptr_to_mobj(sectors[i].thinglist);
|
||||||
while (t)
|
while (t)
|
||||||
{
|
{
|
||||||
AM_drawLineCharacter
|
AM_drawLineCharacter
|
||||||
(thintriangle_guy, arrlen(thintriangle_guy),
|
(thintriangle_guy, arrlen(thintriangle_guy),
|
||||||
16<<FRACBITS, t->angle, colors+lightlev, t->x, t->y);
|
16<<FRACBITS, mobj_angle(t), colors+lightlev, t->xy.x, t->xy.y);
|
||||||
t = t->snext;
|
t = mobj_snext(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1345,8 +1371,14 @@ void AM_drawMarks(void)
|
||||||
h = 6; // because something's wrong with the wad, i guess
|
h = 6; // because something's wrong with the wad, i guess
|
||||||
fx = CXMTOF(markpoints[i].x);
|
fx = CXMTOF(markpoints[i].x);
|
||||||
fy = CYMTOF(markpoints[i].y);
|
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)
|
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)
|
void AM_Drawer (void)
|
||||||
{
|
{
|
||||||
if (!automapactive) return;
|
if (!automapactive) return;
|
||||||
|
fb = I_VideoBuffer;
|
||||||
|
|
||||||
AM_clearFB(BACKGROUND);
|
AM_clearFB(BACKGROUND);
|
||||||
if (grid)
|
if (grid)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -34,7 +35,7 @@
|
||||||
// atkstate, i.e. attack/fire/hit frame
|
// atkstate, i.e. attack/fire/hit frame
|
||||||
// flashstate, muzzle flash
|
// flashstate, muzzle flash
|
||||||
//
|
//
|
||||||
weaponinfo_t weaponinfo[NUMWEAPONS] =
|
should_be_const weaponinfo_t weaponinfo[NUMWEAPONS] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
// fist
|
// fist
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -28,14 +29,14 @@
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
ammotype_t ammo;
|
ammotype_t ammo;
|
||||||
int upstate;
|
isb_int16_t upstate;
|
||||||
int downstate;
|
isb_int16_t downstate;
|
||||||
int readystate;
|
isb_int16_t readystate;
|
||||||
int atkstate;
|
isb_int16_t atkstate;
|
||||||
int flashstate;
|
isb_int16_t flashstate;
|
||||||
|
|
||||||
} weaponinfo_t;
|
} weaponinfo_t;
|
||||||
|
|
||||||
extern weaponinfo_t weaponinfo[NUMWEAPONS];
|
extern should_be_const weaponinfo_t weaponinfo[NUMWEAPONS];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -44,7 +45,8 @@ void D_StartTitle (void);
|
||||||
//
|
//
|
||||||
|
|
||||||
extern gameaction_t gameaction;
|
extern gameaction_t gameaction;
|
||||||
|
extern const char* pagename;
|
||||||
|
extern boolean advancedemo;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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)
|
static void RunTic(ticcmd_t *cmds, boolean *ingame)
|
||||||
{
|
{
|
||||||
extern boolean advancedemo;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
// Check for player quits.
|
// Check for player quits.
|
||||||
|
|
@ -143,7 +143,11 @@ static void SaveGameSettings(net_gamesettings_t *settings)
|
||||||
settings->episode = startepisode;
|
settings->episode = startepisode;
|
||||||
settings->map = startmap;
|
settings->map = startmap;
|
||||||
settings->skill = startskill;
|
settings->skill = startskill;
|
||||||
|
#if !DOOM_TINY
|
||||||
settings->loadgame = startloadgame;
|
settings->loadgame = startloadgame;
|
||||||
|
#else
|
||||||
|
settings->loadgame = -1;
|
||||||
|
#endif
|
||||||
settings->gameversion = gameversion;
|
settings->gameversion = gameversion;
|
||||||
settings->nomonsters = nomonsters;
|
settings->nomonsters = nomonsters;
|
||||||
settings->fast_monsters = fastparm;
|
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.
|
// Run as the left screen in three screen mode.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
if (M_CheckParm("-left") > 0)
|
if (M_CheckParm("-left") > 0)
|
||||||
{
|
{
|
||||||
viewangleoffset = ANG90;
|
viewangleoffset = ANG90;
|
||||||
|
|
@ -183,16 +188,17 @@ static void InitConnectData(net_connect_data_t *connect_data)
|
||||||
viewangleoffset = ANG270;
|
viewangleoffset = ANG270;
|
||||||
connect_data->drone = true;
|
connect_data->drone = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
//
|
//
|
||||||
// Connect data
|
// Connect data
|
||||||
//
|
//
|
||||||
|
|
||||||
// Game type fields:
|
// Game type fields:
|
||||||
|
|
||||||
connect_data->gamemode = gamemode;
|
connect_data->_gamemode = gamemode;
|
||||||
connect_data->gamemission = gamemission;
|
connect_data->gamemission = gamemission;
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
// Are we recording a demo? Possibly set lowres turn mode
|
// Are we recording a demo? Possibly set lowres turn mode
|
||||||
|
|
||||||
connect_data->lowres_turn = (M_ParmExists("-record")
|
connect_data->lowres_turn = (M_ParmExists("-record")
|
||||||
|
|
@ -200,9 +206,14 @@ static void InitConnectData(net_connect_data_t *connect_data)
|
||||||
|| M_ParmExists("-shorttics");
|
|| M_ParmExists("-shorttics");
|
||||||
|
|
||||||
// Read checksums of our WAD directory and dehacked information
|
// Read checksums of our WAD directory and dehacked information
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !NO_USE_CHECKSUM
|
||||||
W_Checksum(connect_data->wad_sha1sum);
|
W_Checksum(connect_data->wad_sha1sum);
|
||||||
DEH_Checksum(connect_data->deh_sha1sum);
|
DEH_Checksum(connect_data->deh_sha1sum);
|
||||||
|
#else
|
||||||
|
// leave as garbage for now
|
||||||
|
#endif
|
||||||
|
|
||||||
// Are we playing with the Freedoom IWAD?
|
// Are we playing with the Freedoom IWAD?
|
||||||
|
|
||||||
|
|
@ -214,6 +225,7 @@ void D_ConnectNetGame(void)
|
||||||
net_connect_data_t connect_data;
|
net_connect_data_t connect_data;
|
||||||
|
|
||||||
InitConnectData(&connect_data);
|
InitConnectData(&connect_data);
|
||||||
|
#if !NO_USE_NET
|
||||||
netgame = D_InitNetGame(&connect_data);
|
netgame = D_InitNetGame(&connect_data);
|
||||||
|
|
||||||
//!
|
//!
|
||||||
|
|
@ -224,10 +236,13 @@ void D_ConnectNetGame(void)
|
||||||
// demos.
|
// demos.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if !NO_USE_ARGS
|
||||||
if (M_CheckParm("-solo-net") > 0)
|
if (M_CheckParm("-solo-net") > 0)
|
||||||
{
|
{
|
||||||
netgame = true;
|
netgame = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -249,6 +264,7 @@ void D_CheckNetGame (void)
|
||||||
D_StartNetGame(&settings, NULL);
|
D_StartNetGame(&settings, NULL);
|
||||||
LoadGameSettings(&settings);
|
LoadGameSettings(&settings);
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
|
DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
|
||||||
startskill, deathmatch, startmap, startepisode);
|
startskill, deathmatch, startmap, startepisode);
|
||||||
|
|
||||||
|
|
@ -274,5 +290,6 @@ void D_CheckNetGame (void)
|
||||||
printf(".\n");
|
printf(".\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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.
|
// Is wp_nochange if not changing.
|
||||||
weapontype_t pendingweapon;
|
weapontype_t pendingweapon;
|
||||||
|
|
||||||
int weaponowned[NUMWEAPONS];
|
isb_int8_t weaponowned[NUMWEAPONS];
|
||||||
int ammo[NUMAMMO];
|
int ammo[NUMAMMO]; // int because pointed to by st_ stuff
|
||||||
int maxammo[NUMAMMO];
|
int maxammo[NUMAMMO]; // int because pointed to by st_ stuff
|
||||||
|
|
||||||
// True if button down last tic.
|
// True if button down last tic.
|
||||||
int attackdown;
|
isb_int8_t attackdown;
|
||||||
int usedown;
|
isb_int8_t usedown;
|
||||||
|
|
||||||
// Bit flags, for cheats and debug.
|
// Bit flags, for cheats and debug.
|
||||||
// See cheat_t, above.
|
// See cheat_t, above.
|
||||||
|
|
@ -142,21 +143,21 @@ typedef struct player_s
|
||||||
mobj_t* attacker;
|
mobj_t* attacker;
|
||||||
|
|
||||||
// So gun flashes light up areas.
|
// So gun flashes light up areas.
|
||||||
int extralight;
|
isb_int8_t extralight;
|
||||||
|
|
||||||
// Current PLAYPAL, ???
|
// Current PLAYPAL, ???
|
||||||
// can be set to REDCOLORMAP for pain, etc.
|
// can be set to REDCOLORMAP for pain, etc.
|
||||||
int fixedcolormap;
|
isb_int8_t fixedcolormap;
|
||||||
|
|
||||||
// Player skin colorshift,
|
// Player skin colorshift,
|
||||||
// 0-3 for which color to draw player.
|
// 0-3 for which color to draw player.
|
||||||
int colormap;
|
isb_int8_t colormap;
|
||||||
|
|
||||||
// Overlay view sprites (gun, etc).
|
|
||||||
pspdef_t psprites[NUMPSPRITES];
|
|
||||||
|
|
||||||
// True if secret level has been done.
|
// True if secret level has been done.
|
||||||
boolean didsecret;
|
boolean didsecret;
|
||||||
|
|
||||||
|
// Overlay view sprites (gun, etc).
|
||||||
|
pspdef_t psprites[NUMPSPRITES];
|
||||||
|
|
||||||
} player_t;
|
} player_t;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -51,17 +52,64 @@ typedef union
|
||||||
// Historically, "think_t" is yet another
|
// Historically, "think_t" is yet another
|
||||||
// function pointer to a routine to handle
|
// function pointer to a routine to handle
|
||||||
// an actor.
|
// 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;
|
||||||
|
|
||||||
|
// linked list of actors.
|
||||||
// Doubly linked list of actors.
|
|
||||||
typedef struct thinker_s
|
typedef struct thinker_s
|
||||||
{
|
{
|
||||||
struct thinker_s* prev;
|
// todo graham this can be an array index into an active thinker array
|
||||||
struct thinker_s* next;
|
shortptr_t /*struct thinker_s*/ sp_next;
|
||||||
think_t function;
|
think_t function;
|
||||||
|
#if DOOM_SMALL
|
||||||
} thinker_t;
|
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) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
#include "p_local.h"
|
#include "p_local.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
static void *DEH_AmmoStart(deh_context_t *context, char *line)
|
static void *DEH_AmmoStart(deh_context_t *context, char *line)
|
||||||
{
|
{
|
||||||
int ammo_number = 0;
|
int ammo_number = 0;
|
||||||
|
|
@ -101,3 +103,4 @@ deh_section_t deh_section_ammo =
|
||||||
DEH_AmmoSHA1Hash,
|
DEH_AmmoSHA1Hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
// Copyright(C) 2014 Fabian Greffrath
|
// Copyright(C) 2014 Fabian Greffrath
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
#include "dstrings.h"
|
#include "dstrings.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *macro;
|
const char *macro;
|
||||||
const char *string;
|
const char *string;
|
||||||
|
|
@ -374,3 +376,4 @@ deh_section_t deh_section_bexstr =
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
#include "am_map.h"
|
#include "am_map.h"
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
@ -144,3 +146,4 @@ deh_section_t deh_section_cheat =
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,6 +20,8 @@
|
||||||
#include "deh_defs.h"
|
#include "deh_defs.h"
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
|
|
||||||
const char *deh_signatures[] =
|
const char *deh_signatures[] =
|
||||||
{
|
{
|
||||||
"Patch File for DeHackEd v2.3",
|
"Patch File for DeHackEd v2.3",
|
||||||
|
|
@ -66,3 +69,4 @@ deh_section_t *deh_section_types[] =
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
#include "deh_mapping.h"
|
#include "deh_mapping.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
DEH_BEGIN_MAPPING(state_mapping, state_t)
|
DEH_BEGIN_MAPPING(state_mapping, state_t)
|
||||||
DEH_MAPPING("Sprite number", sprite)
|
DEH_MAPPING("Sprite number", sprite)
|
||||||
DEH_MAPPING("Sprite subnumber", frame)
|
DEH_MAPPING("Sprite subnumber", frame)
|
||||||
|
|
@ -159,3 +161,4 @@ deh_section_t deh_section_frame =
|
||||||
DEH_FrameSHA1Sum,
|
DEH_FrameSHA1Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
#include "deh_misc.h"
|
#include "deh_misc.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
// Dehacked: "Initial Health"
|
// Dehacked: "Initial Health"
|
||||||
// This is the initial health a player has when starting anew.
|
// This is the initial health a player has when starting anew.
|
||||||
// See G_PlayerReborn in g_game.c
|
// See G_PlayerReborn in g_game.c
|
||||||
|
|
@ -226,3 +228,4 @@ deh_section_t deh_section_misc =
|
||||||
DEH_MiscSHA1Sum,
|
DEH_MiscSHA1Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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_BFG_CELLS_PER_SHOT 40
|
||||||
#define DEH_DEFAULT_SPECIES_INFIGHTING 0
|
#define DEH_DEFAULT_SPECIES_INFIGHTING 0
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
|
|
||||||
extern int deh_initial_health;
|
extern int deh_initial_health;
|
||||||
extern int deh_initial_bullets;
|
extern int deh_initial_bullets;
|
||||||
extern int deh_max_health;
|
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_bfg_cells_per_shot;
|
||||||
extern int deh_species_infighting;
|
extern int deh_species_infighting;
|
||||||
|
|
||||||
#if 0
|
#else
|
||||||
|
|
||||||
// To compile without dehacked, it's possible to use these:
|
// To compile without dehacked, it's possible to use these:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
#include "deh_io.h"
|
#include "deh_io.h"
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
static actionf_t codeptrs[NUMSTATES];
|
static actionf_t codeptrs[NUMSTATES];
|
||||||
|
|
||||||
static int CodePointerIndex(actionf_t *ptr)
|
static int CodePointerIndex(actionf_t *ptr)
|
||||||
|
|
@ -140,3 +142,4 @@ deh_section_t deh_section_pointer =
|
||||||
DEH_PointerSHA1Sum,
|
DEH_PointerSHA1Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
#include "deh_mapping.h"
|
#include "deh_mapping.h"
|
||||||
#include "sounds.h"
|
#include "sounds.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t)
|
DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t)
|
||||||
DEH_UNSUPPORTED_MAPPING("Offset")
|
DEH_UNSUPPORTED_MAPPING("Offset")
|
||||||
DEH_UNSUPPORTED_MAPPING("Zero/One")
|
DEH_UNSUPPORTED_MAPPING("Zero/One")
|
||||||
|
|
@ -100,3 +102,4 @@ deh_section_t deh_section_sound =
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
|
|
||||||
#include "info.h"
|
#include "info.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
|
DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
|
||||||
DEH_MAPPING("ID #", doomednum)
|
DEH_MAPPING("ID #", doomednum)
|
||||||
DEH_MAPPING("Initial frame", spawnstate)
|
DEH_MAPPING("Initial frame", spawnstate)
|
||||||
|
|
@ -129,3 +131,4 @@ deh_section_t deh_section_thing =
|
||||||
DEH_ThingSHA1Sum,
|
DEH_ThingSHA1Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
#include "deh_main.h"
|
#include "deh_main.h"
|
||||||
#include "deh_mapping.h"
|
#include "deh_mapping.h"
|
||||||
|
|
||||||
|
#if !NO_USE_DEH
|
||||||
DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
|
DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
|
||||||
DEH_MAPPING("Ammo type", ammo)
|
DEH_MAPPING("Ammo type", ammo)
|
||||||
DEH_MAPPING("Deselect frame", upstate)
|
DEH_MAPPING("Deselect frame", upstate)
|
||||||
|
|
@ -99,3 +101,4 @@ deh_section_t deh_section_weapon =
|
||||||
DEH_WeaponSHA1Sum,
|
DEH_WeaponSHA1Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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.
|
// Set if already seen, thus drawn in automap.
|
||||||
#define ML_MAPPED 256
|
#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.
|
// Sector definition, from editing.
|
||||||
typedef PACKED_STRUCT (
|
typedef PACKED_STRUCT (
|
||||||
{
|
{
|
||||||
|
|
@ -206,8 +212,14 @@ typedef PACKED_STRUCT (
|
||||||
short options;
|
short options;
|
||||||
}) mapthing_t;
|
}) 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__
|
#endif // __DOOMDATA__
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -39,7 +40,9 @@
|
||||||
|
|
||||||
// If rangecheck is undefined,
|
// If rangecheck is undefined,
|
||||||
// most parameter validation debugging code will not be compiled
|
// most parameter validation debugging code will not be compiled
|
||||||
|
#ifndef NDEBUG
|
||||||
#define RANGECHECK
|
#define RANGECHECK
|
||||||
|
#endif
|
||||||
|
|
||||||
// The maximum number of players, multiplayer/networking.
|
// The maximum number of players, multiplayer/networking.
|
||||||
#define MAXPLAYERS 4
|
#define MAXPLAYERS 4
|
||||||
|
|
@ -66,7 +69,15 @@ typedef enum
|
||||||
ga_completed,
|
ga_completed,
|
||||||
ga_victory,
|
ga_victory,
|
||||||
ga_worlddone,
|
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;
|
} gameaction_t;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -22,14 +23,18 @@
|
||||||
|
|
||||||
|
|
||||||
// Game Mode - identify IWAD as shareware, retail etc.
|
// Game Mode - identify IWAD as shareware, retail etc.
|
||||||
|
#if !DEMO1_ONLY
|
||||||
GameMode_t gamemode = indetermined;
|
GameMode_t gamemode = indetermined;
|
||||||
|
#endif
|
||||||
GameMission_t gamemission = doom;
|
GameMission_t gamemission = doom;
|
||||||
GameVersion_t gameversion = exe_final2;
|
GameVersion_t gameversion = exe_final2;
|
||||||
GameVariant_t gamevariant = vanilla;
|
GameVariant_t gamevariant = vanilla;
|
||||||
const char *gamedescription;
|
const char *gamedescription;
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
// Set if homebrew PWAD stuff has been added.
|
// Set if homebrew PWAD stuff has been added.
|
||||||
boolean modifiedgame;
|
boolean modifiedgame;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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.
|
// Game Mode - identify IWAD as shareware, retail etc.
|
||||||
//
|
//
|
||||||
|
#if !DEMO1_ONLY
|
||||||
extern GameMode_t gamemode;
|
extern GameMode_t gamemode;
|
||||||
|
#else
|
||||||
|
#define gamemode shareware
|
||||||
|
#endif
|
||||||
extern GameMission_t gamemission;
|
extern GameMission_t gamemission;
|
||||||
extern GameVersion_t gameversion;
|
extern GameVersion_t gameversion;
|
||||||
extern GameVariant_t gamevariant;
|
extern GameVariant_t gamevariant;
|
||||||
|
|
@ -65,11 +70,15 @@ extern const char *gamedescription;
|
||||||
// as the same most of the time.
|
// as the same most of the time.
|
||||||
|
|
||||||
#define logical_gamemission \
|
#define logical_gamemission \
|
||||||
(gamemission == pack_chex ? doom : \
|
(gamemission_is_chex(gamemission) ? doom : \
|
||||||
gamemission == pack_hacx ? doom2 : gamemission)
|
gamemission_is_hacx(gamemission) ? doom2 : gamemission)
|
||||||
|
|
||||||
// Set if homebrew PWAD stuff has been added.
|
// Set if homebrew PWAD stuff has been added.
|
||||||
|
#if !DOOM_TINY
|
||||||
extern boolean modifiedgame;
|
extern boolean modifiedgame;
|
||||||
|
#else
|
||||||
|
#define modifiedgame 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------
|
// -------------------------------------------
|
||||||
|
|
@ -78,20 +87,20 @@ extern boolean modifiedgame;
|
||||||
|
|
||||||
// Defaults for menu, methinks.
|
// Defaults for menu, methinks.
|
||||||
extern skill_t startskill;
|
extern skill_t startskill;
|
||||||
extern int startepisode;
|
extern isb_int8_t startepisode;
|
||||||
extern int startmap;
|
extern isb_int8_t startmap;
|
||||||
|
|
||||||
// Savegame slot to load on startup. This is the value provided to
|
// Savegame slot to load on startup. This is the value provided to
|
||||||
// the -loadgame option. If this has not been provided, this is -1.
|
// the -loadgame option. If this has not been provided, this is -1.
|
||||||
|
|
||||||
extern int startloadgame;
|
extern isb_int8_t startloadgame;
|
||||||
|
|
||||||
extern boolean autostart;
|
extern boolean autostart;
|
||||||
|
|
||||||
// Selected by user.
|
// Selected by user.
|
||||||
extern skill_t gameskill;
|
extern skill_t gameskill;
|
||||||
extern int gameepisode;
|
extern isb_int8_t gameepisode;
|
||||||
extern int gamemap;
|
extern isb_int8_t gamemap;
|
||||||
|
|
||||||
// If non-zero, exit the level after this number of minutes
|
// If non-zero, exit the level after this number of minutes
|
||||||
extern int timelimit;
|
extern int timelimit;
|
||||||
|
|
@ -100,10 +109,14 @@ extern int timelimit;
|
||||||
extern boolean respawnmonsters;
|
extern boolean respawnmonsters;
|
||||||
|
|
||||||
// Netgame? Only true if >1 player.
|
// Netgame? Only true if >1 player.
|
||||||
|
#if !NO_USE_NET || USE_PICO_NET
|
||||||
extern boolean netgame;
|
extern boolean netgame;
|
||||||
|
#else
|
||||||
|
#define netgame false
|
||||||
|
#endif
|
||||||
|
|
||||||
// 0=Cooperative; 1=Deathmatch; 2=Altdeath
|
// 0=Cooperative; 1=Deathmatch; 2=Altdeath
|
||||||
extern int deathmatch;
|
extern isb_int8_t deathmatch;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Internal parameters for sound rendering.
|
// Internal parameters for sound rendering.
|
||||||
|
|
@ -145,11 +158,20 @@ extern boolean paused; // Game Pause?
|
||||||
|
|
||||||
extern boolean viewactive;
|
extern boolean viewactive;
|
||||||
|
|
||||||
|
#if !FORCE_NODRAW
|
||||||
extern boolean nodrawers;
|
extern boolean nodrawers;
|
||||||
|
#else
|
||||||
|
#define nodrawers true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
extern boolean testcontrols;
|
extern boolean testcontrols;
|
||||||
extern int testcontrols_mousespeed;
|
extern int testcontrols_mousespeed;
|
||||||
|
#else
|
||||||
|
#define testcontrols false
|
||||||
|
#define testcontrols_mousespeed false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,8 +181,8 @@ extern int testcontrols_mousespeed;
|
||||||
extern int viewangleoffset;
|
extern int viewangleoffset;
|
||||||
|
|
||||||
// Player taking events, and displaying.
|
// Player taking events, and displaying.
|
||||||
extern int consoleplayer;
|
extern isb_int8_t consoleplayer;
|
||||||
extern int displayplayer;
|
extern isb_int8_t displayplayer;
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
|
|
@ -185,7 +207,11 @@ extern boolean usergame;
|
||||||
|
|
||||||
//?
|
//?
|
||||||
extern boolean demoplayback;
|
extern boolean demoplayback;
|
||||||
|
#if !NO_DEMO_RECORDING
|
||||||
extern boolean demorecording;
|
extern boolean demorecording;
|
||||||
|
#else
|
||||||
|
#define demorecording 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// Round angleturn in ticcmds to the nearest 256. This is used when
|
// Round angleturn in ticcmds to the nearest 256. This is used when
|
||||||
// recording Vanilla demos in netgames.
|
// recording Vanilla demos in netgames.
|
||||||
|
|
@ -243,8 +269,10 @@ extern wbstartstruct_t wminfo;
|
||||||
// Internal parameters, used for engine.
|
// Internal parameters, used for engine.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if !NO_FILE_ACCESS
|
||||||
// File handling stuff.
|
// File handling stuff.
|
||||||
extern char *savegamedir;
|
extern char *savegamedir;
|
||||||
|
#endif
|
||||||
|
|
||||||
// if true, load all graphics at level load
|
// if true, load all graphics at level load
|
||||||
extern boolean precache;
|
extern boolean precache;
|
||||||
|
|
@ -254,16 +282,16 @@ extern boolean precache;
|
||||||
// to force a wipe on the next draw
|
// to force a wipe on the next draw
|
||||||
extern gamestate_t wipegamestate;
|
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.
|
// Needed to store the number of the dummy sky flat.
|
||||||
// Used for rendering,
|
// Used for rendering,
|
||||||
// as well as tracking projectiles etc.
|
// 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) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -20,7 +21,7 @@
|
||||||
|
|
||||||
#include "dstrings.h"
|
#include "dstrings.h"
|
||||||
|
|
||||||
const char *doom1_endmsg[] =
|
const constcharstar doom1_endmsg[NUM_QUITMESSAGES] =
|
||||||
{
|
{
|
||||||
"are you sure you want to\nquit this great game?",
|
"are you sure you want to\nquit this great game?",
|
||||||
"please don't leave, there's more\ndemons to toast!",
|
"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.",
|
"go ahead and leave. see if i care.",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *doom2_endmsg[] =
|
const constcharstar doom2_endmsg[NUM_QUITMESSAGES] =
|
||||||
{
|
{
|
||||||
// QuitDOOM II messages
|
// QuitDOOM II messages
|
||||||
"are you sure you want to\nquit this great game?",
|
"are you sure you want to\nquit this great game?",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -34,8 +35,9 @@
|
||||||
// 8 per each game type
|
// 8 per each game type
|
||||||
#define NUM_QUITMESSAGES 8
|
#define NUM_QUITMESSAGES 8
|
||||||
|
|
||||||
extern const char *doom1_endmsg[];
|
#include "doomtype.h"
|
||||||
extern const char *doom2_endmsg[];
|
extern const constcharstar doom1_endmsg[NUM_QUITMESSAGES];
|
||||||
|
extern const constcharstar doom2_endmsg[NUM_QUITMESSAGES];
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -35,19 +36,13 @@
|
||||||
#include "sounds.h"
|
#include "sounds.h"
|
||||||
|
|
||||||
#include "doomstat.h"
|
#include "doomstat.h"
|
||||||
|
#include "r_data.h"
|
||||||
#include "r_state.h"
|
#include "r_state.h"
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
F_STAGE_TEXT,
|
|
||||||
F_STAGE_ARTSCREEN,
|
|
||||||
F_STAGE_CAST,
|
|
||||||
} finalestage_t;
|
|
||||||
|
|
||||||
// ?
|
// ?
|
||||||
//#include "doomstat.h"
|
//#include "doomstat.h"
|
||||||
//#include "r_local.h"
|
//#include "r_local.h"
|
||||||
//#include "f_finale.h"
|
#include "f_finale.h"
|
||||||
|
|
||||||
// Stage of animation:
|
// Stage of animation:
|
||||||
finalestage_t finalestage;
|
finalestage_t finalestage;
|
||||||
|
|
@ -67,7 +62,16 @@ typedef struct
|
||||||
|
|
||||||
static textscreen_t textscreens[] =
|
static textscreen_t textscreens[] =
|
||||||
{
|
{
|
||||||
|
#if !HACK_FINALE_E1M1
|
||||||
{ doom, 1, 8, "FLOOR4_8", E1TEXT},
|
{ 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, 2, 8, "SFLR6_1", E2TEXT},
|
||||||
{ doom, 3, 8, "MFLR8_4", E3TEXT},
|
{ doom, 3, 8, "MFLR8_4", E3TEXT},
|
||||||
{ doom, 4, 8, "MFLR8_3", E4TEXT},
|
{ doom, 4, 8, "MFLR8_3", E4TEXT},
|
||||||
|
|
@ -92,6 +96,7 @@ static textscreen_t textscreens[] =
|
||||||
{ pack_plut, 1, 30, "RROCK17", P4TEXT},
|
{ pack_plut, 1, 30, "RROCK17", P4TEXT},
|
||||||
{ pack_plut, 1, 15, "RROCK13", P5TEXT},
|
{ pack_plut, 1, 15, "RROCK13", P5TEXT},
|
||||||
{ pack_plut, 1, 31, "RROCK19", P6TEXT},
|
{ pack_plut, 1, 31, "RROCK19", P6TEXT},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *finaletext;
|
const char *finaletext;
|
||||||
|
|
@ -100,8 +105,10 @@ const char *finaleflat;
|
||||||
void F_StartCast (void);
|
void F_StartCast (void);
|
||||||
void F_CastTicker (void);
|
void F_CastTicker (void);
|
||||||
boolean F_CastResponder (event_t *ev);
|
boolean F_CastResponder (event_t *ev);
|
||||||
void F_CastDrawer (void);
|
|
||||||
|
|
||||||
|
#if DOOM_TINY
|
||||||
|
#include "doom/m_menu.h"
|
||||||
|
#endif
|
||||||
//
|
//
|
||||||
// F_StartFinale
|
// F_StartFinale
|
||||||
//
|
//
|
||||||
|
|
@ -114,6 +121,9 @@ void F_StartFinale (void)
|
||||||
viewactive = false;
|
viewactive = false;
|
||||||
automapactive = 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)
|
if (logical_gamemission == doom)
|
||||||
{
|
{
|
||||||
S_ChangeMusic(mus_victor, true);
|
S_ChangeMusic(mus_victor, true);
|
||||||
|
|
@ -131,7 +141,7 @@ void F_StartFinale (void)
|
||||||
|
|
||||||
// Hack for Chex Quest
|
// Hack for Chex Quest
|
||||||
|
|
||||||
if (gameversion == exe_chex && screen->mission == doom)
|
if (gameversion_is_chex(gameversion) && screen->mission == doom)
|
||||||
{
|
{
|
||||||
screen->level = 5;
|
screen->level = 5;
|
||||||
}
|
}
|
||||||
|
|
@ -141,6 +151,9 @@ void F_StartFinale (void)
|
||||||
&& gamemap == screen->level)
|
&& gamemap == screen->level)
|
||||||
{
|
{
|
||||||
finaletext = screen->text;
|
finaletext = screen->text;
|
||||||
|
#if 0 && HACK_FINALE_E1M1
|
||||||
|
finaletext = "MUCH SHORTER!";
|
||||||
|
#endif
|
||||||
finaleflat = screen->background;
|
finaleflat = screen->background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -159,9 +172,10 @@ void F_StartFinale (void)
|
||||||
|
|
||||||
boolean F_Responder (event_t *event)
|
boolean F_Responder (event_t *event)
|
||||||
{
|
{
|
||||||
|
#if !NO_USE_FINALE_CAST
|
||||||
if (finalestage == F_STAGE_CAST)
|
if (finalestage == F_STAGE_CAST)
|
||||||
return F_CastResponder (event);
|
return F_CastResponder (event);
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,47 +185,50 @@ boolean F_Responder (event_t *event)
|
||||||
//
|
//
|
||||||
void F_Ticker (void)
|
void F_Ticker (void)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
// check for skipping
|
// check for skipping
|
||||||
if ( (gamemode == commercial)
|
if ((gamemode == commercial)
|
||||||
&& ( finalecount > 50) )
|
&& (finalecount > 50)) {
|
||||||
{
|
// go on to the next level
|
||||||
// go on to the next level
|
for (i = 0; i < MAXPLAYERS; i++)
|
||||||
for (i=0 ; i<MAXPLAYERS ; i++)
|
if (players[i].cmd.buttons)
|
||||||
if (players[i].cmd.buttons)
|
break;
|
||||||
break;
|
|
||||||
|
if (i < MAXPLAYERS) {
|
||||||
if (i < MAXPLAYERS)
|
#if !NO_USE_FINALE_CAST
|
||||||
{
|
if (gamemap == 30)
|
||||||
if (gamemap == 30)
|
F_StartCast();
|
||||||
F_StartCast ();
|
else
|
||||||
else
|
#endif
|
||||||
gameaction = ga_worlddone;
|
gameaction = ga_worlddone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance animation
|
// advance animation
|
||||||
finalecount++;
|
finalecount++;
|
||||||
|
|
||||||
if (finalestage == F_STAGE_CAST)
|
#if !NO_USE_FINALE_CAST
|
||||||
{
|
if (finalestage == F_STAGE_CAST) {
|
||||||
F_CastTicker ();
|
F_CastTicker();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if ( gamemode == commercial)
|
|
||||||
return;
|
if (gamemode == commercial)
|
||||||
|
return;
|
||||||
|
|
||||||
if (finalestage == F_STAGE_TEXT
|
if (finalestage == F_STAGE_TEXT
|
||||||
&& finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
|
&& finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT) {
|
||||||
{
|
finalecount = 0;
|
||||||
finalecount = 0;
|
finalestage = F_STAGE_ARTSCREEN;
|
||||||
finalestage = F_STAGE_ARTSCREEN;
|
wipegamestate = -1; // force a wipe
|
||||||
wipegamestate = -1; // force a wipe
|
if (gameepisode == 3)
|
||||||
if (gameepisode == 3)
|
S_StartMusic(mus_bunny);
|
||||||
S_StartMusic (mus_bunny);
|
|
||||||
}
|
}
|
||||||
|
#if PICO_ON_DEVICE
|
||||||
|
I_UpdateSound();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -221,12 +238,12 @@ void F_Ticker (void)
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "hu_stuff.h"
|
#include "hu_stuff.h"
|
||||||
extern patch_t *hu_font[HU_FONTSIZE];
|
extern vpatch_sequence_t hu_font;
|
||||||
|
|
||||||
|
|
||||||
void F_TextWrite (void)
|
void F_TextWrite (void)
|
||||||
{
|
{
|
||||||
byte* src;
|
should_be_const byte* src;
|
||||||
pixel_t* dest;
|
pixel_t* dest;
|
||||||
|
|
||||||
int x,y,w;
|
int x,y,w;
|
||||||
|
|
@ -235,7 +252,8 @@ void F_TextWrite (void)
|
||||||
int c;
|
int c;
|
||||||
int cx;
|
int cx;
|
||||||
int cy;
|
int cy;
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
// erase the entire screen to a tiled background
|
// erase the entire screen to a tiled background
|
||||||
src = W_CacheLumpName ( finaleflat , PU_CACHE);
|
src = W_CacheLumpName ( finaleflat , PU_CACHE);
|
||||||
dest = I_VideoBuffer;
|
dest = I_VideoBuffer;
|
||||||
|
|
@ -255,6 +273,7 @@ void F_TextWrite (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
||||||
|
#endif
|
||||||
|
|
||||||
// draw some of the text onto the screen
|
// draw some of the text onto the screen
|
||||||
cx = 10;
|
cx = 10;
|
||||||
|
|
@ -275,7 +294,7 @@ void F_TextWrite (void)
|
||||||
cy += 11;
|
cy += 11;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = toupper(c) - HU_FONTSTART;
|
c = toupper(c) - HU_FONTSTART;
|
||||||
if (c < 0 || c> HU_FONTSIZE)
|
if (c < 0 || c> HU_FONTSIZE)
|
||||||
{
|
{
|
||||||
|
|
@ -283,15 +302,22 @@ void F_TextWrite (void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
w = SHORT (hu_font[c]->width);
|
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||||
if (cx+w > SCREENWIDTH)
|
if (cx+w > SCREENWIDTH)
|
||||||
break;
|
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;
|
cx+=w;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NO_USE_FINALE_CAST
|
||||||
//
|
//
|
||||||
// Final DOOM 2 animation
|
// Final DOOM 2 animation
|
||||||
// Casting by id Software.
|
// Casting by id Software.
|
||||||
|
|
@ -327,7 +353,7 @@ castinfo_t castorder[] = {
|
||||||
|
|
||||||
int castnum;
|
int castnum;
|
||||||
int casttics;
|
int casttics;
|
||||||
state_t* caststate;
|
should_be_const state_t* caststate;
|
||||||
boolean castdeath;
|
boolean castdeath;
|
||||||
int castframes;
|
int castframes;
|
||||||
int castonmelee;
|
int castonmelee;
|
||||||
|
|
@ -339,10 +365,11 @@ boolean castattacking;
|
||||||
//
|
//
|
||||||
void F_StartCast (void)
|
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
|
wipegamestate = -1; // force a screen wipe
|
||||||
castnum = 0;
|
castnum = 0;
|
||||||
caststate = &states[mobjinfo[castorder[castnum].type].seestate];
|
caststate = &states[mobjinfo[castorder[castnum].type].seestate];
|
||||||
casttics = caststate->tics;
|
casttics = state_tics(caststate);
|
||||||
castdeath = false;
|
castdeath = false;
|
||||||
finalestage = F_STAGE_CAST;
|
finalestage = F_STAGE_CAST;
|
||||||
castframes = 0;
|
castframes = 0;
|
||||||
|
|
@ -363,7 +390,7 @@ void F_CastTicker (void)
|
||||||
if (--casttics > 0)
|
if (--casttics > 0)
|
||||||
return; // not time to change state yet
|
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
|
// switch from deathstate to next monster
|
||||||
castnum++;
|
castnum++;
|
||||||
|
|
@ -452,7 +479,7 @@ void F_CastTicker (void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
casttics = caststate->tics;
|
casttics = state_tics(caststate);
|
||||||
if (casttics == -1)
|
if (casttics == -1)
|
||||||
casttics = 15;
|
casttics = 15;
|
||||||
}
|
}
|
||||||
|
|
@ -473,7 +500,7 @@ boolean F_CastResponder (event_t* ev)
|
||||||
// go into death frame
|
// go into death frame
|
||||||
castdeath = true;
|
castdeath = true;
|
||||||
caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
|
caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
|
||||||
casttics = caststate->tics;
|
casttics = state_tics(caststate);
|
||||||
castframes = 0;
|
castframes = 0;
|
||||||
castattacking = false;
|
castattacking = false;
|
||||||
if (mobjinfo[castorder[castnum].type].deathsound)
|
if (mobjinfo[castorder[castnum].type].deathsound)
|
||||||
|
|
@ -507,7 +534,7 @@ void F_CastPrint (const char *text)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
w = SHORT (hu_font[c]->width);
|
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||||
width += w;
|
width += w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,8 +553,8 @@ void F_CastPrint (const char *text)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
w = SHORT (hu_font[c]->width);
|
w = vpatch_width(resolve_vpatch_handle(vpatch_n(hu_font,c)));
|
||||||
V_DrawPatch(cx, 180, hu_font[c]);
|
V_DrawPatch(cx, 180, vpatch_n(hu_font,c));
|
||||||
cx+=w;
|
cx+=w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,32 +565,44 @@ void F_CastPrint (const char *text)
|
||||||
// F_CastDrawer
|
// 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)
|
void F_CastDrawer (void)
|
||||||
{
|
{
|
||||||
spritedef_t* sprdef;
|
#if !DOOM_TINY
|
||||||
spriteframe_t* sprframe;
|
int lump = F_CastSprite();
|
||||||
int lump;
|
boolean flip = lump < 0;
|
||||||
boolean flip;
|
if (flip) lump = -1 - lump;
|
||||||
patch_t* patch;
|
|
||||||
|
|
||||||
// erase the entire screen to a background
|
// erase the entire screen to a background
|
||||||
V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE));
|
V_DrawPatch (0, 0, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE));
|
||||||
|
|
||||||
F_CastPrint (DEH_String(castorder[castnum].name));
|
F_CastPrint (DEH_String(castorder[castnum].name));
|
||||||
|
|
||||||
// draw the current frame in the middle of the screen
|
// 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
|
// F_DrawPatchCol
|
||||||
|
|
@ -571,7 +610,7 @@ void F_CastDrawer (void)
|
||||||
void
|
void
|
||||||
F_DrawPatchCol
|
F_DrawPatchCol
|
||||||
( int x,
|
( int x,
|
||||||
patch_t* patch,
|
should_be_const patch_t* patch,
|
||||||
int col )
|
int col )
|
||||||
{
|
{
|
||||||
column_t* column;
|
column_t* column;
|
||||||
|
|
@ -580,7 +619,7 @@ F_DrawPatchCol
|
||||||
pixel_t* desttop;
|
pixel_t* desttop;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
|
column = (column_t *)((byte *)patch + patch_columnofs(patch, col));
|
||||||
desttop = I_VideoBuffer + x;
|
desttop = I_VideoBuffer + x;
|
||||||
|
|
||||||
// step through the posts in a column
|
// 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
|
// F_BunnyScroll
|
||||||
//
|
//
|
||||||
void F_BunnyScroll (void)
|
void F_BunnyScroll (void)
|
||||||
{
|
{
|
||||||
signed int scrolled;
|
signed int scrolled = F_BunnyScrollPos();
|
||||||
int x;
|
int x;
|
||||||
patch_t* p1;
|
should_be_const patch_t* p1;
|
||||||
patch_t* p2;
|
should_be_const patch_t* p2;
|
||||||
char name[10];
|
|
||||||
int stage;
|
|
||||||
static int laststage;
|
|
||||||
|
|
||||||
p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
|
p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
|
||||||
p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
|
p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
|
||||||
|
|
||||||
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
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++)
|
for ( x=0 ; x<SCREENWIDTH ; x++)
|
||||||
{
|
{
|
||||||
if (x+scrolled < SCREENWIDTH)
|
if (x+scrolled < SCREENWIDTH)
|
||||||
|
|
@ -631,69 +712,56 @@ void F_BunnyScroll (void)
|
||||||
else
|
else
|
||||||
F_DrawPatchCol (x, p2, x+scrolled - SCREENWIDTH);
|
F_DrawPatchCol (x, p2, x+scrolled - SCREENWIDTH);
|
||||||
}
|
}
|
||||||
|
F_BunnyDrawPatches();
|
||||||
if (finalecount < 1130)
|
}
|
||||||
return;
|
#endif
|
||||||
if (finalecount < 1180)
|
#endif
|
||||||
|
|
||||||
|
const char *F_ArtScreenLumpName(void) {
|
||||||
|
const char* lumpname;
|
||||||
|
switch (gameepisode)
|
||||||
{
|
{
|
||||||
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
|
case 1:
|
||||||
(SCREENHEIGHT - 8 * 8) / 2,
|
if (gameversion >= exe_ultimate)
|
||||||
W_CacheLumpName(DEH_String("END0"), PU_CACHE));
|
{
|
||||||
laststage = 0;
|
lumpname = "CREDIT";
|
||||||
return;
|
}
|
||||||
|
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;
|
return DEH_String(lumpname);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void F_ArtScreenDrawer(void)
|
static void F_ArtScreenDrawer(void)
|
||||||
{
|
{
|
||||||
const char *lumpname;
|
#if !DOOM_TINY
|
||||||
|
const char *lumpname = F_ArtScreenLumpName();
|
||||||
if (gameepisode == 3)
|
|
||||||
|
if (!lumpname)
|
||||||
{
|
{
|
||||||
|
#if !NO_USE_FINALE_BUNNY
|
||||||
F_BunnyScroll();
|
F_BunnyScroll();
|
||||||
}
|
#endif
|
||||||
else
|
} 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));
|
V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -704,8 +772,10 @@ void F_Drawer (void)
|
||||||
switch (finalestage)
|
switch (finalestage)
|
||||||
{
|
{
|
||||||
case F_STAGE_CAST:
|
case F_STAGE_CAST:
|
||||||
|
#if !DOOM_TINY && !NO_USE_FINALE_CAST
|
||||||
F_CastDrawer();
|
F_CastDrawer();
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
case F_STAGE_TEXT:
|
case F_STAGE_TEXT:
|
||||||
F_TextWrite();
|
F_TextWrite();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -39,7 +40,25 @@ void F_Drawer (void);
|
||||||
|
|
||||||
void F_StartFinale (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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -234,8 +235,10 @@ wipe_StartScreen
|
||||||
int width,
|
int width,
|
||||||
int height )
|
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);
|
I_ReadScreen(wipe_scr_start);
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,9 +249,11 @@ wipe_EndScreen
|
||||||
int width,
|
int width,
|
||||||
int height )
|
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);
|
I_ReadScreen(wipe_scr_end);
|
||||||
V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr.
|
V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr.
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,6 +266,7 @@ wipe_ScreenWipe
|
||||||
int height,
|
int height,
|
||||||
int ticks )
|
int ticks )
|
||||||
{
|
{
|
||||||
|
#if !NO_USE_WIPE
|
||||||
int rc;
|
int rc;
|
||||||
static int (*wipes[])(int, int, int) =
|
static int (*wipes[])(int, int, int) =
|
||||||
{
|
{
|
||||||
|
|
@ -290,5 +296,8 @@ wipe_ScreenWipe
|
||||||
}
|
}
|
||||||
|
|
||||||
return !go;
|
return !go;
|
||||||
|
#else
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -60,4 +61,16 @@ wipe_ScreenWipe
|
||||||
int height,
|
int height,
|
||||||
int ticks );
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// 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.
|
// Can be called by the startup code or M_Responder.
|
||||||
// A normal game starts at map 1,
|
// A normal game starts at map 1,
|
||||||
// but a warp test can start elsewhere
|
// 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);
|
void G_DeferedPlayDemo (const char* demo);
|
||||||
|
|
||||||
|
|
@ -48,10 +49,12 @@ void G_DoLoadGame (void);
|
||||||
// Called by M_Responder.
|
// Called by M_Responder.
|
||||||
void G_SaveGame (int slot, char* description);
|
void G_SaveGame (int slot, char* description);
|
||||||
|
|
||||||
|
#if !NO_DEMO_RECORDING
|
||||||
// Only called by startup code.
|
// Only called by startup code.
|
||||||
void G_RecordDemo (char* name);
|
void G_RecordDemo (char* name);
|
||||||
|
|
||||||
void G_BeginRecording (void);
|
void G_BeginRecording (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
void G_PlayDemo (char* name);
|
void G_PlayDemo (char* name);
|
||||||
void G_TimeDemo (char* name);
|
void G_TimeDemo (char* name);
|
||||||
|
|
@ -74,7 +77,11 @@ void G_ScreenShot (void);
|
||||||
void G_DrawMouseSpeedBox(void);
|
void G_DrawMouseSpeedBox(void);
|
||||||
int G_VanillaVersionCode(void);
|
int G_VanillaVersionCode(void);
|
||||||
|
|
||||||
extern int vanilla_savegame_limit;
|
extern isb_int8_t vanilla_savegame_limit;
|
||||||
extern int vanilla_demo_limit;
|
extern isb_int8_t vanilla_demo_limit;
|
||||||
|
|
||||||
|
#if PICO_ON_DEVICE
|
||||||
|
extern uint8_t g_load_slot;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -24,6 +25,7 @@
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
#include "i_swap.h"
|
#include "i_swap.h"
|
||||||
|
|
||||||
|
#include "w_wad.h"
|
||||||
#include "hu_lib.h"
|
#include "hu_lib.h"
|
||||||
#include "r_local.h"
|
#include "r_local.h"
|
||||||
#include "r_draw.h"
|
#include "r_draw.h"
|
||||||
|
|
@ -49,7 +51,7 @@ HUlib_initTextLine
|
||||||
( hu_textline_t* t,
|
( hu_textline_t* t,
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
patch_t** f,
|
vpatch_sequence_t f,
|
||||||
int sc )
|
int sc )
|
||||||
{
|
{
|
||||||
t->x = x;
|
t->x = x;
|
||||||
|
|
@ -110,10 +112,10 @@ HUlib_drawTextLine
|
||||||
&& c >= l->sc
|
&& c >= l->sc
|
||||||
&& c <= '_')
|
&& 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)
|
if (x+w > SCREENWIDTH)
|
||||||
break;
|
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;
|
x += w;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -126,9 +128,9 @@ HUlib_drawTextLine
|
||||||
|
|
||||||
// draw the cursor if requested
|
// draw the cursor if requested
|
||||||
if (drawcursor
|
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
|
// and the text must either need updating or refreshing
|
||||||
// (because of a recent change back from the automap)
|
// (because of a recent change back from the automap)
|
||||||
|
|
||||||
|
#if !NO_RDRAW
|
||||||
if (!automapactive &&
|
if (!automapactive &&
|
||||||
viewwindowx && l->needsupdate)
|
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)
|
for (y=l->y,yoffset=y*SCREENWIDTH ; y<l->y+lh ; y++,yoffset+=SCREENWIDTH)
|
||||||
{
|
{
|
||||||
if (y < viewwindowy || y >= viewwindowy + viewheight)
|
if (y < viewwindowy || y >= viewwindowy + viewheight)
|
||||||
|
|
@ -160,6 +163,7 @@ void HUlib_eraseTextLine(hu_textline_t* l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (l->needsupdate) l->needsupdate--;
|
if (l->needsupdate) l->needsupdate--;
|
||||||
|
|
||||||
|
|
@ -171,7 +175,7 @@ HUlib_initSText
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
int h,
|
int h,
|
||||||
patch_t** font,
|
vpatch_sequence_t font,
|
||||||
int startchar,
|
int startchar,
|
||||||
boolean* on )
|
boolean* on )
|
||||||
{
|
{
|
||||||
|
|
@ -182,9 +186,10 @@ HUlib_initSText
|
||||||
s->on = on;
|
s->on = on;
|
||||||
s->laston = true;
|
s->laston = true;
|
||||||
s->cl = 0;
|
s->cl = 0;
|
||||||
|
int pheight = vpatch_height(resolve_vpatch_handle(vpatch_n(font, 0)));
|
||||||
for (i=0;i<h;i++)
|
for (i=0;i<h;i++)
|
||||||
HUlib_initTextLine(&s->l[i],
|
HUlib_initTextLine(&s->l[i],
|
||||||
x, y - i*(SHORT(font[0]->height)+1),
|
x, y - i*(pheight+1),
|
||||||
font, startchar);
|
font, startchar);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +268,7 @@ HUlib_initIText
|
||||||
( hu_itext_t* it,
|
( hu_itext_t* it,
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
patch_t** font,
|
vpatch_sequence_t font,
|
||||||
int startchar,
|
int startchar,
|
||||||
boolean* on )
|
boolean* on )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -39,7 +40,7 @@ typedef struct
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
patch_t** f; // font
|
vpatch_sequence_t f; // font
|
||||||
int sc; // start character
|
int sc; // start character
|
||||||
char l[HU_MAXLINELENGTH+1]; // line of text
|
char l[HU_MAXLINELENGTH+1]; // line of text
|
||||||
int len; // current line length
|
int len; // current line length
|
||||||
|
|
@ -97,7 +98,7 @@ void HUlib_init(void);
|
||||||
// clear a line of text
|
// clear a line of text
|
||||||
void HUlib_clearTextLine(hu_textline_t *t);
|
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
|
// returns success
|
||||||
boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch);
|
boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch);
|
||||||
|
|
@ -123,7 +124,7 @@ HUlib_initSText
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
int h,
|
int h,
|
||||||
patch_t** font,
|
vpatch_sequence_t font,
|
||||||
int startchar,
|
int startchar,
|
||||||
boolean* on );
|
boolean* on );
|
||||||
|
|
||||||
|
|
@ -149,7 +150,7 @@ HUlib_initIText
|
||||||
( hu_itext_t* it,
|
( hu_itext_t* it,
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
patch_t** font,
|
vpatch_sequence_t font,
|
||||||
int startchar,
|
int startchar,
|
||||||
boolean* on );
|
boolean* on );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -41,6 +42,7 @@
|
||||||
// Data.
|
// Data.
|
||||||
#include "dstrings.h"
|
#include "dstrings.h"
|
||||||
#include "sounds.h"
|
#include "sounds.h"
|
||||||
|
#include "r_data.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Locally used constants, shortcuts.
|
// Locally used constants, shortcuts.
|
||||||
|
|
@ -52,17 +54,19 @@
|
||||||
#define HU_TITLE_CHEX (mapnames_chex[(gameepisode-1)*9+gamemap-1])
|
#define HU_TITLE_CHEX (mapnames_chex[(gameepisode-1)*9+gamemap-1])
|
||||||
#define HU_TITLEHEIGHT 1
|
#define HU_TITLEHEIGHT 1
|
||||||
#define HU_TITLEX 0
|
#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_INPUTTOGGLE 't'
|
||||||
#define HU_INPUTX HU_MSGX
|
#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_INPUTWIDTH 64
|
||||||
#define HU_INPUTHEIGHT 1
|
#define HU_INPUTHEIGHT 1
|
||||||
|
|
||||||
|
#if !DOOM_TINY
|
||||||
|
const char *chat_macros[10] =
|
||||||
char *chat_macros[10] =
|
#else
|
||||||
|
const constcharstar chat_macros[10] =
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
HUSTR_CHATMACRO0,
|
HUSTR_CHATMACRO0,
|
||||||
HUSTR_CHATMACRO1,
|
HUSTR_CHATMACRO1,
|
||||||
|
|
@ -76,7 +80,7 @@ char *chat_macros[10] =
|
||||||
HUSTR_CHATMACRO9
|
HUSTR_CHATMACRO9
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *player_names[] =
|
const constcharstar player_names[] =
|
||||||
{
|
{
|
||||||
HUSTR_PLRGREEN,
|
HUSTR_PLRGREEN,
|
||||||
HUSTR_PLRINDIGO,
|
HUSTR_PLRINDIGO,
|
||||||
|
|
@ -86,7 +90,12 @@ const char *player_names[] =
|
||||||
|
|
||||||
char chat_char; // remove later.
|
char chat_char; // remove later.
|
||||||
static player_t* plr;
|
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;
|
static hu_textline_t w_title;
|
||||||
boolean chat_on;
|
boolean chat_on;
|
||||||
static hu_itext_t w_chat;
|
static hu_itext_t w_chat;
|
||||||
|
|
@ -101,7 +110,7 @@ static boolean message_nottobefuckedwith;
|
||||||
static hu_stext_t w_message;
|
static hu_stext_t w_message;
|
||||||
static int message_counter;
|
static int message_counter;
|
||||||
|
|
||||||
extern int showMessages;
|
extern isb_int8_t showMessages;
|
||||||
|
|
||||||
static boolean headsupactive = false;
|
static boolean headsupactive = false;
|
||||||
|
|
||||||
|
|
@ -110,7 +119,7 @@ static boolean headsupactive = false;
|
||||||
// The actual names can be found in DStrings.h.
|
// 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,
|
HUSTR_E1M1,
|
||||||
|
|
@ -123,6 +132,7 @@ const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
||||||
HUSTR_E1M8,
|
HUSTR_E1M8,
|
||||||
HUSTR_E1M9,
|
HUSTR_E1M9,
|
||||||
|
|
||||||
|
#if !DEMO1_ONLY
|
||||||
HUSTR_E2M1,
|
HUSTR_E2M1,
|
||||||
HUSTR_E2M2,
|
HUSTR_E2M2,
|
||||||
HUSTR_E2M3,
|
HUSTR_E2M3,
|
||||||
|
|
@ -162,6 +172,7 @@ const char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
|
||||||
"NEWLEVEL",
|
"NEWLEVEL",
|
||||||
"NEWLEVEL",
|
"NEWLEVEL",
|
||||||
"NEWLEVEL"
|
"NEWLEVEL"
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *mapnames_chex[] = // Chex Quest names.
|
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
|
// the layout in the Vanilla executable, where it is possible to
|
||||||
// overflow the end of one array into the next.
|
// overflow the end of one array into the next.
|
||||||
|
|
||||||
const char *mapnames_commercial[] =
|
const char * const mapnames_commercial[] =
|
||||||
{
|
{
|
||||||
// DOOM 2 map names.
|
// DOOM 2 map names.
|
||||||
|
|
||||||
|
|
@ -352,12 +363,19 @@ void HU_Init(void)
|
||||||
char buffer[9];
|
char buffer[9];
|
||||||
|
|
||||||
// load the heads-up font
|
// load the heads-up font
|
||||||
|
#if !USE_WHD
|
||||||
j = HU_FONTSTART;
|
j = HU_FONTSTART;
|
||||||
for (i=0;i<HU_FONTSIZE;i++)
|
for (i=0;i<HU_FONTSIZE;i++)
|
||||||
{
|
{
|
||||||
DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
|
DEH_snprintf(buffer, 9, "STCFN%.3d", j++);
|
||||||
hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
|
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:
|
case doom:
|
||||||
s = HU_TITLE;
|
s = HU_TITLE;
|
||||||
break;
|
break;
|
||||||
|
#if !DEMO1_ONLY
|
||||||
case doom2:
|
case doom2:
|
||||||
s = HU_TITLE2;
|
s = HU_TITLE2;
|
||||||
// Pre-Final Doom compatibility: map33-map35 names don't spill over
|
// Pre-Final Doom compatibility: map33-map35 names don't spill over
|
||||||
|
|
@ -411,16 +430,19 @@ void HU_Start(void)
|
||||||
break;
|
break;
|
||||||
case pack_tnt:
|
case pack_tnt:
|
||||||
s = HU_TITLET;
|
s = HU_TITLET;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s = "Unknown level";
|
s = "Unknown level";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !DOOM_ONLY
|
||||||
if (logical_gamemission == doom && gameversion == exe_chex)
|
if (logical_gamemission == doom && gameversion == exe_chex)
|
||||||
{
|
{
|
||||||
s = HU_TITLE_CHEX;
|
s = HU_TITLE_CHEX;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// dehacked substitution to get modified level name
|
// dehacked substitution to get modified level name
|
||||||
|
|
||||||
|
|
@ -445,12 +467,12 @@ void HU_Start(void)
|
||||||
|
|
||||||
void HU_Drawer(void)
|
void HU_Drawer(void)
|
||||||
{
|
{
|
||||||
|
#if !NO_HU_DRAWER
|
||||||
HUlib_drawSText(&w_message);
|
HUlib_drawSText(&w_message);
|
||||||
HUlib_drawIText(&w_chat);
|
HUlib_drawIText(&w_chat);
|
||||||
if (automapactive)
|
if (automapactive)
|
||||||
HUlib_drawTextLine(&w_title, false);
|
HUlib_drawTextLine(&w_title, false);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void HU_Erase(void)
|
void HU_Erase(void)
|
||||||
|
|
@ -521,9 +543,9 @@ void HU_Ticker(void)
|
||||||
message_on = true;
|
message_on = true;
|
||||||
message_counter = HU_MSGTIMEOUT;
|
message_counter = HU_MSGTIMEOUT;
|
||||||
if ( gamemode == commercial )
|
if ( gamemode == commercial )
|
||||||
S_StartSound(0, sfx_radio);
|
S_StartUnpositionedSound( sfx_radio);
|
||||||
else
|
else
|
||||||
S_StartSound(0, sfx_tink);
|
S_StartUnpositionedSound( sfx_tink);
|
||||||
}
|
}
|
||||||
HUlib_resetIText(&w_inputbuffer[i]);
|
HUlib_resetIText(&w_inputbuffer[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -535,6 +557,8 @@ void HU_Ticker(void)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !NO_USE_NET
|
||||||
|
|
||||||
#define QUEUESIZE 128
|
#define QUEUESIZE 128
|
||||||
|
|
||||||
static char chatchars[QUEUESIZE];
|
static char chatchars[QUEUESIZE];
|
||||||
|
|
@ -716,3 +740,4 @@ boolean HU_Responder(event_t *ev)
|
||||||
|
|
||||||
return eatkey;
|
return eatkey;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -53,7 +54,11 @@ void HU_Drawer(void);
|
||||||
char HU_dequeueChatChar(void);
|
char HU_dequeueChatChar(void);
|
||||||
void HU_Erase(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
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
2085
src/doom/info.c
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||||
// Copyright(C) 2005-2014 Simon Howard
|
// Copyright(C) 2005-2014 Simon Howard
|
||||||
|
// Copyright(C) 2021-2022 Graham Sanderson
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License
|
// modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -21,6 +22,8 @@
|
||||||
#ifndef __INFO__
|
#ifndef __INFO__
|
||||||
#define __INFO__
|
#define __INFO__
|
||||||
|
|
||||||
|
#include "doomdef.h"
|
||||||
|
|
||||||
// Needed for action function pointer handling.
|
// Needed for action function pointer handling.
|
||||||
#include "d_think.h"
|
#include "d_think.h"
|
||||||
|
|
||||||
|
|
@ -166,7 +169,11 @@ typedef enum
|
||||||
SPR_TLP2,
|
SPR_TLP2,
|
||||||
NUMSPRITES
|
NUMSPRITES
|
||||||
|
|
||||||
} spritenum_t;
|
} spritenum_t_orig;
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
static_assert(NUMSPRITES <= 256, "");
|
||||||
|
typedef uint8_t spritenum_t;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
|
@ -1138,22 +1145,52 @@ typedef enum
|
||||||
S_TECH2LAMP3,
|
S_TECH2LAMP3,
|
||||||
S_TECH2LAMP4,
|
S_TECH2LAMP4,
|
||||||
NUMSTATES
|
NUMSTATES
|
||||||
} statenum_t;
|
} statenum_t_orig;
|
||||||
|
|
||||||
|
static_assert(NUMSTATES < 1024, ""); // just so we know
|
||||||
|
typedef uint16_t statenum_t;
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
#if !DOOM_SMALL
|
||||||
spritenum_t sprite;
|
spritenum_t sprite;
|
||||||
int frame;
|
int frame;
|
||||||
int tics;
|
int tics;
|
||||||
// void (*action) ();
|
// void (*action) ();
|
||||||
actionf_t action;
|
actionf_t action;
|
||||||
statenum_t nextstate;
|
statenum_t nextstate;
|
||||||
|
#ifndef NO_USE_STATE_MISC
|
||||||
int misc1;
|
int misc1;
|
||||||
int misc2;
|
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;
|
} 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[];
|
extern const char *sprnames[];
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -1296,36 +1333,40 @@ typedef enum {
|
||||||
MT_MISC86,
|
MT_MISC86,
|
||||||
NUMMOBJTYPES
|
NUMMOBJTYPES
|
||||||
|
|
||||||
} mobjtype_t;
|
} mobjtype_t_orig;
|
||||||
|
static_assert(NUMMOBJTYPES<256, "");
|
||||||
|
typedef uint8_t mobjtype_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int doomednum;
|
isb_int16_t doomednum;
|
||||||
int spawnstate;
|
statenum_t spawnstate;
|
||||||
int spawnhealth;
|
|
||||||
int seestate;
|
isb_int16_t spawnhealth;
|
||||||
int seesound;
|
statenum_t seestate;
|
||||||
int reactiontime;
|
|
||||||
int attacksound;
|
isb_uint8_t seesound;
|
||||||
int painstate;
|
isb_int8_t reactiontime;
|
||||||
int painchance;
|
isb_uint8_t attacksound;
|
||||||
int painsound;
|
statenum_t painstate;
|
||||||
int meleestate;
|
isb_int16_t painchance;
|
||||||
int missilestate;
|
isb_uint8_t painsound;
|
||||||
int deathstate;
|
statenum_t meleestate;
|
||||||
int xdeathstate;
|
statenum_t missilestate;
|
||||||
int deathsound;
|
statenum_t deathstate;
|
||||||
|
statenum_t xdeathstate;
|
||||||
|
isb_uint8_t deathsound;
|
||||||
int speed;
|
int speed;
|
||||||
int radius;
|
int radius; // todo wasteful
|
||||||
int height;
|
int height; // todo wasteful
|
||||||
int mass;
|
int mass;
|
||||||
int damage;
|
isb_int8_t damage;
|
||||||
int activesound;
|
isb_uint8_t activesound;
|
||||||
int flags;
|
int flags;
|
||||||
int raisestate;
|
statenum_t raisestate;
|
||||||
|
|
||||||
} mobjinfo_t;
|
} mobjinfo_t;
|
||||||
|
|
||||||
extern mobjinfo_t mobjinfo[NUMMOBJTYPES];
|
extern should_be_const mobjinfo_t mobjinfo[NUMMOBJTYPES];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||