Stripped down init versin of CPU Doom, specifically microdoom by atroche

This commit is contained in:
jndean
2022-07-08 00:24:18 +01:00
commit d6910fcffc
195 changed files with 70688 additions and 0 deletions

22
Makefile Normal file
View File

@@ -0,0 +1,22 @@
CPU_OBJ = $(addprefix build/obj/, i_system.o d_mode.o i_main.o i_timer.o m_argv.o m_misc.o net_common.o net_dedicated.o net_io.o net_packet.o net_query.o net_sdl.o net_server.o net_structrw.o aes_prng.o d_event.o d_iwad.o d_loop.o d_mode.o gusconf.o i_cdmus.o i_input.o i_joystick.o i_sdlmusic.o i_sdlsound.o i_sound.o i_timer.o i_video.o i_videohr.o midifile.o mus2mid.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o net_client.o sha1.o memio.o tables.o v_video.o w_checksum.o w_main.o w_wad.o w_file.o w_file_stdc.o w_file_posix.o w_merge.o z_zone.o net_loop.o am_map.o d_items.o d_main.o d_net.o doomdef.o doomstat.o dstrings.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o m_menu.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o s_sound.o sounds.o st_lib.o st_stuff.o statdump.o wi_stuff.o)
CFLAGS = -I /usr/local/include/SDL2 -I/usr/include/libpng16 -I src \
-D_REENTRANT -lSDL2 -lSDL2_mixer -lSDL2_net -lpng16 -lz \
-Wall -Werror \
-O2
all: build build/doom
build/doom: $(CPU_OBJ)
gcc $^ $(CFLAGS) -o build/doom
build/obj/%.o: src/%.c
gcc $(CFLAGS) $^ -c -o $@
build:
mkdir -p build/obj
clean:
rm -r build/

956
src/aes_prng.c Normal file
View File

@@ -0,0 +1,956 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// This implements a cryptographically secure pseudorandom number
// generator for implementing secure demos. The approach taken is to
// use the AES (Rijndael) stream cipher in "counter" mode, encrypting
// an incrementing counter. The cipher key acts as the random seed.
// Cryptanalysis of AES used in this way has shown it to be an
// effective PRNG (see: Empirical Evidence concerning AES, Hellekalek
// & Wegenkittl, 2003).
//
// AES implementation is taken from the Linux kernel's AES
// implementation, found in crypto/aes_generic.c. It has been hacked
// up to work independently.
//
#include <stdint.h>
#include "SDL2/SDL_endian.h"
#include "aes_prng.h"
#include "doomtype.h"
/*
* Cryptographic API.
*
* AES Cipher Algorithm.
*
* Based on Brian Gladman's code.
*
* Linux developers:
* Alexander Kjeldaas <astor@fast.no>
* Herbert Valerio Riedel <hvr@hvrlab.org>
* Kyle McMartin <kyle@debian.org>
* Adam J. Richter <adam@yggdrasil.com> (conversion to 2.5 API).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ---------------------------------------------------------------------------
* Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
* All rights reserved.
*
* LICENSE TERMS
*
* The free distribution and use of this software in both source and binary
* form is allowed (with or without changes) provided that:
*
* 1. distributions of this source code include the above copyright
* notice, this list of conditions and the following disclaimer;
*
* 2. distributions in binary form include the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other associated materials;
*
* 3. the copyright holder's name is not used to endorse products
* built using this software without specific written permission.
*
* ALTERNATIVELY, provided that this notice is retained in full, this product
* may be distributed under the terms of the GNU General Public License (GPL),
* in which case the provisions of the GPL apply INSTEAD OF those given above.
*
* DISCLAIMER
*
* This software is provided 'as is' with no explicit or implied warranties
* in respect of its properties, including, but not limited to, correctness
* and/or fitness for purpose.
* ---------------------------------------------------------------------------
*/
#define AES_MIN_KEY_SIZE 16
#define AES_MAX_KEY_SIZE 32
#define AES_KEYSIZE_128 16
#define AES_KEYSIZE_192 24
#define AES_KEYSIZE_256 32
#define AES_BLOCK_SIZE 16
#define AES_MAX_KEYLENGTH (15 * 16)
#define AES_MAX_KEYLENGTH_U32 (AES_MAX_KEYLENGTH / sizeof(uint32_t))
/*
* Please ensure that the first two fields are 16-byte aligned
* relative to the start of the structure, i.e., don't move them!
*/
typedef struct
{
uint32_t key_enc[AES_MAX_KEYLENGTH_U32];
uint32_t key_dec[AES_MAX_KEYLENGTH_U32];
uint32_t key_length;
} aes_context_t;
static inline uint8_t get_byte(const uint32_t x, const unsigned n)
{
return x >> (n << 3);
}
static const uint32_t rco_tab[10] = { 1, 2, 4, 8, 16, 32, 64, 128, 27, 54 };
static const uint32_t crypto_ft_tab[4][256] = {
{
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6,
0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591,
0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56,
0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec,
0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45,
0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b,
0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9,
0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d,
0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f,
0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea,
0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34,
0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d,
0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1,
0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6,
0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972,
0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511,
0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe,
0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b,
0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05,
0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142,
0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf,
0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e,
0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3,
0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b,
0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14,
0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4,
0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2,
0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949,
0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf,
0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c,
0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f,
0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc,
0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27,
0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433,
0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9,
0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a,
0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0,
0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
}, {
0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d,
0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154,
0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d,
0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a,
0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea,
0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b,
0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a,
0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908,
0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e,
0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5,
0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f,
0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e,
0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce,
0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c,
0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed,
0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b,
0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194,
0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81,
0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3,
0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a,
0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263,
0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d,
0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39,
0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f,
0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83,
0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c,
0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e,
0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6,
0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b,
0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0,
0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25,
0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72,
0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85,
0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa,
0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9,
0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233,
0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7,
0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920,
0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17,
0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8,
0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11,
0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
}, {
0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b,
0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5,
0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b,
0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76,
0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf,
0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0,
0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26,
0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1,
0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3,
0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a,
0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75,
0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a,
0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3,
0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced,
0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b,
0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39,
0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485,
0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f,
0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8,
0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f,
0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321,
0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2,
0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917,
0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc,
0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388,
0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14,
0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a,
0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662,
0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79,
0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9,
0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea,
0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e,
0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a,
0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66,
0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e,
0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311,
0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794,
0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9,
0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d,
0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868,
0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f,
0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
}, {
0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b,
0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5,
0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b,
0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676,
0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf,
0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0,
0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626,
0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1,
0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3,
0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a,
0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575,
0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a,
0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3,
0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded,
0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b,
0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939,
0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585,
0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f,
0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8,
0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f,
0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121,
0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2,
0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717,
0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc,
0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888,
0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414,
0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a,
0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262,
0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979,
0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9,
0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea,
0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e,
0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a,
0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666,
0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e,
0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111,
0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494,
0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9,
0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d,
0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868,
0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f,
0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
}
};
static const uint32_t crypto_fl_tab[4][256] = {
{
0x00000063, 0x0000007c, 0x00000077, 0x0000007b,
0x000000f2, 0x0000006b, 0x0000006f, 0x000000c5,
0x00000030, 0x00000001, 0x00000067, 0x0000002b,
0x000000fe, 0x000000d7, 0x000000ab, 0x00000076,
0x000000ca, 0x00000082, 0x000000c9, 0x0000007d,
0x000000fa, 0x00000059, 0x00000047, 0x000000f0,
0x000000ad, 0x000000d4, 0x000000a2, 0x000000af,
0x0000009c, 0x000000a4, 0x00000072, 0x000000c0,
0x000000b7, 0x000000fd, 0x00000093, 0x00000026,
0x00000036, 0x0000003f, 0x000000f7, 0x000000cc,
0x00000034, 0x000000a5, 0x000000e5, 0x000000f1,
0x00000071, 0x000000d8, 0x00000031, 0x00000015,
0x00000004, 0x000000c7, 0x00000023, 0x000000c3,
0x00000018, 0x00000096, 0x00000005, 0x0000009a,
0x00000007, 0x00000012, 0x00000080, 0x000000e2,
0x000000eb, 0x00000027, 0x000000b2, 0x00000075,
0x00000009, 0x00000083, 0x0000002c, 0x0000001a,
0x0000001b, 0x0000006e, 0x0000005a, 0x000000a0,
0x00000052, 0x0000003b, 0x000000d6, 0x000000b3,
0x00000029, 0x000000e3, 0x0000002f, 0x00000084,
0x00000053, 0x000000d1, 0x00000000, 0x000000ed,
0x00000020, 0x000000fc, 0x000000b1, 0x0000005b,
0x0000006a, 0x000000cb, 0x000000be, 0x00000039,
0x0000004a, 0x0000004c, 0x00000058, 0x000000cf,
0x000000d0, 0x000000ef, 0x000000aa, 0x000000fb,
0x00000043, 0x0000004d, 0x00000033, 0x00000085,
0x00000045, 0x000000f9, 0x00000002, 0x0000007f,
0x00000050, 0x0000003c, 0x0000009f, 0x000000a8,
0x00000051, 0x000000a3, 0x00000040, 0x0000008f,
0x00000092, 0x0000009d, 0x00000038, 0x000000f5,
0x000000bc, 0x000000b6, 0x000000da, 0x00000021,
0x00000010, 0x000000ff, 0x000000f3, 0x000000d2,
0x000000cd, 0x0000000c, 0x00000013, 0x000000ec,
0x0000005f, 0x00000097, 0x00000044, 0x00000017,
0x000000c4, 0x000000a7, 0x0000007e, 0x0000003d,
0x00000064, 0x0000005d, 0x00000019, 0x00000073,
0x00000060, 0x00000081, 0x0000004f, 0x000000dc,
0x00000022, 0x0000002a, 0x00000090, 0x00000088,
0x00000046, 0x000000ee, 0x000000b8, 0x00000014,
0x000000de, 0x0000005e, 0x0000000b, 0x000000db,
0x000000e0, 0x00000032, 0x0000003a, 0x0000000a,
0x00000049, 0x00000006, 0x00000024, 0x0000005c,
0x000000c2, 0x000000d3, 0x000000ac, 0x00000062,
0x00000091, 0x00000095, 0x000000e4, 0x00000079,
0x000000e7, 0x000000c8, 0x00000037, 0x0000006d,
0x0000008d, 0x000000d5, 0x0000004e, 0x000000a9,
0x0000006c, 0x00000056, 0x000000f4, 0x000000ea,
0x00000065, 0x0000007a, 0x000000ae, 0x00000008,
0x000000ba, 0x00000078, 0x00000025, 0x0000002e,
0x0000001c, 0x000000a6, 0x000000b4, 0x000000c6,
0x000000e8, 0x000000dd, 0x00000074, 0x0000001f,
0x0000004b, 0x000000bd, 0x0000008b, 0x0000008a,
0x00000070, 0x0000003e, 0x000000b5, 0x00000066,
0x00000048, 0x00000003, 0x000000f6, 0x0000000e,
0x00000061, 0x00000035, 0x00000057, 0x000000b9,
0x00000086, 0x000000c1, 0x0000001d, 0x0000009e,
0x000000e1, 0x000000f8, 0x00000098, 0x00000011,
0x00000069, 0x000000d9, 0x0000008e, 0x00000094,
0x0000009b, 0x0000001e, 0x00000087, 0x000000e9,
0x000000ce, 0x00000055, 0x00000028, 0x000000df,
0x0000008c, 0x000000a1, 0x00000089, 0x0000000d,
0x000000bf, 0x000000e6, 0x00000042, 0x00000068,
0x00000041, 0x00000099, 0x0000002d, 0x0000000f,
0x000000b0, 0x00000054, 0x000000bb, 0x00000016,
}, {
0x00006300, 0x00007c00, 0x00007700, 0x00007b00,
0x0000f200, 0x00006b00, 0x00006f00, 0x0000c500,
0x00003000, 0x00000100, 0x00006700, 0x00002b00,
0x0000fe00, 0x0000d700, 0x0000ab00, 0x00007600,
0x0000ca00, 0x00008200, 0x0000c900, 0x00007d00,
0x0000fa00, 0x00005900, 0x00004700, 0x0000f000,
0x0000ad00, 0x0000d400, 0x0000a200, 0x0000af00,
0x00009c00, 0x0000a400, 0x00007200, 0x0000c000,
0x0000b700, 0x0000fd00, 0x00009300, 0x00002600,
0x00003600, 0x00003f00, 0x0000f700, 0x0000cc00,
0x00003400, 0x0000a500, 0x0000e500, 0x0000f100,
0x00007100, 0x0000d800, 0x00003100, 0x00001500,
0x00000400, 0x0000c700, 0x00002300, 0x0000c300,
0x00001800, 0x00009600, 0x00000500, 0x00009a00,
0x00000700, 0x00001200, 0x00008000, 0x0000e200,
0x0000eb00, 0x00002700, 0x0000b200, 0x00007500,
0x00000900, 0x00008300, 0x00002c00, 0x00001a00,
0x00001b00, 0x00006e00, 0x00005a00, 0x0000a000,
0x00005200, 0x00003b00, 0x0000d600, 0x0000b300,
0x00002900, 0x0000e300, 0x00002f00, 0x00008400,
0x00005300, 0x0000d100, 0x00000000, 0x0000ed00,
0x00002000, 0x0000fc00, 0x0000b100, 0x00005b00,
0x00006a00, 0x0000cb00, 0x0000be00, 0x00003900,
0x00004a00, 0x00004c00, 0x00005800, 0x0000cf00,
0x0000d000, 0x0000ef00, 0x0000aa00, 0x0000fb00,
0x00004300, 0x00004d00, 0x00003300, 0x00008500,
0x00004500, 0x0000f900, 0x00000200, 0x00007f00,
0x00005000, 0x00003c00, 0x00009f00, 0x0000a800,
0x00005100, 0x0000a300, 0x00004000, 0x00008f00,
0x00009200, 0x00009d00, 0x00003800, 0x0000f500,
0x0000bc00, 0x0000b600, 0x0000da00, 0x00002100,
0x00001000, 0x0000ff00, 0x0000f300, 0x0000d200,
0x0000cd00, 0x00000c00, 0x00001300, 0x0000ec00,
0x00005f00, 0x00009700, 0x00004400, 0x00001700,
0x0000c400, 0x0000a700, 0x00007e00, 0x00003d00,
0x00006400, 0x00005d00, 0x00001900, 0x00007300,
0x00006000, 0x00008100, 0x00004f00, 0x0000dc00,
0x00002200, 0x00002a00, 0x00009000, 0x00008800,
0x00004600, 0x0000ee00, 0x0000b800, 0x00001400,
0x0000de00, 0x00005e00, 0x00000b00, 0x0000db00,
0x0000e000, 0x00003200, 0x00003a00, 0x00000a00,
0x00004900, 0x00000600, 0x00002400, 0x00005c00,
0x0000c200, 0x0000d300, 0x0000ac00, 0x00006200,
0x00009100, 0x00009500, 0x0000e400, 0x00007900,
0x0000e700, 0x0000c800, 0x00003700, 0x00006d00,
0x00008d00, 0x0000d500, 0x00004e00, 0x0000a900,
0x00006c00, 0x00005600, 0x0000f400, 0x0000ea00,
0x00006500, 0x00007a00, 0x0000ae00, 0x00000800,
0x0000ba00, 0x00007800, 0x00002500, 0x00002e00,
0x00001c00, 0x0000a600, 0x0000b400, 0x0000c600,
0x0000e800, 0x0000dd00, 0x00007400, 0x00001f00,
0x00004b00, 0x0000bd00, 0x00008b00, 0x00008a00,
0x00007000, 0x00003e00, 0x0000b500, 0x00006600,
0x00004800, 0x00000300, 0x0000f600, 0x00000e00,
0x00006100, 0x00003500, 0x00005700, 0x0000b900,
0x00008600, 0x0000c100, 0x00001d00, 0x00009e00,
0x0000e100, 0x0000f800, 0x00009800, 0x00001100,
0x00006900, 0x0000d900, 0x00008e00, 0x00009400,
0x00009b00, 0x00001e00, 0x00008700, 0x0000e900,
0x0000ce00, 0x00005500, 0x00002800, 0x0000df00,
0x00008c00, 0x0000a100, 0x00008900, 0x00000d00,
0x0000bf00, 0x0000e600, 0x00004200, 0x00006800,
0x00004100, 0x00009900, 0x00002d00, 0x00000f00,
0x0000b000, 0x00005400, 0x0000bb00, 0x00001600,
}, {
0x00630000, 0x007c0000, 0x00770000, 0x007b0000,
0x00f20000, 0x006b0000, 0x006f0000, 0x00c50000,
0x00300000, 0x00010000, 0x00670000, 0x002b0000,
0x00fe0000, 0x00d70000, 0x00ab0000, 0x00760000,
0x00ca0000, 0x00820000, 0x00c90000, 0x007d0000,
0x00fa0000, 0x00590000, 0x00470000, 0x00f00000,
0x00ad0000, 0x00d40000, 0x00a20000, 0x00af0000,
0x009c0000, 0x00a40000, 0x00720000, 0x00c00000,
0x00b70000, 0x00fd0000, 0x00930000, 0x00260000,
0x00360000, 0x003f0000, 0x00f70000, 0x00cc0000,
0x00340000, 0x00a50000, 0x00e50000, 0x00f10000,
0x00710000, 0x00d80000, 0x00310000, 0x00150000,
0x00040000, 0x00c70000, 0x00230000, 0x00c30000,
0x00180000, 0x00960000, 0x00050000, 0x009a0000,
0x00070000, 0x00120000, 0x00800000, 0x00e20000,
0x00eb0000, 0x00270000, 0x00b20000, 0x00750000,
0x00090000, 0x00830000, 0x002c0000, 0x001a0000,
0x001b0000, 0x006e0000, 0x005a0000, 0x00a00000,
0x00520000, 0x003b0000, 0x00d60000, 0x00b30000,
0x00290000, 0x00e30000, 0x002f0000, 0x00840000,
0x00530000, 0x00d10000, 0x00000000, 0x00ed0000,
0x00200000, 0x00fc0000, 0x00b10000, 0x005b0000,
0x006a0000, 0x00cb0000, 0x00be0000, 0x00390000,
0x004a0000, 0x004c0000, 0x00580000, 0x00cf0000,
0x00d00000, 0x00ef0000, 0x00aa0000, 0x00fb0000,
0x00430000, 0x004d0000, 0x00330000, 0x00850000,
0x00450000, 0x00f90000, 0x00020000, 0x007f0000,
0x00500000, 0x003c0000, 0x009f0000, 0x00a80000,
0x00510000, 0x00a30000, 0x00400000, 0x008f0000,
0x00920000, 0x009d0000, 0x00380000, 0x00f50000,
0x00bc0000, 0x00b60000, 0x00da0000, 0x00210000,
0x00100000, 0x00ff0000, 0x00f30000, 0x00d20000,
0x00cd0000, 0x000c0000, 0x00130000, 0x00ec0000,
0x005f0000, 0x00970000, 0x00440000, 0x00170000,
0x00c40000, 0x00a70000, 0x007e0000, 0x003d0000,
0x00640000, 0x005d0000, 0x00190000, 0x00730000,
0x00600000, 0x00810000, 0x004f0000, 0x00dc0000,
0x00220000, 0x002a0000, 0x00900000, 0x00880000,
0x00460000, 0x00ee0000, 0x00b80000, 0x00140000,
0x00de0000, 0x005e0000, 0x000b0000, 0x00db0000,
0x00e00000, 0x00320000, 0x003a0000, 0x000a0000,
0x00490000, 0x00060000, 0x00240000, 0x005c0000,
0x00c20000, 0x00d30000, 0x00ac0000, 0x00620000,
0x00910000, 0x00950000, 0x00e40000, 0x00790000,
0x00e70000, 0x00c80000, 0x00370000, 0x006d0000,
0x008d0000, 0x00d50000, 0x004e0000, 0x00a90000,
0x006c0000, 0x00560000, 0x00f40000, 0x00ea0000,
0x00650000, 0x007a0000, 0x00ae0000, 0x00080000,
0x00ba0000, 0x00780000, 0x00250000, 0x002e0000,
0x001c0000, 0x00a60000, 0x00b40000, 0x00c60000,
0x00e80000, 0x00dd0000, 0x00740000, 0x001f0000,
0x004b0000, 0x00bd0000, 0x008b0000, 0x008a0000,
0x00700000, 0x003e0000, 0x00b50000, 0x00660000,
0x00480000, 0x00030000, 0x00f60000, 0x000e0000,
0x00610000, 0x00350000, 0x00570000, 0x00b90000,
0x00860000, 0x00c10000, 0x001d0000, 0x009e0000,
0x00e10000, 0x00f80000, 0x00980000, 0x00110000,
0x00690000, 0x00d90000, 0x008e0000, 0x00940000,
0x009b0000, 0x001e0000, 0x00870000, 0x00e90000,
0x00ce0000, 0x00550000, 0x00280000, 0x00df0000,
0x008c0000, 0x00a10000, 0x00890000, 0x000d0000,
0x00bf0000, 0x00e60000, 0x00420000, 0x00680000,
0x00410000, 0x00990000, 0x002d0000, 0x000f0000,
0x00b00000, 0x00540000, 0x00bb0000, 0x00160000,
}, {
0x63000000, 0x7c000000, 0x77000000, 0x7b000000,
0xf2000000, 0x6b000000, 0x6f000000, 0xc5000000,
0x30000000, 0x01000000, 0x67000000, 0x2b000000,
0xfe000000, 0xd7000000, 0xab000000, 0x76000000,
0xca000000, 0x82000000, 0xc9000000, 0x7d000000,
0xfa000000, 0x59000000, 0x47000000, 0xf0000000,
0xad000000, 0xd4000000, 0xa2000000, 0xaf000000,
0x9c000000, 0xa4000000, 0x72000000, 0xc0000000,
0xb7000000, 0xfd000000, 0x93000000, 0x26000000,
0x36000000, 0x3f000000, 0xf7000000, 0xcc000000,
0x34000000, 0xa5000000, 0xe5000000, 0xf1000000,
0x71000000, 0xd8000000, 0x31000000, 0x15000000,
0x04000000, 0xc7000000, 0x23000000, 0xc3000000,
0x18000000, 0x96000000, 0x05000000, 0x9a000000,
0x07000000, 0x12000000, 0x80000000, 0xe2000000,
0xeb000000, 0x27000000, 0xb2000000, 0x75000000,
0x09000000, 0x83000000, 0x2c000000, 0x1a000000,
0x1b000000, 0x6e000000, 0x5a000000, 0xa0000000,
0x52000000, 0x3b000000, 0xd6000000, 0xb3000000,
0x29000000, 0xe3000000, 0x2f000000, 0x84000000,
0x53000000, 0xd1000000, 0x00000000, 0xed000000,
0x20000000, 0xfc000000, 0xb1000000, 0x5b000000,
0x6a000000, 0xcb000000, 0xbe000000, 0x39000000,
0x4a000000, 0x4c000000, 0x58000000, 0xcf000000,
0xd0000000, 0xef000000, 0xaa000000, 0xfb000000,
0x43000000, 0x4d000000, 0x33000000, 0x85000000,
0x45000000, 0xf9000000, 0x02000000, 0x7f000000,
0x50000000, 0x3c000000, 0x9f000000, 0xa8000000,
0x51000000, 0xa3000000, 0x40000000, 0x8f000000,
0x92000000, 0x9d000000, 0x38000000, 0xf5000000,
0xbc000000, 0xb6000000, 0xda000000, 0x21000000,
0x10000000, 0xff000000, 0xf3000000, 0xd2000000,
0xcd000000, 0x0c000000, 0x13000000, 0xec000000,
0x5f000000, 0x97000000, 0x44000000, 0x17000000,
0xc4000000, 0xa7000000, 0x7e000000, 0x3d000000,
0x64000000, 0x5d000000, 0x19000000, 0x73000000,
0x60000000, 0x81000000, 0x4f000000, 0xdc000000,
0x22000000, 0x2a000000, 0x90000000, 0x88000000,
0x46000000, 0xee000000, 0xb8000000, 0x14000000,
0xde000000, 0x5e000000, 0x0b000000, 0xdb000000,
0xe0000000, 0x32000000, 0x3a000000, 0x0a000000,
0x49000000, 0x06000000, 0x24000000, 0x5c000000,
0xc2000000, 0xd3000000, 0xac000000, 0x62000000,
0x91000000, 0x95000000, 0xe4000000, 0x79000000,
0xe7000000, 0xc8000000, 0x37000000, 0x6d000000,
0x8d000000, 0xd5000000, 0x4e000000, 0xa9000000,
0x6c000000, 0x56000000, 0xf4000000, 0xea000000,
0x65000000, 0x7a000000, 0xae000000, 0x08000000,
0xba000000, 0x78000000, 0x25000000, 0x2e000000,
0x1c000000, 0xa6000000, 0xb4000000, 0xc6000000,
0xe8000000, 0xdd000000, 0x74000000, 0x1f000000,
0x4b000000, 0xbd000000, 0x8b000000, 0x8a000000,
0x70000000, 0x3e000000, 0xb5000000, 0x66000000,
0x48000000, 0x03000000, 0xf6000000, 0x0e000000,
0x61000000, 0x35000000, 0x57000000, 0xb9000000,
0x86000000, 0xc1000000, 0x1d000000, 0x9e000000,
0xe1000000, 0xf8000000, 0x98000000, 0x11000000,
0x69000000, 0xd9000000, 0x8e000000, 0x94000000,
0x9b000000, 0x1e000000, 0x87000000, 0xe9000000,
0xce000000, 0x55000000, 0x28000000, 0xdf000000,
0x8c000000, 0xa1000000, 0x89000000, 0x0d000000,
0xbf000000, 0xe6000000, 0x42000000, 0x68000000,
0x41000000, 0x99000000, 0x2d000000, 0x0f000000,
0xb0000000, 0x54000000, 0xbb000000, 0x16000000,
}
};
/* initialise the key schedule from the user supplied key */
static uint32_t aes_ror32(uint32_t word, unsigned int shift)
{
return (word >> shift) | (word << (32 - shift));
}
#define cpu_to_le32(x) SDL_SwapLE32(x)
#define le32_to_cpu(x) SDL_SwapLE32(x)
#define star_x(x) (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b)
#define imix_col(y, x) do { \
u = star_x(x); \
v = star_x(u); \
w = star_x(v); \
t = w ^ (x); \
(y) = u ^ v ^ w; \
(y) ^= aes_ror32(u ^ t, 8) ^ \
aes_ror32(v ^ t, 16) ^ \
aes_ror32(t, 24); \
} while (0)
#define ls_box(x) \
crypto_fl_tab[0][get_byte(x, 0)] ^ \
crypto_fl_tab[1][get_byte(x, 1)] ^ \
crypto_fl_tab[2][get_byte(x, 2)] ^ \
crypto_fl_tab[3][get_byte(x, 3)]
#define loop4(i) do { \
t = aes_ror32(t, 8); \
t = ls_box(t) ^ rco_tab[i]; \
t ^= ctx->key_enc[4 * i]; \
ctx->key_enc[4 * i + 4] = t; \
t ^= ctx->key_enc[4 * i + 1]; \
ctx->key_enc[4 * i + 5] = t; \
t ^= ctx->key_enc[4 * i + 2]; \
ctx->key_enc[4 * i + 6] = t; \
t ^= ctx->key_enc[4 * i + 3]; \
ctx->key_enc[4 * i + 7] = t; \
} while (0)
#define loop6(i) do { \
t = aes_ror32(t, 8); \
t = ls_box(t) ^ rco_tab[i]; \
t ^= ctx->key_enc[6 * i]; \
ctx->key_enc[6 * i + 6] = t; \
t ^= ctx->key_enc[6 * i + 1]; \
ctx->key_enc[6 * i + 7] = t; \
t ^= ctx->key_enc[6 * i + 2]; \
ctx->key_enc[6 * i + 8] = t; \
t ^= ctx->key_enc[6 * i + 3]; \
ctx->key_enc[6 * i + 9] = t; \
t ^= ctx->key_enc[6 * i + 4]; \
ctx->key_enc[6 * i + 10] = t; \
t ^= ctx->key_enc[6 * i + 5]; \
ctx->key_enc[6 * i + 11] = t; \
} while (0)
#define loop8tophalf(i) do { \
t = aes_ror32(t, 8); \
t = ls_box(t) ^ rco_tab[i]; \
t ^= ctx->key_enc[8 * i]; \
ctx->key_enc[8 * i + 8] = t; \
t ^= ctx->key_enc[8 * i + 1]; \
ctx->key_enc[8 * i + 9] = t; \
t ^= ctx->key_enc[8 * i + 2]; \
ctx->key_enc[8 * i + 10] = t; \
t ^= ctx->key_enc[8 * i + 3]; \
ctx->key_enc[8 * i + 11] = t; \
} while (0)
#define loop8(i) do { \
loop8tophalf(i); \
t = ctx->key_enc[8 * i + 4] ^ ls_box(t); \
ctx->key_enc[8 * i + 12] = t; \
t ^= ctx->key_enc[8 * i + 5]; \
ctx->key_enc[8 * i + 13] = t; \
t ^= ctx->key_enc[8 * i + 6]; \
ctx->key_enc[8 * i + 14] = t; \
t ^= ctx->key_enc[8 * i + 7]; \
ctx->key_enc[8 * i + 15] = t; \
} while (0)
/**
* AES_ExpandKey - Expands the AES key as described in FIPS-197
* @ctx: The location where the computed key will be stored.
* @in_key: The supplied key.
* @key_len: The length of the supplied key.
*
* Returns 0 on success. The function fails only if an invalid key size (or
* pointer) is supplied.
* The expanded key size is 240 bytes (max of 14 rounds with a unique 16 bytes
* key schedule plus a 16 bytes key which is used before the first round).
* The decryption key is prepared for the "Equivalent Inverse Cipher" as
* described in FIPS-197. The first slot (16 bytes) of each key (enc or dec) is
* for the initial combination, the second slot for the first round and so on.
*/
static int AES_ExpandKey(aes_context_t *ctx, const uint8_t *in_key,
unsigned int key_len)
{
const uint32_t *key = (const uint32_t *)in_key;
uint32_t i, t, u, v, w, j;
if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 &&
key_len != AES_KEYSIZE_256)
return -1;
ctx->key_length = key_len;
ctx->key_dec[key_len + 24] = ctx->key_enc[0] = le32_to_cpu(key[0]);
ctx->key_dec[key_len + 25] = ctx->key_enc[1] = le32_to_cpu(key[1]);
ctx->key_dec[key_len + 26] = ctx->key_enc[2] = le32_to_cpu(key[2]);
ctx->key_dec[key_len + 27] = ctx->key_enc[3] = le32_to_cpu(key[3]);
switch (key_len) {
case AES_KEYSIZE_128:
t = ctx->key_enc[3];
for (i = 0; i < 10; ++i)
loop4(i);
break;
case AES_KEYSIZE_192:
ctx->key_enc[4] = le32_to_cpu(key[4]);
t = ctx->key_enc[5] = le32_to_cpu(key[5]);
for (i = 0; i < 8; ++i)
loop6(i);
break;
case AES_KEYSIZE_256:
ctx->key_enc[4] = le32_to_cpu(key[4]);
ctx->key_enc[5] = le32_to_cpu(key[5]);
ctx->key_enc[6] = le32_to_cpu(key[6]);
t = ctx->key_enc[7] = le32_to_cpu(key[7]);
for (i = 0; i < 6; ++i)
loop8(i);
loop8tophalf(i);
break;
}
ctx->key_dec[0] = ctx->key_enc[key_len + 24];
ctx->key_dec[1] = ctx->key_enc[key_len + 25];
ctx->key_dec[2] = ctx->key_enc[key_len + 26];
ctx->key_dec[3] = ctx->key_enc[key_len + 27];
for (i = 4; i < key_len + 24; ++i) {
j = key_len + 24 - (i & ~3) + (i & 3);
imix_col(ctx->key_dec[j], ctx->key_enc[i]);
}
return 0;
}
/**
* AES_SetKey - Set the AES key.
* @ctx: AES context struct.
* @in_key: The input key.
* @key_len: The size of the key.
*
* Returns 0 on success, on failure -1 is returned.
* The function uses AES_ExpandKey() to expand the key.
*/
static int AES_SetKey(aes_context_t *ctx, const uint8_t *in_key,
unsigned int key_len)
{
int ret;
ret = AES_ExpandKey(ctx, in_key, key_len);
if (!ret)
return 0;
return -1;
}
/* encrypt a block of text */
#define f_rn(bo, bi, n, k) do { \
bo[n] = crypto_ft_tab[0][get_byte(bi[n], 0)] ^ \
crypto_ft_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \
crypto_ft_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \
crypto_ft_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \
} while (0)
#define f_nround(bo, bi, k) do {\
f_rn(bo, bi, 0, k); \
f_rn(bo, bi, 1, k); \
f_rn(bo, bi, 2, k); \
f_rn(bo, bi, 3, k); \
k += 4; \
} while (0)
#define f_rl(bo, bi, n, k) do { \
bo[n] = crypto_fl_tab[0][get_byte(bi[n], 0)] ^ \
crypto_fl_tab[1][get_byte(bi[(n + 1) & 3], 1)] ^ \
crypto_fl_tab[2][get_byte(bi[(n + 2) & 3], 2)] ^ \
crypto_fl_tab[3][get_byte(bi[(n + 3) & 3], 3)] ^ *(k + n); \
} while (0)
#define f_lround(bo, bi, k) do {\
f_rl(bo, bi, 0, k); \
f_rl(bo, bi, 1, k); \
f_rl(bo, bi, 2, k); \
f_rl(bo, bi, 3, k); \
} while (0)
static void AES_Encrypt(aes_context_t *ctx, uint8_t *out,
const uint8_t *in)
{
const uint32_t *src = (const uint32_t *)in;
uint32_t *dst = (uint32_t *)out;
uint32_t b0[4], b1[4];
const uint32_t *kp = ctx->key_enc + 4;
const int key_len = ctx->key_length;
b0[0] = le32_to_cpu(src[0]) ^ ctx->key_enc[0];
b0[1] = le32_to_cpu(src[1]) ^ ctx->key_enc[1];
b0[2] = le32_to_cpu(src[2]) ^ ctx->key_enc[2];
b0[3] = le32_to_cpu(src[3]) ^ ctx->key_enc[3];
if (key_len > 24) {
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
}
if (key_len > 16) {
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
}
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
f_nround(b1, b0, kp);
f_nround(b0, b1, kp);
f_nround(b1, b0, kp);
f_lround(b0, b1, kp);
dst[0] = cpu_to_le32(b0[0]);
dst[1] = cpu_to_le32(b0[1]);
dst[2] = cpu_to_le32(b0[2]);
dst[3] = cpu_to_le32(b0[3]);
}
static boolean prng_enabled = false;
static aes_context_t prng_context;
static uint32_t prng_input_counter;
static uint32_t prng_values[4];
static unsigned int prng_value_index = 0;
// Initialize Pseudo-RNG using the specified 128-bit key.
void PRNG_Start(prng_seed_t key)
{
AES_SetKey(&prng_context, key, sizeof(prng_seed_t));
prng_value_index = 4;
prng_input_counter = 0;
prng_enabled = true;
}
void PRNG_Stop(void)
{
prng_enabled = false;
}
// Generate a set of new PRNG values by encrypting a new block.
static void PRNG_Generate(void)
{
byte input[16], output[16];
unsigned int i;
// Input for the cipher is a consecutively increasing 32-bit counter.
for (i = 0; i < 4; ++i)
{
input[4*i] = prng_input_counter & 0xff;
input[4*i + 1] = (prng_input_counter >> 8) & 0xff;
input[4*i + 2] = (prng_input_counter >> 16) & 0xff;
input[4*i + 3] = (prng_input_counter >> 24) & 0xff;
++prng_input_counter;
}
AES_Encrypt(&prng_context, output, input);
for (i = 0; i < 4; ++i)
{
prng_values[i] = output[4*i]
| (output[4*i + 1] << 8)
| (output[4*i + 2] << 16)
| (output[4*i + 3] << 24);
}
prng_value_index = 0;
}
// Read a random 32-bit integer from the PRNG.
unsigned int PRNG_Random(void)
{
unsigned int result;
if (!prng_enabled)
{
return 0;
}
if (prng_value_index >= 4)
{
PRNG_Generate();
}
result = prng_values[prng_value_index];
++prng_value_index;
return result;
}

32
src/aes_prng.h Normal file
View File

@@ -0,0 +1,32 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Pseudo-random number generator for secure demos.
//
#ifndef __AES_PRNG_H__
#define __AES_PRNG_H__
#include "doomtype.h"
// Nonce value used as random seed for secure demos.
typedef byte prng_seed_t[16];
void PRNG_Start(prng_seed_t seed);
void PRNG_Stop(void);
unsigned int PRNG_Random(void);
#endif /* #ifndef __AES_PRNG_H__ */

1174
src/am_map.c Normal file

File diff suppressed because it is too large Load Diff

47
src/am_map.h Normal file
View File

@@ -0,0 +1,47 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// AutoMap module.
//
#ifndef __AMMAP_H__
#define __AMMAP_H__
#include "d_event.h"
#include "doomtype.h"
#include "m_cheat.h"
// Used by ST StatusBar stuff.
#define AM_MSGHEADER (('a' << 24) + ('m' << 16))
#define AM_MSGENTERED (AM_MSGHEADER | ('e' << 8))
#define AM_MSGEXITED (AM_MSGHEADER | ('x' << 8))
// Called by main loop.
boolean AM_Responder(event_t *ev);
// Called by main loop.
void AM_Ticker(void);
// Called by main loop,
// called instead of view drawer if automap active.
void AM_Drawer(void);
// Called to force the automap to quit
// if the level is completed while it is up.
void AM_Stop(void);
extern cheatseq_t cheat_amap;
#endif

631
src/d_englsh.h Normal file
View File

@@ -0,0 +1,631 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Printed strings for translation.
// English language support (default).
//
#ifndef __D_ENGLSH__
#define __D_ENGLSH__
//
// Printed strings for translation
//
//
// D_Main.C
//
#define D_DEVSTR "Development mode ON.\n"
//
// M_Menu.C
//
#define PRESSKEY "press a key."
#define PRESSYN "press y or n."
#define QUITMSG "are you sure you want to\nquit this great game?"
#define LOADNET "you can't do load while in a net game!\n\n" PRESSKEY
#define QLOADNET "you can't quickload during a netgame!\n\n" PRESSKEY
#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n" PRESSKEY
#define SAVEDEAD "you can't save if you aren't playing!\n\n" PRESSKEY
#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n" PRESSYN
#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n" PRESSYN
#define NEWGAME \
"you can't start a new game\n" \
"while in a network game.\n\n" PRESSKEY
#define NIGHTMARE \
"are you sure? this skill level\n" \
"isn't even remotely fair.\n\n" PRESSYN
#define SWSTRING \
"this is the shareware version of doom.\n\n" \
"you need to order the entire trilogy.\n\n" PRESSKEY
#define MSGOFF "Messages OFF"
#define MSGON "Messages ON"
#define NETEND "you can't end a netgame!\n\n" PRESSKEY
#define ENDGAME "are you sure you want to end the game?\n\n" PRESSYN
#define DOSY "(press y to quit to dos.)"
#define DETAILHI "High detail"
#define DETAILLO "Low detail"
#define GAMMALVL0 "Gamma correction OFF"
#define GAMMALVL1 "Gamma correction level 1"
#define GAMMALVL2 "Gamma correction level 2"
#define GAMMALVL3 "Gamma correction level 3"
#define GAMMALVL4 "Gamma correction level 4"
#define EMPTYSTRING "empty slot"
//
// P_inter.C
//
#define GOTARMOR "Picked up the armor."
#define GOTMEGA "Picked up the MegaArmor!"
#define GOTHTHBONUS "Picked up a health bonus."
#define GOTARMBONUS "Picked up an armor bonus."
#define GOTSTIM "Picked up a stimpack."
#define GOTMEDINEED "Picked up a medikit that you REALLY need!"
#define GOTMEDIKIT "Picked up a medikit."
#define GOTSUPER "Supercharge!"
#define GOTBLUECARD "Picked up a blue keycard."
#define GOTYELWCARD "Picked up a yellow keycard."
#define GOTREDCARD "Picked up a red keycard."
#define GOTBLUESKUL "Picked up a blue skull key."
#define GOTYELWSKUL "Picked up a yellow skull key."
#define GOTREDSKULL "Picked up a red skull key."
#define GOTINVUL "Invulnerability!"
#define GOTBERSERK "Berserk!"
#define GOTINVIS "Partial Invisibility"
#define GOTSUIT "Radiation Shielding Suit"
#define GOTMAP "Computer Area Map"
#define GOTVISOR "Light Amplification Visor"
#define GOTMSPHERE "MegaSphere!"
#define GOTCLIP "Picked up a clip."
#define GOTCLIPBOX "Picked up a box of bullets."
#define GOTROCKET "Picked up a rocket."
#define GOTROCKBOX "Picked up a box of rockets."
#define GOTCELL "Picked up an energy cell."
#define GOTCELLBOX "Picked up an energy cell pack."
#define GOTSHELLS "Picked up 4 shotgun shells."
#define GOTSHELLBOX "Picked up a box of shotgun shells."
#define GOTBACKPACK "Picked up a backpack full of ammo!"
#define GOTBFG9000 "You got the BFG9000! Oh, yes."
#define GOTCHAINGUN "You got the chaingun!"
#define GOTCHAINSAW "A chainsaw! Find some meat!"
#define GOTLAUNCHER "You got the rocket launcher!"
#define GOTPLASMA "You got the plasma gun!"
#define GOTSHOTGUN "You got the shotgun!"
#define GOTSHOTGUN2 "You got the super shotgun!"
//
// P_Doors.C
//
#define PD_BLUEO "You need a blue key to activate this object"
#define PD_REDO "You need a red key to activate this object"
#define PD_YELLOWO "You need a yellow key to activate this object"
#define PD_BLUEK "You need a blue key to open this door"
#define PD_REDK "You need a red key to open this door"
#define PD_YELLOWK "You need a yellow key to open this door"
//
// G_game.C
//
#define GGSAVED "game saved."
//
// HU_stuff.C
//
#define HUSTR_MSGU "[Message unsent]"
#define HUSTR_E1M1 "E1M1: Hangar"
#define HUSTR_E1M2 "E1M2: Nuclear Plant"
#define HUSTR_E1M3 "E1M3: Toxin Refinery"
#define HUSTR_E1M4 "E1M4: Command Control"
#define HUSTR_E1M5 "E1M5: Phobos Lab"
#define HUSTR_E1M6 "E1M6: Central Processing"
#define HUSTR_E1M7 "E1M7: Computer Station"
#define HUSTR_E1M8 "E1M8: Phobos Anomaly"
#define HUSTR_E1M9 "E1M9: Military Base"
#define HUSTR_E2M1 "E2M1: Deimos Anomaly"
#define HUSTR_E2M2 "E2M2: Containment Area"
#define HUSTR_E2M3 "E2M3: Refinery"
#define HUSTR_E2M4 "E2M4: Deimos Lab"
#define HUSTR_E2M5 "E2M5: Command Center"
#define HUSTR_E2M6 "E2M6: Halls of the Damned"
#define HUSTR_E2M7 "E2M7: Spawning Vats"
#define HUSTR_E2M8 "E2M8: Tower of Babel"
#define HUSTR_E2M9 "E2M9: Fortress of Mystery"
#define HUSTR_E3M1 "E3M1: Hell Keep"
#define HUSTR_E3M2 "E3M2: Slough of Despair"
#define HUSTR_E3M3 "E3M3: Pandemonium"
#define HUSTR_E3M4 "E3M4: House of Pain"
#define HUSTR_E3M5 "E3M5: Unholy Cathedral"
#define HUSTR_E3M6 "E3M6: Mt. Erebus"
#define HUSTR_E3M7 "E3M7: Limbo"
#define HUSTR_E3M8 "E3M8: Dis"
#define HUSTR_E3M9 "E3M9: Warrens"
#define HUSTR_E4M1 "E4M1: Hell Beneath"
#define HUSTR_E4M2 "E4M2: Perfect Hatred"
#define HUSTR_E4M3 "E4M3: Sever The Wicked"
#define HUSTR_E4M4 "E4M4: Unruly Evil"
#define HUSTR_E4M5 "E4M5: They Will Repent"
#define HUSTR_E4M6 "E4M6: Against Thee Wickedly"
#define HUSTR_E4M7 "E4M7: And Hell Followed"
#define HUSTR_E4M8 "E4M8: Unto The Cruel"
#define HUSTR_E4M9 "E4M9: Fear"
#define HUSTR_1 "level 1: entryway"
#define HUSTR_2 "level 2: underhalls"
#define HUSTR_3 "level 3: the gantlet"
#define HUSTR_4 "level 4: the focus"
#define HUSTR_5 "level 5: the waste tunnels"
#define HUSTR_6 "level 6: the crusher"
#define HUSTR_7 "level 7: dead simple"
#define HUSTR_8 "level 8: tricks and traps"
#define HUSTR_9 "level 9: the pit"
#define HUSTR_10 "level 10: refueling base"
#define HUSTR_11 "level 11: 'o' of destruction!"
#define HUSTR_12 "level 12: the factory"
#define HUSTR_13 "level 13: downtown"
#define HUSTR_14 "level 14: the inmost dens"
#define HUSTR_15 "level 15: industrial zone"
#define HUSTR_16 "level 16: suburbs"
#define HUSTR_17 "level 17: tenements"
#define HUSTR_18 "level 18: the courtyard"
#define HUSTR_19 "level 19: the citadel"
#define HUSTR_20 "level 20: gotcha!"
#define HUSTR_21 "level 21: nirvana"
#define HUSTR_22 "level 22: the catacombs"
#define HUSTR_23 "level 23: barrels o' fun"
#define HUSTR_24 "level 24: the chasm"
#define HUSTR_25 "level 25: bloodfalls"
#define HUSTR_26 "level 26: the abandoned mines"
#define HUSTR_27 "level 27: monster condo"
#define HUSTR_28 "level 28: the spirit world"
#define HUSTR_29 "level 29: the living end"
#define HUSTR_30 "level 30: icon of sin"
#define HUSTR_31 "level 31: wolfenstein"
#define HUSTR_32 "level 32: grosse"
#define PHUSTR_1 "level 1: congo"
#define PHUSTR_2 "level 2: well of souls"
#define PHUSTR_3 "level 3: aztec"
#define PHUSTR_4 "level 4: caged"
#define PHUSTR_5 "level 5: ghost town"
#define PHUSTR_6 "level 6: baron's lair"
#define PHUSTR_7 "level 7: caughtyard"
#define PHUSTR_8 "level 8: realm"
#define PHUSTR_9 "level 9: abattoire"
#define PHUSTR_10 "level 10: onslaught"
#define PHUSTR_11 "level 11: hunted"
#define PHUSTR_12 "level 12: speed"
#define PHUSTR_13 "level 13: the crypt"
#define PHUSTR_14 "level 14: genesis"
#define PHUSTR_15 "level 15: the twilight"
#define PHUSTR_16 "level 16: the omen"
#define PHUSTR_17 "level 17: compound"
#define PHUSTR_18 "level 18: neurosphere"
#define PHUSTR_19 "level 19: nme"
#define PHUSTR_20 "level 20: the death domain"
#define PHUSTR_21 "level 21: slayer"
#define PHUSTR_22 "level 22: impossible mission"
#define PHUSTR_23 "level 23: tombstone"
#define PHUSTR_24 "level 24: the final frontier"
#define PHUSTR_25 "level 25: the temple of darkness"
#define PHUSTR_26 "level 26: bunker"
#define PHUSTR_27 "level 27: anti-christ"
#define PHUSTR_28 "level 28: the sewers"
#define PHUSTR_29 "level 29: odyssey of noises"
#define PHUSTR_30 "level 30: the gateway of hell"
#define PHUSTR_31 "level 31: cyberden"
#define PHUSTR_32 "level 32: go 2 it"
#define THUSTR_1 "level 1: system control"
#define THUSTR_2 "level 2: human bbq"
#define THUSTR_3 "level 3: power control"
#define THUSTR_4 "level 4: wormhole"
#define THUSTR_5 "level 5: hanger"
#define THUSTR_6 "level 6: open season"
#define THUSTR_7 "level 7: prison"
#define THUSTR_8 "level 8: metal"
#define THUSTR_9 "level 9: stronghold"
#define THUSTR_10 "level 10: redemption"
#define THUSTR_11 "level 11: storage facility"
#define THUSTR_12 "level 12: crater"
#define THUSTR_13 "level 13: nukage processing"
#define THUSTR_14 "level 14: steel works"
#define THUSTR_15 "level 15: dead zone"
#define THUSTR_16 "level 16: deepest reaches"
#define THUSTR_17 "level 17: processing area"
#define THUSTR_18 "level 18: mill"
#define THUSTR_19 "level 19: shipping/respawning"
#define THUSTR_20 "level 20: central processing"
#define THUSTR_21 "level 21: administration center"
#define THUSTR_22 "level 22: habitat"
#define THUSTR_23 "level 23: lunar mining project"
#define THUSTR_24 "level 24: quarry"
#define THUSTR_25 "level 25: baron's den"
#define THUSTR_26 "level 26: ballistyx"
#define THUSTR_27 "level 27: mount pain"
#define THUSTR_28 "level 28: heck"
#define THUSTR_29 "level 29: river styx"
#define THUSTR_30 "level 30: last call"
#define THUSTR_31 "level 31: pharaoh"
#define THUSTR_32 "level 32: caribbean"
#define HUSTR_CHATMACRO1 "I'm ready to kick butt!"
#define HUSTR_CHATMACRO2 "I'm OK."
#define HUSTR_CHATMACRO3 "I'm not looking too good!"
#define HUSTR_CHATMACRO4 "Help!"
#define HUSTR_CHATMACRO5 "You suck!"
#define HUSTR_CHATMACRO6 "Next time, scumbag..."
#define HUSTR_CHATMACRO7 "Come here!"
#define HUSTR_CHATMACRO8 "I'll take care of it."
#define HUSTR_CHATMACRO9 "Yes"
#define HUSTR_CHATMACRO0 "No"
#define HUSTR_TALKTOSELF1 "You mumble to yourself"
#define HUSTR_TALKTOSELF2 "Who's there?"
#define HUSTR_TALKTOSELF3 "You scare yourself"
#define HUSTR_TALKTOSELF4 "You start to rave"
#define HUSTR_TALKTOSELF5 "You've lost it..."
#define HUSTR_MESSAGESENT "[Message Sent]"
// The following should NOT be changed unless it seems
// just AWFULLY necessary
#define HUSTR_PLRGREEN "Green: "
#define HUSTR_PLRINDIGO "Indigo: "
#define HUSTR_PLRBROWN "Brown: "
#define HUSTR_PLRRED "Red: "
#define HUSTR_KEYGREEN 'g'
#define HUSTR_KEYINDIGO 'i'
#define HUSTR_KEYBROWN 'b'
#define HUSTR_KEYRED 'r'
//
// AM_map.C
//
#define AMSTR_FOLLOWON "Follow Mode ON"
#define AMSTR_FOLLOWOFF "Follow Mode OFF"
#define AMSTR_GRIDON "Grid ON"
#define AMSTR_GRIDOFF "Grid OFF"
#define AMSTR_MARKEDSPOT "Marked Spot"
#define AMSTR_MARKSCLEARED "All Marks Cleared"
//
// ST_stuff.C
//
#define STSTR_MUS "Music Change"
#define STSTR_NOMUS "IMPOSSIBLE SELECTION"
#define STSTR_DQDON "Degreelessness Mode On"
#define STSTR_DQDOFF "Degreelessness Mode Off"
#define STSTR_KFAADDED "Very Happy Ammo Added"
#define STSTR_FAADDED "Ammo (no keys) Added"
#define STSTR_NCON "No Clipping Mode ON"
#define STSTR_NCOFF "No Clipping Mode OFF"
#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp"
#define STSTR_BEHOLDX "Power-up Toggled"
#define STSTR_CHOPPERS "... doesn't suck - GM"
#define STSTR_CLEV "Changing Level..."
#define E2TEXT \
"You've done it! The hideous cyber-\n" \
"demon lord that ruled the lost Deimos\n" \
"moon base has been slain and you\n" \
"are triumphant! But ... where are\n" \
"you? You clamber to the edge of the\n" \
"moon and look down to see the awful\n" \
"truth.\n" \
"\n" \
"Deimos floats above Hell itself!\n" \
"You've never heard of anyone escaping\n" \
"from Hell, but you'll make the bastards\n" \
"sorry they ever heard of you! Quickly,\n" \
"you rappel down to the surface of\n" \
"Hell.\n" \
"\n" \
"Now, it's on to the final chapter of\n" \
"DOOM! -- Inferno."
#define E3TEXT \
"The loathsome spiderdemon that\n" \
"masterminded the invasion of the moon\n" \
"bases and caused so much death has had\n" \
"its ass kicked for all time.\n" \
"\n" \
"A hidden doorway opens and you enter.\n" \
"You've proven too tough for Hell to\n" \
"contain, and now Hell at last plays\n" \
"fair -- for you emerge from the door\n" \
"to see the green fields of Earth!\n" \
"Home at last.\n" \
"\n" \
"You wonder what's been happening on\n" \
"Earth while you were battling evil\n" \
"unleashed. It's good that no Hell-\n" \
"spawn could have come through that\n" \
"door with you ..."
#define E4TEXT \
"the spider mastermind must have sent forth\n" \
"its legions of hellspawn before your\n" \
"final confrontation with that terrible\n" \
"beast from hell. but you stepped forward\n" \
"and brought forth eternal damnation and\n" \
"suffering upon the horde as a true hero\n" \
"would in the face of something so evil.\n" \
"\n" \
"besides, someone was gonna pay for what\n" \
"happened to daisy, your pet rabbit.\n" \
"\n" \
"but now, you see spread before you more\n" \
"potential pain and gibbitude as a nation\n" \
"of demons run amok among our cities.\n" \
"\n" \
"next stop, hell on earth!"
// after level 6, put this:
#define C1TEXT \
"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \
"STARPORT. BUT SOMETHING IS WRONG. THE\n" \
"MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \
"WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \
"IS BEING SUBVERTED BY THEIR PRESENCE.\n" \
"\n" \
"AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \
"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \
"YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \
"OF THE STARBASE AND FIND THE CONTROLLING\n" \
"SWITCH WHICH HOLDS EARTH'S POPULATION\n" \
"HOSTAGE."
// After level 11, put this:
#define C2TEXT \
"YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \
"HUMANKIND TO EVACUATE EARTH AND ESCAPE\n" \
"THE NIGHTMARE. NOW YOU ARE THE ONLY\n" \
"HUMAN LEFT ON THE FACE OF THE PLANET.\n" \
"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n" \
"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n" \
"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n" \
"THAT YOU HAVE SAVED YOUR SPECIES.\n" \
"\n" \
"BUT THEN, EARTH CONTROL BEAMS DOWN A\n" \
"MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n" \
"THE SOURCE OF THE ALIEN INVASION. IF YOU\n" \
"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n" \
"ENTRY. THE ALIEN BASE IS IN THE HEART OF\n" \
"YOUR OWN HOME CITY, NOT FAR FROM THE\n" \
"STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n" \
"UP AND RETURN TO THE FRAY."
// After level 20, put this:
#define C3TEXT \
"YOU ARE AT THE CORRUPT HEART OF THE CITY,\n" \
"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n" \
"YOU SEE NO WAY TO DESTROY THE CREATURES'\n" \
"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n" \
"TEETH AND PLUNGE THROUGH IT.\n" \
"\n" \
"THERE MUST BE A WAY TO CLOSE IT ON THE\n" \
"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n" \
"GOT TO GO THROUGH HELL TO GET TO IT?"
// After level 29, put this:
#define C4TEXT \
"THE HORRENDOUS VISAGE OF THE BIGGEST\n" \
"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n" \
"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n" \
"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n" \
"UP AND DIES, ITS THRASHING LIMBS\n" \
"DEVASTATING UNTOLD MILES OF HELL'S\n" \
"SURFACE.\n" \
"\n" \
"YOU'VE DONE IT. THE INVASION IS OVER.\n" \
"EARTH IS SAVED. HELL IS A WRECK. YOU\n" \
"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n" \
"DIE, NOW. WIPING THE SWEAT FROM YOUR\n" \
"FOREHEAD YOU BEGIN THE LONG TREK BACK\n" \
"HOME. REBUILDING EARTH OUGHT TO BE A\n" \
"LOT MORE FUN THAN RUINING IT WAS.\n"
// Before level 31, put this:
#define C5TEXT \
"CONGRATULATIONS, YOU'VE FOUND THE SECRET\n" \
"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n" \
"HUMANS, RATHER THAN DEMONS. YOU WONDER\n" \
"WHO THE INMATES OF THIS CORNER OF HELL\n" \
"WILL BE."
// Before level 32, put this:
#define C6TEXT \
"CONGRATULATIONS, YOU'VE FOUND THE\n" \
"SUPER SECRET LEVEL! YOU'D BETTER\n" \
"BLAZE THROUGH THIS ONE!\n"
// after map 06
#define P1TEXT \
"You gloat over the steaming carcass of the\n" \
"Guardian. With its death, you've wrested\n" \
"the Accelerator from the stinking claws\n" \
"of Hell. You relax and glance around the\n" \
"room. Damn! There was supposed to be at\n" \
"least one working prototype, but you can't\n" \
"see it. The demons must have taken it.\n" \
"\n" \
"You must find the prototype, or all your\n" \
"struggles will have been wasted. Keep\n" \
"moving, keep fighting, keep killing.\n" \
"Oh yes, keep living, too."
// after map 11
#define P2TEXT \
"Even the deadly Arch-Vile labyrinth could\n" \
"not stop you, and you've gotten to the\n" \
"prototype Accelerator which is soon\n" \
"efficiently and permanently deactivated.\n" \
"\n" \
"You're good at that kind of thing."
// after map 20
#define P3TEXT \
"You've bashed and battered your way into\n" \
"the heart of the devil-hive. Time for a\n" \
"Search-and-Destroy mission, aimed at the\n" \
"Gatekeeper, whose foul offspring is\n" \
"cascading to Earth. Yeah, he's bad. But\n" \
"you know who's worse!\n" \
"\n" \
"Grinning evilly, you check your gear, and\n" \
"get ready to give the bastard a little Hell\n" \
"of your own making!"
// after map 30
#define P4TEXT \
"The Gatekeeper's evil face is splattered\n" \
"all over the place. As its tattered corpse\n" \
"collapses, an inverted Gate forms and\n" \
"sucks down the shards of the last\n" \
"prototype Accelerator, not to mention the\n" \
"few remaining demons. You're done. Hell\n" \
"has gone back to pounding bad dead folks \n" \
"instead of good live ones. Remember to\n" \
"tell your grandkids to put a rocket\n" \
"launcher in your coffin. If you go to Hell\n" \
"when you die, you'll need it for some\n" \
"final cleaning-up ..."
// before map 31
#define P5TEXT \
"You've found the second-hardest level we\n" \
"got. Hope you have a saved game a level or\n" \
"two previous. If not, be prepared to die\n" \
"aplenty. For master marines only."
// before map 32
#define P6TEXT \
"Betcha wondered just what WAS the hardest\n" \
"level we had ready for ya? Now you know.\n" \
"No one gets out alive."
#define T1TEXT \
"You've fought your way out of the infested\n" \
"experimental labs. It seems that UAC has\n" \
"once again gulped it down. With their\n" \
"high turnover, it must be hard for poor\n" \
"old UAC to buy corporate health insurance\n" \
"nowadays..\n" \
"\n" \
"Ahead lies the military complex, now\n" \
"swarming with diseased horrors hot to get\n" \
"their teeth into you. With luck, the\n" \
"complex still has some warlike ordnance\n" \
"laying around."
#define T2TEXT \
"You hear the grinding of heavy machinery\n" \
"ahead. You sure hope they're not stamping\n" \
"out new hellspawn, but you're ready to\n" \
"ream out a whole herd if you have to.\n" \
"They might be planning a blood feast, but\n" \
"you feel about as mean as two thousand\n" \
"maniacs packed into one mad killer.\n" \
"\n" \
"You don't plan to go down easy."
#define T3TEXT \
"The vista opening ahead looks real damn\n" \
"familiar. Smells familiar, too -- like\n" \
"fried excrement. You didn't like this\n" \
"place before, and you sure as hell ain't\n" \
"planning to like it now. The more you\n" \
"brood on it, the madder you get.\n" \
"Hefting your gun, an evil grin trickles\n" \
"onto your face. Time to take some names."
#define T4TEXT \
"Suddenly, all is silent, from one horizon\n" \
"to the other. The agonizing echo of Hell\n" \
"fades away, the nightmare sky turns to\n" \
"blue, the heaps of monster corpses start \n" \
"to evaporate along with the evil stench \n" \
"that filled the air. Jeeze, maybe you've\n" \
"done it. Have you really won?\n" \
"\n" \
"Something rumbles in the distance.\n" \
"A blue light begins to glow inside the\n" \
"ruined skull of the demon-spitter."
#define T5TEXT \
"What now? Looks totally different. Kind\n" \
"of like King Tut's condo. Well,\n" \
"whatever's here can't be any worse\n" \
"than usual. Can it? Or maybe it's best\n" \
"to let sleeping gods lie.."
#define T6TEXT \
"Time for a vacation. You've burst the\n" \
"bowels of hell and by golly you're ready\n" \
"for a break. You mutter to yourself,\n" \
"Maybe someone else can kick Hell's ass\n" \
"next time around. Ahead lies a quiet town,\n" \
"with peaceful flowing water, quaint\n" \
"buildings, and presumably no Hellspawn.\n" \
"\n" \
"As you step off the transport, you hear\n" \
"the stomp of a cyberdemon's iron shoe."
#endif

63
src/d_event.c Normal file
View File

@@ -0,0 +1,63 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// DESCRIPTION: Event handling.
//
// Events are asynchronous inputs generally generated by the game user.
// Events can be discarded if no responder claims them
//
#include <stdlib.h>
#include "d_event.h"
#define MAXEVENTS 64
static event_t events[MAXEVENTS];
static int eventhead;
static int eventtail;
//
// D_PostEvent
// Called by the I/O functions when input is detected
//
void D_PostEvent (event_t* ev)
{
events[eventhead] = *ev;
eventhead = (eventhead + 1) % MAXEVENTS;
}
// Read an event from the queue.
event_t *D_PopEvent(void)
{
event_t *result;
// No more events waiting.
if (eventtail == eventhead)
{
return NULL;
}
result = &events[eventtail];
// Advance to the next event in the queue.
eventtail = (eventtail + 1) % MAXEVENTS;
return result;
}

140
src/d_event.h Normal file
View File

@@ -0,0 +1,140 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
//
#ifndef __D_EVENT__
#define __D_EVENT__
//
// Event handling.
//
// Input event types.
typedef enum
{
// Key press/release events.
// data1: Key code (from doomkeys.h) of the key that was
// pressed or released. This is the key as it appears
// on a US keyboard layout, and does not change with
// layout.
// For ev_keydown only:
// data2: ASCII representation of the key that was pressed that
// changes with the keyboard layout; eg. if 'Z' is
// pressed on a German keyboard, data1='y',data2='z'.
// Not affected by modifier keys.
// data3: ASCII input, fully modified according to keyboard
// layout and any modifier keys that are held down.
// Only set if I_StartTextInput() has been called.
ev_keydown,
ev_keyup,
// Mouse movement event.
// data1: Bitfield of buttons currently held down.
// (bit 0 = left; bit 1 = right; bit 2 = middle).
// data2: X axis mouse movement (turn).
// data3: Y axis mouse movement (forward/backward).
ev_mouse,
// Joystick state.
// data1: Bitfield of buttons currently pressed.
// data2: X axis mouse movement (turn).
// data3: Y axis mouse movement (forward/backward).
// data4: Third axis mouse movement (strafe).
ev_joystick,
// Quit event. Triggered when the user clicks the "close" button
// to terminate the application.
ev_quit
} evtype_t;
// Event structure.
typedef struct
{
evtype_t type;
// Event-specific data; see the descriptions given above.
int data1, data2, data3, data4;
} event_t;
//
// Button/action code definitions.
//
typedef enum
{
// Press "Fire".
BT_ATTACK = 1,
// Use button, to open doors, activate switches.
BT_USE = 2,
// Flag: game events, not really buttons.
BT_SPECIAL = 128,
BT_SPECIALMASK = 3,
// Flag, weapon change pending.
// If true, the next 3 bits hold weapon num.
BT_CHANGE = 4,
// The 3bit weapon mask and shift, convenience.
BT_WEAPONMASK = (8+16+32),
BT_WEAPONSHIFT = 3,
// Pause the game.
BTS_PAUSE = 1,
// Save the game at each console.
BTS_SAVEGAME = 2,
// Savegame slot numbers
// occupy the second byte of buttons.
BTS_SAVEMASK = (4+8+16),
BTS_SAVESHIFT = 2,
} buttoncode_t;
// villsa [STRIFE] Strife specific buttons
// TODO - not finished
typedef enum
{
// Player view look up
BT2_LOOKUP = 1,
// Player view look down
BT2_LOOKDOWN = 2,
// Center player's view
BT2_CENTERVIEW = 4,
// Use inventory item
BT2_INVUSE = 8,
// Drop inventory item
BT2_INVDROP = 16,
// Jump up and down
BT2_JUMP = 32,
// Use medkit
BT2_HEALTH = 128,
} buttoncode2_t;
// Called by IO functions when input is detected.
void D_PostEvent (event_t *ev);
// Read an event from the event queue
event_t *D_PopEvent(void);
#endif

55
src/d_items.c Normal file
View File

@@ -0,0 +1,55 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
// We are referring to sprite numbers.
#include "info.h"
#include "d_items.h"
//
// PSPRITE ACTIONS for waepons.
// This struct controls the weapon animations.
//
// Each entry is:
// ammo/amunition type
// upstate
// downstate
// readystate
// atkstate, i.e. attack/fire/hit frame
// flashstate, muzzle flash
//
weaponinfo_t weaponinfo[NUMWEAPONS] = {
{// fist
am_noammo, S_PUNCHUP, S_PUNCHDOWN, S_PUNCH, S_PUNCH1, S_NULL},
{// pistol
am_clip, S_PISTOLUP, S_PISTOLDOWN, S_PISTOL, S_PISTOL1, S_PISTOLFLASH},
{// shotgun
am_shell, S_SGUNUP, S_SGUNDOWN, S_SGUN, S_SGUN1, S_SGUNFLASH1},
{// chaingun
am_clip, S_CHAINUP, S_CHAINDOWN, S_CHAIN, S_CHAIN1, S_CHAINFLASH1},
{// missile launcher
am_misl, S_MISSILEUP, S_MISSILEDOWN, S_MISSILE, S_MISSILE1,
S_MISSILEFLASH1},
{// plasma rifle
am_cell, S_PLASMAUP, S_PLASMADOWN, S_PLASMA, S_PLASMA1, S_PLASMAFLASH1},
{// bfg 9000
am_cell, S_BFGUP, S_BFGDOWN, S_BFG, S_BFG1, S_BFGFLASH1},
{// chainsaw
am_noammo, S_SAWUP, S_SAWDOWN, S_SAW, S_SAW1, S_NULL},
{// super shotgun
am_shell, S_DSGUNUP, S_DSGUNDOWN, S_DSGUN, S_DSGUN1, S_DSGUNFLASH1},
};

37
src/d_items.h Normal file
View File

@@ -0,0 +1,37 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Items: key cards, artifacts, weapon, ammunition.
//
#ifndef __D_ITEMS__
#define __D_ITEMS__
#include "doomdef.h"
// Weapon info: sprite frames, ammunition use.
typedef struct {
ammotype_t ammo;
int upstate;
int downstate;
int readystate;
int atkstate;
int flashstate;
} weaponinfo_t;
extern weaponinfo_t weaponinfo[NUMWEAPONS];
#endif

489
src/d_iwad.c Normal file
View File

@@ -0,0 +1,489 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Search for and locate an IWAD file, and initialize according
// to the IWAD type.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "d_iwad.h"
#include "doomtype.h"
#include "i_system.h"
#include "m_argv.h"
#include "m_misc.h"
static const iwad_t iwads[] =
{
{ "doom2.wad", doom2, commercial, "Doom II" },
{ "plutonia.wad", pack_plut, commercial, "Final Doom: Plutonia Experiment" },
{ "tnt.wad", pack_tnt, commercial, "Final Doom: TNT: Evilution" },
{ "doom.wad", doom, retail, "Doom" },
{ "doom1.wad", doom, shareware, "Doom Shareware" },
{ "chex.wad", pack_chex, retail, "Chex Quest" },
{ "hacx.wad", pack_hacx, commercial, "Hacx" },
{ "freedm.wad", doom2, commercial, "FreeDM" },
{ "freedoom2.wad", doom2, commercial, "Freedoom: Phase 2" },
{ "freedoom1.wad", doom, retail, "Freedoom: Phase 1" },
{ "heretic.wad", heretic, retail, "Heretic" },
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
{ "hexen.wad", hexen, commercial, "Hexen" },
//{ "strife0.wad", strife, commercial, "Strife" }, // haleyjd: STRIFE-FIXME
{ "strife1.wad", strife, commercial, "Strife" },
};
// Array of locations to search for IWAD files
//
// "128 IWAD search directories should be enough for anybody".
#define MAX_IWAD_DIRS 128
static boolean iwad_dirs_built = false;
static char *iwad_dirs[MAX_IWAD_DIRS];
static int num_iwad_dirs = 0;
static void AddIWADDir(char *dir)
{
if (num_iwad_dirs < MAX_IWAD_DIRS)
{
iwad_dirs[num_iwad_dirs] = dir;
++num_iwad_dirs;
}
}
// Returns true if the specified path is a path to a file
// of the specified name.
static boolean DirIsFile(char *path, char *filename)
{
size_t path_len;
size_t filename_len;
path_len = strlen(path);
filename_len = strlen(filename);
return path_len >= filename_len + 1
&& path[path_len - filename_len - 1] == DIR_SEPARATOR
&& !strcasecmp(&path[path_len - filename_len], filename);
}
// Check if the specified directory contains the specified IWAD
// file, returning the full path to the IWAD if found, or NULL
// if not found.
static char *CheckDirectoryHasIWAD(char *dir, char *iwadname)
{
char *filename;
char *probe;
// As a special case, the "directory" may refer directly to an
// IWAD file if the path comes from DOOMWADDIR or DOOMWADPATH.
probe = M_FileCaseExists(dir);
if (DirIsFile(dir, iwadname) && probe != NULL)
{
return probe;
}
// Construct the full path to the IWAD if it is located in
// this directory, and check if it exists.
if (!strcmp(dir, "."))
{
filename = M_StringDuplicate(iwadname);
}
else
{
filename = M_StringJoin(dir, DIR_SEPARATOR_S, iwadname, NULL);
}
probe = M_FileCaseExists(filename);
if (probe != NULL)
{
return probe;
}
free(filename);
return NULL;
}
// Search a directory to try to find an IWAD
// Returns the location of the IWAD if found, otherwise NULL.
static char *SearchDirectoryForIWAD(char *dir, int mask, GameMission_t *mission)
{
char *filename;
size_t i;
for (i=0; i<arrlen(iwads); ++i)
{
if (((1 << iwads[i].mission) & mask) == 0)
{
continue;
}
filename = CheckDirectoryHasIWAD(dir, iwads[i].name);
if (filename != NULL)
{
*mission = iwads[i].mission;
return filename;
}
}
return NULL;
}
// When given an IWAD with the '-iwad' parameter,
// attempt to identify it by its name.
static GameMission_t IdentifyIWADByName(char *name, int mask)
{
size_t i;
GameMission_t mission;
char *p;
p = strrchr(name, DIR_SEPARATOR);
if (p != NULL)
{
name = p + 1;
}
mission = none;
for (i=0; i<arrlen(iwads); ++i)
{
// Check if the filename is this IWAD name.
// Only use supported missions:
if (((1 << iwads[i].mission) & mask) == 0)
continue;
// Check if it ends in this IWAD name.
if (!strcasecmp(name, iwads[i].name))
{
mission = iwads[i].mission;
break;
}
}
return mission;
}
// Add IWAD directories parsed from splitting a path string containing
// paths separated by PATH_SEPARATOR. 'suffix' is a string to concatenate
// to the end of the paths before adding them.
static void AddIWADPath(char *path, char *suffix)
{
char *left, *p;
path = M_StringDuplicate(path);
// Split into individual dirs within the list.
left = path;
for (;;)
{
p = strchr(left, PATH_SEPARATOR);
if (p != NULL)
{
// Break at the separator and use the left hand side
// as another iwad dir
*p = '\0';
AddIWADDir(M_StringJoin(left, suffix, NULL));
left = p + 1;
}
else
{
break;
}
}
AddIWADDir(M_StringJoin(left, suffix, NULL));
free(path);
}
//
// Build a list of IWAD files
//
static void BuildIWADDirList(void)
{
char *env;
if (iwad_dirs_built)
{
return;
}
// Look in the current directory. Doom always does this.
AddIWADDir(".");
// Add DOOMWADDIR if it is in the environment
env = getenv("DOOMWADDIR");
if (env != NULL)
{
AddIWADDir(env);
}
// Add dirs from DOOMWADPATH:
env = getenv("DOOMWADPATH");
if (env != NULL)
{
AddIWADPath(env, "");
}
// Don't run this function again.
iwad_dirs_built = true;
}
//
// Searches WAD search paths for an WAD with a specific filename.
//
char *D_FindWADByName(char *name)
{
char *path;
char *probe;
int i;
// Absolute path?
probe = M_FileCaseExists(name);
if (probe != NULL)
{
return probe;
}
BuildIWADDirList();
// Search through all IWAD paths for a file with the given name.
for (i=0; i<num_iwad_dirs; ++i)
{
// As a special case, if this is in DOOMWADDIR or DOOMWADPATH,
// the "directory" may actually refer directly to an IWAD
// file.
probe = M_FileCaseExists(iwad_dirs[i]);
if (DirIsFile(iwad_dirs[i], name) && probe != NULL)
{
return probe;
}
// Construct a string for the full path
path = M_StringJoin(iwad_dirs[i], DIR_SEPARATOR_S, name, NULL);
probe = M_FileCaseExists(path);
if (probe != NULL)
{
return probe;
}
free(path);
}
// File not found
return NULL;
}
//
// D_TryWADByName
//
// Searches for a WAD by its filename, or passes through the filename
// if not found.
//
char *D_TryFindWADByName(char *filename)
{
char *result;
result = D_FindWADByName(filename);
if (result != NULL)
{
return result;
}
else
{
return filename;
}
}
//
// FindIWAD
// Checks availability of IWAD files by name,
// to determine whether registered/commercial features
// should be executed (notably loading PWADs).
//
char *D_FindIWAD(int mask, GameMission_t *mission)
{
char *result;
char *iwadfile;
int iwadparm;
int i;
// Check for the -iwad parameter
//!
// Specify an IWAD file to use.
//
// @arg <file>
//
iwadparm = M_CheckParmWithArgs("-iwad", 1);
if (iwadparm)
{
// Search through IWAD dirs for an IWAD with the given name.
iwadfile = myargv[iwadparm + 1];
result = D_FindWADByName(iwadfile);
if (result == NULL)
{
I_Error("IWAD file '%s' not found!", iwadfile);
}
*mission = IdentifyIWADByName(result, mask);
}
else
{
// Search through the list and look for an IWAD
result = NULL;
BuildIWADDirList();
for (i=0; result == NULL && i<num_iwad_dirs; ++i)
{
result = SearchDirectoryForIWAD(iwad_dirs[i], mask, mission);
}
}
return result;
}
// Find all IWADs in the IWAD search path matching the given mask.
const iwad_t **D_FindAllIWADs(int mask)
{
const iwad_t **result;
int result_len;
char *filename;
int i;
result = malloc(sizeof(iwad_t *) * (arrlen(iwads) + 1));
result_len = 0;
// Try to find all IWADs
for (i=0; i<arrlen(iwads); ++i)
{
if (((1 << iwads[i].mission) & mask) == 0)
{
continue;
}
filename = D_FindWADByName(iwads[i].name);
if (filename != NULL)
{
result[result_len] = &iwads[i];
++result_len;
}
}
// End of list
result[result_len] = NULL;
return result;
}
//
// Get the IWAD name used for savegames.
//
char *D_SaveGameIWADName(GameMission_t gamemission)
{
size_t i;
// Determine the IWAD name to use for savegames.
// This determines the directory the savegame files get put into.
//
// Note that we match on gamemission rather than on IWAD name.
// This ensures that doom1.wad and doom.wad saves are stored
// in the same place.
for (i=0; i<arrlen(iwads); ++i)
{
if (gamemission == iwads[i].mission)
{
return iwads[i].name;
}
}
// Default fallback:
return "unknown.wad";
}
char *D_SuggestIWADName(GameMission_t mission, GameMode_t mode)
{
int i;
for (i = 0; i < arrlen(iwads); ++i)
{
if (iwads[i].mission == mission && iwads[i].mode == mode)
{
return iwads[i].name;
}
}
return "unknown.wad";
}
char *D_SuggestGameName(GameMission_t mission, GameMode_t mode)
{
int i;
for (i = 0; i < arrlen(iwads); ++i)
{
if (iwads[i].mission == mission
&& (mode == indetermined || iwads[i].mode == mode))
{
return iwads[i].description;
}
}
return "Unknown game?";
}

52
src/d_iwad.h Normal file
View File

@@ -0,0 +1,52 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Find IWAD and initialize according to IWAD type.
//
#ifndef __D_IWAD__
#define __D_IWAD__
#include "d_mode.h"
#define IWAD_MASK_DOOM ((1 << doom) \
| (1 << doom2) \
| (1 << pack_tnt) \
| (1 << pack_plut) \
| (1 << pack_chex) \
| (1 << pack_hacx))
#define IWAD_MASK_HERETIC (1 << heretic)
#define IWAD_MASK_HEXEN (1 << hexen)
#define IWAD_MASK_STRIFE (1 << strife)
typedef struct
{
char *name;
GameMission_t mission;
GameMode_t mode;
char *description;
} iwad_t;
char *D_FindWADByName(char *filename);
char *D_TryFindWADByName(char *filename);
char *D_FindIWAD(int mask, GameMission_t *mission);
const iwad_t **D_FindAllIWADs(int mask);
char *D_SaveGameIWADName(GameMission_t gamemission);
char *D_SuggestIWADName(GameMission_t mission, GameMode_t mode);
char *D_SuggestGameName(GameMission_t mission, GameMode_t mode);
void D_CheckCorrectIWAD(GameMission_t mission);
#endif

882
src/d_loop.c Normal file
View File

@@ -0,0 +1,882 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Main loop code.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "d_event.h"
#include "d_loop.h"
#include "d_ticcmd.h"
#include "i_system.h"
#include "i_timer.h"
#include "i_video.h"
#include "m_argv.h"
#include "m_fixed.h"
#include "net_client.h"
#include "net_io.h"
#include "net_loop.h"
#include "net_query.h"
#include "net_sdl.h"
#include "net_server.h"
#include "w_file.h"
// The complete set of data for a particular tic.
typedef struct
{
ticcmd_t cmds[NET_MAXPLAYERS];
boolean ingame[NET_MAXPLAYERS];
} ticcmd_set_t;
// Maximum time that we wait in TryRunTics() for netgame data to be
// received before we bail out and render a frame anyway.
// Vanilla Doom used 20 for this value, but we use a smaller value
// instead for better responsiveness of the menu when we're stuck.
#define MAX_NETGAME_STALL_TICS 5
//
// gametic is the tic about to (or currently being) run
// maketic is the tic that hasn't had control made for it yet
// recvtic is the latest tic received from the server.
//
// a gametic cannot be run until ticcmds are received for it
// from all players.
//
static ticcmd_set_t ticdata[BACKUPTICS];
// The index of the next tic to be made (with a call to BuildTiccmd).
static int maketic;
// The number of complete tics received from the server so far.
static int recvtic;
// The number of tics that have been run (using RunTic) so far.
int gametic;
// When set to true, a single tic is run each time TryRunTics() is called.
// This is used for -timedemo mode.
boolean singletics = false;
// Index of the local player.
static int localplayer;
// Used for original sync code.
static int skiptics = 0;
// Reduce the bandwidth needed by sampling game input less and transmitting
// less. If ticdup is 2, sample half normal, 3 = one third normal, etc.
int ticdup;
// Amount to offset the timer for game sync.
fixed_t offsetms;
// Use new client syncronisation code
static boolean new_sync = true;
// Callback functions for loop code.
static loop_interface_t *loop_interface = NULL;
// Current players in the multiplayer game.
// This is distinct from playeringame[] used by the game code, which may
// modify playeringame[] when playing back multiplayer demos.
static boolean local_playeringame[NET_MAXPLAYERS];
// Requested player class "sent" to the server on connect.
// If we are only doing a single player game then this needs to be remembered
// and saved in the game settings.
static int player_class;
// 35 fps clock adjusted by offsetms milliseconds
static int GetAdjustedTime(void)
{
int time_ms;
time_ms = I_GetTimeMS();
if (new_sync)
{
// Use the adjustments from net_client.c only if we are
// using the new sync mode.
time_ms += (offsetms / FRACUNIT);
}
return (time_ms * TICRATE) / 1000;
}
static boolean BuildNewTic(void)
{
int gameticdiv;
ticcmd_t cmd;
gameticdiv = gametic/ticdup;
I_StartTic ();
loop_interface->ProcessEvents();
// Always run the menu
loop_interface->RunMenu();
if (drone)
{
// In drone mode, do not generate any ticcmds.
return false;
}
if (new_sync)
{
// If playing single player, do not allow tics to buffer
// up very far
if (!net_client_connected && maketic - gameticdiv > 2)
return false;
// Never go more than ~200ms ahead
if (maketic - gameticdiv > 8)
return false;
}
else
{
if (maketic - gameticdiv >= 5)
return false;
}
//printf ("mk:%i ",maketic);
memset(&cmd, 0, sizeof(ticcmd_t));
loop_interface->BuildTiccmd(&cmd, maketic);
if (net_client_connected)
{
NET_CL_SendTiccmd(&cmd, maketic);
}
ticdata[maketic % BACKUPTICS].cmds[localplayer] = cmd;
ticdata[maketic % BACKUPTICS].ingame[localplayer] = true;
++maketic;
return true;
}
//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
int lasttime;
void NetUpdate (void)
{
int nowtime;
int newtics;
int i;
// If we are running with singletics (timing a demo), this
// is all done separately.
if (singletics)
return;
// Run network subsystems
NET_CL_Run();
NET_SV_Run();
// check time
nowtime = GetAdjustedTime() / ticdup;
newtics = nowtime - lasttime;
lasttime = nowtime;
if (skiptics <= newtics)
{
newtics -= skiptics;
skiptics = 0;
}
else
{
skiptics -= newtics;
newtics = 0;
}
// build new ticcmds for console player
for (i=0 ; i<newtics ; i++)
{
if (!BuildNewTic())
{
break;
}
}
}
static void D_Disconnected(void)
{
// In drone mode, the game cannot continue once disconnected.
if (drone)
{
I_Error("Disconnected from server in drone mode.");
}
// disconnected from server
printf("Disconnected from server.\n");
}
//
// Invoked by the network engine when a complete set of ticcmds is
// available.
//
void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
{
int i;
// Disconnected from server?
if (ticcmds == NULL && players_mask == NULL)
{
D_Disconnected();
return;
}
for (i = 0; i < NET_MAXPLAYERS; ++i)
{
if (!drone && i == localplayer)
{
// This is us. Don't overwrite it.
}
else
{
ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
}
}
++recvtic;
}
//
// Start game loop
//
// Called after the screen is set but before the game starts running.
//
void D_StartGameLoop(void)
{
lasttime = GetAdjustedTime() / ticdup;
}
//
// Block until the game start message is received from the server.
//
static void BlockUntilStart(net_gamesettings_t *settings,
netgame_startup_callback_t callback)
{
while (!NET_CL_GetSettings(settings))
{
NET_CL_Run();
NET_SV_Run();
if (!net_client_connected)
{
I_Error("Lost connection to server");
}
if (callback != NULL && !callback(net_client_wait_data.ready_players,
net_client_wait_data.num_players))
{
I_Error("Netgame startup aborted.");
}
I_Sleep(100);
}
}
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 new network client sync code rather than the classic
// sync code. This is currently disabled by default because it
// has some bugs.
//
if (M_CheckParm("-newsync") > 0)
settings->new_sync = 1;
else
settings->new_sync = 0;
// TODO: New sync code is not enabled by default because it's
// currently broken.
//if (M_CheckParm("-oldsync") > 0)
// settings->new_sync = 0;
//else
// settings->new_sync = 1;
//!
// @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 result = false;
net_addr_t *addr = NULL;
int i;
// Call D_QuitNetGame on exit:
I_AtExit(D_QuitNetGame, true);
player_class = connect_data->player_class;
//!
// @category net
//
// Start a multiplayer server, listening for connections.
//
if (M_CheckParm("-server") > 0
|| M_CheckParm("-privateserver") > 0)
{
NET_SV_Init();
NET_SV_AddModule(&net_loop_server_module);
NET_SV_AddModule(&net_sdl_module);
NET_SV_RegisterWithMaster();
net_loop_client_module.InitClient();
addr = net_loop_client_module.ResolveAddress(NULL);
}
else
{
//!
// @category net
//
// Automatically search the local LAN for a multiplayer
// server and join it.
//
i = M_CheckParm("-autojoin");
if (i > 0)
{
addr = NET_FindLANServer();
if (addr == NULL)
{
I_Error("No server found on local LAN");
}
}
//!
// @arg <address>
// @category net
//
// Connect to a multiplayer server running on the given
// address.
//
i = M_CheckParmWithArgs("-connect", 1);
if (i > 0)
{
net_sdl_module.InitClient();
addr = net_sdl_module.ResolveAddress(myargv[i+1]);
if (addr == NULL)
{
I_Error("Unable to resolve '%s'\n", myargv[i+1]);
}
}
}
if (addr != NULL)
{
if (M_CheckParm("-drone") > 0)
{
connect_data->drone = true;
}
if (!NET_CL_Connect(addr, connect_data))
{
I_Error("D_InitNetGame: Failed to connect to %s\n",
NET_AddrToString(addr));
}
printf("D_InitNetGame: Connected to %s\n", NET_AddrToString(addr));
// Wait for launch message received from server.
/*NET_WaitForLaunch();*/
result = true;
}
return result;
}
//
// D_QuitNetGame
// Called before quitting to leave a net game
// without hanging the other players
//
void D_QuitNetGame (void)
{
NET_SV_Shutdown();
NET_CL_Disconnect();
}
static int GetLowTic(void)
{
int lowtic;
lowtic = maketic;
if (net_client_connected)
{
if (drone || recvtic < lowtic)
{
lowtic = recvtic;
}
}
return lowtic;
}
static int frameon;
static int frameskip[4];
static int oldnettics;
static void OldNetSync(void)
{
unsigned int i;
int keyplayer = -1;
frameon++;
// ideally maketic should be 1 - 3 tics above lowtic
// if we are consistantly slower, speed up time
for (i=0 ; i<NET_MAXPLAYERS ; i++)
{
if (local_playeringame[i])
{
keyplayer = i;
break;
}
}
if (keyplayer < 0)
{
// If there are no players, we can never advance anyway
return;
}
if (localplayer == keyplayer)
{
// the key player does not adapt
}
else
{
if (maketic <= recvtic)
{
lasttime--;
// printf ("-");
}
frameskip[frameon & 3] = oldnettics > recvtic;
oldnettics = maketic;
if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
{
skiptics = 1;
// printf ("+");
}
}
}
// Returns true if there are players in the game:
static boolean PlayersInGame(void)
{
boolean result = false;
unsigned int i;
// If we are connected to a server, check if there are any players
// in the game.
if (net_client_connected)
{
for (i = 0; i < NET_MAXPLAYERS; ++i)
{
result = result || local_playeringame[i];
}
}
// Whether single or multi-player, unless we are running as a drone,
// we are in the game.
if (!drone)
{
result = true;
}
return result;
}
// When using ticdup, certain values must be cleared out when running
// the duplicate ticcmds.
static void TicdupSquash(ticcmd_set_t *set)
{
ticcmd_t *cmd;
unsigned int i;
for (i = 0; i < NET_MAXPLAYERS ; ++i)
{
cmd = &set->cmds[i];
cmd->chatchar = 0;
if (cmd->buttons & BT_SPECIAL)
cmd->buttons = 0;
}
}
// When running in single player mode, clear all the ingame[] array
// except the local player.
static void SinglePlayerClear(ticcmd_set_t *set)
{
unsigned int i;
for (i = 0; i < NET_MAXPLAYERS; ++i)
{
if (i != localplayer)
{
set->ingame[i] = false;
}
}
}
//
// TryRunTics
//
void TryRunTics (void)
{
int i;
int lowtic;
int entertic;
static int oldentertics;
int realtics;
int availabletics;
int counts;
// get real tics
entertic = I_GetTime() / ticdup;
realtics = entertic - oldentertics;
oldentertics = entertic;
// in singletics mode, run a single tic every time this function
// is called.
if (singletics)
{
BuildNewTic();
}
else
{
NetUpdate ();
}
lowtic = GetLowTic();
availabletics = lowtic - gametic/ticdup;
// decide how many tics to run
if (new_sync)
{
counts = availabletics;
}
else
{
// decide how many tics to run
if (realtics < availabletics-1)
counts = realtics+1;
else if (realtics < availabletics)
counts = realtics;
else
counts = availabletics;
if (counts < 1)
counts = 1;
if (net_client_connected)
{
OldNetSync();
}
}
if (counts < 1)
counts = 1;
// wait for new tics if needed
while (!PlayersInGame() || lowtic < gametic/ticdup + counts)
{
NetUpdate ();
lowtic = GetLowTic();
if (lowtic < gametic/ticdup)
I_Error ("TryRunTics: lowtic < gametic");
// Still no tics to run? Sleep until some are available.
if (lowtic < gametic/ticdup + counts)
{
// If we're in a netgame, we might spin forever waiting for
// new network data to be received. So don't stay in here
// forever - give the menu a chance to work.
if (I_GetTime() / ticdup - entertic >= MAX_NETGAME_STALL_TICS)
{
return;
}
I_Sleep(1);
}
}
// run the count * ticdup dics
while (counts--)
{
ticcmd_set_t *set;
if (!PlayersInGame())
{
return;
}
set = &ticdata[(gametic / ticdup) % BACKUPTICS];
if (!net_client_connected)
{
SinglePlayerClear(set);
}
for (i=0 ; i<ticdup ; i++)
{
if (gametic/ticdup > lowtic)
I_Error ("gametic>lowtic");
memcpy(local_playeringame, set->ingame, sizeof(local_playeringame));
loop_interface->RunTic(set->cmds, set->ingame);
gametic++;
// modify command for duplicated tics
TicdupSquash(set);
}
NetUpdate (); // check for new console commands
}
}
void D_RegisterLoopCallbacks(loop_interface_t *i)
{
loop_interface = i;
}
// TODO: Move nonvanilla demo functions into a dedicated file.
#include "m_misc.h"
#include "w_wad.h"
static boolean StrictDemos(void)
{
//!
// @category demo
//
// When recording or playing back demos, disable any extensions
// of the vanilla demo format - record demos as vanilla would do,
// and play back demos as vanilla would do.
//
return M_ParmExists("-strictdemos");
}
// If the provided conditional value is true, we're trying to record
// a demo file that will include a non-vanilla extension. The function
// will return true if the conditional is true and it's allowed to use
// this extension (no extensions are allowed if -strictdemos is given
// on the command line). A warning is shown on the console using the
// provided string describing the non-vanilla expansion.
boolean D_NonVanillaRecord(boolean conditional, char *feature)
{
if (!conditional || StrictDemos())
{
return false;
}
printf("Warning: Recording a demo file with a non-vanilla extension "
"(%s). Use -strictdemos to disable this extension.\n",
feature);
return true;
}
// Returns true if the given lump number corresponds to data from a .lmp
// file, as opposed to a WAD.
static boolean IsDemoFile(int lumpnum)
{
char *lower;
boolean result;
lower = M_StringDuplicate(lumpinfo[lumpnum]->wad_file->path);
M_ForceLowercase(lower);
result = M_StringEndsWith(lower, ".lmp");
free(lower);
return result;
}
// If the provided conditional value is true, we're trying to play back
// a demo that includes a non-vanilla extension. We return true if the
// conditional is true and it's allowed to use this extension, checking
// that:
// - The -strictdemos command line argument is not provided.
// - The given lumpnum identifying the demo to play back identifies a
// demo that comes from a .lmp file, not a .wad file.
// - Before proceeding, a warning is shown to the user on the console.
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
char *feature)
{
if (!conditional || StrictDemos())
{
return false;
}
if (!IsDemoFile(lumpnum))
{
printf("Warning: WAD contains demo with a non-vanilla extension "
"(%s)\n", feature);
return false;
}
printf("Warning: Playing back a demo file with a non-vanilla extension "
"(%s). Use -strictdemos to disable this extension.\n",
feature);
return true;
}

90
src/d_loop.h Normal file
View File

@@ -0,0 +1,90 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Main loop stuff.
//
#ifndef __D_LOOP__
#define __D_LOOP__
#include "d_ticcmd.h"
#include "doomtype.h"
#include "net_defs.h"
// Callback function invoked while waiting for the netgame to start.
// The callback is invoked when new players are ready. The callback
// should return true, or return false to abort startup.
typedef boolean (*netgame_startup_callback_t)(int ready_players,
int num_players);
typedef struct
{
// Read events from the event queue, and process them.
void (*ProcessEvents)();
// Given the current input state, fill in the fields of the specified
// ticcmd_t structure with data for a new tic.
void (*BuildTiccmd)(ticcmd_t *cmd, int maketic);
// Advance the game forward one tic, using the specified player input.
void (*RunTic)(ticcmd_t *cmds, boolean *ingame);
// Run the menu (runs independently of the game).
void (*RunMenu)();
} loop_interface_t;
// Register callback functions for the main loop code to use.
void D_RegisterLoopCallbacks(loop_interface_t *i);
// Create any new ticcmds and broadcast to other players.
void NetUpdate (void);
// Broadcasts special packets to other players
// to notify of game exit
void D_QuitNetGame (void);
//? how many ticks to run?
void TryRunTics (void);
// Called at start of game loop to initialize timers
void D_StartGameLoop(void);
// Initialize networking code and connect to server.
boolean D_InitNetGame(net_connect_data_t *connect_data);
// Start game with specified settings. The structure will be updated
// with the actual settings for the game.
void D_StartNetGame(net_gamesettings_t *settings,
netgame_startup_callback_t callback);
extern boolean singletics;
extern int gametic, ticdup;
// Check if it is permitted to record a demo with a non-vanilla feature.
boolean D_NonVanillaRecord(boolean conditional, char *feature);
// Check if it is permitted to play back a demo with a non-vanilla feature.
boolean D_NonVanillaPlayback(boolean conditional, int lumpnum,
char *feature);
#endif

1295
src/d_main.c Normal file

File diff suppressed because it is too large Load Diff

45
src/d_main.h Normal file
View File

@@ -0,0 +1,45 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System specific interface stuff.
//
#ifndef __D_MAIN__
#define __D_MAIN__
#include "doomdef.h"
// Read events from all input devices
void D_ProcessEvents(void);
//
// BASE LEVEL
//
void D_PageTicker(void);
void D_PageDrawer(void);
void D_AdvanceDemo(void);
void D_DoAdvanceDemo(void);
void D_StartTitle(void);
extern void D_DoomMain(void);
//
// GLOBAL VARIABLES
//
extern gameaction_t gameaction;
#endif

212
src/d_mode.c Normal file
View File

@@ -0,0 +1,212 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
// Functions and definitions relating to the game type and operational
// mode.
//
#include "doomtype.h"
#include "d_mode.h"
// Valid game mode/mission combinations, with the number of
// episodes/maps for each.
static struct
{
GameMission_t mission;
GameMode_t mode;
int episode;
int map;
} valid_modes[] = {
{ pack_chex, retail, 1, 5 },
{ doom, shareware, 1, 9 },
{ doom, registered, 3, 9 },
{ doom, retail, 4, 9 },
{ doom2, commercial, 1, 32 },
{ pack_tnt, commercial, 1, 32 },
{ pack_plut, commercial, 1, 32 },
{ pack_hacx, commercial, 1, 32 },
{ heretic, shareware, 1, 9 },
{ heretic, registered, 3, 9 },
{ heretic, retail, 5, 9 },
{ hexen, commercial, 1, 60 },
{ strife, commercial, 1, 34 },
};
// Check that a gamemode+gamemission received over the network is valid.
boolean D_ValidGameMode(GameMission_t mission, GameMode_t mode)
{
int i;
for (i=0; i<arrlen(valid_modes); ++i)
{
if (valid_modes[i].mode == mode && valid_modes[i].mission == mission)
{
return true;
}
}
return false;
}
boolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,
int episode, int map)
{
int i;
// Hacks for Heretic secret episodes
if (mission == heretic)
{
if (mode == retail && episode == 6)
{
return map >= 1 && map <= 3;
}
else if (mode == registered && episode == 4)
{
return map == 1;
}
}
// Find the table entry for this mission/mode combination.
for (i=0; i<arrlen(valid_modes); ++i)
{
if (mission == valid_modes[i].mission
&& mode == valid_modes[i].mode)
{
return episode >= 1 && episode <= valid_modes[i].episode
&& map >= 1 && map <= valid_modes[i].map;
}
}
// Unknown mode/mission combination
return false;
}
// Get the number of valid episodes for the specified mission/mode.
int D_GetNumEpisodes(GameMission_t mission, GameMode_t mode)
{
int episode;
episode = 1;
while (D_ValidEpisodeMap(mission, mode, episode, 1))
{
++episode;
}
return episode - 1;
}
// Table of valid versions
static struct {
GameMission_t mission;
GameVersion_t version;
} valid_versions[] = {
{ doom, exe_doom_1_666 },
{ doom, exe_doom_1_7 },
{ doom, exe_doom_1_8 },
{ doom, exe_doom_1_9 },
{ doom, exe_hacx },
{ doom, exe_ultimate },
{ doom, exe_final },
{ doom, exe_final2 },
{ doom, exe_chex },
{ heretic, exe_heretic_1_3 },
{ hexen, exe_hexen_1_1 },
{ strife, exe_strife_1_2 },
{ strife, exe_strife_1_31 },
};
boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version)
{
int i;
// All Doom variants can use the Doom versions.
if (mission == doom2 || mission == pack_plut || mission == pack_tnt
|| mission == pack_hacx || mission == pack_chex)
{
mission = doom;
}
for (i=0; i<arrlen(valid_versions); ++i)
{
if (valid_versions[i].mission == mission
&& valid_versions[i].version == version)
{
return true;
}
}
return false;
}
// Does this mission type use ExMy form, rather than MAPxy form?
boolean D_IsEpisodeMap(GameMission_t mission)
{
switch (mission)
{
case doom:
case heretic:
case pack_chex:
return true;
case none:
case hexen:
case doom2:
case pack_hacx:
case pack_tnt:
case pack_plut:
case strife:
default:
return false;
}
}
char *D_GameMissionString(GameMission_t mission)
{
switch (mission)
{
case none:
default:
return "none";
case doom:
return "doom";
case doom2:
return "doom2";
case pack_tnt:
return "tnt";
case pack_plut:
return "plutonia";
case pack_hacx:
return "hacx";
case pack_chex:
return "chex";
case heretic:
return "heretic";
case hexen:
return "hexen";
case strife:
return "strife";
}
}

108
src/d_mode.h Normal file
View File

@@ -0,0 +1,108 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Functions and definitions relating to the game type and operational
// mode.
//
#ifndef __D_MODE__
#define __D_MODE__
#include "doomtype.h"
// The "mission" controls what game we are playing.
typedef enum
{
doom, // Doom 1
doom2, // Doom 2
pack_tnt, // Final Doom: TNT: Evilution
pack_plut, // Final Doom: The Plutonia Experiment
pack_chex, // Chex Quest (modded doom)
pack_hacx, // Hacx (modded doom2)
heretic, // Heretic
hexen, // Hexen
strife, // Strife
none
} GameMission_t;
// The "mode" allows more accurate specification of the game mode we are
// in: eg. shareware vs. registered. So doom1.wad and doom.wad are the
// same mission, but a different mode.
typedef enum
{
shareware, // Doom/Heretic shareware
registered, // Doom/Heretic registered
commercial, // Doom II/Hexen
retail, // Ultimate Doom
indetermined // Unknown.
} GameMode_t;
// What version are we emulating?
typedef enum
{
exe_doom_1_2, // Doom 1.2: shareware and registered
exe_doom_1_666, // Doom 1.666: for shareware, registered and commercial
exe_doom_1_7, // Doom 1.7/1.7a: "
exe_doom_1_8, // Doom 1.8: "
exe_doom_1_9, // Doom 1.9: "
exe_hacx, // Hacx
exe_ultimate, // Ultimate Doom (retail)
exe_final, // Final Doom
exe_final2, // Final Doom (alternate exe)
exe_chex, // Chex Quest executable (based on Final Doom)
exe_heretic_1_3, // Heretic 1.3
exe_hexen_1_1, // Hexen 1.1
exe_strife_1_2, // Strife v1.2
exe_strife_1_31 // Strife v1.31
} GameVersion_t;
// What IWAD variant are we using?
typedef enum
{
vanilla, // Vanilla Doom
freedoom, // FreeDoom: Phase 1 + 2
freedm, // FreeDM
bfgedition, // Doom Classic (Doom 3: BFG Edition)
} GameVariant_t;
// Skill level.
typedef enum
{
sk_noitems = -1, // the "-skill 0" hack
sk_baby = 0,
sk_easy,
sk_medium,
sk_hard,
sk_nightmare
} skill_t;
boolean D_ValidGameMode(GameMission_t mission, GameMode_t mode);
boolean D_ValidGameVersion(GameMission_t mission, GameVersion_t version);
boolean D_ValidEpisodeMap(GameMission_t mission, GameMode_t mode,
int episode, int map);
int D_GetNumEpisodes(GameMission_t mission, GameMode_t mode);
boolean D_IsEpisodeMap(GameMission_t mission);
char *D_GameMissionString(GameMission_t mission);
#endif /* #ifndef __D_MODE__ */

247
src/d_net.c Normal file
View File

@@ -0,0 +1,247 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// DOOM Network game communication and protocol,
// all OS independend parts.
//
#include <stdio.h>
#include "d_loop.h"
#include "d_main.h"
#include "d_player.h"
#include "d_ticcmd.h"
#include "doomdef.h"
#include "doomstat.h"
#include "doomtype.h"
#include "g_game.h"
#include "m_argv.h"
#include "m_menu.h"
#include "m_misc.h"
#include "net_defs.h"
#include "tables.h"
#include "w_checksum.h"
#include "w_wad.h"
ticcmd_t *netcmds;
// Called when a player leaves the game
static void PlayerQuitGame(player_t *player) {
static char exitmsg[80];
unsigned int player_num;
player_num = player - players;
// Do this the same way as Vanilla Doom does, to allow dehacked
// replacements of this message
M_StringCopy(exitmsg, "Player 1 left the game", sizeof(exitmsg));
exitmsg[7] += player_num;
playeringame[player_num] = false;
players[consoleplayer].message = exitmsg;
// TODO: check if it is sensible to do this:
if (demorecording) {
G_CheckDemoStatus();
}
}
static void RunTic(ticcmd_t *cmds, boolean *ingame) {
extern boolean advancedemo;
unsigned int i;
// Check for player quits.
for (i = 0; i < MAXPLAYERS; ++i) {
if (!demoplayback && playeringame[i] && !ingame[i]) {
PlayerQuitGame(&players[i]);
}
}
netcmds = cmds;
// check that there are players in the game. if not, we cannot
// run a tic.
if (advancedemo)
D_DoAdvanceDemo();
G_Ticker();
}
static loop_interface_t doom_loop_interface = {D_ProcessEvents, G_BuildTiccmd,
RunTic, M_Ticker};
// Load game settings from the specified structure and
// set global variables.
static void LoadGameSettings(net_gamesettings_t *settings) {
unsigned int i;
deathmatch = settings->deathmatch;
startepisode = settings->episode;
startmap = settings->map;
startskill = settings->skill;
startloadgame = settings->loadgame;
lowres_turn = settings->lowres_turn;
nomonsters = settings->nomonsters;
fastparm = settings->fast_monsters;
respawnparm = settings->respawn_monsters;
timelimit = settings->timelimit;
consoleplayer = settings->consoleplayer;
if (lowres_turn) {
printf("NOTE: Turning resolution is reduced; this is probably "
"because there is a client recording a Vanilla demo.\n");
}
for (i = 0; i < MAXPLAYERS; ++i) {
playeringame[i] = i < settings->num_players;
}
}
// Save the game settings from global variables to the specified
// game settings structure.
static void SaveGameSettings(net_gamesettings_t *settings) {
// Fill in game settings structure with appropriate parameters
// for the new game
settings->deathmatch = deathmatch;
settings->episode = startepisode;
settings->map = startmap;
settings->skill = startskill;
settings->loadgame = startloadgame;
settings->gameversion = gameversion;
settings->nomonsters = nomonsters;
settings->fast_monsters = fastparm;
settings->respawn_monsters = respawnparm;
settings->timelimit = timelimit;
settings->lowres_turn =
M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0;
}
static void InitConnectData(net_connect_data_t *connect_data) {
connect_data->max_players = MAXPLAYERS;
connect_data->drone = false;
//!
// @category net
//
// Run as the left screen in three screen mode.
//
if (M_CheckParm("-left") > 0) {
viewangleoffset = ANG90;
connect_data->drone = true;
}
//!
// @category net
//
// Run as the right screen in three screen mode.
//
if (M_CheckParm("-right") > 0) {
viewangleoffset = ANG270;
connect_data->drone = true;
}
//
// Connect data
//
// Game type fields:
connect_data->gamemode = gamemode;
connect_data->gamemission = gamemission;
// Are we recording a demo? Possibly set lowres turn mode
connect_data->lowres_turn =
M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0;
// Read checksums of our WAD directory
W_Checksum(connect_data->wad_sha1sum);
// Are we playing with the Freedoom IWAD?
connect_data->is_freedoom = W_CheckNumForName("FREEDOOM") >= 0;
}
void D_ConnectNetGame(void) {
net_connect_data_t connect_data;
InitConnectData(&connect_data);
netgame = D_InitNetGame(&connect_data);
//!
// @category net
//
// Start the game playing as though in a netgame with a single
// player. This can also be used to play back single player netgame
// demos.
//
if (M_CheckParm("-solo-net") > 0) {
netgame = true;
}
}
//
// D_CheckNetGame
// Works out player numbers among the net participants
//
void D_CheckNetGame(void) {
net_gamesettings_t settings;
if (netgame) {
autostart = true;
}
D_RegisterLoopCallbacks(&doom_loop_interface);
SaveGameSettings(&settings);
D_StartNetGame(&settings, NULL);
LoadGameSettings(&settings);
printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
startskill, deathmatch, startmap, startepisode);
printf("player %i of %i (%i nodes)\n", consoleplayer + 1,
settings.num_players, settings.num_players);
// Show players here; the server might have specified a time limit
if (timelimit > 0 && deathmatch) {
// Gross hack to work like Vanilla:
if (timelimit == 20 && M_CheckParm("-avg")) {
printf("Austin Virtual Gaming: Levels will end "
"after 20 minutes\n");
} else {
printf("Levels will end after %d minute", timelimit);
if (timelimit > 1)
printf("s");
printf(".\n");
}
}
}

195
src/d_player.h Normal file
View File

@@ -0,0 +1,195 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
//
#ifndef __D_PLAYER__
#define __D_PLAYER__
// The player data structure depends on a number
// of other structs: items (internal inventory),
// animation states (closely tied to the sprites
// used to represent them, unfortunately).
#include "d_items.h"
#include "p_pspr.h"
// In addition, the player is just a special
// case of the generic moving object/actor.
#include "p_mobj.h"
// Finally, for odd reasons, the player input
// is buffered within the player data struct,
// as commands per game tick.
#include "d_ticcmd.h"
#include "net_defs.h"
//
// Player states.
//
typedef enum {
// Playing or camping.
PST_LIVE,
// Dead on the ground, view follows killer.
PST_DEAD,
// Ready to restart/respawn???
PST_REBORN
} playerstate_t;
//
// Player internal flags, for cheats and debug.
//
typedef enum {
// No clipping, walk through barriers.
CF_NOCLIP = 1,
// No damage, no health loss.
CF_GODMODE = 2,
// Not really a cheat, just a debug aid.
CF_NOMOMENTUM = 4
} cheat_t;
//
// Extended player object info: player_t
//
typedef struct player_s {
mobj_t *mo;
playerstate_t playerstate;
ticcmd_t cmd;
// Determine POV,
// including viewpoint bobbing during movement.
// Focal origin above r.z
fixed_t viewz;
// Base height above floor for viewz.
fixed_t viewheight;
// Bob/squat speed.
fixed_t deltaviewheight;
// bounded/scaled total momentum.
fixed_t bob;
// This is only used between levels,
// mo->health is used during levels.
int health;
int armorpoints;
// Armor type is 0-2.
int armortype;
// Power ups. invinc and invis are tic counters.
int powers[NUMPOWERS];
boolean cards[NUMCARDS];
boolean backpack;
// Frags, kills of other players.
int frags[MAXPLAYERS];
weapontype_t readyweapon;
// Is wp_nochange if not changing.
weapontype_t pendingweapon;
int weaponowned[NUMWEAPONS];
int ammo[NUMAMMO];
int maxammo[NUMAMMO];
// True if button down last tic.
int attackdown;
int usedown;
// Bit flags, for cheats and debug.
// See cheat_t, above.
int cheats;
// Refired shots are less accurate.
int refire;
// For intermission stats.
int killcount;
int itemcount;
int secretcount;
// Hint messages.
char *message;
// For screen flashing (red or bright).
int damagecount;
int bonuscount;
// Who did damage (NULL for floors/ceilings).
mobj_t *attacker;
// So gun flashes light up areas.
int extralight;
// Current PLAYPAL, ???
// can be set to REDCOLORMAP for pain, etc.
int fixedcolormap;
// Player skin colorshift,
// 0-3 for which color to draw player.
int colormap;
// Overlay view sprites (gun, etc).
pspdef_t psprites[NUMPSPRITES];
// True if secret level has been done.
boolean didsecret;
} player_t;
//
// INTERMISSION
// Structure passed e.g. to WI_Start(wb)
//
typedef struct {
boolean in; // whether the player is in game
// Player stats, kills, collected items etc.
int skills;
int sitems;
int ssecret;
int stime;
int frags[4];
int score; // current score on entry, modified on return
} wbplayerstruct_t;
typedef struct {
int epsd; // episode # (0-2)
// if true, splash the secret level
boolean didsecret;
// previous and next levels, origin 0
int last;
int next;
int maxkills;
int maxitems;
int maxsecret;
int maxfrags;
// the par time
int partime;
// index of this player in game
int pnum;
wbplayerstruct_t plyr[MAXPLAYERS];
} wbstartstruct_t;
#endif

35
src/d_textur.h Normal file
View File

@@ -0,0 +1,35 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Typedefs related to to textures etc.,
// isolated here to make it easier separating modules.
//
#ifndef __D_TEXTUR__
#define __D_TEXTUR__
#include "doomtype.h"
//
// Flats?
//
// a pic is an unmasked block of pixels
typedef struct {
byte width;
byte height;
byte data;
} pic_t;
#endif

54
src/d_think.h Normal file
View File

@@ -0,0 +1,54 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// MapObj data. Map Objects or mobjs are actors, entities,
// thinker, take-your-pick... anything that moves, acts, or
// suffers state changes of more or less violent nature.
//
#ifndef __D_THINK__
#define __D_THINK__
//
// Experimental stuff.
// To compile this as "ANSI C with classes"
// we will need to handle the various
// action functions cleanly.
//
typedef void (*actionf_v)();
typedef void (*actionf_p1)(void *);
typedef void (*actionf_p2)(void *, void *);
typedef union {
actionf_v acv;
actionf_p1 acp1;
actionf_p2 acp2;
} actionf_t;
// Historically, "think_t" is yet another
// function pointer to a routine to handle
// an actor.
typedef actionf_t think_t;
// Doubly linked list of actors.
typedef struct thinker_s {
struct thinker_s *prev;
struct thinker_s *next;
think_t function;
} thinker_t;
#endif

56
src/d_ticcmd.h Normal file
View File

@@ -0,0 +1,56 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System specific interface stuff.
//
#ifndef __D_TICCMD__
#define __D_TICCMD__
#include "doomtype.h"
// The data sampled per tick (single player)
// and transmitted to other peers (multiplayer).
// Mainly movements/button commands per game tick,
// plus a checksum for internal state consistency.
typedef struct
{
signed char forwardmove; // *2048 for move
signed char sidemove; // *2048 for move
short angleturn; // <<16 for angle delta
byte chatchar;
byte buttons;
// villsa [STRIFE] according to the asm,
// consistancy is a short, not a byte
byte consistancy; // checks for net game
// villsa - Strife specific:
byte buttons2;
int inventory;
// Heretic/Hexen specific:
byte lookfly; // look/fly up/down/centering
byte arti; // artitype_t to use
} ticcmd_t;
#endif

184
src/doomdata.h Normal file
View File

@@ -0,0 +1,184 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// all external data is defined here
// most of the data is loaded into different structures at run time
// some internal structures shared by many modules are here
//
#ifndef __DOOMDATA__
#define __DOOMDATA__
// The most basic types we use, portability.
#include "doomtype.h"
// Some global defines, that configure the game.
#include "doomdef.h"
//
// Map level types.
// The following data structures define the persistent format
// used in the lumps of the WAD files.
//
// Lump order in a map WAD: each map needs a couple of lumps
// to provide a complete scene geometry description.
enum {
ML_LABEL, // A separator, name, ExMx or MAPxx
ML_THINGS, // Monsters, items..
ML_LINEDEFS, // LineDefs, from editing
ML_SIDEDEFS, // SideDefs, from editing
ML_VERTEXES, // Vertices, edited and BSP splits generated
ML_SEGS, // LineSegs, from LineDefs split by BSP
ML_SSECTORS, // SubSectors, list of LineSegs
ML_NODES, // BSP nodes
ML_SECTORS, // Sectors, from editing
ML_REJECT, // LUT, sector-sector visibility
ML_BLOCKMAP // LUT, motion clipping, walls/grid element
};
// A single Vertex.
typedef PACKED_STRUCT({
short x;
short y;
}) mapvertex_t;
// A SideDef, defining the visual appearance of a wall,
// by setting textures and offsets.
typedef PACKED_STRUCT({
short textureoffset;
short rowoffset;
char toptexture[8];
char bottomtexture[8];
char midtexture[8];
// Front sector, towards viewer.
short sector;
}) mapsidedef_t;
// A LineDef, as used for editing, and as input
// to the BSP builder.
typedef PACKED_STRUCT({
short v1;
short v2;
short flags;
short special;
short tag;
// sidenum[1] will be -1 if one sided
short sidenum[2];
}) maplinedef_t;
//
// LineDef attributes.
//
// Solid, is an obstacle.
#define ML_BLOCKING 1
// Blocks monsters only.
#define ML_BLOCKMONSTERS 2
// Backside will not be present at all
// if not two sided.
#define ML_TWOSIDED 4
// If a texture is pegged, the texture will have
// the end exposed to air held constant at the
// top or bottom of the texture (stairs or pulled
// down things) and will move with a height change
// of one of the neighbor sectors.
// Unpegged textures allways have the first row of
// the texture at the top pixel of the line for both
// top and bottom textures (use next to windows).
// upper texture unpegged
#define ML_DONTPEGTOP 8
// lower texture unpegged
#define ML_DONTPEGBOTTOM 16
// In AutoMap: don't map as two sided: IT'S A SECRET!
#define ML_SECRET 32
// Sound rendering: don't let sound cross two of these.
#define ML_SOUNDBLOCK 64
// Don't draw on the automap at all.
#define ML_DONTDRAW 128
// Set if already seen, thus drawn in automap.
#define ML_MAPPED 256
// Sector definition, from editing.
typedef PACKED_STRUCT({
short floorheight;
short ceilingheight;
char floorpic[8];
char ceilingpic[8];
short lightlevel;
short special;
short tag;
}) mapsector_t;
// SubSector, as generated by BSP.
typedef PACKED_STRUCT({
short numsegs;
// Index of first one, segs are stored sequentially.
short firstseg;
}) mapsubsector_t;
// LineSeg, generated by splitting LineDefs
// using partition lines selected by BSP builder.
typedef PACKED_STRUCT({
short v1;
short v2;
short angle;
short linedef;
short side;
short offset;
}) mapseg_t;
// BSP node structure.
// Indicate a leaf.
#define NF_SUBSECTOR 0x8000
typedef PACKED_STRUCT({
// Partition line from (x,y) to x+dx,y+dy)
short x;
short y;
short dx;
short dy;
// Bounding box for each child,
// clip against view frustum.
short bbox[2][4];
// If NF_SUBSECTOR its a subsector,
// else it's a node of another subtree.
unsigned short children[2];
}) mapnode_t;
// Thing definition, position, orientation and type,
// plus skill/visibility flags and attributes.
typedef PACKED_STRUCT({
short x;
short y;
short angle;
short type;
short options;
}) mapthing_t;
#endif // __DOOMDATA__

24
src/doomdef.c Normal file
View File

@@ -0,0 +1,24 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// DoomDef - basic defines for DOOM, e.g. Version, game mode
// and skill level, and display parameters.
//
#include "doomdef.h"
// Location for any defines turned variables.
// None.

143
src/doomdef.h Normal file
View File

@@ -0,0 +1,143 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Internally used data structures for virtually everything,
// lots of other stuff.
//
#ifndef __DOOMDEF__
#define __DOOMDEF__
#include "i_timer.h"
//
// Global parameters/defines.
//
// DOOM version
#define DOOM_VERSION 109
// Version code for cph's longtics hack ("v1.91")
#define DOOM_191_VERSION 111
// The maximum number of players, multiplayer/networking.
#define MAXPLAYERS 4
// The current state of the game: whether we are
// playing, gazing at the intermission screen,
// the game final animation, or a demo.
typedef enum {
GS_LEVEL,
GS_INTERMISSION,
GS_DEMOSCREEN,
} gamestate_t;
typedef enum {
ga_nothing,
ga_loadlevel,
ga_newgame,
ga_loadgame,
ga_savegame,
ga_playdemo,
ga_completed,
ga_victory,
ga_worlddone,
ga_screenshot
} gameaction_t;
//
// Difficulty/skill settings/filters.
//
// Skill flags.
#define MTF_EASY 1
#define MTF_NORMAL 2
#define MTF_HARD 4
// Deaf monsters/do not react to sound.
#define MTF_AMBUSH 8
//
// Key cards.
//
typedef enum {
it_bluecard,
it_yellowcard,
it_redcard,
it_blueskull,
it_yellowskull,
it_redskull,
NUMCARDS
} card_t;
// The defined weapons,
// including a marker indicating
// user has not changed weapon.
typedef enum {
wp_fist,
wp_pistol,
wp_shotgun,
wp_chaingun,
wp_missile,
wp_plasma,
wp_bfg,
wp_chainsaw,
wp_supershotgun,
NUMWEAPONS,
// No pending weapon change.
wp_nochange
} weapontype_t;
// Ammunition types defined.
typedef enum {
am_clip, // Pistol / chaingun ammo.
am_shell, // Shotgun / double barreled shotgun.
am_cell, // Plasma rifle, BFG.
am_misl, // Missile launcher.
NUMAMMO,
am_noammo // Unlimited for chainsaw / fist.
} ammotype_t;
// Power up artifacts.
typedef enum {
pw_invulnerability,
pw_strength,
pw_invisibility,
pw_ironfeet,
pw_allmap,
pw_infrared,
NUMPOWERS
} powertype_t;
//
// Power up durations,
// how many seconds till expiration,
// assuming TICRATE is 35 ticks/second.
//
typedef enum {
INVULNTICS = (30 * TICRATE),
INVISTICS = (60 * TICRATE),
INFRATICS = (120 * TICRATE),
IRONTICS = (60 * TICRATE)
} powerduration_t;
#endif // __DOOMDEF__

153
src/doomkeys.h Normal file
View File

@@ -0,0 +1,153 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Key definitions
//
#ifndef __DOOMKEYS__
#define __DOOMKEYS__
//
// DOOM keyboard definition.
// This is the stuff configured by Setup.Exe.
// Most key data are simple ascii (uppercased).
//
#define KEY_RIGHTARROW 0xae
#define KEY_LEFTARROW 0xac
#define KEY_UPARROW 0xad
#define KEY_DOWNARROW 0xaf
#define KEY_ESCAPE 27
#define KEY_ENTER 13
#define KEY_TAB 9
#define KEY_F1 (0x80+0x3b)
#define KEY_F2 (0x80+0x3c)
#define KEY_F3 (0x80+0x3d)
#define KEY_F4 (0x80+0x3e)
#define KEY_F5 (0x80+0x3f)
#define KEY_F6 (0x80+0x40)
#define KEY_F7 (0x80+0x41)
#define KEY_F8 (0x80+0x42)
#define KEY_F9 (0x80+0x43)
#define KEY_F10 (0x80+0x44)
#define KEY_F11 (0x80+0x57)
#define KEY_F12 (0x80+0x58)
#define KEY_BACKSPACE 0x7f
#define KEY_PAUSE 0xff
#define KEY_EQUALS 0x3d
#define KEY_MINUS 0x2d
#define KEY_RSHIFT (0x80+0x36)
#define KEY_RCTRL (0x80+0x1d)
#define KEY_RALT (0x80+0x38)
#define KEY_LALT KEY_RALT
// new keys:
#define KEY_CAPSLOCK (0x80+0x3a)
#define KEY_NUMLOCK (0x80+0x45)
#define KEY_SCRLCK (0x80+0x46)
#define KEY_PRTSCR (0x80+0x59)
#define KEY_HOME (0x80+0x47)
#define KEY_END (0x80+0x4f)
#define KEY_PGUP (0x80+0x49)
#define KEY_PGDN (0x80+0x51)
#define KEY_INS (0x80+0x52)
#define KEY_DEL (0x80+0x53)
#define KEYP_0 0
#define KEYP_1 KEY_END
#define KEYP_2 KEY_DOWNARROW
#define KEYP_3 KEY_PGDN
#define KEYP_4 KEY_LEFTARROW
#define KEYP_5 '5'
#define KEYP_6 KEY_RIGHTARROW
#define KEYP_7 KEY_HOME
#define KEYP_8 KEY_UPARROW
#define KEYP_9 KEY_PGUP
#define KEYP_DIVIDE '/'
#define KEYP_PLUS '+'
#define KEYP_MINUS '-'
#define KEYP_MULTIPLY '*'
#define KEYP_PERIOD 0
#define KEYP_EQUALS KEY_EQUALS
#define KEYP_ENTER KEY_ENTER
#define SCANCODE_TO_KEYS_ARRAY { \
0, 0, 0, 0, 'a', /* 0-9 */ \
'b', 'c', 'd', 'e', 'f', \
'g', 'h', 'i', 'j', 'k', /* 10-19 */ \
'l', 'm', 'n', 'o', 'p', \
'q', 'r', 's', 't', 'u', /* 20-29 */ \
'v', 'w', 'x', 'y', 'z', \
'1', '2', '3', '4', '5', /* 30-39 */ \
'6', '7', '8', '9', '0', \
KEY_ENTER, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, ' ', /* 40-49 */ \
KEY_MINUS, KEY_EQUALS, '[', ']', '\\', \
0, ';', '\'', '`', ',', /* 50-59 */ \
'.', '/', KEY_CAPSLOCK, KEY_F1, KEY_F2, \
KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* 60-69 */ \
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, \
KEY_PRTSCR, KEY_SCRLCK, KEY_PAUSE, KEY_INS, KEY_HOME, /* 70-79 */ \
KEY_PGUP, KEY_DEL, KEY_END, KEY_PGDN, KEY_RIGHTARROW, \
KEY_LEFTARROW, KEY_DOWNARROW, KEY_UPARROW, /* 80-89 */ \
KEY_NUMLOCK, KEYP_DIVIDE, \
KEYP_MULTIPLY, KEYP_MINUS, KEYP_PLUS, KEYP_ENTER, KEYP_1, \
KEYP_2, KEYP_3, KEYP_4, KEYP_5, KEYP_6, /* 90-99 */ \
KEYP_7, KEYP_8, KEYP_9, KEYP_0, KEYP_PERIOD, \
0, 0, 0, KEYP_EQUALS, /* 100-103 */ \
}
// Default names for keys, to use in English or as fallback.
#define KEY_NAMES_ARRAY { \
{ KEY_BACKSPACE, "BACKSP" }, { KEY_TAB, "TAB" }, \
{ KEY_INS, "INS" }, { KEY_DEL, "DEL" }, \
{ KEY_PGUP, "PGUP" }, { KEY_PGDN, "PGDN" }, \
{ KEY_ENTER, "ENTER" }, { KEY_ESCAPE, "ESC" }, \
{ KEY_F1, "F1" }, { KEY_F2, "F2" }, \
{ KEY_F3, "F3" }, { KEY_F4, "F4" }, \
{ KEY_F5, "F5" }, { KEY_F6, "F6" }, \
{ KEY_F7, "F7" }, { KEY_F8, "F8" }, \
{ KEY_F9, "F9" }, { KEY_F10, "F10" }, \
{ KEY_F11, "F11" }, { KEY_F12, "F12" }, \
{ KEY_HOME, "HOME" }, { KEY_END, "END" }, \
{ KEY_MINUS, "-" }, { KEY_EQUALS, "=" }, \
{ KEY_NUMLOCK, "NUMLCK" }, { KEY_SCRLCK, "SCRLCK" }, \
{ KEY_PAUSE, "PAUSE" }, { KEY_PRTSCR, "PRTSC" }, \
{ KEY_UPARROW, "UP" }, { KEY_DOWNARROW, "DOWN" }, \
{ KEY_LEFTARROW, "LEFT" }, { KEY_RIGHTARROW, "RIGHT" }, \
{ KEY_RALT, "ALT" }, { KEY_LALT, "ALT" }, \
{ KEY_RSHIFT, "SHIFT" }, { KEY_CAPSLOCK, "CAPS" }, \
{ KEY_RCTRL, "CTRL" }, { ' ', "SPACE" }, \
{ 'a', "A" }, { 'b', "B" }, { 'c', "C" }, { 'd', "D" }, \
{ 'e', "E" }, { 'f', "F" }, { 'g', "G" }, { 'h', "H" }, \
{ 'i', "I" }, { 'j', "J" }, { 'k', "K" }, { 'l', "L" }, \
{ 'm', "M" }, { 'n', "N" }, { 'o', "O" }, { 'p', "P" }, \
{ 'q', "Q" }, { 'r', "R" }, { 's', "S" }, { 't', "T" }, \
{ 'u', "U" }, { 'v', "V" }, { 'w', "W" }, { 'x', "X" }, \
{ 'y', "Y" }, { 'z', "Z" }, { '0', "0" }, { '1', "1" }, \
{ '2', "2" }, { '3', "3" }, { '4', "4" }, { '5', "5" }, \
{ '6', "6" }, { '7', "7" }, { '8', "8" }, { '9', "9" }, \
{ '[', "[" }, { ']', "]" }, { ';', ";" }, { '`', "`" }, \
{ ',', "," }, { '.', "." }, { '/', "/" }, { '\\', "\\" }, \
{ '\'', "\'" }, \
}
#endif // __DOOMKEYS__

29
src/doomstat.c Normal file
View File

@@ -0,0 +1,29 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Put all global tate variables here.
//
#include "doomstat.h"
// Game Mode - identify IWAD as shareware, retail etc.
GameMode_t gamemode = indetermined;
GameMission_t gamemission = doom;
GameVersion_t gameversion = exe_final2;
GameVariant_t gamevariant = vanilla;
char *gamedescription;
// Set if homebrew PWAD stuff has been added.
boolean modifiedgame;

239
src/doomstat.h Normal file
View File

@@ -0,0 +1,239 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// All the global variables that store the internal state.
// Theoretically speaking, the internal state of the engine
// should be found by looking at the variables collected
// here, and every relevant module will have to include
// this header file.
// In practice, things are a bit messy.
//
#ifndef __D_STATE__
#define __D_STATE__
// Game mode/mission
#include "d_mode.h"
// We need the playr data structure as well.
#include "d_player.h"
#include "d_ticcmd.h"
// We need globally shared data structures,
// for defining the global state variables.
#include "doomdata.h"
#include "doomdef.h"
#include "doomtype.h"
// ------------------------
// Command line parameters.
//
extern boolean nomonsters; // checkparm of -nomonsters
extern boolean respawnparm; // checkparm of -respawn
extern boolean fastparm; // checkparm of -fast
extern boolean devparm; // DEBUG: launched with -devparm
// -----------------------------------------------------
// Game Mode - identify IWAD as shareware, retail etc.
//
extern GameMode_t gamemode;
extern GameMission_t gamemission;
extern GameVersion_t gameversion;
extern GameVariant_t gamevariant;
extern char *gamedescription;
// Convenience macro.
// 'gamemission' can be equal to pack_chex or pack_hacx, but these are
// just modified versions of doom and doom2, and should be interpreted
// as the same most of the time.
#define logical_gamemission \
(gamemission == pack_chex ? doom \
: gamemission == pack_hacx ? doom2 : gamemission)
// Set if homebrew PWAD stuff has been added.
extern boolean modifiedgame;
// -------------------------------------------
// Selected skill type, map etc.
//
// Defaults for menu, methinks.
extern skill_t startskill;
extern int startepisode;
extern int startmap;
// Savegame slot to load on startup. This is the value provided to
// the -loadgame option. If this has not been provided, this is -1.
extern int startloadgame;
extern boolean autostart;
// Selected by user.
extern skill_t gameskill;
extern int gameepisode;
extern int gamemap;
// If non-zero, exit the level after this number of minutes
extern int timelimit;
// Nightmare mode flag, single player.
extern boolean respawnmonsters;
// Netgame? Only true if >1 player.
extern boolean netgame;
// 0=Cooperative; 1=Deathmatch; 2=Altdeath
extern int deathmatch;
// -------------------------
// Internal parameters for sound rendering.
// These have been taken from the DOS version,
// but are not (yet) supported with Linux
// (e.g. no sound volume adjustment with menu.
// From m_menu.c:
// Sound FX volume has default, 0 - 15
// Music volume has default, 0 - 15
// These are multiplied by 8.
extern int sfxVolume;
extern int musicVolume;
// Current music/sfx card - index useless
// w/o a reference LUT in a sound module.
// Ideally, this would use indices found
// in: /usr/include/linux/soundcard.h
extern int snd_MusicDevice;
extern int snd_SfxDevice;
// Config file? Same disclaimer as above.
extern int snd_DesiredMusicDevice;
extern int snd_DesiredSfxDevice;
// -------------------------
// Status flags for refresh.
//
// Depending on view size - no status bar?
// Note that there is no way to disable the
// status bar explicitely.
extern boolean statusbaractive;
extern boolean automapactive; // In AutoMap mode?
extern boolean menuactive; // Menu overlayed?
extern boolean paused; // Game Pause?
extern boolean viewactive;
extern boolean nodrawers;
extern boolean testcontrols;
extern int testcontrols_mousespeed;
// This one is related to the 3-screen display mode.
// ANG90 = left side, ANG270 = right
extern int viewangleoffset;
// Player taking events, and displaying.
extern int consoleplayer;
extern int displayplayer;
// -------------------------------------
// Scores, rating.
// Statistics on a given map, for intermission.
//
extern int totalkills;
extern int totalitems;
extern int totalsecret;
// Timer, for scores.
extern int levelstarttic; // gametic at level start
extern int leveltime; // tics in game play for par
// --------------------------------------
// DEMO playback/recording related stuff.
// No demo, there is a human player in charge?
// Disable save/end game?
extern boolean usergame;
//?
extern boolean demoplayback;
extern boolean demorecording;
// Round angleturn in ticcmds to the nearest 256. This is used when
// recording Vanilla demos in netgames.
extern boolean lowres_turn;
// Quit after playing a demo from cmdline.
extern boolean singledemo;
//?
extern gamestate_t gamestate;
//-----------------------------
// Internal parameters, fixed.
// These are set by the engine, and not changed
// according to user inputs. Partly load from
// WAD, partly set at startup time.
// Bookkeeping on players - state.
extern player_t players[MAXPLAYERS];
// Alive? Disconnected?
extern boolean playeringame[MAXPLAYERS];
// Player spawn spots for deathmatch.
#define MAX_DM_STARTS 10
extern mapthing_t deathmatchstarts[MAX_DM_STARTS];
extern mapthing_t *deathmatch_p;
// Player spawn spots.
extern mapthing_t playerstarts[MAXPLAYERS];
// Intermission stats.
// Parameters for world map / intermission.
extern wbstartstruct_t wminfo;
//-----------------------------------------
// Internal parameters, used for engine.
//
// File handling stuff.
extern char *savegamedir;
extern char basedefault[1024];
// if true, load all graphics at level load
extern boolean precache;
// wipegamestate can be set to -1
// to force a wipe on the next draw
extern gamestate_t wipegamestate;
extern int mouseSensitivity;
extern int bodyqueslot;
// Needed to store the number of the dummy sky flat.
// Used for rendering,
// as well as tracking projectiles etc.
extern int skyflatnum;
// Netgame stuff (buffers and pointers, i.e. indices).
extern int rndindex;
extern ticcmd_t *netcmds;
#endif

80
src/doomtype.h Normal file
View File

@@ -0,0 +1,80 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Simple basic typedefs, isolated here to make it easier
// separating modules.
//
#ifndef __DOOMTYPE__
#define __DOOMTYPE__
#include <strings.h>
//
// The packed attribute forces structures to be packed into the minimum
// space necessary. If this is not done, the compiler may align structure
// fields differently to optimize memory access, inflating the overall
// structure size. It is important to use the packed attribute on certain
// structures where alignment is important, particularly data read/written
// to disk.
//
#ifdef __GNUC__
#define PACKEDATTR __attribute__((packed))
#else
#define PACKEDATTR
#endif
#define PACKEDPREFIX
#define PACKED_STRUCT(...) PACKEDPREFIX struct __VA_ARGS__ PACKEDATTR
// C99 integer types; with gcc we just use this. Other compilers
// should add conditional statements that define the C99 types.
// What is really wanted here is stdint.h; however, some old versions
// of Solaris don't have stdint.h and only have inttypes.h (the
// pre-standardisation version). inttypes.h is also in the C99
// standard and defined to include stdint.h, so include this.
#include <inttypes.h>
typedef enum
{
false,
true
} boolean;
typedef uint8_t byte;
typedef uint8_t pixel_t;
typedef int16_t dpixel_t;
#include <limits.h>
#define DIR_SEPARATOR '/'
#define DIR_SEPARATOR_S "/"
#define PATH_SEPARATOR ':'
#define arrlen(array) (sizeof(array) / sizeof(*array))
#endif

66
src/dstrings.c Normal file
View File

@@ -0,0 +1,66 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Globally defined strings.
//
#include "dstrings.h"
char *doom1_endmsg[] = {
"are you sure you want to\nquit this great game?",
"please don't leave, there's more\ndemons to toast!",
"let's beat it -- this is turning\ninto a bloodbath!",
"i wouldn't leave if i were you.\ndos is much worse.",
"you're trying to say you like dos\nbetter than me, right?",
"don't leave yet -- there's a\ndemon around that corner!",
"ya know, next time you come in here\ni'm gonna toast ya.",
"go ahead and leave. see if i care.",
};
char *doom2_endmsg[] = {
// QuitDOOM II messages
"are you sure you want to\nquit this great game?",
"you want to quit?\nthen, thou hast lost an eighth!",
"don't go now, there's a \ndimensional shambler waiting\nat the dos "
"prompt!",
"get outta here and go back\nto your boring programs.",
"if i were your boss, i'd \n deathmatch ya in a minute!",
"look, bud. you leave now\nand you forfeit your body count!",
"just leave. when you come\nback, i'll be waiting with a bat.",
"you're lucky i don't smack\nyou for thinking about leaving.",
};
#if 0
// UNUSED messages included in the source release
char* endmsg[] =
{
// DOOM1
QUITMSG,
// FinalDOOM?
"fuck you, pussy!\nget the fuck out!",
"you quit and i'll jizz\nin your cystholes!",
"if you leave, i'll make\nthe lord drink my jizz.",
"hey, ron! can we say\n'fuck' in the game?",
"i'd leave: this is just\nmore monsters and levels.\nwhat a load.",
"suck it down, asshole!\nyou're a fucking wimp!",
"don't quit now! we're \nstill spending your money!",
// Internal debug. Different style, too.
"THIS IS NO MESSAGE!\nPage intentionally left blank."
};
#endif

33
src/dstrings.h Normal file
View File

@@ -0,0 +1,33 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// DESCRIPTION:
// DOOM strings, by language.
//
#ifndef __DSTRINGS__
#define __DSTRINGS__
// Misc. other strings.
#define SAVEGAMENAME "doomsav"
// QuitDOOM messages
// 8 per each game type
#define NUM_QUITMESSAGES 8
extern char *doom1_endmsg[];
extern char *doom2_endmsg[];
#endif

221
src/f_wipe.c Normal file
View File

@@ -0,0 +1,221 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Mission begin melt/wipe screen special effect.
//
#include <string.h>
#include "i_video.h"
#include "m_random.h"
#include "v_video.h"
#include "z_zone.h"
#include "doomtype.h"
#include "f_wipe.h"
//
// SCREEN WIPE PACKAGE
//
// when zero, stop the wipe
static boolean go = 0;
static pixel_t *wipe_scr_start;
static pixel_t *wipe_scr_end;
static pixel_t *wipe_scr;
void wipe_shittyColMajorXform(dpixel_t *array, int width, int height) {
int x;
int y;
dpixel_t *dest;
dest = (dpixel_t *)Z_Malloc(width * height * sizeof(*dest), PU_STATIC, 0);
for (y = 0; y < height; y++)
for (x = 0; x < width; x++)
dest[x * height + y] = array[y * width + x];
memcpy(array, dest, width * height * sizeof(*dest));
Z_Free(dest);
}
int wipe_initColorXForm(int width, int height, int ticks) {
memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr));
return 0;
}
int wipe_doColorXForm(int width, int height, int ticks) {
boolean changed;
pixel_t *w;
pixel_t *e;
int newval;
changed = false;
w = wipe_scr;
e = wipe_scr_end;
while (w != wipe_scr + width * height) {
if (*w != *e) {
if (*w > *e) {
newval = *w - ticks;
if (newval < *e)
*w = *e;
else
*w = newval;
changed = true;
} else if (*w < *e) {
newval = *w + ticks;
if (newval > *e)
*w = *e;
else
*w = newval;
changed = true;
}
}
w++;
e++;
}
return !changed;
}
int wipe_exitColorXForm(int width, int height, int ticks) { return 0; }
static int *y;
int wipe_initMelt(int width, int height, int ticks) {
int i, r;
// copy start screen to main screen
memcpy(wipe_scr, wipe_scr_start, width * height * sizeof(*wipe_scr));
// makes this wipe faster (in theory)
// to have stuff in column-major format
wipe_shittyColMajorXform((dpixel_t *)wipe_scr_start, width / 2, height);
wipe_shittyColMajorXform((dpixel_t *)wipe_scr_end, width / 2, height);
// setup initial column positions
// (y<0 => not ready to scroll yet)
y = (int *)Z_Malloc(width * sizeof(int), PU_STATIC, 0);
y[0] = -(M_Random() % 16);
for (i = 1; i < width; i++) {
r = (M_Random() % 3) - 1;
y[i] = y[i - 1] + r;
if (y[i] > 0)
y[i] = 0;
else if (y[i] == -16)
y[i] = -15;
}
return 0;
}
int wipe_doMelt(int width, int height, int ticks) {
int i;
int j;
int dy;
int idx;
dpixel_t *s;
dpixel_t *d;
boolean done = true;
width /= 2;
while (ticks--) {
for (i = 0; i < width; i++) {
if (y[i] < 0) {
y[i]++;
done = false;
} else if (y[i] < height) {
dy = (y[i] < 16) ? y[i] + 1 : 8;
if (y[i] + dy >= height)
dy = height - y[i];
s = &((dpixel_t *)wipe_scr_end)[i * height + y[i]];
d = &((dpixel_t *)wipe_scr)[y[i] * width + i];
idx = 0;
for (j = dy; j; j--) {
d[idx] = *(s++);
idx += width;
}
y[i] += dy;
s = &((dpixel_t *)wipe_scr_start)[i * height];
d = &((dpixel_t *)wipe_scr)[y[i] * width + i];
idx = 0;
for (j = height - y[i]; j; j--) {
d[idx] = *(s++);
idx += width;
}
done = false;
}
}
}
return done;
}
int wipe_exitMelt(int width, int height, int ticks) {
Z_Free(y);
Z_Free(wipe_scr_start);
Z_Free(wipe_scr_end);
return 0;
}
int wipe_StartScreen(int x, int y, int width, int height) {
wipe_scr_start = Z_Malloc(
SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_start), PU_STATIC, NULL);
I_ReadScreen(wipe_scr_start);
return 0;
}
int wipe_EndScreen(int x, int y, int width, int height) {
wipe_scr_end = Z_Malloc(SCREENWIDTH * SCREENHEIGHT * sizeof(*wipe_scr_end),
PU_STATIC, NULL);
I_ReadScreen(wipe_scr_end);
V_DrawBlock(x, y, width, height, wipe_scr_start); // restore start scr.
return 0;
}
int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height,
int ticks) {
int rc;
static int (*wipes[])(int, int, int) = {
wipe_initColorXForm, wipe_doColorXForm, wipe_exitColorXForm,
wipe_initMelt, wipe_doMelt, wipe_exitMelt};
// initial stuff
if (!go) {
go = 1;
// wipe_scr = (pixel_t *) Z_Malloc(width*height, PU_STATIC, 0); // DEBUG
wipe_scr = I_VideoBuffer;
(*wipes[wipeno * 3])(width, height, ticks);
}
// do a piece of wipe-in
V_MarkRect(0, 0, width, height);
rc = (*wipes[wipeno * 3 + 1])(width, height, ticks);
// V_DrawBlock(x, y, 0, width, height, wipe_scr); // DEBUG
// final stuff
if (rc) {
go = 0;
(*wipes[wipeno * 3 + 2])(width, height, ticks);
}
return !go;
}

42
src/f_wipe.h Normal file
View File

@@ -0,0 +1,42 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Mission start screen wipe/melt, special effects.
//
#ifndef __F_WIPE_H__
#define __F_WIPE_H__
//
// SCREEN WIPE PACKAGE
//
enum {
// simple gradual pixel change for 8-bit only
wipe_ColorXForm,
// weird screen melt
wipe_Melt,
wipe_NUMWIPES
};
int wipe_StartScreen(int x, int y, int width, int height);
int wipe_EndScreen(int x, int y, int width, int height);
int wipe_ScreenWipe(int wipeno, int x, int y, int width, int height, int ticks);
#endif

1989
src/g_game.c Normal file

File diff suppressed because it is too large Load Diff

78
src/g_game.h Normal file
View File

@@ -0,0 +1,78 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Duh.
//
#ifndef __G_GAME__
#define __G_GAME__
#include "d_event.h"
#include "d_mode.h"
#include "d_ticcmd.h"
#include "doomtype.h"
//
// GAME
//
void G_DeathMatchSpawnPlayer(int playernum);
void G_InitNew(skill_t skill, int episode, int map);
// Can be called by the startup code or M_Responder.
// A normal game starts at map 1,
// but a warp test can start elsewhere
void G_DeferedInitNew(skill_t skill, int episode, int map);
void G_DeferedPlayDemo(char *demo);
// Can be called by the startup code or M_Responder,
// calls P_SetupLevel or W_EnterWorld.
void G_LoadGame(char *name);
void G_DoLoadGame(void);
// Called by M_Responder.
void G_SaveGame(int slot, char *description);
// Only called by startup code.
void G_RecordDemo(char *name);
void G_BeginRecording(void);
void G_PlayDemo(char *name);
void G_TimeDemo(char *name);
boolean G_CheckDemoStatus(void);
void G_ExitLevel(void);
void G_SecretExitLevel(void);
void G_WorldDone(void);
// Read current data from inputs and build a player movement command.
void G_BuildTiccmd(ticcmd_t *cmd, int maketic);
void G_Ticker(void);
boolean G_Responder(event_t *ev);
void G_ScreenShot(void);
void G_DrawMouseSpeedBox(void);
int G_VanillaVersionCode(void);
extern int vanilla_savegame_limit;
extern int vanilla_demo_limit;
#endif

302
src/gusconf.c Normal file
View File

@@ -0,0 +1,302 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// GUS emulation code.
//
// Actually emulating a GUS is far too much work; fortunately
// GUS "emulation" already exists in the form of Timidity, which
// supports GUS patch files. This code therefore converts Doom's
// DMXGUS lump into an equivalent Timidity configuration file.
//
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "doomtype.h"
#include "m_misc.h"
#include "w_wad.h"
#include "z_zone.h"
#define MAX_INSTRUMENTS 256
typedef struct
{
char *patch_names[MAX_INSTRUMENTS];
int used[MAX_INSTRUMENTS];
int mapping[MAX_INSTRUMENTS];
unsigned int count;
} gus_config_t;
char *gus_patch_path = "";
int gus_ram_kb = 1024;
static unsigned int MappingIndex(void)
{
unsigned int result = gus_ram_kb / 256;
if (result < 1)
{
return 1;
}
else if (result > 4)
{
return 4;
}
else
{
return result;
}
}
static int SplitLine(char *line, char **fields, unsigned int max_fields)
{
unsigned int num_fields;
char *p;
fields[0] = line;
num_fields = 1;
for (p = line; *p != '\0'; ++p)
{
if (*p == ',')
{
*p = '\0';
// Skip spaces following the comma.
do
{
++p;
} while (*p != '\0' && isspace(*p));
fields[num_fields] = p;
++num_fields;
--p;
if (num_fields >= max_fields)
{
break;
}
}
else if (*p == '#')
{
*p = '\0';
break;
}
}
// Strip off trailing whitespace from the end of the line.
p = fields[num_fields - 1] + strlen(fields[num_fields - 1]);
while (p > fields[num_fields - 1] && isspace(*(p - 1)))
{
--p;
*p = '\0';
}
return num_fields;
}
static void ParseLine(gus_config_t *config, char *line)
{
char *fields[6];
unsigned int i;
unsigned int num_fields;
unsigned int instr_id, mapped_id;
num_fields = SplitLine(line, fields, 6);
if (num_fields < 6)
{
return;
}
instr_id = atoi(fields[0]);
// Skip non GM percussions.
if ((instr_id >= 128 && instr_id < 128 + 35) || instr_id > 128 + 81)
{
return;
}
mapped_id = atoi(fields[MappingIndex()]);
for (i = 0; i < config->count; i++)
{
if (config->used[i] == mapped_id)
{
break;
}
}
if (i == config->count)
{
// DMX uses wrong patch name (we should use name of 'mapped_id'
// instrument, but DMX uses name of 'instr_id' instead).
free(config->patch_names[i]);
config->patch_names[i] = M_StringDuplicate(fields[5]);
config->used[i] = mapped_id;
config->count++;
}
config->mapping[instr_id] = i;
}
static void ParseDMXConfig(char *dmxconf, gus_config_t *config)
{
char *p, *newline;
unsigned int i;
memset(config, 0, sizeof(gus_config_t));
for (i = 0; i < MAX_INSTRUMENTS; ++i)
{
config->mapping[i] = -1;
config->used[i] = -1;
}
config->count = 0;
p = dmxconf;
for (;;)
{
newline = strchr(p, '\n');
if (newline != NULL)
{
*newline = '\0';
}
ParseLine(config, p);
if (newline == NULL)
{
break;
}
else
{
p = newline + 1;
}
}
}
static void FreeDMXConfig(gus_config_t *config)
{
unsigned int i;
for (i = 0; i < MAX_INSTRUMENTS; ++i)
{
free(config->patch_names[i]);
}
}
static char *ReadDMXConfig(void)
{
int lumpnum;
unsigned int len;
char *data;
// TODO: This should be chosen based on gamemode == commercial:
lumpnum = W_CheckNumForName("DMXGUS");
if (lumpnum < 0)
{
lumpnum = W_GetNumForName("DMXGUSC");
}
len = W_LumpLength(lumpnum);
data = Z_Malloc(len + 1, PU_STATIC, NULL);
W_ReadLump(lumpnum, data);
data[len] = '\0';
return data;
}
static boolean WriteTimidityConfig(char *path, gus_config_t *config)
{
FILE *fstream;
unsigned int i;
fstream = fopen(path, "w");
if (fstream == NULL)
{
return false;
}
fprintf(fstream, "# Autogenerated Timidity config.\n\n");
fprintf(fstream, "dir %s\n", gus_patch_path);
fprintf(fstream, "\nbank 0\n\n");
for (i = 0; i < 128; ++i)
{
if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS
&& config->patch_names[config->mapping[i]] != NULL)
{
fprintf(fstream, "%i %s\n",
i, config->patch_names[config->mapping[i]]);
}
}
fprintf(fstream, "\ndrumset 0\n\n");
for (i = 128 + 35; i <= 128 + 81; ++i)
{
if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS
&& config->patch_names[config->mapping[i]] != NULL)
{
fprintf(fstream, "%i %s\n",
i - 128, config->patch_names[config->mapping[i]]);
}
}
fprintf(fstream, "\n");
fclose(fstream);
return true;
}
boolean GUS_WriteConfig(char *path)
{
boolean result;
char *dmxconf;
gus_config_t config;
if (!strcmp(gus_patch_path, ""))
{
printf("You haven't configured gus_patch_path.\n");
printf("gus_patch_path needs to point to the location of "
"your GUS patch set.\n"
"To get a copy of the \"standard\" GUS patches, "
"download a copy of dgguspat.zip.\n");
return false;
}
dmxconf = ReadDMXConfig();
ParseDMXConfig(dmxconf, &config);
result = WriteTimidityConfig(path, &config);
FreeDMXConfig(&config);
Z_Free(dmxconf);
return result;
}

29
src/gusconf.h Normal file
View File

@@ -0,0 +1,29 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// GUS emulation code.
//
#ifndef __GUSCONF_H__
#define __GUSCONF_H__
#include "doomtype.h"
extern char *gus_patch_path;
extern int gus_ram_kb;
boolean GUS_WriteConfig(char *path);
#endif /* #ifndef __GUSCONF_H__ */

261
src/hu_lib.c Normal file
View File

@@ -0,0 +1,261 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: heads-up text and input code
//
#include <ctype.h>
#include "doomkeys.h"
#include "hu_lib.h"
#include "i_swap.h"
#include "i_video.h"
#include "r_draw.h"
#include "r_main.h"
#include "r_state.h"
#include "v_video.h"
// boolean : whether the screen is always erased
#define noterased viewwindowx
extern boolean automapactive; // in AM_map.c
void HUlib_init(void) {}
void HUlib_clearTextLine(hu_textline_t *t) {
t->len = 0;
t->l[0] = 0;
t->needsupdate = true;
}
void HUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc) {
t->x = x;
t->y = y;
t->f = f;
t->sc = sc;
HUlib_clearTextLine(t);
}
boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch) {
if (t->len == HU_MAXLINELENGTH)
return false;
else {
t->l[t->len++] = ch;
t->l[t->len] = 0;
t->needsupdate = 4;
return true;
}
}
boolean HUlib_delCharFromTextLine(hu_textline_t *t) {
if (!t->len)
return false;
else {
t->l[--t->len] = 0;
t->needsupdate = 4;
return true;
}
}
void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor) {
int i;
int w;
int x;
unsigned char c;
// draw the new stuff
x = l->x;
for (i = 0; i < l->len; i++) {
c = toupper(l->l[i]);
if (c != ' ' && c >= l->sc && c <= '_') {
w = SHORT(l->f[c - l->sc]->width);
if (x + w > SCREENWIDTH)
break;
V_DrawPatchDirect(x, l->y, l->f[c - l->sc]);
x += w;
} else {
x += 4;
if (x >= SCREENWIDTH)
break;
}
}
// draw the cursor if requested
if (drawcursor && x + SHORT(l->f['_' - l->sc]->width) <= SCREENWIDTH) {
V_DrawPatchDirect(x, l->y, l->f['_' - l->sc]);
}
}
// sorta called by HU_Erase and just better darn get things straight
void HUlib_eraseTextLine(hu_textline_t *l) {
int lh;
int y;
int yoffset;
// Only erases when NOT in automap and the screen is reduced,
// and the text must either need updating or refreshing
// (because of a recent change back from the automap)
if (!automapactive && viewwindowx && l->needsupdate) {
lh = SHORT(l->f[0]->height) + 1;
for (y = l->y, yoffset = y * SCREENWIDTH; y < l->y + lh;
y++, yoffset += SCREENWIDTH) {
if (y < viewwindowy || y >= viewwindowy + viewheight)
R_VideoErase(yoffset, SCREENWIDTH); // erase entire line
else {
R_VideoErase(yoffset, viewwindowx); // erase left border
R_VideoErase(yoffset + viewwindowx + viewwidth, viewwindowx);
// erase right border
}
}
}
if (l->needsupdate)
l->needsupdate--;
}
void HUlib_initSText(hu_stext_t *s, int x, int y, int h, patch_t **font,
int startchar, boolean *on) {
int i;
s->h = h;
s->on = on;
s->laston = true;
s->cl = 0;
for (i = 0; i < h; i++)
HUlib_initTextLine(&s->l[i], x, y - i * (SHORT(font[0]->height) + 1), font,
startchar);
}
void HUlib_addLineToSText(hu_stext_t *s) {
int i;
// add a clear line
if (++s->cl == s->h)
s->cl = 0;
HUlib_clearTextLine(&s->l[s->cl]);
// everything needs updating
for (i = 0; i < s->h; i++)
s->l[i].needsupdate = 4;
}
void HUlib_addMessageToSText(hu_stext_t *s, char *prefix, char *msg) {
HUlib_addLineToSText(s);
if (prefix)
while (*prefix)
HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++));
while (*msg)
HUlib_addCharToTextLine(&s->l[s->cl], *(msg++));
}
void HUlib_drawSText(hu_stext_t *s) {
int i, idx;
hu_textline_t *l;
if (!*s->on)
return; // if not on, don't draw
// draw everything
for (i = 0; i < s->h; i++) {
idx = s->cl - i;
if (idx < 0)
idx += s->h; // handle queue of lines
l = &s->l[idx];
// need a decision made here on whether to skip the draw
HUlib_drawTextLine(l, false); // no cursor, please
}
}
void HUlib_eraseSText(hu_stext_t *s) {
int i;
for (i = 0; i < s->h; i++) {
if (s->laston && !*s->on)
s->l[i].needsupdate = 4;
HUlib_eraseTextLine(&s->l[i]);
}
s->laston = *s->on;
}
void HUlib_initIText(hu_itext_t *it, int x, int y, patch_t **font,
int startchar, boolean *on) {
it->lm = 0; // default left margin is start of text
it->on = on;
it->laston = true;
HUlib_initTextLine(&it->l, x, y, font, startchar);
}
// The following deletion routines adhere to the left margin restriction
void HUlib_delCharFromIText(hu_itext_t *it) {
if (it->l.len != it->lm)
HUlib_delCharFromTextLine(&it->l);
}
void HUlib_eraseLineFromIText(hu_itext_t *it) {
while (it->lm != it->l.len)
HUlib_delCharFromTextLine(&it->l);
}
// Resets left margin as well
void HUlib_resetIText(hu_itext_t *it) {
it->lm = 0;
HUlib_clearTextLine(&it->l);
}
void HUlib_addPrefixToIText(hu_itext_t *it, char *str) {
while (*str)
HUlib_addCharToTextLine(&it->l, *(str++));
it->lm = it->l.len;
}
// wrapper function for handling general keyed input.
// returns true if it ate the key
boolean HUlib_keyInIText(hu_itext_t *it, unsigned char ch) {
ch = toupper(ch);
if (ch >= ' ' && ch <= '_')
HUlib_addCharToTextLine(&it->l, (char)ch);
else if (ch == KEY_BACKSPACE)
HUlib_delCharFromIText(it);
else if (ch != KEY_ENTER)
return false; // did not eat key
return true; // ate the key
}
void HUlib_drawIText(hu_itext_t *it) {
hu_textline_t *l = &it->l;
if (!*it->on)
return;
HUlib_drawTextLine(l, true); // draw the line w/ cursor
}
void HUlib_eraseIText(hu_itext_t *it) {
if (it->laston && !*it->on)
it->l.needsupdate = 4;
HUlib_eraseTextLine(&it->l);
it->laston = *it->on;
}

152
src/hu_lib.h Normal file
View File

@@ -0,0 +1,152 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: none
//
#ifndef __HULIB__
#define __HULIB__
#include "doomkeys.h"
#include "doomtype.h"
// We are referring to patches.
#include "v_patch.h"
// font stuff
#define HU_CHARERASE KEY_BACKSPACE
#define HU_MAXLINES 4
#define HU_MAXLINELENGTH 80
//
// Typedefs of widgets
//
// Text Line widget
// (parent of Scrolling Text and Input Text widgets)
typedef struct {
// left-justified position of scrolling text window
int x;
int y;
patch_t **f; // font
int sc; // start character
char l[HU_MAXLINELENGTH + 1]; // line of text
int len; // current line length
// whether this line needs to be udpated
int needsupdate;
} hu_textline_t;
// Scrolling Text window widget
// (child of Text Line widget)
typedef struct {
hu_textline_t l[HU_MAXLINES]; // text lines to draw
int h; // height in lines
int cl; // current line number
// pointer to boolean stating whether to update window
boolean *on;
boolean laston; // last value of *->on.
} hu_stext_t;
// Input Text Line widget
// (child of Text Line widget)
typedef struct {
hu_textline_t l; // text line to input on
// left margin past which I am not to delete characters
int lm;
// pointer to boolean stating whether to update window
boolean *on;
boolean laston; // last value of *->on;
} hu_itext_t;
//
// Widget creation, access, and update routines
//
// initializes heads-up widget library
void HUlib_init(void);
//
// textline code
//
// clear a line of text
void HUlib_clearTextLine(hu_textline_t *t);
void HUlib_initTextLine(hu_textline_t *t, int x, int y, patch_t **f, int sc);
// returns success
boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch);
// returns success
boolean HUlib_delCharFromTextLine(hu_textline_t *t);
// draws tline
void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor);
// erases text line
void HUlib_eraseTextLine(hu_textline_t *l);
//
// Scrolling Text window widget routines
//
// ?
void HUlib_initSText(hu_stext_t *s, int x, int y, int h, patch_t **font,
int startchar, boolean *on);
// add a new line
void HUlib_addLineToSText(hu_stext_t *s);
// ?
void HUlib_addMessageToSText(hu_stext_t *s, char *prefix, char *msg);
// draws stext
void HUlib_drawSText(hu_stext_t *s);
// erases all stext lines
void HUlib_eraseSText(hu_stext_t *s);
// Input Text Line widget routines
void HUlib_initIText(hu_itext_t *it, int x, int y, patch_t **font,
int startchar, boolean *on);
// enforces left margin
void HUlib_delCharFromIText(hu_itext_t *it);
// enforces left margin
void HUlib_eraseLineFromIText(hu_itext_t *it);
// resets line and left margin
void HUlib_resetIText(hu_itext_t *it);
// left of left-margin
void HUlib_addPrefixToIText(hu_itext_t *it, char *str);
// whether eaten
boolean HUlib_keyInIText(hu_itext_t *it, unsigned char ch);
void HUlib_drawIText(hu_itext_t *it);
// erases all itext lines
void HUlib_eraseIText(hu_itext_t *it);
#endif

457
src/hu_stuff.c Normal file
View File

@@ -0,0 +1,457 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: Heads-up displays
//
#include "hu_stuff.h"
#include "d_englsh.h"
#include "d_mode.h"
#include "d_player.h"
#include "d_ticcmd.h"
#include "doomdef.h"
#include "doomkeys.h"
#include "doomstat.h"
#include "hu_lib.h"
#include "i_input.h"
#include "i_swap.h"
#include "i_video.h"
#include "m_controls.h"
#include "m_misc.h"
#include "s_sound.h"
#include "sounds.h"
#include "v_patch.h"
#include "w_wad.h"
#include "z_zone.h"
//
// Locally used constants, shortcuts.
//
#define HU_TITLE (mapnames[(gameepisode - 1) * 9 + gamemap - 1])
#define HU_TITLE2 (mapnames_commercial[gamemap - 1])
#define HU_TITLEP (mapnames_commercial[gamemap - 1 + 32])
#define HU_TITLET (mapnames_commercial[gamemap - 1 + 64])
#define HU_TITLE_CHEX (mapnames_chex[(gameepisode - 1) * 9 + gamemap - 1])
#define HU_TITLEHEIGHT 1
#define HU_TITLEX 0
#define HU_TITLEY (167 - SHORT(hu_font[0]->height))
#define HU_INPUTTOGGLE 't'
#define HU_INPUTX HU_MSGX
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT * (SHORT(hu_font[0]->height) + 1))
#define HU_INPUTWIDTH 64
#define HU_INPUTHEIGHT 1
char *chat_macros[10] = {HUSTR_CHATMACRO0, HUSTR_CHATMACRO1, HUSTR_CHATMACRO2,
HUSTR_CHATMACRO3, HUSTR_CHATMACRO4, HUSTR_CHATMACRO5,
HUSTR_CHATMACRO6, HUSTR_CHATMACRO7, HUSTR_CHATMACRO8,
HUSTR_CHATMACRO9};
char *player_names[] = {HUSTR_PLRGREEN, HUSTR_PLRINDIGO, HUSTR_PLRBROWN,
HUSTR_PLRRED};
char chat_char; // remove later.
static player_t *plr;
patch_t *hu_font[HU_FONTSIZE];
static hu_textline_t w_title;
boolean chat_on;
static hu_itext_t w_chat;
static boolean always_off = false;
static char chat_dest[MAXPLAYERS];
static hu_itext_t w_inputbuffer[MAXPLAYERS];
static boolean message_on;
boolean message_dontfuckwithme;
static boolean message_nottobefuckedwith;
static hu_stext_t w_message;
static int message_counter;
extern int showMessages;
static boolean headsupactive = false;
//
// Builtin map names.
// The actual names can be found in DStrings.h.
//
char *mapnames[] = // DOOM shareware/registered/retail (Ultimate) names.
{
HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5,
HUSTR_E1M6, HUSTR_E1M7, HUSTR_E1M8, HUSTR_E1M9,
HUSTR_E2M1, HUSTR_E2M2, HUSTR_E2M3, HUSTR_E2M4, HUSTR_E2M5,
HUSTR_E2M6, HUSTR_E2M7, HUSTR_E2M8, HUSTR_E2M9,
HUSTR_E3M1, HUSTR_E3M2, HUSTR_E3M3, HUSTR_E3M4, HUSTR_E3M5,
HUSTR_E3M6, HUSTR_E3M7, HUSTR_E3M8, HUSTR_E3M9,
HUSTR_E4M1, HUSTR_E4M2, HUSTR_E4M3, HUSTR_E4M4, HUSTR_E4M5,
HUSTR_E4M6, HUSTR_E4M7, HUSTR_E4M8, HUSTR_E4M9,
"NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL",
"NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL"};
char *mapnames_chex[] = // Chex Quest names.
{
HUSTR_E1M1, HUSTR_E1M2, HUSTR_E1M3, HUSTR_E1M4, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5, HUSTR_E1M5,
"NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL",
"NEWLEVEL", "NEWLEVEL", "NEWLEVEL", "NEWLEVEL"};
// List of names for levels in commercial IWADs
// (doom2.wad, plutonia.wad, tnt.wad). These are stored in a
// single large array; WADs like pl2.wad have a MAP33, and rely on
// the layout in the Vanilla executable, where it is possible to
// overflow the end of one array into the next.
char *mapnames_commercial[] = {
// DOOM 2 map names.
HUSTR_1, HUSTR_2, HUSTR_3, HUSTR_4, HUSTR_5, HUSTR_6, HUSTR_7, HUSTR_8,
HUSTR_9, HUSTR_10, HUSTR_11,
HUSTR_12, HUSTR_13, HUSTR_14, HUSTR_15, HUSTR_16, HUSTR_17, HUSTR_18,
HUSTR_19, HUSTR_20,
HUSTR_21, HUSTR_22, HUSTR_23, HUSTR_24, HUSTR_25, HUSTR_26, HUSTR_27,
HUSTR_28, HUSTR_29, HUSTR_30, HUSTR_31, HUSTR_32,
// Plutonia WAD map names.
PHUSTR_1, PHUSTR_2, PHUSTR_3, PHUSTR_4, PHUSTR_5, PHUSTR_6, PHUSTR_7,
PHUSTR_8, PHUSTR_9, PHUSTR_10, PHUSTR_11,
PHUSTR_12, PHUSTR_13, PHUSTR_14, PHUSTR_15, PHUSTR_16, PHUSTR_17, PHUSTR_18,
PHUSTR_19, PHUSTR_20,
PHUSTR_21, PHUSTR_22, PHUSTR_23, PHUSTR_24, PHUSTR_25, PHUSTR_26, PHUSTR_27,
PHUSTR_28, PHUSTR_29, PHUSTR_30, PHUSTR_31, PHUSTR_32,
// TNT WAD map names.
THUSTR_1, THUSTR_2, THUSTR_3, THUSTR_4, THUSTR_5, THUSTR_6, THUSTR_7,
THUSTR_8, THUSTR_9, THUSTR_10, THUSTR_11,
THUSTR_12, THUSTR_13, THUSTR_14, THUSTR_15, THUSTR_16, THUSTR_17, THUSTR_18,
THUSTR_19, THUSTR_20,
THUSTR_21, THUSTR_22, THUSTR_23, THUSTR_24, THUSTR_25, THUSTR_26, THUSTR_27,
THUSTR_28, THUSTR_29, THUSTR_30, THUSTR_31, THUSTR_32};
void HU_Init(void) {
int i;
int j;
char buffer[9];
// load the heads-up font
j = HU_FONTSTART;
for (i = 0; i < HU_FONTSIZE; i++) {
M_snprintf(buffer, 9, "STCFN%.3d", j++);
hu_font[i] = (patch_t *)W_CacheLumpName(buffer, PU_STATIC);
}
}
void HU_Stop(void) { headsupactive = false; }
void HU_Start(void) {
int i;
char *s;
if (headsupactive)
HU_Stop();
plr = &players[consoleplayer];
message_on = false;
message_dontfuckwithme = false;
message_nottobefuckedwith = false;
chat_on = false;
// create the message widget
HUlib_initSText(&w_message, HU_MSGX, HU_MSGY, HU_MSGHEIGHT, hu_font,
HU_FONTSTART, &message_on);
// create the map title widget
HUlib_initTextLine(&w_title, HU_TITLEX, HU_TITLEY, hu_font, HU_FONTSTART);
switch (logical_gamemission) {
case doom:
s = HU_TITLE;
break;
case doom2:
s = HU_TITLE2;
break;
case pack_plut:
s = HU_TITLEP;
break;
case pack_tnt:
s = HU_TITLET;
break;
default:
s = "Unknown level";
break;
}
if (logical_gamemission == doom && gameversion == exe_chex) {
s = HU_TITLE_CHEX;
}
while (*s)
HUlib_addCharToTextLine(&w_title, *(s++));
// create the chat widget
HUlib_initIText(&w_chat, HU_INPUTX, HU_INPUTY, hu_font, HU_FONTSTART,
&chat_on);
// create the inputbuffer widgets
for (i = 0; i < MAXPLAYERS; i++)
HUlib_initIText(&w_inputbuffer[i], 0, 0, 0, 0, &always_off);
headsupactive = true;
}
void HU_Drawer(void) {
HUlib_drawSText(&w_message);
HUlib_drawIText(&w_chat);
if (automapactive)
HUlib_drawTextLine(&w_title, false);
}
void HU_Erase(void) {
HUlib_eraseSText(&w_message);
HUlib_eraseIText(&w_chat);
HUlib_eraseTextLine(&w_title);
}
void HU_Ticker(void) {
int i, rc;
char c;
// tick down message counter if message is up
if (message_counter && !--message_counter) {
message_on = false;
message_nottobefuckedwith = false;
}
if (showMessages || message_dontfuckwithme) {
// display message if necessary
if ((plr->message && !message_nottobefuckedwith) ||
(plr->message && message_dontfuckwithme)) {
HUlib_addMessageToSText(&w_message, 0, plr->message);
plr->message = 0;
message_on = true;
message_counter = HU_MSGTIMEOUT;
message_nottobefuckedwith = message_dontfuckwithme;
message_dontfuckwithme = 0;
}
} // else message_on = false;
// check for incoming chat characters
if (netgame) {
for (i = 0; i < MAXPLAYERS; i++) {
if (!playeringame[i])
continue;
if (i != consoleplayer && (c = players[i].cmd.chatchar)) {
if (c <= HU_BROADCAST)
chat_dest[i] = c;
else {
rc = HUlib_keyInIText(&w_inputbuffer[i], c);
if (rc && c == KEY_ENTER) {
if (w_inputbuffer[i].l.len && (chat_dest[i] == consoleplayer + 1 ||
chat_dest[i] == HU_BROADCAST)) {
HUlib_addMessageToSText(&w_message, (player_names[i]),
w_inputbuffer[i].l.l);
message_nottobefuckedwith = true;
message_on = true;
message_counter = HU_MSGTIMEOUT;
if (gamemode == commercial)
S_StartSound(0, sfx_radio);
else
S_StartSound(0, sfx_tink);
}
HUlib_resetIText(&w_inputbuffer[i]);
}
}
players[i].cmd.chatchar = 0;
}
}
}
}
#define QUEUESIZE 128
static char chatchars[QUEUESIZE];
static int head = 0;
static int tail = 0;
void HU_queueChatChar(char c) {
if (((head + 1) & (QUEUESIZE - 1)) == tail) {
plr->message = (HUSTR_MSGU);
} else {
chatchars[head] = c;
head = (head + 1) & (QUEUESIZE - 1);
}
}
char HU_dequeueChatChar(void) {
char c;
if (head != tail) {
c = chatchars[tail];
tail = (tail + 1) & (QUEUESIZE - 1);
} else {
c = 0;
}
return c;
}
static void StartChatInput(int dest) {
chat_on = true;
HUlib_resetIText(&w_chat);
HU_queueChatChar(HU_BROADCAST);
I_StartTextInput(0, 8, SCREENWIDTH, 16);
}
static void StopChatInput(void) {
chat_on = false;
I_StopTextInput();
}
boolean HU_Responder(event_t *ev) {
static char lastmessage[HU_MAXLINELENGTH + 1];
char *macromessage;
boolean eatkey = false;
static boolean altdown = false;
unsigned char c;
int i;
int numplayers;
static int num_nobrainers = 0;
numplayers = 0;
for (i = 0; i < MAXPLAYERS; i++)
numplayers += playeringame[i];
if (ev->data1 == KEY_RSHIFT) {
return false;
} else if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT) {
altdown = ev->type == ev_keydown;
return false;
}
if (ev->type != ev_keydown)
return false;
if (!chat_on) {
if (ev->data1 == key_message_refresh) {
message_on = true;
message_counter = HU_MSGTIMEOUT;
eatkey = true;
} else if (netgame && ev->data2 == key_multi_msg) {
eatkey = true;
StartChatInput(HU_BROADCAST);
} else if (netgame && numplayers > 2) {
for (i = 0; i < MAXPLAYERS; i++) {
if (ev->data2 == key_multi_msgplayer[i]) {
if (playeringame[i] && i != consoleplayer) {
eatkey = true;
StartChatInput(i + 1);
break;
} else if (i == consoleplayer) {
num_nobrainers++;
if (num_nobrainers < 3)
plr->message = (HUSTR_TALKTOSELF1);
else if (num_nobrainers < 6)
plr->message = (HUSTR_TALKTOSELF2);
else if (num_nobrainers < 9)
plr->message = (HUSTR_TALKTOSELF3);
else if (num_nobrainers < 32)
plr->message = (HUSTR_TALKTOSELF4);
else
plr->message = (HUSTR_TALKTOSELF5);
}
}
}
}
} else {
// send a macro
if (altdown) {
c = ev->data1 - '0';
if (c > 9)
return false;
// fprintf(stderr, "got here\n");
macromessage = chat_macros[c];
// kill last message with a '\n'
HU_queueChatChar(KEY_ENTER); // DEBUG!!!
// send the macro message
while (*macromessage)
HU_queueChatChar(*macromessage++);
HU_queueChatChar(KEY_ENTER);
// leave chat mode and notify that it was sent
StopChatInput();
M_StringCopy(lastmessage, chat_macros[c], sizeof(lastmessage));
plr->message = lastmessage;
eatkey = true;
} else {
c = ev->data3;
eatkey = HUlib_keyInIText(&w_chat, c);
if (eatkey) {
// static unsigned char buf[20]; // DEBUG
HU_queueChatChar(c);
// M_snprintf(buf, sizeof(buf), "KEY: %d => %d", ev->data1, c);
// plr->message = buf;
}
if (c == KEY_ENTER) {
StopChatInput();
if (w_chat.l.len) {
M_StringCopy(lastmessage, w_chat.l.l, sizeof(lastmessage));
plr->message = lastmessage;
}
} else if (c == KEY_ESCAPE) {
StopChatInput();
}
}
}
return eatkey;
}

59
src/hu_stuff.h Normal file
View File

@@ -0,0 +1,59 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: Head up display
//
#ifndef __HU_STUFF_H__
#define __HU_STUFF_H__
#include "d_event.h"
#include "doomtype.h"
#include "i_timer.h"
//
// Globally visible constants.
//
#define HU_FONTSTART '!' // the first font characters
#define HU_FONTEND '_' // the last font characters
// Calculate # of glyphs in font.
#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1)
#define HU_BROADCAST 5
#define HU_MSGX 0
#define HU_MSGY 0
#define HU_MSGWIDTH 64 // in characters
#define HU_MSGHEIGHT 1 // in lines
#define HU_MSGTIMEOUT (4 * TICRATE)
//
// HEADS UP TEXT
//
void HU_Init(void);
void HU_Start(void);
boolean HU_Responder(event_t *ev);
void HU_Ticker(void);
void HU_Drawer(void);
char HU_dequeueChatChar(void);
void HU_Erase(void);
extern char *chat_macros[10];
#endif

69
src/i_cdmus.c Normal file
View File

@@ -0,0 +1,69 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// Hexen CD interface.
//
#include "i_cdmus.h"
int cd_Error;
int I_CDMusInit(void)
{
return 0;
}
// We cannot print status messages inline during startup, they must
// be deferred until after I_CDMusInit has returned.
void I_CDMusPrintStartup(void)
{
}
int I_CDMusPlay(int track)
{
return 0;
}
int I_CDMusStop(void)
{
return 0;
}
int I_CDMusResume(void)
{
return 0;
}
int I_CDMusSetVolume(int volume)
{
return 0;
}
int I_CDMusFirstTrack(void)
{
return 0;
}
int I_CDMusLastTrack(void)
{
return 0;
}
int I_CDMusTrackLength(int track_num)
{
return 0;
}

41
src/i_cdmus.h Normal file
View File

@@ -0,0 +1,41 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// i_cdmus.h
#ifndef __ICDMUS__
#define __ICDMUS__
#define CDERR_NOTINSTALLED 10 // MSCDEX not installed
#define CDERR_NOAUDIOSUPPORT 11 // CD-ROM Doesn't support audio
#define CDERR_NOAUDIOTRACKS 12 // Current CD has no audio tracks
#define CDERR_BADDRIVE 20 // Bad drive number
#define CDERR_BADTRACK 21 // Bad track number
#define CDERR_IOCTLBUFFMEM 22 // Not enough low memory for IOCTL
#define CDERR_DEVREQBASE 100 // DevReq errors
extern int cd_Error;
int I_CDMusInit(void);
void I_CDMusPrintStartup(void);
int I_CDMusPlay(int track);
int I_CDMusStop(void);
int I_CDMusResume(void);
int I_CDMusSetVolume(int volume);
int I_CDMusFirstTrack(void);
int I_CDMusLastTrack(void);
int I_CDMusTrackLength(int track);
#endif

471
src/i_input.c Normal file
View File

@@ -0,0 +1,471 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// SDL implementation of system-specific input interface.
//
#include <string.h>
#include "SDL2/SDL_events.h"
#include "SDL2/SDL_keyboard.h"
#include "SDL2/SDL_keycode.h"
#include "SDL2/SDL_mouse.h"
#include "SDL2/SDL_scancode.h"
#include "d_event.h"
#include "doomkeys.h"
#include "doomtype.h"
#include "i_input.h"
#include "m_config.h"
static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY;
// Lookup table for mapping ASCII characters to their equivalent when
// shift is pressed on a US layout keyboard. This is the original table
// as found in the Doom sources, comments and all.
static const char shiftxform[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, ' ', '!', '"', '#', '$', '%', '&',
'"', // shift-'
'(', ')', '*', '+',
'<', // shift-,
'_', // shift--
'>', // shift-.
'?', // shift-/
')', // shift-0
'!', // shift-1
'@', // shift-2
'#', // shift-3
'$', // shift-4
'%', // shift-5
'^', // shift-6
'&', // shift-7
'*', // shift-8
'(', // shift-9
':',
':', // shift-;
'<',
'+', // shift-=
'>', '?', '@',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'[', // shift-[
'!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
']', // shift-]
'"', '_',
'\'', // shift-`
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'{', '|', '}', '~', 127
};
// If true, I_StartTextInput() has been called, and we are populating
// the data3 field of ev_keydown events.
static boolean text_input_enabled = true;
// Bit mask of mouse button state.
static unsigned int mouse_button_state = 0;
// Disallow mouse and joystick movement to cause forward/backward
// motion. Specified with the '-novert' command line parameter.
// This is an int to allow saving to config file
int novert = 0;
// If true, keyboard mapping is ignored, like in Vanilla Doom.
// The sensible thing to do is to disable this if you have a non-US
// keyboard.
int vanilla_keyboard_mapping = true;
// Mouse acceleration
//
// This emulates some of the behavior of DOS mouse drivers by increasing
// the speed when the mouse is moved fast.
//
// The mouse input values are input directly to the game, but when
// the values exceed the value of mouse_threshold, they are multiplied
// by mouse_acceleration to increase the speed.
float mouse_acceleration = 2.0;
int mouse_threshold = 10;
// Translates the SDL key to a value of the type found in doomkeys.h
static int TranslateKey(SDL_Keysym *sym)
{
int scancode = sym->scancode;
switch (scancode)
{
case SDL_SCANCODE_LCTRL:
case SDL_SCANCODE_RCTRL:
return KEY_RCTRL;
case SDL_SCANCODE_LSHIFT:
case SDL_SCANCODE_RSHIFT:
return KEY_RSHIFT;
case SDL_SCANCODE_LALT:
return KEY_LALT;
case SDL_SCANCODE_RALT:
return KEY_RALT;
default:
if (scancode >= 0 && scancode < arrlen(scancode_translate_table))
{
return scancode_translate_table[scancode];
}
else
{
return 0;
}
}
}
// Get the localized version of the key press. This takes into account the
// keyboard layout, but does not apply any changes due to modifiers, (eg.
// shift-, alt-, etc.)
static int GetLocalizedKey(SDL_Keysym *sym)
{
// When using Vanilla mapping, we just base everything off the scancode
// and always pretend the user is using a US layout keyboard.
if (vanilla_keyboard_mapping)
{
return TranslateKey(sym);
}
else
{
int result = sym->sym;
if (result < 0 || result >= 128)
{
result = 0;
}
return result;
}
}
// Get the equivalent ASCII (Unicode?) character for a keypress.
static int GetTypedChar(SDL_Keysym *sym)
{
// We only return typed characters when entering text, after
// I_StartTextInput() has been called. Otherwise we return nothing.
if (!text_input_enabled)
{
return 0;
}
// If we're strictly emulating Vanilla, we should always act like
// we're using a US layout keyboard (in ev_keydown, data1=data2).
// Otherwise we should use the native key mapping.
if (vanilla_keyboard_mapping)
{
int result = TranslateKey(sym);
// If shift is held down, apply the original uppercase
// translation table used under DOS.
if ((SDL_GetModState() & KMOD_SHIFT) != 0
&& result >= 0 && result < arrlen(shiftxform))
{
result = shiftxform[result];
}
return result;
}
else
{
SDL_Event next_event;
// Special cases, where we always return a fixed value.
switch (sym->sym)
{
case SDLK_BACKSPACE: return KEY_BACKSPACE;
case SDLK_RETURN: return KEY_ENTER;
default:
break;
}
// The following is a gross hack, but I don't see an easier way
// of doing this within the SDL2 API (in SDL1 it was easier).
// We want to get the fully transformed input character associated
// with this keypress - correct keyboard layout, appropriately
// transformed by any modifier keys, etc. So peek ahead in the SDL
// event queue and see if the key press is immediately followed by
// an SDL_TEXTINPUT event. If it is, it's reasonable to assume the
// key press and the text input are connected. Technically the SDL
// API does not guarantee anything of the sort, but in practice this
// is what happens and I've verified it through manual inspect of
// the SDL source code.
//
// In an ideal world we'd split out ev_keydown into a separate
// ev_textinput event, as SDL2 has done. But this doesn't work
// (I experimented with the idea), because lots of Doom's code is
// based around different responders "eating" events to stop them
// being passed on to another responder. If code is listening for
// a text input, it cannot block the corresponding keydown events
// which can affect other responders.
//
// So we're stuck with this as a rather fragile alternative.
if (SDL_PeepEvents(&next_event, 1, SDL_PEEKEVENT,
SDL_FIRSTEVENT, SDL_LASTEVENT) == 1
&& next_event.type == SDL_TEXTINPUT)
{
// If an SDL_TEXTINPUT event is found, we always assume it
// matches the key press. The input text must be a single
// ASCII character - if it isn't, it's possible the input
// char is a Unicode value instead; better to send a null
// character than the unshifted key.
if (strlen(next_event.text.text) == 1
&& (next_event.text.text[0] & 0x80) == 0)
{
return next_event.text.text[0];
}
}
// Failed to find anything :/
return 0;
}
}
void I_HandleKeyboardEvent(SDL_Event *sdlevent)
{
// XXX: passing pointers to event for access after this function
// has terminated is undefined behaviour
event_t event;
switch (sdlevent->type)
{
case SDL_KEYDOWN:
event.type = ev_keydown;
event.data1 = TranslateKey(&sdlevent->key.keysym);
event.data2 = GetLocalizedKey(&sdlevent->key.keysym);
event.data3 = GetTypedChar(&sdlevent->key.keysym);
if (event.data1 != 0)
{
D_PostEvent(&event);
}
break;
case SDL_KEYUP:
event.type = ev_keyup;
event.data1 = TranslateKey(&sdlevent->key.keysym);
// data2/data3 are initialized to zero for ev_keyup.
// For ev_keydown it's the shifted Unicode character
// that was typed, but if something wants to detect
// key releases it should do so based on data1
// (key ID), not the printable char.
event.data2 = 0;
event.data3 = 0;
if (event.data1 != 0)
{
D_PostEvent(&event);
}
break;
default:
break;
}
}
void I_StartTextInput(int x1, int y1, int x2, int y2)
{
text_input_enabled = true;
if (!vanilla_keyboard_mapping)
{
// SDL2-TODO: SDL_SetTextInputRect(...);
SDL_StartTextInput();
}
}
void I_StopTextInput(void)
{
text_input_enabled = false;
if (!vanilla_keyboard_mapping)
{
SDL_StopTextInput();
}
}
static void UpdateMouseButtonState(unsigned int button, boolean on)
{
static event_t event;
if (button < SDL_BUTTON_LEFT || button > MAX_MOUSE_BUTTONS)
{
return;
}
// Note: button "0" is left, button "1" is right,
// button "2" is middle for Doom. This is different
// to how SDL sees things.
switch (button)
{
case SDL_BUTTON_LEFT:
button = 0;
break;
case SDL_BUTTON_RIGHT:
button = 1;
break;
case SDL_BUTTON_MIDDLE:
button = 2;
break;
default:
// SDL buttons are indexed from 1.
--button;
break;
}
// Turn bit representing this button on or off.
if (on)
{
mouse_button_state |= (1 << button);
}
else
{
mouse_button_state &= ~(1 << button);
}
// Post an event with the new button state.
event.type = ev_mouse;
event.data1 = mouse_button_state;
event.data2 = event.data3 = 0;
D_PostEvent(&event);
}
static void MapMouseWheelToButtons(SDL_MouseWheelEvent *wheel)
{
// SDL2 distinguishes button events from mouse wheel events.
// We want to treat the mouse wheel as two buttons, as per
// SDL1
static event_t up, down;
int button;
if (wheel->y <= 0)
{ // scroll down
button = 4;
}
else
{ // scroll up
button = 3;
}
// post a button down event
mouse_button_state |= (1 << button);
down.type = ev_mouse;
down.data1 = mouse_button_state;
down.data2 = down.data3 = 0;
D_PostEvent(&down);
// post a button up event
mouse_button_state &= ~(1 << button);
up.type = ev_mouse;
up.data1 = mouse_button_state;
up.data2 = up.data3 = 0;
D_PostEvent(&up);
}
void I_HandleMouseEvent(SDL_Event *sdlevent)
{
switch (sdlevent->type)
{
case SDL_MOUSEBUTTONDOWN:
UpdateMouseButtonState(sdlevent->button.button, true);
break;
case SDL_MOUSEBUTTONUP:
UpdateMouseButtonState(sdlevent->button.button, false);
break;
case SDL_MOUSEWHEEL:
MapMouseWheelToButtons(&(sdlevent->wheel));
break;
default:
break;
}
}
static int AccelerateMouse(int val)
{
if (val < 0)
return -AccelerateMouse(-val);
if (val > mouse_threshold)
{
return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold);
}
else
{
return val;
}
}
//
// Read the change in mouse state to generate mouse motion events
//
// This is to combine all mouse movement for a tic into one mouse
// motion event.
void I_ReadMouse(void)
{
int x, y;
event_t ev;
SDL_GetRelativeMouseState(&x, &y);
if (x != 0 || y != 0)
{
ev.type = ev_mouse;
ev.data1 = mouse_button_state;
ev.data2 = AccelerateMouse(x);
if (!novert)
{
ev.data3 = -AccelerateMouse(y);
}
else
{
ev.data3 = 0;
}
// XXX: undefined behaviour since event is scoped to
// this function
D_PostEvent(&ev);
}
}
// Bind all variables controlling input options.
void I_BindInputVariables(void)
{
M_BindFloatVariable("mouse_acceleration", &mouse_acceleration);
M_BindIntVariable("mouse_threshold", &mouse_threshold);
M_BindIntVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping);
M_BindIntVariable("novert", &novert);
}

40
src/i_input.h Normal file
View File

@@ -0,0 +1,40 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System-specific keyboard/mouse input.
//
#ifndef __I_INPUT__
#define __I_INPUT__
#define MAX_MOUSE_BUTTONS 8
extern float mouse_acceleration;
extern int mouse_threshold;
void I_BindInputVariables(void);
void I_ReadMouse(void);
// I_StartTextInput begins text input, activating the on-screen keyboard
// (if one is used). The caller indicates that any entered text will be
// displayed in the rectangle given by the provided set of coordinates.
void I_StartTextInput(int x1, int y1, int x2, int y2);
// I_StopTextInput finishes text input, deactivating the on-screen keyboard
// (if one is used).
void I_StopTextInput(void);
#endif

384
src/i_joystick.c Normal file
View File

@@ -0,0 +1,384 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// SDL Joystick code.
//
#include <stdio.h>
#include <string.h>
#include "SDL2/SDL.h"
#include "SDL2/SDL_events.h"
#include "SDL2/SDL_joystick.h"
#include "d_event.h"
#include "doomtype.h"
#include "i_joystick.h"
#include "i_system.h"
#include "m_config.h"
#include "m_misc.h"
// When an axis is within the dead zone, it is set to zero.
// This is 5% of the full range:
#define DEAD_ZONE (32768 / 3)
static SDL_Joystick *joystick = NULL;
// Configuration variables:
// Standard default.cfg Joystick enable/disable
static int usejoystick = 0;
// SDL GUID and index of the joystick to use.
static char *joystick_guid = "";
static int joystick_index = -1;
// Which joystick axis to use for horizontal movement, and whether to
// invert the direction:
static int joystick_x_axis = 0;
static int joystick_x_invert = 0;
// Which joystick axis to use for vertical movement, and whether to
// invert the direction:
static int joystick_y_axis = 1;
static int joystick_y_invert = 0;
// Which joystick axis to use for strafing?
static int joystick_strafe_axis = -1;
static int joystick_strafe_invert = 0;
// Virtual to physical button joystick button mapping. By default this
// is a straight mapping.
static int joystick_physical_buttons[NUM_VIRTUAL_BUTTONS] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
};
void I_ShutdownJoystick(void)
{
if (joystick != NULL)
{
SDL_JoystickClose(joystick);
joystick = NULL;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
}
static boolean IsValidAxis(int axis)
{
int num_axes;
if (axis < 0)
{
return true;
}
if (IS_BUTTON_AXIS(axis))
{
return true;
}
if (IS_HAT_AXIS(axis))
{
return HAT_AXIS_HAT(axis) < SDL_JoystickNumHats(joystick);
}
num_axes = SDL_JoystickNumAxes(joystick);
return axis < num_axes;
}
static int DeviceIndex(void)
{
SDL_JoystickGUID guid, dev_guid;
int i;
guid = SDL_JoystickGetGUIDFromString(joystick_guid);
// GUID identifies a class of device rather than a specific device.
// Check if joystick_index has the expected GUID, as this can act
// as a tie-breaker in case there are multiple identical devices.
if (joystick_index >= 0 && joystick_index < SDL_NumJoysticks())
{
dev_guid = SDL_JoystickGetDeviceGUID(joystick_index);
if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
{
return joystick_index;
}
}
// Check all devices to look for one with the expected GUID.
for (i = 0; i < SDL_NumJoysticks(); ++i)
{
dev_guid = SDL_JoystickGetDeviceGUID(i);
if (!memcmp(&guid, &dev_guid, sizeof(SDL_JoystickGUID)))
{
printf("I_InitJoystick: Joystick moved to index %d.\n", i);
return i;
}
}
// No joystick found with the expected GUID.
return -1;
}
void I_InitJoystick(void)
{
int index;
if (!usejoystick || !strcmp(joystick_guid, ""))
{
return;
}
if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
{
return;
}
index = DeviceIndex();
if (index < 0)
{
printf("I_InitJoystick: Couldn't find joystick with GUID \"%s\": "
"device not found or not connected?\n",
joystick_guid);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return;
}
// Open the joystick
joystick = SDL_JoystickOpen(index);
if (joystick == NULL)
{
printf("I_InitJoystick: Failed to open joystick #%i\n", index);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
return;
}
if (!IsValidAxis(joystick_x_axis)
|| !IsValidAxis(joystick_y_axis)
|| !IsValidAxis(joystick_strafe_axis))
{
printf("I_InitJoystick: Invalid joystick axis for configured joystick "
"(run joystick setup again)\n");
SDL_JoystickClose(joystick);
joystick = NULL;
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
SDL_JoystickEventState(SDL_ENABLE);
// Initialized okay!
printf("I_InitJoystick: %s\n", SDL_JoystickName(joystick));
I_AtExit(I_ShutdownJoystick, true);
}
static boolean IsAxisButton(int physbutton)
{
if (IS_BUTTON_AXIS(joystick_x_axis))
{
if (physbutton == BUTTON_AXIS_NEG(joystick_x_axis)
|| physbutton == BUTTON_AXIS_POS(joystick_x_axis))
{
return true;
}
}
if (IS_BUTTON_AXIS(joystick_y_axis))
{
if (physbutton == BUTTON_AXIS_NEG(joystick_y_axis)
|| physbutton == BUTTON_AXIS_POS(joystick_y_axis))
{
return true;
}
}
if (IS_BUTTON_AXIS(joystick_strafe_axis))
{
if (physbutton == BUTTON_AXIS_NEG(joystick_strafe_axis)
|| physbutton == BUTTON_AXIS_POS(joystick_strafe_axis))
{
return true;
}
}
return false;
}
// Get the state of the given virtual button.
static int ReadButtonState(int vbutton)
{
int physbutton;
// Map from virtual button to physical (SDL) button.
if (vbutton < NUM_VIRTUAL_BUTTONS)
{
physbutton = joystick_physical_buttons[vbutton];
}
else
{
physbutton = vbutton;
}
// Never read axis buttons as buttons.
if (IsAxisButton(physbutton))
{
return 0;
}
return SDL_JoystickGetButton(joystick, physbutton);
}
// Get a bitmask of all currently-pressed buttons
static int GetButtonsState(void)
{
int i;
int result;
result = 0;
for (i = 0; i < 20; ++i)
{
if (ReadButtonState(i))
{
result |= 1 << i;
}
}
return result;
}
// Read the state of an axis, inverting if necessary.
static int GetAxisState(int axis, int invert)
{
int result;
// Axis -1 means disabled.
if (axis < 0)
{
return 0;
}
// Is this a button axis, or a hat axis?
// If so, we need to handle it specially.
result = 0;
if (IS_BUTTON_AXIS(axis))
{
if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_NEG(axis)))
{
result -= 32767;
}
if (SDL_JoystickGetButton(joystick, BUTTON_AXIS_POS(axis)))
{
result += 32767;
}
}
else if (IS_HAT_AXIS(axis))
{
int direction = HAT_AXIS_DIRECTION(axis);
int hatval = SDL_JoystickGetHat(joystick, HAT_AXIS_HAT(axis));
if (direction == HAT_AXIS_HORIZONTAL)
{
if ((hatval & SDL_HAT_LEFT) != 0)
{
result -= 32767;
}
else if ((hatval & SDL_HAT_RIGHT) != 0)
{
result += 32767;
}
}
else if (direction == HAT_AXIS_VERTICAL)
{
if ((hatval & SDL_HAT_UP) != 0)
{
result -= 32767;
}
else if ((hatval & SDL_HAT_DOWN) != 0)
{
result += 32767;
}
}
}
else
{
result = SDL_JoystickGetAxis(joystick, axis);
if (result < DEAD_ZONE && result > -DEAD_ZONE)
{
result = 0;
}
}
if (invert)
{
result = -result;
}
return result;
}
void I_UpdateJoystick(void)
{
if (joystick != NULL)
{
event_t ev;
ev.type = ev_joystick;
ev.data1 = GetButtonsState();
ev.data2 = GetAxisState(joystick_x_axis, joystick_x_invert);
ev.data3 = GetAxisState(joystick_y_axis, joystick_y_invert);
ev.data4 = GetAxisState(joystick_strafe_axis, joystick_strafe_invert);
D_PostEvent(&ev);
}
}
void I_BindJoystickVariables(void)
{
int i;
M_BindIntVariable("use_joystick", &usejoystick);
M_BindStringVariable("joystick_guid", &joystick_guid);
M_BindIntVariable("joystick_index", &joystick_index);
M_BindIntVariable("joystick_x_axis", &joystick_x_axis);
M_BindIntVariable("joystick_y_axis", &joystick_y_axis);
M_BindIntVariable("joystick_strafe_axis", &joystick_strafe_axis);
M_BindIntVariable("joystick_x_invert", &joystick_x_invert);
M_BindIntVariable("joystick_y_invert", &joystick_y_invert);
M_BindIntVariable("joystick_strafe_invert",&joystick_strafe_invert);
for (i = 0; i < NUM_VIRTUAL_BUTTONS; ++i)
{
char name[32];
M_snprintf(name, sizeof(name), "joystick_physical_button%i", i);
M_BindIntVariable(name, &joystick_physical_buttons[i]);
}
}

70
src/i_joystick.h Normal file
View File

@@ -0,0 +1,70 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System-specific joystick interface.
//
#ifndef __I_JOYSTICK__
#define __I_JOYSTICK__
// Number of "virtual" joystick buttons defined in configuration files.
// This needs to be at least as large as the number of different key
// bindings supported by the higher-level game code (joyb* variables).
#define NUM_VIRTUAL_BUTTONS 11
// If this bit is set in a configuration file axis value, the axis is
// not actually a joystick axis, but instead is a "button axis". This
// means that instead of reading an SDL joystick axis, we read the
// state of two buttons to get the axis value. This is needed for eg.
// the PS3 SIXAXIS controller, where the D-pad buttons register as
// buttons, not as two axes.
#define BUTTON_AXIS 0x10000
// Query whether a given axis value describes a button axis.
#define IS_BUTTON_AXIS(axis) ((axis) >= 0 && ((axis) & BUTTON_AXIS) != 0)
// Get the individual buttons from a button axis value.
#define BUTTON_AXIS_NEG(axis) ((axis) & 0xff)
#define BUTTON_AXIS_POS(axis) (((axis) >> 8) & 0xff)
// Create a button axis value from two button values.
#define CREATE_BUTTON_AXIS(neg, pos) (BUTTON_AXIS | (neg) | ((pos) << 8))
// If this bit is set in an axis value, the axis is not actually a
// joystick axis, but is a "hat" axis. This means that we read (one of)
// the hats on the joystick.
#define HAT_AXIS 0x20000
#define IS_HAT_AXIS(axis) ((axis) >= 0 && ((axis) & HAT_AXIS) != 0)
// Get the hat number from a hat axis value.
#define HAT_AXIS_HAT(axis) ((axis) & 0xff)
// Which axis of the hat? (horizonal or vertical)
#define HAT_AXIS_DIRECTION(axis) (((axis) >> 8) & 0xff)
#define CREATE_HAT_AXIS(hat, direction) \
(HAT_AXIS | (hat) | ((direction) << 8))
#define HAT_AXIS_HORIZONTAL 1
#define HAT_AXIS_VERTICAL 2
void I_InitJoystick(void);
void I_ShutdownJoystick(void);
void I_UpdateJoystick(void);
void I_BindJoystickVariables(void);
#endif /* #ifndef __I_JOYSTICK__ */

37
src/i_main.c Normal file
View File

@@ -0,0 +1,37 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Main program, simply calls D_DoomMain high level loop.
//
#include "d_main.h"
#include "m_argv.h"
int main(int argc, char **argv)
{
// save arguments
myargc = argc;
myargv = argv;
M_FindResponseFile();
// start doom
D_DoomMain ();
return 0;
}

1302
src/i_sdlmusic.c Normal file

File diff suppressed because it is too large Load Diff

874
src/i_sdlsound.c Normal file
View File

@@ -0,0 +1,874 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
// Copyright(C) 2008 David Flater
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System interface for sound.
//
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL2/SDL.h"
#include "SDL2/SDL_audio.h"
#include "SDL2/SDL_mixer.h"
#include "SDL2/SDL_stdinc.h"
#include "doomtype.h"
#include "i_sound.h"
#include "i_system.h"
#include "m_misc.h"
#include "w_wad.h"
#include "z_zone.h"
struct allocated_sound_s;
#define NUM_CHANNELS 16
typedef struct allocated_sound_s allocated_sound_t;
struct allocated_sound_s
{
sfxinfo_t *sfxinfo;
Mix_Chunk chunk;
int use_count;
int pitch;
allocated_sound_t *prev, *next;
};
static boolean sound_initialized = false;
static allocated_sound_t *channels_playing[NUM_CHANNELS];
static int mixer_freq;
static Uint16 mixer_format;
static int mixer_channels;
static boolean use_sfx_prefix;
static boolean (*ExpandSoundData)(sfxinfo_t *sfxinfo,
byte *data,
int samplerate,
int length) = NULL;
// Doubly-linked list of allocated sounds.
// When a sound is played, it is moved to the head, so that the oldest
// sounds not used recently are at the tail.
static allocated_sound_t *allocated_sounds_head = NULL;
static allocated_sound_t *allocated_sounds_tail = NULL;
static int allocated_sounds_size = 0;
int use_libsamplerate = 0;
// Scale factor used when converting libsamplerate floating point numbers
// to integers. Too high means the sounds can clip; too low means they
// will be too quiet. This is an amount that should avoid clipping most
// of the time: with all the Doom IWAD sound effects, at least. If a PWAD
// is used, clipping might occur.
float libsamplerate_scale = 0.65f;
// Hook a sound into the linked list at the head.
static void AllocatedSoundLink(allocated_sound_t *snd)
{
snd->prev = NULL;
snd->next = allocated_sounds_head;
allocated_sounds_head = snd;
if (allocated_sounds_tail == NULL)
{
allocated_sounds_tail = snd;
}
else
{
snd->next->prev = snd;
}
}
// Unlink a sound from the linked list.
static void AllocatedSoundUnlink(allocated_sound_t *snd)
{
if (snd->prev == NULL)
{
allocated_sounds_head = snd->next;
}
else
{
snd->prev->next = snd->next;
}
if (snd->next == NULL)
{
allocated_sounds_tail = snd->prev;
}
else
{
snd->next->prev = snd->prev;
}
}
static void FreeAllocatedSound(allocated_sound_t *snd)
{
// Unlink from linked list.
AllocatedSoundUnlink(snd);
// Keep track of the amount of allocated sound data:
allocated_sounds_size -= snd->chunk.alen;
free(snd);
}
// Search from the tail backwards along the allocated sounds list, find
// and free a sound that is not in use, to free up memory. Return true
// for success.
static boolean FindAndFreeSound(void)
{
allocated_sound_t *snd;
snd = allocated_sounds_tail;
while (snd != NULL)
{
if (snd->use_count == 0)
{
FreeAllocatedSound(snd);
return true;
}
snd = snd->prev;
}
// No available sounds to free...
return false;
}
// Enforce SFX cache size limit. We are just about to allocate "len"
// bytes on the heap for a new sound effect, so free up some space
// so that we keep allocated_sounds_size < snd_cachesize
static void ReserveCacheSpace(size_t len)
{
if (snd_cachesize <= 0)
{
return;
}
// Keep freeing sound effects that aren't currently being played,
// until there is enough space for the new sound.
while (allocated_sounds_size + len > snd_cachesize)
{
// Free a sound. If there is nothing more to free, stop.
if (!FindAndFreeSound())
{
break;
}
}
}
// Allocate a block for a new sound effect.
static allocated_sound_t *AllocateSound(sfxinfo_t *sfxinfo, size_t len)
{
allocated_sound_t *snd;
// Keep allocated sounds within the cache size.
ReserveCacheSpace(len);
// Allocate the sound structure and data. The data will immediately
// follow the structure, which acts as a header.
do
{
snd = malloc(sizeof(allocated_sound_t) + len);
// Out of memory? Try to free an old sound, then loop round
// and try again.
if (snd == NULL && !FindAndFreeSound())
{
return NULL;
}
} while (snd == NULL);
// Skip past the chunk structure for the audio buffer
snd->chunk.abuf = (byte *) (snd + 1);
snd->chunk.alen = len;
snd->chunk.allocated = 1;
snd->chunk.volume = MIX_MAX_VOLUME;
snd->pitch = NORM_PITCH;
snd->sfxinfo = sfxinfo;
snd->use_count = 0;
// Keep track of how much memory all these cached sounds are using...
allocated_sounds_size += len;
AllocatedSoundLink(snd);
return snd;
}
// Lock a sound, to indicate that it may not be freed.
static void LockAllocatedSound(allocated_sound_t *snd)
{
// Increase use count, to stop the sound being freed.
++snd->use_count;
//printf("++ %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count);
// When we use a sound, re-link it into the list at the head, so
// that the oldest sounds fall to the end of the list for freeing.
AllocatedSoundUnlink(snd);
AllocatedSoundLink(snd);
}
// Unlock a sound to indicate that it may now be freed.
static void UnlockAllocatedSound(allocated_sound_t *snd)
{
if (snd->use_count <= 0)
{
I_Error("Sound effect released more times than it was locked...");
}
--snd->use_count;
//printf("-- %s: Use count=%i\n", snd->sfxinfo->name, snd->use_count);
}
// Search through the list of allocated sounds and return the one that matches
// the supplied sfxinfo entry and pitch level.
static allocated_sound_t * GetAllocatedSoundBySfxInfoAndPitch(sfxinfo_t *sfxinfo, int pitch)
{
allocated_sound_t * p = allocated_sounds_head;
while (p != NULL)
{
if (p->sfxinfo == sfxinfo && p->pitch == pitch)
{
return p;
}
p = p->next;
}
return NULL;
}
// Allocate a new sound chunk and pitch-shift an existing sound up-or-down
// into it.
static allocated_sound_t * PitchShift(allocated_sound_t *insnd, int pitch)
{
allocated_sound_t * outsnd;
Sint16 *inp, *outp;
Sint16 *srcbuf, *dstbuf;
Uint32 srclen, dstlen;
srcbuf = (Sint16 *)insnd->chunk.abuf;
srclen = insnd->chunk.alen;
// determine ratio pitch:NORM_PITCH and apply to srclen, then invert.
// This is an approximation of vanilla behaviour based on measurements
dstlen = (int)((1 + (1 - (float)pitch / NORM_PITCH)) * srclen);
// ensure that the new buffer is an even length
if ((dstlen % 2) == 0)
{
dstlen++;
}
outsnd = AllocateSound(insnd->sfxinfo, dstlen);
if (!outsnd)
{
return NULL;
}
outsnd->pitch = pitch;
dstbuf = (Sint16 *)outsnd->chunk.abuf;
// loop over output buffer. find corresponding input cell, copy over
for (outp = dstbuf; outp < dstbuf + dstlen/2; ++outp)
{
inp = srcbuf + (int)((float)(outp - dstbuf) / dstlen * srclen);
*outp = *inp;
}
return outsnd;
}
// When a sound stops, check if it is still playing. If it is not,
// we can mark the sound data as CACHE to be freed back for other
// means.
static void ReleaseSoundOnChannel(int channel)
{
allocated_sound_t *snd = channels_playing[channel];
Mix_HaltChannel(channel);
if (snd == NULL)
{
return;
}
channels_playing[channel] = NULL;
UnlockAllocatedSound(snd);
// if the sound is a pitch-shift and it's not in use, immediately
// free it
if (snd->pitch != NORM_PITCH && snd->use_count <= 0)
{
FreeAllocatedSound(snd);
}
}
static boolean ConvertibleRatio(int freq1, int freq2)
{
int ratio;
if (freq1 > freq2)
{
return ConvertibleRatio(freq2, freq1);
}
else if ((freq2 % freq1) != 0)
{
// Not in a direct ratio
return false;
}
else
{
// Check the ratio is a power of 2
ratio = freq2 / freq1;
while ((ratio & 1) == 0)
{
ratio = ratio >> 1;
}
return ratio == 1;
}
}
// Generic sound expansion function for any sample rate.
// Returns number of clipped samples (always 0).
static boolean ExpandSoundData_SDL(sfxinfo_t *sfxinfo,
byte *data,
int samplerate,
int length)
{
SDL_AudioCVT convertor;
allocated_sound_t *snd;
Mix_Chunk *chunk;
uint32_t expanded_length;
// Calculate the length of the expanded version of the sample.
expanded_length = (uint32_t) ((((uint64_t) length) * mixer_freq) / samplerate);
// Double up twice: 8 -> 16 bit and mono -> stereo
expanded_length *= 4;
// Allocate a chunk in which to expand the sound
snd = AllocateSound(sfxinfo, expanded_length);
if (snd == NULL)
{
return false;
}
chunk = &snd->chunk;
// If we can, use the standard / optimized SDL conversion routines.
if (samplerate <= mixer_freq
&& ConvertibleRatio(samplerate, mixer_freq)
&& SDL_BuildAudioCVT(&convertor,
AUDIO_U8, 1, samplerate,
mixer_format, mixer_channels, mixer_freq))
{
convertor.buf = chunk->abuf;
convertor.len = length;
memcpy(convertor.buf, data, length);
SDL_ConvertAudio(&convertor);
}
else
{
Sint16 *expanded = (Sint16 *) chunk->abuf;
int expanded_length;
int expand_ratio;
int i;
// Generic expansion if conversion does not work:
//
// SDL's audio conversion only works for rate conversions that are
// powers of 2; if the two formats are not in a direct power of 2
// ratio, do this naive conversion instead.
// number of samples in the converted sound
expanded_length = ((uint64_t) length * mixer_freq) / samplerate;
expand_ratio = (length << 8) / expanded_length;
for (i=0; i<expanded_length; ++i)
{
Sint16 sample;
int src;
src = (i * expand_ratio) >> 8;
sample = data[src] | (data[src] << 8);
sample -= 32768;
// expand 8->16 bits, mono->stereo
expanded[i * 2] = expanded[i * 2 + 1] = sample;
}
// Perform a low-pass filter on the upscaled sound to filter
// out high-frequency noise from the conversion process.
{
float rc, dt, alpha;
// Low-pass filter for cutoff frequency f:
//
// For sampling rate r, dt = 1 / r
// rc = 1 / 2*pi*f
// alpha = dt / (rc + dt)
// Filter to the half sample rate of the original sound effect
// (maximum frequency, by nyquist)
dt = 1.0f / mixer_freq;
rc = 1.0f / (3.14f * samplerate);
alpha = dt / (rc + dt);
// Both channels are processed in parallel, hence [i-2]:
for (i=2; i<expanded_length * 2; ++i)
{
expanded[i] = (Sint16) (alpha * expanded[i]
+ (1 - alpha) * expanded[i-2]);
}
}
}
return true;
}
// Load and convert a sound effect
// Returns true if successful
static boolean CacheSFX(sfxinfo_t *sfxinfo)
{
int lumpnum;
unsigned int lumplen;
int samplerate;
unsigned int length;
byte *data;
// need to load the sound
lumpnum = sfxinfo->lumpnum;
data = W_CacheLumpNum(lumpnum, PU_STATIC);
lumplen = W_LumpLength(lumpnum);
// Check the header, and ensure this is a valid sound
if (lumplen < 8
|| data[0] != 0x03 || data[1] != 0x00)
{
// Invalid sound
return false;
}
// 16 bit sample rate field, 32 bit length field
samplerate = (data[3] << 8) | data[2];
length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
// If the header specifies that the length of the sound is greater than
// the length of the lump itself, this is an invalid sound lump
// We also discard sound lumps that are less than 49 samples long,
// as this is how DMX behaves - although the actual cut-off length
// seems to vary slightly depending on the sample rate. This needs
// further investigation to better understand the correct
// behavior.
if (length > lumplen - 8 || length <= 48)
{
return false;
}
// The DMX sound library seems to skip the first 16 and last 16
// bytes of the lump - reason unknown.
data += 16;
length -= 32;
// Sample rate conversion
if (!ExpandSoundData(sfxinfo, data + 8, samplerate, length))
{
return false;
}
// don't need the original lump any more
W_ReleaseLumpNum(lumpnum);
return true;
}
static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len)
{
// Linked sfx lumps? Get the lump number for the sound linked to.
if (sfx->link != NULL)
{
sfx = sfx->link;
}
// Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't
// do this.
if (use_sfx_prefix)
{
M_snprintf(buf, buf_len, "ds%s", (sfx->name));
}
else
{
M_StringCopy(buf, (sfx->name), buf_len);
}
}
static void I_SDL_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
{
// no-op
}
// Load a SFX chunk into memory and ensure that it is locked.
static boolean LockSound(sfxinfo_t *sfxinfo)
{
// If the sound isn't loaded, load it now
if (GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH) == NULL)
{
if (!CacheSFX(sfxinfo))
{
return false;
}
}
LockAllocatedSound(GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH));
return true;
}
//
// Retrieve the raw data lump index
// for a given SFX name.
//
static int I_SDL_GetSfxLumpNum(sfxinfo_t *sfx)
{
char namebuf[9];
GetSfxLumpName(sfx, namebuf, sizeof(namebuf));
return W_GetNumForName(namebuf);
}
static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
{
int left, right;
if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
{
return;
}
left = ((254 - sep) * vol) / 127;
right = ((sep) * vol) / 127;
if (left < 0) left = 0;
else if ( left > 255) left = 255;
if (right < 0) right = 0;
else if (right > 255) right = 255;
Mix_SetPanning(handle, left, right);
}
//
// Starting a sound means adding it
// to the current list of active sounds
// in the internal channels.
// As the SFX info struct contains
// e.g. a pointer to the raw data,
// it is ignored.
// As our sound handling does not handle
// priority, it is ignored.
// Pitching (that is, increased speed of playback)
// is set, but currently not used by mixing.
//
static int I_SDL_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch)
{
allocated_sound_t *snd;
if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
{
return -1;
}
// Release a sound effect if there is already one playing
// on this channel
ReleaseSoundOnChannel(channel);
// Get the sound data
if (!LockSound(sfxinfo))
{
return -1;
}
snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, pitch);
if (snd == NULL)
{
allocated_sound_t *newsnd;
// fetch the base sound effect, un-pitch-shifted
snd = GetAllocatedSoundBySfxInfoAndPitch(sfxinfo, NORM_PITCH);
if (snd == NULL)
{
return -1;
}
if (snd_pitchshift)
{
newsnd = PitchShift(snd, pitch);
if (newsnd)
{
LockAllocatedSound(newsnd);
UnlockAllocatedSound(snd);
snd = newsnd;
}
}
}
else
{
LockAllocatedSound(snd);
}
// play sound
Mix_PlayChannel(channel, &snd->chunk, 0);
channels_playing[channel] = snd;
// set separation, etc.
I_SDL_UpdateSoundParams(channel, vol, sep);
return channel;
}
static void I_SDL_StopSound(int handle)
{
if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
{
return;
}
// Sound data is no longer needed; release the
// sound data being used for this channel
ReleaseSoundOnChannel(handle);
}
static boolean I_SDL_SoundIsPlaying(int handle)
{
if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
{
return false;
}
return Mix_Playing(handle);
}
//
// Periodically called to update the sound system
//
static void I_SDL_UpdateSound(void)
{
int i;
// Check all channels to see if a sound has finished
for (i=0; i<NUM_CHANNELS; ++i)
{
if (channels_playing[i] && !I_SDL_SoundIsPlaying(i))
{
// Sound has finished playing on this channel,
// but sound data has not been released to cache
ReleaseSoundOnChannel(i);
}
}
}
static void I_SDL_ShutdownSound(void)
{
if (!sound_initialized)
{
return;
}
Mix_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
sound_initialized = false;
}
// Calculate slice size, based on snd_maxslicetime_ms.
// The result must be a power of two.
static int GetSliceSize(void)
{
int limit;
int n;
limit = (snd_samplerate * snd_maxslicetime_ms) / 1000;
// Try all powers of two, not exceeding the limit.
for (n=0;; ++n)
{
// 2^n <= limit < 2^n+1 ?
if ((1 << (n + 1)) > limit)
{
return (1 << n);
}
}
// Should never happen?
return 1024;
}
static boolean I_SDL_InitSound(boolean _use_sfx_prefix)
{
int i;
use_sfx_prefix = _use_sfx_prefix;
// No sounds yet
for (i=0; i<NUM_CHANNELS; ++i)
{
channels_playing[i] = NULL;
}
if (SDL_Init(SDL_INIT_AUDIO) < 0)
{
fprintf(stderr, "Unable to set up sound.\n");
return false;
}
if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, GetSliceSize()) < 0)
{
fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
return false;
}
ExpandSoundData = ExpandSoundData_SDL;
Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);
if (use_libsamplerate != 0)
{
fprintf(stderr, "I_SDL_InitSound: use_libsamplerate=%i, but "
"libsamplerate support not compiled in.\n",
use_libsamplerate);
}
Mix_AllocateChannels(NUM_CHANNELS);
SDL_PauseAudio(0);
sound_initialized = true;
return true;
}
static snddevice_t sound_sdl_devices[] =
{
SNDDEVICE_SB,
SNDDEVICE_PAS,
SNDDEVICE_GUS,
SNDDEVICE_WAVEBLASTER,
SNDDEVICE_SOUNDCANVAS,
SNDDEVICE_AWE32,
};
sound_module_t sound_sdl_module =
{
sound_sdl_devices,
arrlen(sound_sdl_devices),
I_SDL_InitSound,
I_SDL_ShutdownSound,
I_SDL_GetSfxLumpNum,
I_SDL_UpdateSound,
I_SDL_UpdateSoundParams,
I_SDL_StartSound,
I_SDL_StopSound,
I_SDL_SoundIsPlaying,
I_SDL_PrecacheSounds,
};

435
src/i_sound.c Normal file
View File

@@ -0,0 +1,435 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION: none
//
#include <stdio.h>
#include "doomtype.h"
#include "gusconf.h"
#include "i_sound.h"
#include "i_video.h"
#include "m_argv.h"
#include "m_config.h"
// Sound sample rate to use for digital output (Hz)
int snd_samplerate = 44100;
// Maximum number of bytes to dedicate to allocated sound effects.
// (Default: 64MB)
int snd_cachesize = 64 * 1024 * 1024;
// Config variable that controls the sound buffer size.
// We default to 28ms (1000 / 35fps = 1 buffer per tic).
int snd_maxslicetime_ms = 28;
// External command to invoke to play back music.
char *snd_musiccmd = "";
// Whether to vary the pitch of sound effects
// Each game will set the default differently
int snd_pitchshift = -1;
// Low-level sound and music modules we are using
static sound_module_t *sound_module;
static music_module_t *music_module;
int snd_musicdevice = SNDDEVICE_SB;
int snd_sfxdevice = SNDDEVICE_SB;
// Sound modules
extern void I_InitTimidityConfig(void);
extern sound_module_t sound_sdl_module;
extern music_module_t music_sdl_module;
// For native music module:
extern char *music_pack_path;
extern char *timidity_cfg_path;
// Compiled-in sound modules:
static sound_module_t *sound_modules[] =
{
&sound_sdl_module,
NULL,
};
// Compiled-in music modules:
static music_module_t *music_modules[] =
{
&music_sdl_module,
NULL,
};
// Check if a sound device is in the given list of devices
static boolean SndDeviceInList(snddevice_t device, snddevice_t *list,
int len)
{
int i;
for (i=0; i<len; ++i)
{
if (device == list[i])
{
return true;
}
}
return false;
}
// Find and initialize a sound_module_t appropriate for the setting
// in snd_sfxdevice.
static void InitSfxModule(boolean use_sfx_prefix)
{
int i;
sound_module = NULL;
for (i=0; sound_modules[i] != NULL; ++i)
{
// Is the sfx device in the list of devices supported by
// this module?
if (SndDeviceInList(snd_sfxdevice,
sound_modules[i]->sound_devices,
sound_modules[i]->num_sound_devices))
{
// Initialize the module
if (sound_modules[i]->Init(use_sfx_prefix))
{
sound_module = sound_modules[i];
return;
}
}
}
}
// Initialize music according to snd_musicdevice.
static void InitMusicModule(void)
{
int i;
music_module = NULL;
for (i=0; music_modules[i] != NULL; ++i)
{
// Is the music device in the list of devices supported
// by this module?
printf("snd_musicdevice\n");
printf("%d\n", snd_musicdevice);
if (SndDeviceInList(snd_musicdevice,
music_modules[i]->sound_devices,
music_modules[i]->num_sound_devices))
{
printf("nope!\n");
// Initialize the module
if (music_modules[i]->Init())
{
music_module = music_modules[i];
return;
}
}
}
}
//
// Initializes sound stuff, including volume
// Sets channels, SFX and music volume,
// allocates channel buffer, sets S_sfx lookup.
//
void I_InitSound(boolean use_sfx_prefix)
{
boolean nosound, nosfx, nomusic;
//!
// @vanilla
//
// Disable all sound output.
//
nosound = M_CheckParm("-nosound") > 0;
//!
// @vanilla
//
// Disable sound effects.
//
nosfx = M_CheckParm("-nosfx") > 0;
//!
// @vanilla
//
// Disable music.
//
nomusic = M_CheckParm("-nomusic") > 0;
// Initialize the sound and music subsystems.
if (!nosound && !screensaver_mode)
{
// This is kind of a hack. If native MIDI is enabled, set up
// the TIMIDITY_CFG environment variable here before SDL_mixer
// is opened.
if (!nomusic
&& (snd_musicdevice == SNDDEVICE_GENMIDI
|| snd_musicdevice == SNDDEVICE_GUS))
{
I_InitTimidityConfig();
}
if (!nosfx)
{
InitSfxModule(use_sfx_prefix);
}
if (!nomusic)
{
InitMusicModule();
}
}
}
void I_ShutdownSound(void)
{
if (sound_module != NULL)
{
sound_module->Shutdown();
}
if (music_module != NULL)
{
music_module->Shutdown();
}
}
int I_GetSfxLumpNum(sfxinfo_t *sfxinfo)
{
if (sound_module != NULL)
{
return sound_module->GetSfxLumpNum(sfxinfo);
}
else
{
return 0;
}
}
void I_UpdateSound(void)
{
if (sound_module != NULL)
{
sound_module->Update();
}
if (music_module != NULL && music_module->Poll != NULL)
{
music_module->Poll();
}
}
static void CheckVolumeSeparation(int *vol, int *sep)
{
if (*sep < 0)
{
*sep = 0;
}
else if (*sep > 254)
{
*sep = 254;
}
if (*vol < 0)
{
*vol = 0;
}
else if (*vol > 127)
{
*vol = 127;
}
}
void I_UpdateSoundParams(int channel, int vol, int sep)
{
if (sound_module != NULL)
{
CheckVolumeSeparation(&vol, &sep);
sound_module->UpdateSoundParams(channel, vol, sep);
}
}
int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch)
{
if (sound_module != NULL)
{
CheckVolumeSeparation(&vol, &sep);
return sound_module->StartSound(sfxinfo, channel, vol, sep, pitch);
}
else
{
return 0;
}
}
void I_StopSound(int channel)
{
if (sound_module != NULL)
{
sound_module->StopSound(channel);
}
}
boolean I_SoundIsPlaying(int channel)
{
if (sound_module != NULL)
{
return sound_module->SoundIsPlaying(channel);
}
else
{
return false;
}
}
void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
{
if (sound_module != NULL && sound_module->CacheSounds != NULL)
{
sound_module->CacheSounds(sounds, num_sounds);
}
}
void I_InitMusic(void)
{
}
void I_ShutdownMusic(void)
{
}
void I_SetMusicVolume(int volume)
{
if (music_module != NULL)
{
music_module->SetMusicVolume(volume);
}
}
void I_PauseSong(void)
{
if (music_module != NULL)
{
music_module->PauseMusic();
}
}
void I_ResumeSong(void)
{
if (music_module != NULL)
{
music_module->ResumeMusic();
}
}
void *I_RegisterSong(void *data, int len)
{
if (music_module != NULL)
{
return music_module->RegisterSong(data, len);
}
else
{
return NULL;
}
}
void I_UnRegisterSong(void *handle)
{
if (music_module != NULL)
{
music_module->UnRegisterSong(handle);
}
}
void I_PlaySong(void *handle, boolean looping)
{
if (music_module != NULL)
{
music_module->PlaySong(handle, looping);
}
}
void I_StopSong(void)
{
if (music_module != NULL)
{
music_module->StopSong();
}
}
boolean I_MusicIsPlaying(void)
{
if (music_module != NULL)
{
return music_module->MusicIsPlaying();
}
else
{
return false;
}
}
void I_BindSoundVariables(void)
{
extern int use_libsamplerate;
extern float libsamplerate_scale;
M_BindIntVariable("snd_musicdevice", &snd_musicdevice);
M_BindIntVariable("snd_sfxdevice", &snd_sfxdevice);
M_BindIntVariable("snd_maxslicetime_ms", &snd_maxslicetime_ms);
M_BindStringVariable("snd_musiccmd", &snd_musiccmd);
M_BindIntVariable("snd_samplerate", &snd_samplerate);
M_BindIntVariable("snd_cachesize", &snd_cachesize);
M_BindIntVariable("snd_pitchshift", &snd_pitchshift);
M_BindStringVariable("music_pack_path", &music_pack_path);
M_BindStringVariable("timidity_cfg_path", &timidity_cfg_path);
M_BindStringVariable("gus_patch_path", &gus_patch_path);
M_BindIntVariable("gus_ram_kb", &gus_ram_kb);
M_BindIntVariable("use_libsamplerate", &use_libsamplerate);
M_BindFloatVariable("libsamplerate_scale", &libsamplerate_scale);
}

243
src/i_sound.h Normal file
View File

@@ -0,0 +1,243 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// The not so system specific sound interface.
//
#ifndef __I_SOUND__
#define __I_SOUND__
#include "doomtype.h"
struct sfxinfo_struct;
// so that the individual game logic and sound driver code agree
#define NORM_PITCH 127
//
// SoundFX struct.
//
typedef struct sfxinfo_struct sfxinfo_t;
struct sfxinfo_struct
{
// tag name, used for hexen.
char *tagname;
// lump name. If we are running with use_sfx_prefix=true, a
// 'DS' (or 'DP' for PC speaker sounds) is prepended to this.
char name[9];
// Sfx priority
int priority;
// referenced sound if a link
sfxinfo_t *link;
// pitch if a link (Doom), whether to pitch-shift (Hexen)
int pitch;
// volume if a link
int volume;
// this is checked every second to see if sound
// can be thrown out (if 0, then decrement, if -1,
// then throw out, if > 0, then it is in use)
int usefulness;
// lump number of sfx
int lumpnum;
// Maximum number of channels that the sound can be played on
// (Heretic)
int numchannels;
// data used by the low level code
void *driver_data;
};
//
// MusicInfo struct.
//
typedef struct
{
// up to 6-character name
char *name;
// lump number of music
int lumpnum;
// music data
void *data;
// music handle once registered
void *handle;
} musicinfo_t;
typedef enum
{
SNDDEVICE_NONE = 0,
SNDDEVICE_PCSPEAKER = 1,
SNDDEVICE_ADLIB = 2,
SNDDEVICE_SB = 3,
SNDDEVICE_PAS = 4,
SNDDEVICE_GUS = 5,
SNDDEVICE_WAVEBLASTER = 6,
SNDDEVICE_SOUNDCANVAS = 7,
SNDDEVICE_GENMIDI = 8,
SNDDEVICE_AWE32 = 9,
SNDDEVICE_CD = 10,
} snddevice_t;
// Interface for sound modules
typedef struct
{
// List of sound devices that this sound module is used for.
snddevice_t *sound_devices;
int num_sound_devices;
// Initialise sound module
// Returns true if successfully initialised
boolean (*Init)(boolean use_sfx_prefix);
// Shutdown sound module
void (*Shutdown)(void);
// Returns the lump index of the given sound.
int (*GetSfxLumpNum)(sfxinfo_t *sfxinfo);
// Called periodically to update the subsystem.
void (*Update)(void);
// Update the sound settings on the given channel.
void (*UpdateSoundParams)(int channel, int vol, int sep);
// Start a sound on a given channel. Returns the channel id
// or -1 on failure.
int (*StartSound)(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch);
// Stop the sound playing on the given channel.
void (*StopSound)(int channel);
// Query if a sound is playing on the given channel
boolean (*SoundIsPlaying)(int channel);
// Called on startup to precache sound effects (if necessary)
void (*CacheSounds)(sfxinfo_t *sounds, int num_sounds);
} sound_module_t;
void I_InitSound(boolean use_sfx_prefix);
void I_ShutdownSound(void);
int I_GetSfxLumpNum(sfxinfo_t *sfxinfo);
void I_UpdateSound(void);
void I_UpdateSoundParams(int channel, int vol, int sep);
int I_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep, int pitch);
void I_StopSound(int channel);
boolean I_SoundIsPlaying(int channel);
void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds);
// Interface for music modules
typedef struct
{
// List of sound devices that this music module is used for.
snddevice_t *sound_devices;
int num_sound_devices;
// Initialise the music subsystem
boolean (*Init)(void);
// Shutdown the music subsystem
void (*Shutdown)(void);
// Set music volume - range 0-127
void (*SetMusicVolume)(int volume);
// Pause music
void (*PauseMusic)(void);
// Un-pause music
void (*ResumeMusic)(void);
// Register a song handle from data
// Returns a handle that can be used to play the song
void *(*RegisterSong)(void *data, int len);
// Un-register (free) song data
void (*UnRegisterSong)(void *handle);
// Play the song
void (*PlaySong)(void *handle, boolean looping);
// Stop playing the current song.
void (*StopSong)(void);
// Query if music is playing.
boolean (*MusicIsPlaying)(void);
// Invoked periodically to poll.
void (*Poll)(void);
} music_module_t;
void I_InitMusic(void);
void I_ShutdownMusic(void);
void I_SetMusicVolume(int volume);
void I_PauseSong(void);
void I_ResumeSong(void);
void *I_RegisterSong(void *data, int len);
void I_UnRegisterSong(void *handle);
void I_PlaySong(void *handle, boolean looping);
void I_StopSong(void);
boolean I_MusicIsPlaying(void);
extern int snd_sfxdevice;
extern int snd_musicdevice;
extern int snd_samplerate;
extern int snd_cachesize;
extern int snd_maxslicetime_ms;
extern char *snd_musiccmd;
extern int snd_pitchshift;
void I_BindSoundVariables(void);
#endif

43
src/i_swap.h Normal file
View File

@@ -0,0 +1,43 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Endianess handling, swapping 16bit and 32bit.
//
#ifndef __I_SWAP__
#define __I_SWAP__
#include "SDL2/SDL_endian.h"
// Endianess handling.
// WAD files are stored little endian.
// Just use SDL's endianness swapping functions.
// These are deliberately cast to signed values; this is the behaviour
// of the macros in the original source and some code relies on it.
#define SHORT(x) ((signed short) SDL_SwapLE16(x))
#define LONG(x) ((signed int) SDL_SwapLE32(x))
// Defines for checking the endianness of the system.
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define SYS_BIG_ENDIAN
#endif
#endif

409
src/i_system.c Normal file
View File

@@ -0,0 +1,409 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "SDL2/SDL.h"
#include "SDL2/SDL_messagebox.h"
#include "doomtype.h"
#include "i_system.h"
#include "m_argv.h"
#include "m_misc.h"
struct atexit_listentry_s;
#define DEFAULT_RAM 16 /* MiB */
#define MIN_RAM 4 /* MiB */
typedef struct atexit_listentry_s atexit_listentry_t;
struct atexit_listentry_s
{
atexit_func_t func;
boolean run_on_error;
atexit_listentry_t *next;
};
static atexit_listentry_t *exit_funcs = NULL;
void I_AtExit(atexit_func_t func, boolean run_on_error)
{
atexit_listentry_t *entry;
entry = malloc(sizeof(*entry));
entry->func = func;
entry->run_on_error = run_on_error;
entry->next = exit_funcs;
exit_funcs = entry;
}
// Tactile feedback function, probably used for the Logitech Cyberman
void I_Tactile(int on, int off, int total)
{
}
// Zone memory auto-allocation function that allocates the zone size
// by trying progressively smaller zone sizes until one is found that
// works.
static byte *AutoAllocMemory(int *size, int default_ram, int min_ram)
{
byte *zonemem;
// Allocate the zone memory. This loop tries progressively smaller
// zone sizes until a size is found that can be allocated.
// If we used the -mb command line parameter, only the parameter
// provided is accepted.
zonemem = NULL;
while (zonemem == NULL)
{
// We need a reasonable minimum amount of RAM to start.
if (default_ram < min_ram)
{
I_Error("Unable to allocate %i MiB of RAM for zone", default_ram);
}
// Try to allocate the zone memory.
*size = default_ram * 1024 * 1024;
zonemem = malloc(*size);
// Failed to allocate? Reduce zone size until we reach a size
// that is acceptable.
if (zonemem == NULL)
{
default_ram -= 1;
}
}
return zonemem;
}
byte *I_ZoneBase (int *size)
{
byte *zonemem;
int min_ram, default_ram;
int p;
//!
// @arg <mb>
//
// Specify the heap size, in MiB (default 16).
//
p = M_CheckParmWithArgs("-mb", 1);
if (p > 0)
{
default_ram = atoi(myargv[p+1]);
min_ram = default_ram;
}
else
{
default_ram = DEFAULT_RAM;
min_ram = MIN_RAM;
}
zonemem = AutoAllocMemory(size, default_ram, min_ram);
printf("zone memory: %p, %x allocated for zone\n",
zonemem, *size);
return zonemem;
}
void I_PrintBanner(char *msg)
{
int i;
int spaces = 35 - (strlen(msg) / 2);
for (i=0; i<spaces; ++i)
putchar(' ');
puts(msg);
}
void I_PrintDivider(void)
{
int i;
for (i=0; i<75; ++i)
{
putchar('=');
}
putchar('\n');
}
void I_PrintStartupBanner(char *gamedescription)
{
I_PrintDivider();
I_PrintBanner(gamedescription);
I_PrintDivider();
printf(
" " "Chocolate Doom" " is free software, covered by the GNU General Public\n"
" License. There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
" FOR A PARTICULAR PURPOSE. You are welcome to change and distribute\n"
" copies under certain conditions. See the source for more information.\n");
I_PrintDivider();
}
//
// I_ConsoleStdout
//
// Returns true if stdout is a real console, false if it is a file
//
boolean I_ConsoleStdout(void)
{
return false;
}
//
// I_Init
//
/*
void I_Init (void)
{
I_CheckIsScreensaver();
I_InitTimer();
I_InitJoystick();
}
void I_BindVariables(void)
{
I_BindVideoVariables();
I_BindJoystickVariables();
I_BindSoundVariables();
}
*/
//
// I_Quit
//
void I_Quit (void)
{
atexit_listentry_t *entry;
// Run through all exit functions
entry = exit_funcs;
while (entry != NULL)
{
entry->func();
entry = entry->next;
}
SDL_Quit();
exit(0);
}
//
// I_Error
//
static boolean already_quitting = false;
void I_Error (char *error, ...)
{
char msgbuf[512];
va_list argptr;
atexit_listentry_t *entry;
boolean exit_gui_popup;
if (already_quitting)
{
fprintf(stderr, "Warning: recursive call to I_Error detected.\n");
exit(-1);
}
else
{
already_quitting = true;
}
// Message first.
va_start(argptr, error);
//fprintf(stderr, "\nError: ");
vfprintf(stderr, error, argptr);
fprintf(stderr, "\n\n");
va_end(argptr);
fflush(stderr);
// Write a copy of the message into buffer.
va_start(argptr, error);
memset(msgbuf, 0, sizeof(msgbuf));
M_vsnprintf(msgbuf, sizeof(msgbuf), error, argptr);
va_end(argptr);
// Shutdown. Here might be other errors.
entry = exit_funcs;
while (entry != NULL)
{
if (entry->run_on_error)
{
entry->func();
}
entry = entry->next;
}
exit_gui_popup = !M_ParmExists("-nogui");
// Pop up a GUI dialog box to show the error message, if the
// game was not run from the console (and the user will
// therefore be unable to otherwise see the message).
if (exit_gui_popup && !I_ConsoleStdout())
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"Chocolate Doom 3", msgbuf, NULL);
}
// abort();
SDL_Quit();
exit(-1);
}
//
// Read Access Violation emulation.
//
// From PrBoom+, by entryway.
//
// C:\>debug
// -d 0:0
//
// DOS 6.22:
// 0000:0000 (57 92 19 00) F4 06 70 00-(16 00)
// DOS 7.1:
// 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00)
// Win98:
// 0000:0000 (9E 0F C9 00) 65 04 70 00-(16 00)
// DOSBox under XP:
// 0000:0000 (00 00 00 F1) ?? ?? ?? 00-(07 00)
#define DOS_MEM_DUMP_SIZE 10
static const unsigned char mem_dump_dos622[DOS_MEM_DUMP_SIZE] = {
0x57, 0x92, 0x19, 0x00, 0xF4, 0x06, 0x70, 0x00, 0x16, 0x00};
static const unsigned char mem_dump_win98[DOS_MEM_DUMP_SIZE] = {
0x9E, 0x0F, 0xC9, 0x00, 0x65, 0x04, 0x70, 0x00, 0x16, 0x00};
static const unsigned char mem_dump_dosbox[DOS_MEM_DUMP_SIZE] = {
0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};
static unsigned char mem_dump_custom[DOS_MEM_DUMP_SIZE];
static const unsigned char *dos_mem_dump = mem_dump_dos622;
boolean I_GetMemoryValue(unsigned int offset, void *value, int size)
{
static boolean firsttime = true;
if (firsttime)
{
int p, i, val;
firsttime = false;
i = 0;
//!
// @category compat
// @arg <version>
//
// Specify DOS version to emulate for NULL pointer dereference
// emulation. Supported versions are: dos622, dos71, dosbox.
// The default is to emulate DOS 7.1 (Windows 98).
//
p = M_CheckParmWithArgs("-setmem", 1);
if (p > 0)
{
if (!strcasecmp(myargv[p + 1], "dos622"))
{
dos_mem_dump = mem_dump_dos622;
}
if (!strcasecmp(myargv[p + 1], "dos71"))
{
dos_mem_dump = mem_dump_win98;
}
else if (!strcasecmp(myargv[p + 1], "dosbox"))
{
dos_mem_dump = mem_dump_dosbox;
}
else
{
for (i = 0; i < DOS_MEM_DUMP_SIZE; ++i)
{
++p;
if (p >= myargc || myargv[p][0] == '-')
{
break;
}
M_StrToInt(myargv[p], &val);
mem_dump_custom[i++] = (unsigned char) val;
}
dos_mem_dump = mem_dump_custom;
}
}
}
switch (size)
{
case 1:
*((unsigned char *) value) = dos_mem_dump[offset];
return true;
case 2:
*((unsigned short *) value) = dos_mem_dump[offset]
| (dos_mem_dump[offset + 1] << 8);
return true;
case 4:
*((unsigned int *) value) = dos_mem_dump[offset]
| (dos_mem_dump[offset + 1] << 8)
| (dos_mem_dump[offset + 2] << 16)
| (dos_mem_dump[offset + 3] << 24);
return true;
}
return false;
}

84
src/i_system.h Normal file
View File

@@ -0,0 +1,84 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System specific interface stuff.
//
#ifndef __I_SYSTEM__
#define __I_SYSTEM__
#include "d_ticcmd.h"
#include "doomtype.h"
typedef void (*atexit_func_t)(void);
// Called by DoomMain.
void I_Init (void);
// Called by startup code
// to get the ammount of memory to malloc
// for the zone management.
byte* I_ZoneBase (int *size);
boolean I_ConsoleStdout(void);
// Asynchronous interrupt functions should maintain private queues
// that are read by the synchronous functions
// to be converted into events.
// Either returns a null ticcmd,
// or calls a loadable driver to build it.
// This ticcmd will then be modified by the gameloop
// for normal input.
ticcmd_t* I_BaseTiccmd (void);
// Called by M_Responder when quit is selected.
// Clean exit, displays sell blurb.
void I_Quit (void);
void I_Error (char *error, ...);
void I_Tactile (int on, int off, int total);
boolean I_GetMemoryValue(unsigned int offset, void *value, int size);
// Schedule a function to be called when the program exits.
// If run_if_error is true, the function is called if the exit
// is due to an error (I_Error)
void I_AtExit(atexit_func_t func, boolean run_if_error);
// Add all system-specific config file variable bindings.
void I_BindVariables(void);
// Print startup banner copyright message.
void I_PrintStartupBanner(char *gamedescription);
// Print a centered text banner displaying the given string.
void I_PrintBanner(char *text);
// Print a dividing line for startup banners.
void I_PrintDivider(void);
#endif

80
src/i_timer.c Normal file
View File

@@ -0,0 +1,80 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Timer functions.
//
#include "SDL2/SDL.h"
#include "SDL2/SDL_stdinc.h"
#include "SDL2/SDL_timer.h"
#include "i_timer.h"
//
// I_GetTime
// returns time in 1/35th second tics
//
static Uint32 basetime = 0;
int I_GetTime (void)
{
Uint32 ticks;
ticks = SDL_GetTicks();
if (basetime == 0)
basetime = ticks;
ticks -= basetime;
return (ticks * TICRATE) / 1000;
}
//
// Same as I_GetTime, but returns time in milliseconds
//
int I_GetTimeMS(void)
{
Uint32 ticks;
ticks = SDL_GetTicks();
if (basetime == 0)
basetime = ticks;
return ticks - basetime;
}
// Sleep for a specified number of ms
void I_Sleep(int ms)
{
SDL_Delay(ms);
}
void I_WaitVBL(int count)
{
I_Sleep((count * 1000) / 70);
}
void I_InitTimer(void)
{
// initialize timer
SDL_Init(SDL_INIT_TIMER);
}

42
src/i_timer.h Normal file
View File

@@ -0,0 +1,42 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System-specific timer interface
//
#ifndef __I_TIMER__
#define __I_TIMER__
#define TICRATE 35
// Called by D_DoomLoop,
// returns current time in tics.
int I_GetTime (void);
// returns current time in ms
int I_GetTimeMS (void);
// Pause for a specified number of ms
void I_Sleep(int ms);
// Initialize timer
void I_InitTimer(void);
// Wait for vertical retrace or pause a bit.
void I_WaitVBL(int count);
#endif

1451
src/i_video.c Normal file

File diff suppressed because it is too large Load Diff

103
src/i_video.h Normal file
View File

@@ -0,0 +1,103 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System specific interface stuff.
//
#ifndef __I_VIDEO__
#define __I_VIDEO__
#include "doomtype.h"
// Screen width and height.
#define SCREENWIDTH 320
#define SCREENHEIGHT 200
// Screen height used when aspect_ratio_correct=true.
#define SCREENHEIGHT_4_3 240
typedef boolean (*grabmouse_callback_t)(void);
// Called by D_DoomMain,
// determines the hardware configuration
// and sets up the video mode
void I_InitGraphics (void);
void I_GraphicsCheckCommandLine(void);
void I_ShutdownGraphics(void);
// Takes full 8 bit values.
void I_SetPalette (byte* palette);
int I_GetPaletteIndex(int r, int g, int b);
void I_UpdateNoBlit (void);
void I_FinishUpdate (void);
void I_ReadScreen (pixel_t* scr);
void I_BeginRead (void);
void I_SetWindowTitle(char *title);
void I_CheckIsScreensaver(void);
void I_SetGrabMouseCallback(grabmouse_callback_t func);
void I_DisplayFPSDots(boolean dots_on);
void I_BindVideoVariables(void);
void I_InitWindowTitle(void);
void I_InitWindowIcon(void);
// Called before processing any tics in a frame (just after displaying a frame).
// Time consuming syncronous operations are performed here (joystick reading).
void I_StartFrame (void);
// Called before processing each tic in a frame.
// Quick syncronous operations are performed here.
void I_StartTic (void);
// Enable the loading disk image displayed when reading from disk.
void I_EnableLoadingDisk(int xoffs, int yoffs);
extern char *video_driver;
extern boolean screenvisible;
extern int vanilla_keyboard_mapping;
extern boolean screensaver_mode;
extern int usegamma;
extern pixel_t *I_VideoBuffer;
extern int screen_width;
extern int screen_height;
extern int fullscreen;
extern int aspect_ratio_correct;
extern int integer_scaling;
extern int vga_porch_flash;
extern int force_software_renderer;
extern char *window_position;
void I_GetWindowPosition(int *x, int *y, int w, int h);
// Joystic/gamepad hysteresis
extern unsigned int joywait;
#endif

264
src/i_videohr.c Normal file
View File

@@ -0,0 +1,264 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// SDL emulation of VGA 640x480x4 planar video mode,
// for Hexen startup loading screen.
//
#include <string.h>
#include "SDL2/SDL.h"
#include "SDL2/SDL_events.h"
#include "SDL2/SDL_keyboard.h"
#include "SDL2/SDL_keycode.h"
#include "SDL2/SDL_pixels.h"
#include "SDL2/SDL_rect.h"
#include "SDL2/SDL_surface.h"
#include "SDL2/SDL_video.h"
#include "doomtype.h"
#include "i_timer.h"
#include "i_video.h"
// Palette fade-in takes two seconds
#define FADE_TIME 2000
#define HR_SCREENWIDTH 640
#define HR_SCREENHEIGHT 480
static SDL_Window *hr_screen = NULL;
static SDL_Surface *hr_surface = NULL;
static char *window_title = "";
boolean I_SetVideoModeHR(void)
{
int x, y;
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
return false;
}
I_GetWindowPosition(&x, &y, HR_SCREENWIDTH, HR_SCREENHEIGHT);
// Create screen surface at the native desktop pixel depth (bpp=0),
// as we cannot trust true 8-bit to reliably work nowadays.
hr_screen = SDL_CreateWindow(window_title, x, y,
HR_SCREENWIDTH, HR_SCREENHEIGHT,
0);
if (hr_screen == NULL)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return false;
}
// We do all actual drawing into an intermediate surface.
hr_surface = SDL_CreateRGBSurface(0, HR_SCREENWIDTH, HR_SCREENHEIGHT,
8, 0, 0, 0, 0);
return true;
}
void I_SetWindowTitleHR(char *title)
{
window_title = title;
}
void I_UnsetVideoModeHR(void)
{
if (hr_screen != NULL)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
hr_screen = NULL;
SDL_FreeSurface(hr_surface);
hr_surface = NULL;
}
}
void I_ClearScreenHR(void)
{
SDL_Rect area = { 0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT };
SDL_FillRect(hr_surface, &area, 0);
}
void I_SlamBlockHR(int x, int y, int w, int h, const byte *src)
{
SDL_Rect blit_rect;
const byte *srcptrs[4];
byte srcbits[4];
byte *dest;
int x1, y1;
int i;
int bit;
// Set up source pointers to read from source buffer - each 4-bit
// pixel has its bits split into four sub-buffers
for (i=0; i<4; ++i)
{
srcptrs[i] = src + (i * w * h / 8);
}
if (SDL_LockSurface(hr_surface) < 0)
{
return;
}
// Draw each pixel
bit = 0;
for (y1=y; y1<y+h; ++y1)
{
dest = ((byte *) hr_surface->pixels) + y1 * hr_surface->pitch + x;
for (x1=x; x1<x+w; ++x1)
{
// Get the bits for this pixel
// For each bit, find the byte containing it, shift down
// and mask out the specific bit wanted.
for (i=0; i<4; ++i)
{
srcbits[i] = (srcptrs[i][bit / 8] >> (7 - (bit % 8))) & 0x1;
}
// Reassemble the pixel value
*dest = (srcbits[0] << 0)
| (srcbits[1] << 1)
| (srcbits[2] << 2)
| (srcbits[3] << 3);
// Next pixel!
++dest;
++bit;
}
}
SDL_UnlockSurface(hr_surface);
// Update the region we drew.
blit_rect.x = x;
blit_rect.y = y;
blit_rect.w = w;
blit_rect.h = h;
SDL_BlitSurface(hr_surface, &blit_rect,
SDL_GetWindowSurface(hr_screen), &blit_rect);
SDL_UpdateWindowSurfaceRects(hr_screen, &blit_rect, 1);
}
void I_SlamHR(const byte *buffer)
{
I_SlamBlockHR(0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT, buffer);
}
void I_InitPaletteHR(void)
{
// ...
}
void I_SetPaletteHR(const byte *palette)
{
SDL_Rect screen_rect = {0, 0, HR_SCREENWIDTH, HR_SCREENHEIGHT};
SDL_Color sdlpal[16];
int i;
for (i=0; i<16; ++i)
{
sdlpal[i].r = palette[i * 3 + 0] * 4;
sdlpal[i].g = palette[i * 3 + 1] * 4;
sdlpal[i].b = palette[i * 3 + 2] * 4;
}
// After setting colors, update the screen.
SDL_SetPaletteColors(hr_surface->format->palette, sdlpal, 0, 16);
SDL_BlitSurface(hr_surface, &screen_rect,
SDL_GetWindowSurface(hr_screen), &screen_rect);
SDL_UpdateWindowSurfaceRects(hr_screen, &screen_rect, 1);
}
void I_FadeToPaletteHR(const byte *palette)
{
byte tmppal[16 * 3];
int starttime;
int elapsed;
int i;
starttime = I_GetTimeMS();
for (;;)
{
elapsed = I_GetTimeMS() - starttime;
if (elapsed >= FADE_TIME)
{
break;
}
// Generate the fake palette
for (i=0; i<16 * 3; ++i)
{
tmppal[i] = (palette[i] * elapsed) / FADE_TIME;
}
I_SetPaletteHR(tmppal);
SDL_UpdateWindowSurface(hr_screen);
// Sleep a bit
I_Sleep(10);
}
// Set the final palette
I_SetPaletteHR(palette);
}
void I_BlackPaletteHR(void)
{
byte blackpal[16 * 3];
memset(blackpal, 0, sizeof(blackpal));
I_SetPaletteHR(blackpal);
}
// Check if the user has hit the escape key to abort startup.
boolean I_CheckAbortHR(void)
{
SDL_Event ev;
boolean result = false;
// Not initialized?
if (hr_surface == NULL)
{
return false;
}
while (SDL_PollEvent(&ev))
{
if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE)
{
result = true;
}
}
return result;
}

35
src/i_videohr.h Normal file
View File

@@ -0,0 +1,35 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// SDL emulation of VGA 640x480x4 planar video mode,
// for Hexen startup loading screen.
//
#ifndef I_VIDEOHR_H
#define I_VIDEOHR_H
boolean I_SetVideoModeHR(void);
void I_UnsetVideoModeHR(void);
void I_SetWindowTitleHR(char *title);
void I_ClearScreenHR(void);
void I_SlamBlockHR(int x, int y, int w, int h, const byte *src);
void I_SlamHR(const byte *buffer);
void I_InitPaletteHR(void);
void I_SetPaletteHR(const byte *palette);
void I_FadeToPaletteHR(const byte *palette);
void I_BlackPaletteHR(void);
boolean I_CheckAbortHR(void);
#endif /* #ifndef I_VIDEOHR_H */

4797
src/info.c Normal file

File diff suppressed because it is too large Load Diff

1326
src/info.h Normal file

File diff suppressed because it is too large Load Diff

10
src/ipu/AM_map.c Normal file
View File

@@ -0,0 +1,10 @@
typedef unsigned char pixel_t;
void AM_Drawer(pixel_t* fb) {
for (int i = 0; i < 100; ++i) {
fb[(i + 1) * 320] = 56;
}
}

BIN
src/ipu/AM_map.gp Normal file

Binary file not shown.

16
src/ipu/Makefile Normal file
View File

@@ -0,0 +1,16 @@
GARPHOBJS = AM_map.gp # m_fixed.gp
all: ipu_rt.gp
ipu_rt.gp: $(GARPHOBJS) ipu_vertices.gp
popc $(GARPHOBJS) ipu_vertices.gp -o $@
ipu_vertices.gp: ipu_vertices.cpp
popc -Os $^ -o $@
%.gp: %.c
popc -Os $^ -o $@
clean:
rm $(GARPHOBJS) ipu_vertices.gp

11
src/ipu/am_map.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __AMMAP_H__
#define __AMMAP_H__
typedef uint8_t pixel_t;
void AM_Drawer(pixel_t*);
#endif

BIN
src/ipu/ipu_rt.gp Normal file

Binary file not shown.

31
src/ipu/ipu_vertices.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include <Vertex.hpp>
// #include "doomtype.h"
typedef uint8_t pixel_t;
extern "C" {
void AM_LevelInit(void);
void AM_Drawer(pixel_t*);
};
class AM_LevelInit_Vertex : public poplar::Vertex {
public:
bool compute() {
AM_LevelInit();
return true;
}
};
class AM_Drawer_Vertex : public poplar::Vertex {
public:
poplar::InOut<poplar::Vector<unsigned char>> frame;
bool compute() {
AM_Drawer(&frame[0]);
return true;
}
};

BIN
src/ipu/ipu_vertices.gp Normal file

Binary file not shown.

262
src/m_argv.c Normal file
View File

@@ -0,0 +1,262 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "doomtype.h"
#include "i_system.h"
#include "m_argv.h" // haleyjd 20110212: warning fix
#include "m_misc.h"
int myargc;
char** myargv;
//
// M_CheckParm
// Checks for the given parameter
// in the program's command line arguments.
// Returns the argument number (1 to argc-1)
// or 0 if not present
//
int M_CheckParmWithArgs(char *check, int num_args)
{
int i;
for (i = 1; i < myargc - num_args; i++)
{
if (!strcasecmp(check, myargv[i]))
return i;
}
return 0;
}
//
// M_ParmExists
//
// Returns true if the given parameter exists in the program's command
// line arguments, false if not.
//
boolean M_ParmExists(char *check)
{
return M_CheckParm(check) != 0;
}
int M_CheckParm(char *check)
{
return M_CheckParmWithArgs(check, 0);
}
#define MAXARGVS 100
static void LoadResponseFile(int argv_index)
{
FILE *handle;
int size;
char *infile;
char *file;
char *response_filename;
char **newargv;
int newargc;
int i, k;
response_filename = myargv[argv_index] + 1;
// Read the response file into memory
handle = fopen(response_filename, "rb");
if (handle == NULL)
{
printf ("\nNo such response file!");
exit(1);
}
printf("Found response file %s!\n", response_filename);
size = M_FileLength(handle);
// Read in the entire file
// Allocate one byte extra - this is in case there is an argument
// at the end of the response file, in which case a '\0' will be
// needed.
file = malloc(size + 1);
i = 0;
while (i < size)
{
k = fread(file + i, 1, size - i, handle);
if (k < 0)
{
I_Error("Failed to read full contents of '%s'", response_filename);
}
i += k;
}
fclose(handle);
// Create new arguments list array
newargv = malloc(sizeof(char *) * MAXARGVS);
newargc = 0;
memset(newargv, 0, sizeof(char *) * MAXARGVS);
// Copy all the arguments in the list up to the response file
for (i=0; i<argv_index; ++i)
{
newargv[i] = myargv[i];
++newargc;
}
infile = file;
k = 0;
while(k < size)
{
// Skip past space characters to the next argument
while(k < size && isspace(infile[k]))
{
++k;
}
if (k >= size)
{
break;
}
// If the next argument is enclosed in quote marks, treat
// the contents as a single argument. This allows long filenames
// to be specified.
if (infile[k] == '\"')
{
// Skip the first character(")
++k;
newargv[newargc++] = &infile[k];
// Read all characters between quotes
while (k < size && infile[k] != '\"' && infile[k] != '\n')
{
++k;
}
if (k >= size || infile[k] == '\n')
{
I_Error("Quotes unclosed in response file '%s'",
response_filename);
}
// Cut off the string at the closing quote
infile[k] = '\0';
++k;
}
else
{
// Read in the next argument until a space is reached
newargv[newargc++] = &infile[k];
while(k < size && !isspace(infile[k]))
{
++k;
}
// Cut off the end of the argument at the first space
infile[k] = '\0';
++k;
}
}
// Add arguments following the response file argument
for (i=argv_index + 1; i<myargc; ++i)
{
newargv[newargc] = myargv[i];
++newargc;
}
myargv = newargv;
myargc = newargc;
#if 0
// Disabled - Vanilla Doom does not do this.
// Display arguments
printf("%d command-line args:\n", myargc);
for (k=1; k<myargc; k++)
{
printf("'%s'\n", myargv[k]);
}
#endif
}
//
// Find a Response File
//
void M_FindResponseFile(void)
{
int i;
for (i = 1; i < myargc; i++)
{
if (myargv[i][0] == '@')
{
LoadResponseFile(i);
}
}
}
// Return the name of the executable used to start the program:
char *M_GetExecutableName(void)
{
char *sep;
sep = strrchr(myargv[0], DIR_SEPARATOR);
if (sep == NULL)
{
return myargv[0];
}
else
{
return sep + 1;
}
}

49
src/m_argv.h Normal file
View File

@@ -0,0 +1,49 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Nil.
//
#ifndef __M_ARGV__
#define __M_ARGV__
#include "doomtype.h"
//
// MISC
//
extern int myargc;
extern char** myargv;
// Returns the position of the given parameter
// in the arg list (0 if not found).
int M_CheckParm (char* check);
// Same as M_CheckParm, but checks that num_args arguments are available
// following the specified argument.
int M_CheckParmWithArgs(char *check, int num_args);
void M_FindResponseFile(void);
// Parameter has been specified?
boolean M_ParmExists(char *check);
// Get name of executable used to run this program:
char *M_GetExecutableName(void);
#endif

56
src/m_bbox.c Normal file
View File

@@ -0,0 +1,56 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Main loop menu stuff.
// Random number LUT.
// Default Config File.
// PCX Screenshots.
//
#include "m_bbox.h"
#include <limits.h>
void M_ClearBox (fixed_t *box)
{
box[BOXTOP] = box[BOXRIGHT] = INT_MIN;
box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX;
}
void
M_AddToBox
( fixed_t* box,
fixed_t x,
fixed_t y )
{
if (x<box[BOXLEFT])
box[BOXLEFT] = x;
else if (x>box[BOXRIGHT])
box[BOXRIGHT] = x;
if (y<box[BOXBOTTOM])
box[BOXBOTTOM] = y;
else if (y>box[BOXTOP])
box[BOXTOP] = y;
}

45
src/m_bbox.h Normal file
View File

@@ -0,0 +1,45 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Nil.
//
#ifndef __M_BBOX__
#define __M_BBOX__
#include "m_fixed.h"
// Bounding box coordinate storage.
enum
{
BOXTOP,
BOXBOTTOM,
BOXLEFT,
BOXRIGHT
}; // bbox coordinates
// Bounding box functions.
void M_ClearBox (fixed_t* box);
void
M_AddToBox
( fixed_t* box,
fixed_t x,
fixed_t y );
#endif

89
src/m_cheat.c Normal file
View File

@@ -0,0 +1,89 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Cheat sequence checking.
//
#include <string.h>
#include "doomtype.h"
#include "m_cheat.h"
//
// CHEAT SEQUENCE PACKAGE
//
//
// Called in st_stuff module, which handles the input.
// Returns a 1 if the cheat was successful, 0 if failed.
//
int
cht_CheckCheat
( cheatseq_t* cht,
char key )
{
// if we make a short sequence on a cheat with parameters, this
// will not work in vanilla doom. behave the same.
if (cht->parameter_chars > 0 && strlen(cht->sequence) < cht->sequence_len)
return false;
if (cht->chars_read < strlen(cht->sequence))
{
// still reading characters from the cheat code
// and verifying. reset back to the beginning
// if a key is wrong
if (key == cht->sequence[cht->chars_read])
++cht->chars_read;
else
cht->chars_read = 0;
cht->param_chars_read = 0;
}
else if (cht->param_chars_read < cht->parameter_chars)
{
// we have passed the end of the cheat sequence and are
// entering parameters now
cht->parameter_buf[cht->param_chars_read] = key;
++cht->param_chars_read;
}
if (cht->chars_read >= strlen(cht->sequence)
&& cht->param_chars_read >= cht->parameter_chars)
{
cht->chars_read = cht->param_chars_read = 0;
return true;
}
// cheat not matched yet
return false;
}
void
cht_GetParam
( cheatseq_t* cht,
char* buffer )
{
memcpy(buffer, cht->parameter_buf, cht->parameter_chars);
}

63
src/m_cheat.h Normal file
View File

@@ -0,0 +1,63 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Cheat code checking.
//
#ifndef __M_CHEAT__
#define __M_CHEAT__
#include <stddef.h>
//
// CHEAT SEQUENCE PACKAGE
//
// declaring a cheat
#define CHEAT(value, parameters) \
{ value, sizeof(value) - 1, parameters, 0, 0, "" }
#define MAX_CHEAT_LEN 25
#define MAX_CHEAT_PARAMS 5
typedef struct
{
// settings for this cheat
char sequence[MAX_CHEAT_LEN];
size_t sequence_len;
int parameter_chars;
// state used during the game
size_t chars_read;
int param_chars_read;
char parameter_buf[MAX_CHEAT_PARAMS];
} cheatseq_t;
int
cht_CheckCheat
( cheatseq_t* cht,
char key );
void
cht_GetParam
( cheatseq_t* cht,
char* buffer );
#endif

2134
src/m_config.c Normal file

File diff suppressed because it is too large Load Diff

41
src/m_config.h Normal file
View File

@@ -0,0 +1,41 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Configuration file interface.
//
#ifndef __M_CONFIG__
#define __M_CONFIG__
#include "doomtype.h"
void M_LoadDefaults(void);
void M_SaveDefaults(void);
void M_SaveDefaultsAlternate(char *main, char *extra);
void M_SetConfigDir(char *dir);
void M_BindIntVariable(char *name, int *variable);
void M_BindFloatVariable(char *name, float *variable);
void M_BindStringVariable(char *name, char **variable);
boolean M_SetVariable(char *name, char *value);
int M_GetIntVariable(char *name);
const char *M_GetStringVariable(char *name);
float M_GetFloatVariable(char *name);
void M_SetConfigFilenames(char *main_config, char *extra_config);
char *M_GetSaveGameDir(char *iwadname);
extern char *configdir;
#endif

397
src/m_controls.c Normal file
View File

@@ -0,0 +1,397 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#include "doomkeys.h"
#include "m_config.h"
#include "m_misc.h"
//
// Keyboard controls
//
int key_right = KEY_RIGHTARROW;
int key_left = KEY_LEFTARROW;
int key_up = KEY_UPARROW;
int key_down = KEY_DOWNARROW;
int key_strafeleft = ',';
int key_straferight = '.';
int key_fire = KEY_RCTRL;
int key_use = ' ';
int key_strafe = KEY_RALT;
int key_speed = KEY_RSHIFT;
//
// Heretic keyboard controls
//
int key_flyup = KEY_PGUP;
int key_flydown = KEY_INS;
int key_flycenter = KEY_HOME;
int key_lookup = KEY_PGDN;
int key_lookdown = KEY_DEL;
int key_lookcenter = KEY_END;
int key_invleft = '[';
int key_invright = ']';
int key_useartifact = KEY_ENTER;
//
// Hexen key controls
//
int key_jump = '/';
int key_arti_all = KEY_BACKSPACE;
int key_arti_health = '\\';
int key_arti_poisonbag = '0';
int key_arti_blastradius = '9';
int key_arti_teleport = '8';
int key_arti_teleportother = '7';
int key_arti_egg = '6';
int key_arti_invulnerability = '5';
//
// Strife key controls
//
// haleyjd 09/01/10
//
// Note: Strife also uses key_invleft, key_invright, key_jump, key_lookup, and
// key_lookdown, but with different default values.
int key_usehealth = 'h';
int key_invquery = 'q';
int key_mission = 'w';
int key_invpop = 'z';
int key_invkey = 'k';
int key_invhome = KEY_HOME;
int key_invend = KEY_END;
int key_invuse = KEY_ENTER;
int key_invdrop = KEY_BACKSPACE;
//
// Mouse controls
//
int mousebfire = 0;
int mousebstrafe = 1;
int mousebforward = 2;
int mousebjump = -1;
int mousebstrafeleft = -1;
int mousebstraferight = -1;
int mousebbackward = -1;
int mousebuse = -1;
int mousebprevweapon = -1;
int mousebnextweapon = -1;
int key_message_refresh = KEY_ENTER;
int key_pause = KEY_PAUSE;
int key_demo_quit = 'q';
int key_spy = KEY_F12;
// Multiplayer chat keys:
int key_multi_msg = 't';
int key_multi_msgplayer[8];
// Weapon selection keys:
int key_weapon1 = '1';
int key_weapon2 = '2';
int key_weapon3 = '3';
int key_weapon4 = '4';
int key_weapon5 = '5';
int key_weapon6 = '6';
int key_weapon7 = '7';
int key_weapon8 = '8';
int key_prevweapon = 0;
int key_nextweapon = 0;
// Map control keys:
int key_map_north = KEY_UPARROW;
int key_map_south = KEY_DOWNARROW;
int key_map_east = KEY_RIGHTARROW;
int key_map_west = KEY_LEFTARROW;
int key_map_zoomin = '=';
int key_map_zoomout = '-';
int key_map_toggle = KEY_TAB;
int key_map_maxzoom = '0';
int key_map_follow = 'f';
int key_map_grid = 'g';
int key_map_mark = 'm';
int key_map_clearmark = 'c';
// menu keys:
int key_menu_activate = KEY_ESCAPE;
int key_menu_up = KEY_UPARROW;
int key_menu_down = KEY_DOWNARROW;
int key_menu_left = KEY_LEFTARROW;
int key_menu_right = KEY_RIGHTARROW;
int key_menu_back = KEY_BACKSPACE;
int key_menu_forward = KEY_ENTER;
int key_menu_confirm = 'y';
int key_menu_abort = 'n';
int key_menu_help = KEY_F1;
int key_menu_save = KEY_F2;
int key_menu_load = KEY_F3;
int key_menu_volume = KEY_F4;
int key_menu_detail = KEY_F5;
int key_menu_qsave = KEY_F6;
int key_menu_endgame = KEY_F7;
int key_menu_messages = KEY_F8;
int key_menu_qload = KEY_F9;
int key_menu_quit = KEY_F10;
int key_menu_gamma = KEY_F11;
int key_menu_incscreen = KEY_EQUALS;
int key_menu_decscreen = KEY_MINUS;
int key_menu_screenshot = 0;
//
// Joystick controls
//
int joybfire = 0;
int joybstrafe = 1;
int joybuse = 3;
int joybspeed = 2;
int joybstrafeleft = -1;
int joybstraferight = -1;
int joybjump = -1;
int joybprevweapon = -1;
int joybnextweapon = -1;
int joybmenu = -1;
int joybautomap = -1;
// Control whether if a mouse button is double clicked, it acts like
// "use" has been pressed
int dclick_use = 1;
//
// Bind all of the common controls used by Doom and all other games.
//
void M_BindBaseControls(void)
{
M_BindIntVariable("key_right", &key_right);
M_BindIntVariable("key_left", &key_left);
M_BindIntVariable("key_up", &key_up);
M_BindIntVariable("key_down", &key_down);
M_BindIntVariable("key_strafeleft", &key_strafeleft);
M_BindIntVariable("key_straferight", &key_straferight);
M_BindIntVariable("key_fire", &key_fire);
M_BindIntVariable("key_use", &key_use);
M_BindIntVariable("key_strafe", &key_strafe);
M_BindIntVariable("key_speed", &key_speed);
M_BindIntVariable("mouseb_fire", &mousebfire);
M_BindIntVariable("mouseb_strafe", &mousebstrafe);
M_BindIntVariable("mouseb_forward", &mousebforward);
M_BindIntVariable("joyb_fire", &joybfire);
M_BindIntVariable("joyb_strafe", &joybstrafe);
M_BindIntVariable("joyb_use", &joybuse);
M_BindIntVariable("joyb_speed", &joybspeed);
M_BindIntVariable("joyb_menu_activate", &joybmenu);
M_BindIntVariable("joyb_toggle_automap", &joybautomap);
// Extra controls that are not in the Vanilla versions:
M_BindIntVariable("joyb_strafeleft", &joybstrafeleft);
M_BindIntVariable("joyb_straferight", &joybstraferight);
M_BindIntVariable("mouseb_strafeleft", &mousebstrafeleft);
M_BindIntVariable("mouseb_straferight", &mousebstraferight);
M_BindIntVariable("mouseb_use", &mousebuse);
M_BindIntVariable("mouseb_backward", &mousebbackward);
M_BindIntVariable("dclick_use", &dclick_use);
M_BindIntVariable("key_pause", &key_pause);
M_BindIntVariable("key_message_refresh", &key_message_refresh);
}
void M_BindHereticControls(void)
{
M_BindIntVariable("key_flyup", &key_flyup);
M_BindIntVariable("key_flydown", &key_flydown);
M_BindIntVariable("key_flycenter", &key_flycenter);
M_BindIntVariable("key_lookup", &key_lookup);
M_BindIntVariable("key_lookdown", &key_lookdown);
M_BindIntVariable("key_lookcenter", &key_lookcenter);
M_BindIntVariable("key_invleft", &key_invleft);
M_BindIntVariable("key_invright", &key_invright);
M_BindIntVariable("key_useartifact", &key_useartifact);
}
void M_BindHexenControls(void)
{
M_BindIntVariable("key_jump", &key_jump);
M_BindIntVariable("mouseb_jump", &mousebjump);
M_BindIntVariable("joyb_jump", &joybjump);
M_BindIntVariable("key_arti_all", &key_arti_all);
M_BindIntVariable("key_arti_health", &key_arti_health);
M_BindIntVariable("key_arti_poisonbag", &key_arti_poisonbag);
M_BindIntVariable("key_arti_blastradius", &key_arti_blastradius);
M_BindIntVariable("key_arti_teleport", &key_arti_teleport);
M_BindIntVariable("key_arti_teleportother", &key_arti_teleportother);
M_BindIntVariable("key_arti_egg", &key_arti_egg);
M_BindIntVariable("key_arti_invulnerability", &key_arti_invulnerability);
}
void M_BindStrifeControls(void)
{
// These are shared with all games, but have different defaults:
key_message_refresh = '/';
// These keys are shared with Heretic/Hexen but have different defaults:
key_jump = 'a';
key_lookup = KEY_PGUP;
key_lookdown = KEY_PGDN;
key_invleft = KEY_INS;
key_invright = KEY_DEL;
M_BindIntVariable("key_jump", &key_jump);
M_BindIntVariable("key_lookUp", &key_lookup);
M_BindIntVariable("key_lookDown", &key_lookdown);
M_BindIntVariable("key_invLeft", &key_invleft);
M_BindIntVariable("key_invRight", &key_invright);
// Custom Strife-only Keys:
M_BindIntVariable("key_useHealth", &key_usehealth);
M_BindIntVariable("key_invquery", &key_invquery);
M_BindIntVariable("key_mission", &key_mission);
M_BindIntVariable("key_invPop", &key_invpop);
M_BindIntVariable("key_invKey", &key_invkey);
M_BindIntVariable("key_invHome", &key_invhome);
M_BindIntVariable("key_invEnd", &key_invend);
M_BindIntVariable("key_invUse", &key_invuse);
M_BindIntVariable("key_invDrop", &key_invdrop);
// Strife also supports jump on mouse and joystick, and in the exact same
// manner as Hexen!
M_BindIntVariable("mouseb_jump", &mousebjump);
M_BindIntVariable("joyb_jump", &joybjump);
}
void M_BindWeaponControls(void)
{
M_BindIntVariable("key_weapon1", &key_weapon1);
M_BindIntVariable("key_weapon2", &key_weapon2);
M_BindIntVariable("key_weapon3", &key_weapon3);
M_BindIntVariable("key_weapon4", &key_weapon4);
M_BindIntVariable("key_weapon5", &key_weapon5);
M_BindIntVariable("key_weapon6", &key_weapon6);
M_BindIntVariable("key_weapon7", &key_weapon7);
M_BindIntVariable("key_weapon8", &key_weapon8);
M_BindIntVariable("key_prevweapon", &key_prevweapon);
M_BindIntVariable("key_nextweapon", &key_nextweapon);
M_BindIntVariable("joyb_prevweapon", &joybprevweapon);
M_BindIntVariable("joyb_nextweapon", &joybnextweapon);
M_BindIntVariable("mouseb_prevweapon", &mousebprevweapon);
M_BindIntVariable("mouseb_nextweapon", &mousebnextweapon);
}
void M_BindMapControls(void)
{
M_BindIntVariable("key_map_north", &key_map_north);
M_BindIntVariable("key_map_south", &key_map_south);
M_BindIntVariable("key_map_east", &key_map_east);
M_BindIntVariable("key_map_west", &key_map_west);
M_BindIntVariable("key_map_zoomin", &key_map_zoomin);
M_BindIntVariable("key_map_zoomout", &key_map_zoomout);
M_BindIntVariable("key_map_toggle", &key_map_toggle);
M_BindIntVariable("key_map_maxzoom", &key_map_maxzoom);
M_BindIntVariable("key_map_follow", &key_map_follow);
M_BindIntVariable("key_map_grid", &key_map_grid);
M_BindIntVariable("key_map_mark", &key_map_mark);
M_BindIntVariable("key_map_clearmark", &key_map_clearmark);
}
void M_BindMenuControls(void)
{
M_BindIntVariable("key_menu_activate", &key_menu_activate);
M_BindIntVariable("key_menu_up", &key_menu_up);
M_BindIntVariable("key_menu_down", &key_menu_down);
M_BindIntVariable("key_menu_left", &key_menu_left);
M_BindIntVariable("key_menu_right", &key_menu_right);
M_BindIntVariable("key_menu_back", &key_menu_back);
M_BindIntVariable("key_menu_forward", &key_menu_forward);
M_BindIntVariable("key_menu_confirm", &key_menu_confirm);
M_BindIntVariable("key_menu_abort", &key_menu_abort);
M_BindIntVariable("key_menu_help", &key_menu_help);
M_BindIntVariable("key_menu_save", &key_menu_save);
M_BindIntVariable("key_menu_load", &key_menu_load);
M_BindIntVariable("key_menu_volume", &key_menu_volume);
M_BindIntVariable("key_menu_detail", &key_menu_detail);
M_BindIntVariable("key_menu_qsave", &key_menu_qsave);
M_BindIntVariable("key_menu_endgame", &key_menu_endgame);
M_BindIntVariable("key_menu_messages", &key_menu_messages);
M_BindIntVariable("key_menu_qload", &key_menu_qload);
M_BindIntVariable("key_menu_quit", &key_menu_quit);
M_BindIntVariable("key_menu_gamma", &key_menu_gamma);
M_BindIntVariable("key_menu_incscreen", &key_menu_incscreen);
M_BindIntVariable("key_menu_decscreen", &key_menu_decscreen);
M_BindIntVariable("key_menu_screenshot",&key_menu_screenshot);
M_BindIntVariable("key_demo_quit", &key_demo_quit);
M_BindIntVariable("key_spy", &key_spy);
}
void M_BindChatControls(unsigned int num_players)
{
char name[32]; // haleyjd: 20 not large enough - Thank you, come again!
unsigned int i; // haleyjd: signedness conflict
M_BindIntVariable("key_multi_msg", &key_multi_msg);
for (i=0; i<num_players; ++i)
{
M_snprintf(name, sizeof(name), "key_multi_msgplayer%i", i + 1);
M_BindIntVariable(name, &key_multi_msgplayer[i]);
}
}
//
// Apply custom patches to the default values depending on the
// platform we are running on.
//
void M_ApplyPlatformDefaults(void)
{
// no-op. Add your platform-specific patches here.
}

169
src/m_controls.h Normal file
View File

@@ -0,0 +1,169 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#ifndef __M_CONTROLS_H__
#define __M_CONTROLS_H__
extern int key_right;
extern int key_left;
extern int key_up;
extern int key_down;
extern int key_strafeleft;
extern int key_straferight;
extern int key_fire;
extern int key_use;
extern int key_strafe;
extern int key_speed;
extern int key_jump;
extern int key_flyup;
extern int key_flydown;
extern int key_flycenter;
extern int key_lookup;
extern int key_lookdown;
extern int key_lookcenter;
extern int key_invleft;
extern int key_invright;
extern int key_useartifact;
// villsa [STRIFE] strife keys
extern int key_usehealth;
extern int key_invquery;
extern int key_mission;
extern int key_invpop;
extern int key_invkey;
extern int key_invhome;
extern int key_invend;
extern int key_invuse;
extern int key_invdrop;
extern int key_message_refresh;
extern int key_pause;
extern int key_multi_msg;
extern int key_multi_msgplayer[8];
extern int key_weapon1;
extern int key_weapon2;
extern int key_weapon3;
extern int key_weapon4;
extern int key_weapon5;
extern int key_weapon6;
extern int key_weapon7;
extern int key_weapon8;
extern int key_arti_all;
extern int key_arti_health;
extern int key_arti_poisonbag;
extern int key_arti_blastradius;
extern int key_arti_teleport;
extern int key_arti_teleportother;
extern int key_arti_egg;
extern int key_arti_invulnerability;
extern int key_demo_quit;
extern int key_spy;
extern int key_prevweapon;
extern int key_nextweapon;
extern int key_map_north;
extern int key_map_south;
extern int key_map_east;
extern int key_map_west;
extern int key_map_zoomin;
extern int key_map_zoomout;
extern int key_map_toggle;
extern int key_map_maxzoom;
extern int key_map_follow;
extern int key_map_grid;
extern int key_map_mark;
extern int key_map_clearmark;
// menu keys:
extern int key_menu_activate;
extern int key_menu_up;
extern int key_menu_down;
extern int key_menu_left;
extern int key_menu_right;
extern int key_menu_back;
extern int key_menu_forward;
extern int key_menu_confirm;
extern int key_menu_abort;
extern int key_menu_help;
extern int key_menu_save;
extern int key_menu_load;
extern int key_menu_volume;
extern int key_menu_detail;
extern int key_menu_qsave;
extern int key_menu_endgame;
extern int key_menu_messages;
extern int key_menu_qload;
extern int key_menu_quit;
extern int key_menu_gamma;
extern int key_menu_incscreen;
extern int key_menu_decscreen;
extern int key_menu_screenshot;
extern int mousebfire;
extern int mousebstrafe;
extern int mousebforward;
extern int mousebjump;
extern int mousebstrafeleft;
extern int mousebstraferight;
extern int mousebbackward;
extern int mousebuse;
extern int mousebprevweapon;
extern int mousebnextweapon;
extern int joybfire;
extern int joybstrafe;
extern int joybuse;
extern int joybspeed;
extern int joybjump;
extern int joybstrafeleft;
extern int joybstraferight;
extern int joybprevweapon;
extern int joybnextweapon;
extern int joybmenu;
extern int joybautomap;
extern int dclick_use;
void M_BindBaseControls(void);
void M_BindHereticControls(void);
void M_BindHexenControls(void);
void M_BindStrifeControls(void);
void M_BindWeaponControls(void);
void M_BindMapControls(void);
void M_BindMenuControls(void);
void M_BindChatControls(unsigned int num_players);
void M_ApplyPlatformDefaults(void);
#endif /* #ifndef __M_CONTROLS_H__ */

61
src/m_fixed.c Normal file
View File

@@ -0,0 +1,61 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Fixed point implementation.
//
#include <limits.h>
#include <stdint.h>
#include "m_fixed.h"
#include "stdlib.h"
// Fixme. __USE_C_FIXED__ or something.
fixed_t
FixedMul
( fixed_t a,
fixed_t b )
{
return ((int64_t) a * (int64_t) b) >> FRACBITS;
}
//
// FixedDiv, C version.
//
fixed_t FixedDiv(fixed_t a, fixed_t b)
{
if ((abs(a) >> 14) >= abs(b))
{
return (a^b) < 0 ? INT_MIN : INT_MAX;
}
else
{
int64_t result;
result = ((int64_t) a << FRACBITS) / b;
return (fixed_t) result;
}
}

39
src/m_fixed.h Normal file
View File

@@ -0,0 +1,39 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Fixed point arithemtics, implementation.
//
#ifndef __M_FIXED__
#define __M_FIXED__
//
// Fixed point, 32bit as 16.16.
//
#define FRACBITS 16
#define FRACUNIT (1<<FRACBITS)
typedef int fixed_t;
fixed_t FixedMul (fixed_t a, fixed_t b);
fixed_t FixedDiv (fixed_t a, fixed_t b);
#endif

1608
src/m_menu.c Normal file

File diff suppressed because it is too large Load Diff

54
src/m_menu.h Normal file
View File

@@ -0,0 +1,54 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Menu widget stuff, episode selection and such.
//
#ifndef __M_MENU__
#define __M_MENU__
#include "d_event.h"
#include "doomtype.h"
//
// MENUS
//
// Called by main loop,
// saves config file and calls I_Quit when user exits.
// Even when the menu is not displayed,
// this can resize the view and change game parameters.
// Does all the real work of the menu interaction.
boolean M_Responder(event_t *ev);
// Called by main loop,
// only used for menu (skull cursor) animation.
void M_Ticker(void);
// Called by main loop,
// draws the menus directly into the screen buffer.
void M_Drawer(void);
// Called by D_DoomMain,
// loads the config file.
void M_Init(void);
// Called by intro code to force menu up upon a keypress,
// does nothing if menu is already up.
void M_StartControlPanel(void);
extern int detailLevel;
extern int screenblocks;
#endif

562
src/m_misc.c Normal file
View File

@@ -0,0 +1,562 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Miscellaneous.
//
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/stat.h>
#include "doomtype.h"
#include "i_system.h"
#include "m_misc.h"
#include "z_zone.h"
//
// Create a directory
//
void M_MakeDirectory(char *path)
{
mkdir(path, 0755);
}
// Check if a file exists
boolean M_FileExists(char *filename)
{
FILE *fstream;
fstream = fopen(filename, "r");
if (fstream != NULL)
{
fclose(fstream);
return true;
}
else
{
// If we can't open because the file is a directory, the
// "file" exists at least!
return errno == EISDIR;
}
}
// Check if a file exists by probing for common case variation of its filename.
// Returns a newly allocated string that the caller is responsible for freeing.
char *M_FileCaseExists(char *path)
{
char *path_dup, *filename, *ext;
path_dup = M_StringDuplicate(path);
// 0: actual path
if (M_FileExists(path_dup))
{
return path_dup;
}
filename = strrchr(path_dup, DIR_SEPARATOR);
if (filename != NULL)
{
filename++;
}
else
{
filename = path_dup;
}
// 1: lowercase filename, e.g. doom2.wad
M_ForceLowercase(filename);
if (M_FileExists(path_dup))
{
return path_dup;
}
// 2: uppercase filename, e.g. DOOM2.WAD
M_ForceUppercase(filename);
if (M_FileExists(path_dup))
{
return path_dup;
}
// 3. uppercase basename with lowercase extension, e.g. DOOM2.wad
ext = strrchr(path_dup, '.');
if (ext != NULL && ext > filename)
{
M_ForceLowercase(ext + 1);
if (M_FileExists(path_dup))
{
return path_dup;
}
}
// 4. lowercase filename with uppercase first letter, e.g. Doom2.wad
if (strlen(filename) > 1)
{
M_ForceLowercase(filename + 1);
if (M_FileExists(path_dup))
{
return path_dup;
}
}
// 5. no luck
free(path_dup);
return NULL;
}
//
// Determine the length of an open file.
//
long M_FileLength(FILE *handle)
{
long savedpos;
long length;
// save the current position in the file
savedpos = ftell(handle);
// jump to the end and find the length
fseek(handle, 0, SEEK_END);
length = ftell(handle);
// go back to the old location
fseek(handle, savedpos, SEEK_SET);
return length;
}
//
// M_WriteFile
//
boolean M_WriteFile(char *name, void *source, int length)
{
FILE *handle;
int count;
handle = fopen(name, "wb");
if (handle == NULL)
return false;
count = fwrite(source, 1, length, handle);
fclose(handle);
if (count < length)
return false;
return true;
}
//
// M_ReadFile
//
int M_ReadFile(char *name, byte **buffer)
{
FILE *handle;
int count, length;
byte *buf;
handle = fopen(name, "rb");
if (handle == NULL)
I_Error ("Couldn't read file %s", name);
// find the size of the file by seeking to the end and
// reading the current position
length = M_FileLength(handle);
buf = Z_Malloc (length, PU_STATIC, NULL);
count = fread(buf, 1, length, handle);
fclose (handle);
if (count < length)
I_Error ("Couldn't read file %s", name);
*buffer = buf;
return length;
}
// Returns the path to a temporary file of the given name, stored
// inside the system temporary directory.
//
// The returned value must be freed with Z_Free after use.
char *M_TempFile(char *s)
{
char *tempdir;
tempdir = "/tmp";
return M_StringJoin(tempdir, DIR_SEPARATOR_S, s, NULL);
}
boolean M_StrToInt(const char *str, int *result)
{
return sscanf(str, " 0x%x", result) == 1
|| sscanf(str, " 0X%x", result) == 1
|| sscanf(str, " 0%o", result) == 1
|| sscanf(str, " %d", result) == 1;
}
void M_ExtractFileBase(char *path, char *dest)
{
char *src;
char *filename;
int length;
src = path + strlen(path) - 1;
// back up until a \ or the start
while (src != path && *(src - 1) != DIR_SEPARATOR)
{
src--;
}
filename = src;
// Copy up to eight characters
// Note: Vanilla Doom exits with an error if a filename is specified
// with a base of more than eight characters. To remove the 8.3
// filename limit, instead we simply truncate the name.
length = 0;
memset(dest, 0, 8);
while (*src != '\0' && *src != '.')
{
if (length >= 8)
{
printf("Warning: Truncated '%s' lump name to '%.8s'.\n",
filename, dest);
break;
}
dest[length++] = toupper((int)*src++);
}
}
//---------------------------------------------------------------------------
//
// PROC M_ForceUppercase
//
// Change string to uppercase.
//
//---------------------------------------------------------------------------
void M_ForceUppercase(char *text)
{
char *p;
for (p = text; *p != '\0'; ++p)
{
*p = toupper(*p);
}
}
//---------------------------------------------------------------------------
//
// PROC M_ForceLowercase
//
// Change string to lowercase.
//
//---------------------------------------------------------------------------
void M_ForceLowercase(char *text)
{
char *p;
for (p = text; *p != '\0'; ++p)
{
*p = tolower(*p);
}
}
//
// M_StrCaseStr
//
// Case-insensitive version of strstr()
//
char *M_StrCaseStr(char *haystack, char *needle)
{
unsigned int haystack_len;
unsigned int needle_len;
unsigned int len;
unsigned int i;
haystack_len = strlen(haystack);
needle_len = strlen(needle);
if (haystack_len < needle_len)
{
return NULL;
}
len = haystack_len - needle_len;
for (i = 0; i <= len; ++i)
{
if (!strncasecmp(haystack + i, needle, needle_len))
{
return haystack + i;
}
}
return NULL;
}
//
// Safe version of strdup() that checks the string was successfully
// allocated.
//
char *M_StringDuplicate(const char *orig)
{
char *result;
result = strdup(orig);
if (result == NULL)
{
I_Error("Failed to duplicate string (length %i)\n",
strlen(orig));
}
return result;
}
//
// String replace function.
//
char *M_StringReplace(const char *haystack, const char *needle,
const char *replacement)
{
char *result, *dst;
const char *p;
size_t needle_len = strlen(needle);
size_t result_len, dst_len;
// Iterate through occurrences of 'needle' and calculate the size of
// the new string.
result_len = strlen(haystack) + 1;
p = haystack;
for (;;)
{
p = strstr(p, needle);
if (p == NULL)
{
break;
}
p += needle_len;
result_len += strlen(replacement) - needle_len;
}
// Construct new string.
result = malloc(result_len);
if (result == NULL)
{
I_Error("M_StringReplace: Failed to allocate new string");
return NULL;
}
dst = result; dst_len = result_len;
p = haystack;
while (*p != '\0')
{
if (!strncmp(p, needle, needle_len))
{
M_StringCopy(dst, replacement, dst_len);
p += needle_len;
dst += strlen(replacement);
dst_len -= strlen(replacement);
}
else
{
*dst = *p;
++dst; --dst_len;
++p;
}
}
*dst = '\0';
return result;
}
// Safe string copy function that works like OpenBSD's strlcpy().
// Returns true if the string was not truncated.
boolean M_StringCopy(char *dest, const char *src, size_t dest_size)
{
size_t len;
if (dest_size >= 1)
{
dest[dest_size - 1] = '\0';
strncpy(dest, src, dest_size - 1);
}
else
{
return false;
}
len = strlen(dest);
return src[len] == '\0';
}
// Safe string concat function that works like OpenBSD's strlcat().
// Returns true if string not truncated.
boolean M_StringConcat(char *dest, const char *src, size_t dest_size)
{
size_t offset;
offset = strlen(dest);
if (offset > dest_size)
{
offset = dest_size;
}
return M_StringCopy(dest + offset, src, dest_size - offset);
}
// Returns true if 's' begins with the specified prefix.
boolean M_StringStartsWith(const char *s, const char *prefix)
{
return strlen(s) > strlen(prefix)
&& strncmp(s, prefix, strlen(prefix)) == 0;
}
// Returns true if 's' ends with the specified suffix.
boolean M_StringEndsWith(const char *s, const char *suffix)
{
return strlen(s) >= strlen(suffix)
&& strcmp(s + strlen(s) - strlen(suffix), suffix) == 0;
}
// Return a newly-malloced string with all the strings given as arguments
// concatenated together.
char *M_StringJoin(const char *s, ...)
{
char *result;
const char *v;
va_list args;
size_t result_len;
result_len = strlen(s) + 1;
va_start(args, s);
for (;;)
{
v = va_arg(args, const char *);
if (v == NULL)
{
break;
}
result_len += strlen(v);
}
va_end(args);
result = malloc(result_len);
if (result == NULL)
{
I_Error("M_StringJoin: Failed to allocate new string.");
return NULL;
}
M_StringCopy(result, s, result_len);
va_start(args, s);
for (;;)
{
v = va_arg(args, const char *);
if (v == NULL)
{
break;
}
M_StringConcat(result, v, result_len);
}
va_end(args);
return result;
}
// Safe, portable vsnprintf().
int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
{
int result;
if (buf_len < 1)
{
return 0;
}
// Windows (and other OSes?) has a vsnprintf() that doesn't always
// append a trailing \0. So we must do it, and write into a buffer
// that is one byte shorter; otherwise this function is unsafe.
result = vsnprintf(buf, buf_len, s, args);
// If truncated, change the final char in the buffer to a \0.
// A negative result indicates a truncated buffer on Windows.
if (result < 0 || result >= buf_len)
{
buf[buf_len - 1] = '\0';
result = buf_len - 1;
}
return result;
}
// Safe, portable snprintf().
int M_snprintf(char *buf, size_t buf_len, const char *s, ...)
{
va_list args;
int result;
va_start(args, s);
result = M_vsnprintf(buf, buf_len, s, args);
va_end(args);
return result;
}

52
src/m_misc.h Normal file
View File

@@ -0,0 +1,52 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Miscellaneous.
//
#ifndef __M_MISC__
#define __M_MISC__
#include <stdio.h>
#include <stdarg.h>
#include "doomtype.h"
boolean M_WriteFile(char *name, void *source, int length);
int M_ReadFile(char *name, byte **buffer);
void M_MakeDirectory(char *dir);
char *M_TempFile(char *s);
boolean M_FileExists(char *file);
char *M_FileCaseExists(char *file);
long M_FileLength(FILE *handle);
boolean M_StrToInt(const char *str, int *result);
void M_ExtractFileBase(char *path, char *dest);
void M_ForceUppercase(char *text);
void M_ForceLowercase(char *text);
char *M_StrCaseStr(char *haystack, char *needle);
char *M_StringDuplicate(const char *orig);
boolean M_StringCopy(char *dest, const char *src, size_t dest_size);
boolean M_StringConcat(char *dest, const char *src, size_t dest_size);
char *M_StringReplace(const char *haystack, const char *needle,
const char *replacement);
char *M_StringJoin(const char *s, ...);
boolean M_StringStartsWith(const char *s, const char *prefix);
boolean M_StringEndsWith(const char *s, const char *suffix);
int M_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args);
int M_snprintf(char *buf, size_t buf_len, const char *s, ...);
#endif

64
src/m_random.c Normal file
View File

@@ -0,0 +1,64 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Random number LUT.
//
//
// M_Random
// Returns a 0-255 number
//
static const unsigned char rndtable[256] = {
0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74,
21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110,
85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188, 52, 140, 202,
120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224, 149, 104, 25, 178,
252, 182, 202, 182, 141, 197, 4, 81, 181, 242, 145, 42, 39, 227, 156,
198, 225, 193, 219, 93, 122, 175, 249, 0, 175, 143, 70, 239, 46, 246,
163, 53, 163, 109, 168, 135, 2, 235, 25, 92, 20, 145, 138, 77, 69,
166, 78, 176, 173, 212, 166, 113, 94, 161, 41, 50, 239, 49, 111, 164,
70, 60, 2, 37, 171, 75, 136, 156, 11, 56, 42, 146, 138, 229, 73,
146, 77, 61, 98, 196, 135, 106, 63, 197, 195, 86, 96, 203, 113, 101,
170, 247, 181, 113, 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112,
166, 103, 241, 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66,
143, 224, 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159,
95, 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226,
71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36, 17,
46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106, 197, 242,
98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136, 120, 163, 236,
249};
int rndindex = 0;
int prndindex = 0;
// Which one is deterministic?
int P_Random(void) {
prndindex = (prndindex + 1) & 0xff;
return rndtable[prndindex];
}
int M_Random(void) {
rndindex = (rndindex + 1) & 0xff;
return rndtable[rndindex];
}
void M_ClearRandom(void) { rndindex = prndindex = 0; }
// inspired by the same routine in Eternity, thanks haleyjd
int P_SubRandom(void) {
int r = P_Random();
return r - P_Random();
}

37
src/m_random.h Normal file
View File

@@ -0,0 +1,37 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//
//
#ifndef __M_RANDOM__
#define __M_RANDOM__
#include "doomtype.h"
// Returns a number from 0 to 255,
// from a lookup table.
int M_Random(void);
// As M_Random, but used only by the play simulation.
int P_Random(void);
// Fix randoms for demos.
void M_ClearRandom(void);
// Defined version of P_Random() - P_Random()
int P_SubRandom(void);
#endif

195
src/memio.c Normal file
View File

@@ -0,0 +1,195 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Emulates the IO functions in C stdio.h reading and writing to
// memory.
//
#include <stdio.h>
#include <string.h>
#include "memio.h"
#include "z_zone.h"
typedef enum {
MODE_READ,
MODE_WRITE,
} memfile_mode_t;
struct _MEMFILE {
unsigned char *buf;
size_t buflen;
size_t alloced;
unsigned int position;
memfile_mode_t mode;
};
// Open a memory area for reading
MEMFILE *mem_fopen_read(void *buf, size_t buflen)
{
MEMFILE *file;
file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);
file->buf = (unsigned char *) buf;
file->buflen = buflen;
file->position = 0;
file->mode = MODE_READ;
return file;
}
// Read bytes
size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream)
{
size_t items;
if (stream->mode != MODE_READ)
{
printf("not a read stream\n");
return -1;
}
// Trying to read more bytes than we have left?
items = nmemb;
if (items * size > stream->buflen - stream->position)
{
items = (stream->buflen - stream->position) / size;
}
// Copy bytes to buffer
memcpy(buf, stream->buf + stream->position, items * size);
// Update position
stream->position += items * size;
return items;
}
// Open a memory area for writing
MEMFILE *mem_fopen_write(void)
{
MEMFILE *file;
file = Z_Malloc(sizeof(MEMFILE), PU_STATIC, 0);
file->alloced = 1024;
file->buf = Z_Malloc(file->alloced, PU_STATIC, 0);
file->buflen = 0;
file->position = 0;
file->mode = MODE_WRITE;
return file;
}
// Write bytes to stream
size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream)
{
size_t bytes;
if (stream->mode != MODE_WRITE)
{
return -1;
}
// More bytes than can fit in the buffer?
// If so, reallocate bigger.
bytes = size * nmemb;
while (bytes > stream->alloced - stream->position)
{
unsigned char *newbuf;
newbuf = Z_Malloc(stream->alloced * 2, PU_STATIC, 0);
memcpy(newbuf, stream->buf, stream->alloced);
Z_Free(stream->buf);
stream->buf = newbuf;
stream->alloced *= 2;
}
// Copy into buffer
memcpy(stream->buf + stream->position, ptr, bytes);
stream->position += bytes;
if (stream->position > stream->buflen)
stream->buflen = stream->position;
return nmemb;
}
void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen)
{
*buf = stream->buf;
*buflen = stream->buflen;
}
void mem_fclose(MEMFILE *stream)
{
if (stream->mode == MODE_WRITE)
{
Z_Free(stream->buf);
}
Z_Free(stream);
}
long mem_ftell(MEMFILE *stream)
{
return stream->position;
}
int mem_fseek(MEMFILE *stream, signed long position, mem_rel_t whence)
{
unsigned int newpos;
switch (whence)
{
case MEM_SEEK_SET:
newpos = (int) position;
break;
case MEM_SEEK_CUR:
newpos = (int) (stream->position + position);
break;
case MEM_SEEK_END:
newpos = (int) (stream->buflen + position);
break;
default:
return -1;
}
if (newpos < stream->buflen)
{
stream->position = newpos;
return 0;
}
else
{
printf("Error seeking to %i\n", newpos);
return -1;
}
}

40
src/memio.h Normal file
View File

@@ -0,0 +1,40 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
#ifndef MEMIO_H
#define MEMIO_H
#include <stddef.h>
typedef struct _MEMFILE MEMFILE;
typedef enum
{
MEM_SEEK_SET,
MEM_SEEK_CUR,
MEM_SEEK_END,
} mem_rel_t;
MEMFILE *mem_fopen_read(void *buf, size_t buflen);
size_t mem_fread(void *buf, size_t size, size_t nmemb, MEMFILE *stream);
MEMFILE *mem_fopen_write(void);
size_t mem_fwrite(const void *ptr, size_t size, size_t nmemb, MEMFILE *stream);
void mem_get_buf(MEMFILE *stream, void **buf, size_t *buflen);
void mem_fclose(MEMFILE *stream);
long mem_ftell(MEMFILE *stream);
int mem_fseek(MEMFILE *stream, signed long offset, mem_rel_t whence);
#endif /* #ifndef MEMIO_H */

819
src/midifile.c Normal file
View File

@@ -0,0 +1,819 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Reading of MIDI files.
//
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL2/SDL_endian.h"
#include "doomtype.h"
#include "midifile.h"
#define HEADER_CHUNK_ID "MThd"
#define TRACK_CHUNK_ID "MTrk"
#define MAX_BUFFER_SIZE 0x10000
typedef PACKED_STRUCT (
{
byte chunk_id[4];
unsigned int chunk_size;
}) chunk_header_t;
typedef PACKED_STRUCT (
{
chunk_header_t chunk_header;
unsigned short format_type;
unsigned short num_tracks;
unsigned short time_division;
}) midi_header_t;
typedef struct
{
// Length in bytes:
unsigned int data_len;
// Events in this track:
midi_event_t *events;
int num_events;
} midi_track_t;
struct midi_track_iter_s
{
midi_track_t *track;
unsigned int position;
};
struct midi_file_s
{
midi_header_t header;
// All tracks in this file:
midi_track_t *tracks;
unsigned int num_tracks;
// Data buffer used to store data read for SysEx or meta events:
byte *buffer;
unsigned int buffer_size;
};
// Check the header of a chunk:
static boolean CheckChunkHeader(chunk_header_t *chunk,
char *expected_id)
{
boolean result;
result = (memcmp((char *) chunk->chunk_id, expected_id, 4) == 0);
if (!result)
{
fprintf(stderr, "CheckChunkHeader: Expected '%s' chunk header, "
"got '%c%c%c%c'\n",
expected_id,
chunk->chunk_id[0], chunk->chunk_id[1],
chunk->chunk_id[2], chunk->chunk_id[3]);
}
return result;
}
// Read a single byte. Returns false on error.
static boolean ReadByte(byte *result, FILE *stream)
{
int c;
c = fgetc(stream);
if (c == EOF)
{
fprintf(stderr, "ReadByte: Unexpected end of file\n");
return false;
}
else
{
*result = (byte) c;
return true;
}
}
// Read a variable-length value.
static boolean ReadVariableLength(unsigned int *result, FILE *stream)
{
int i;
byte b = 0;
*result = 0;
for (i=0; i<4; ++i)
{
if (!ReadByte(&b, stream))
{
fprintf(stderr, "ReadVariableLength: Error while reading "
"variable-length value\n");
return false;
}
// Insert the bottom seven bits from this byte.
*result <<= 7;
*result |= b & 0x7f;
// If the top bit is not set, this is the end.
if ((b & 0x80) == 0)
{
return true;
}
}
fprintf(stderr, "ReadVariableLength: Variable-length value too "
"long: maximum of four bytes\n");
return false;
}
// Read a byte sequence into the data buffer.
static void *ReadByteSequence(unsigned int num_bytes, FILE *stream)
{
unsigned int i;
byte *result;
// Allocate a buffer. Allocate one extra byte, as malloc(0) is
// non-portable.
result = malloc(num_bytes + 1);
if (result == NULL)
{
fprintf(stderr, "ReadByteSequence: Failed to allocate buffer\n");
return NULL;
}
// Read the data:
for (i=0; i<num_bytes; ++i)
{
if (!ReadByte(&result[i], stream))
{
fprintf(stderr, "ReadByteSequence: Error while reading byte %u\n",
i);
free(result);
return NULL;
}
}
return result;
}
// Read a MIDI channel event.
// two_param indicates that the event type takes two parameters
// (three byte) otherwise it is single parameter (two byte)
static boolean ReadChannelEvent(midi_event_t *event,
byte event_type, boolean two_param,
FILE *stream)
{
byte b = 0;
// Set basics:
event->event_type = event_type & 0xf0;
event->data.channel.channel = event_type & 0x0f;
// Read parameters:
if (!ReadByte(&b, stream))
{
fprintf(stderr, "ReadChannelEvent: Error while reading channel "
"event parameters\n");
return false;
}
event->data.channel.param1 = b;
// Second parameter:
if (two_param)
{
if (!ReadByte(&b, stream))
{
fprintf(stderr, "ReadChannelEvent: Error while reading channel "
"event parameters\n");
return false;
}
event->data.channel.param2 = b;
}
return true;
}
// Read sysex event:
static boolean ReadSysExEvent(midi_event_t *event, int event_type,
FILE *stream)
{
event->event_type = event_type;
if (!ReadVariableLength(&event->data.sysex.length, stream))
{
fprintf(stderr, "ReadSysExEvent: Failed to read length of "
"SysEx block\n");
return false;
}
// Read the byte sequence:
event->data.sysex.data = ReadByteSequence(event->data.sysex.length, stream);
if (event->data.sysex.data == NULL)
{
fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
return false;
}
return true;
}
// Read meta event:
static boolean ReadMetaEvent(midi_event_t *event, FILE *stream)
{
byte b = 0;
event->event_type = MIDI_EVENT_META;
// Read meta event type:
if (!ReadByte(&b, stream))
{
fprintf(stderr, "ReadMetaEvent: Failed to read meta event type\n");
return false;
}
event->data.meta.type = b;
// Read length of meta event data:
if (!ReadVariableLength(&event->data.meta.length, stream))
{
fprintf(stderr, "ReadSysExEvent: Failed to read length of "
"SysEx block\n");
return false;
}
// Read the byte sequence:
event->data.meta.data = ReadByteSequence(event->data.meta.length, stream);
if (event->data.meta.data == NULL)
{
fprintf(stderr, "ReadSysExEvent: Failed while reading SysEx event\n");
return false;
}
return true;
}
static boolean ReadEvent(midi_event_t *event, unsigned int *last_event_type,
FILE *stream)
{
byte event_type = 0;
if (!ReadVariableLength(&event->delta_time, stream))
{
fprintf(stderr, "ReadEvent: Failed to read event timestamp\n");
return false;
}
if (!ReadByte(&event_type, stream))
{
fprintf(stderr, "ReadEvent: Failed to read event type\n");
return false;
}
// All event types have their top bit set. Therefore, if
// the top bit is not set, it is because we are using the "same
// as previous event type" shortcut to save a byte. Skip back
// a byte so that we read this byte again.
if ((event_type & 0x80) == 0)
{
event_type = *last_event_type;
if (fseek(stream, -1, SEEK_CUR) < 0)
{
fprintf(stderr, "ReadEvent: Unable to seek in stream\n");
return false;
}
}
else
{
*last_event_type = event_type;
}
// Check event type:
switch (event_type & 0xf0)
{
// Two parameter channel events:
case MIDI_EVENT_NOTE_OFF:
case MIDI_EVENT_NOTE_ON:
case MIDI_EVENT_AFTERTOUCH:
case MIDI_EVENT_CONTROLLER:
case MIDI_EVENT_PITCH_BEND:
return ReadChannelEvent(event, event_type, true, stream);
// Single parameter channel events:
case MIDI_EVENT_PROGRAM_CHANGE:
case MIDI_EVENT_CHAN_AFTERTOUCH:
return ReadChannelEvent(event, event_type, false, stream);
default:
break;
}
// Specific value?
switch (event_type)
{
case MIDI_EVENT_SYSEX:
case MIDI_EVENT_SYSEX_SPLIT:
return ReadSysExEvent(event, event_type, stream);
case MIDI_EVENT_META:
return ReadMetaEvent(event, stream);
default:
break;
}
fprintf(stderr, "ReadEvent: Unknown MIDI event type: 0x%x\n", event_type);
return false;
}
// Free an event:
static void FreeEvent(midi_event_t *event)
{
// Some event types have dynamically allocated buffers assigned
// to them that must be freed.
switch (event->event_type)
{
case MIDI_EVENT_SYSEX:
case MIDI_EVENT_SYSEX_SPLIT:
free(event->data.sysex.data);
break;
case MIDI_EVENT_META:
free(event->data.meta.data);
break;
default:
// Nothing to do.
break;
}
}
// Read and check the track chunk header
static boolean ReadTrackHeader(midi_track_t *track, FILE *stream)
{
size_t records_read;
chunk_header_t chunk_header;
records_read = fread(&chunk_header, sizeof(chunk_header_t), 1, stream);
if (records_read < 1)
{
return false;
}
if (!CheckChunkHeader(&chunk_header, TRACK_CHUNK_ID))
{
return false;
}
track->data_len = SDL_SwapBE32(chunk_header.chunk_size);
return true;
}
static boolean ReadTrack(midi_track_t *track, FILE *stream)
{
midi_event_t *new_events;
midi_event_t *event;
unsigned int last_event_type;
track->num_events = 0;
track->events = NULL;
// Read the header:
if (!ReadTrackHeader(track, stream))
{
return false;
}
// Then the events:
last_event_type = 0;
for (;;)
{
// Resize the track slightly larger to hold another event:
new_events = realloc(track->events,
sizeof(midi_event_t) * (track->num_events + 1));
if (new_events == NULL)
{
return false;
}
track->events = new_events;
// Read the next event:
event = &track->events[track->num_events];
if (!ReadEvent(event, &last_event_type, stream))
{
return false;
}
++track->num_events;
// End of track?
if (event->event_type == MIDI_EVENT_META
&& event->data.meta.type == MIDI_META_END_OF_TRACK)
{
break;
}
}
return true;
}
// Free a track:
static void FreeTrack(midi_track_t *track)
{
unsigned int i;
for (i=0; i<track->num_events; ++i)
{
FreeEvent(&track->events[i]);
}
free(track->events);
}
static boolean ReadAllTracks(midi_file_t *file, FILE *stream)
{
unsigned int i;
// Allocate list of tracks and read each track:
file->tracks = malloc(sizeof(midi_track_t) * file->num_tracks);
if (file->tracks == NULL)
{
return false;
}
memset(file->tracks, 0, sizeof(midi_track_t) * file->num_tracks);
// Read each track:
for (i=0; i<file->num_tracks; ++i)
{
if (!ReadTrack(&file->tracks[i], stream))
{
return false;
}
}
return true;
}
// Read and check the header chunk.
static boolean ReadFileHeader(midi_file_t *file, FILE *stream)
{
size_t records_read;
unsigned int format_type;
records_read = fread(&file->header, sizeof(midi_header_t), 1, stream);
if (records_read < 1)
{
return false;
}
if (!CheckChunkHeader(&file->header.chunk_header, HEADER_CHUNK_ID)
|| SDL_SwapBE32(file->header.chunk_header.chunk_size) != 6)
{
fprintf(stderr, "ReadFileHeader: Invalid MIDI chunk header! "
"chunk_size=%i\n",
SDL_SwapBE32(file->header.chunk_header.chunk_size));
return false;
}
format_type = SDL_SwapBE16(file->header.format_type);
file->num_tracks = SDL_SwapBE16(file->header.num_tracks);
if ((format_type != 0 && format_type != 1)
|| file->num_tracks < 1)
{
fprintf(stderr, "ReadFileHeader: Only type 0/1 "
"MIDI files supported!\n");
return false;
}
return true;
}
void MIDI_FreeFile(midi_file_t *file)
{
int i;
if (file->tracks != NULL)
{
for (i=0; i<file->num_tracks; ++i)
{
FreeTrack(&file->tracks[i]);
}
free(file->tracks);
}
free(file);
}
midi_file_t *MIDI_LoadFile(char *filename)
{
midi_file_t *file;
FILE *stream;
file = malloc(sizeof(midi_file_t));
if (file == NULL)
{
return NULL;
}
file->tracks = NULL;
file->num_tracks = 0;
file->buffer = NULL;
file->buffer_size = 0;
// Open file
stream = fopen(filename, "rb");
if (stream == NULL)
{
fprintf(stderr, "MIDI_LoadFile: Failed to open '%s'\n", filename);
MIDI_FreeFile(file);
return NULL;
}
// Read MIDI file header
if (!ReadFileHeader(file, stream))
{
fclose(stream);
MIDI_FreeFile(file);
return NULL;
}
// Read all tracks:
if (!ReadAllTracks(file, stream))
{
fclose(stream);
MIDI_FreeFile(file);
return NULL;
}
fclose(stream);
return file;
}
// Get the number of tracks in a MIDI file.
unsigned int MIDI_NumTracks(midi_file_t *file)
{
return file->num_tracks;
}
// Start iterating over the events in a track.
midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track)
{
midi_track_iter_t *iter;
assert(track < file->num_tracks);
iter = malloc(sizeof(*iter));
iter->track = &file->tracks[track];
iter->position = 0;
return iter;
}
void MIDI_FreeIterator(midi_track_iter_t *iter)
{
free(iter);
}
// Get the time until the next MIDI event in a track.
unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter)
{
if (iter->position < iter->track->num_events)
{
midi_event_t *next_event;
next_event = &iter->track->events[iter->position];
return next_event->delta_time;
}
else
{
return 0;
}
}
// Get a pointer to the next MIDI event.
int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event)
{
if (iter->position < iter->track->num_events)
{
*event = &iter->track->events[iter->position];
++iter->position;
return 1;
}
else
{
return 0;
}
}
unsigned int MIDI_GetFileTimeDivision(midi_file_t *file)
{
short result = SDL_SwapBE16(file->header.time_division);
// Negative time division indicates SMPTE time and must be handled
// differently.
if (result < 0)
{
return (signed int)(-(result/256))
* (signed int)(result & 0xFF);
}
else
{
return result;
}
}
void MIDI_RestartIterator(midi_track_iter_t *iter)
{
iter->position = 0;
}
#ifdef TEST
static char *MIDI_EventTypeToString(midi_event_type_t event_type)
{
switch (event_type)
{
case MIDI_EVENT_NOTE_OFF:
return "MIDI_EVENT_NOTE_OFF";
case MIDI_EVENT_NOTE_ON:
return "MIDI_EVENT_NOTE_ON";
case MIDI_EVENT_AFTERTOUCH:
return "MIDI_EVENT_AFTERTOUCH";
case MIDI_EVENT_CONTROLLER:
return "MIDI_EVENT_CONTROLLER";
case MIDI_EVENT_PROGRAM_CHANGE:
return "MIDI_EVENT_PROGRAM_CHANGE";
case MIDI_EVENT_CHAN_AFTERTOUCH:
return "MIDI_EVENT_CHAN_AFTERTOUCH";
case MIDI_EVENT_PITCH_BEND:
return "MIDI_EVENT_PITCH_BEND";
case MIDI_EVENT_SYSEX:
return "MIDI_EVENT_SYSEX";
case MIDI_EVENT_SYSEX_SPLIT:
return "MIDI_EVENT_SYSEX_SPLIT";
case MIDI_EVENT_META:
return "MIDI_EVENT_META";
default:
return "(unknown)";
}
}
void PrintTrack(midi_track_t *track)
{
midi_event_t *event;
unsigned int i;
for (i=0; i<track->num_events; ++i)
{
event = &track->events[i];
if (event->delta_time > 0)
{
printf("Delay: %i ticks\n", event->delta_time);
}
printf("Event type: %s (%i)\n",
MIDI_EventTypeToString(event->event_type),
event->event_type);
switch(event->event_type)
{
case MIDI_EVENT_NOTE_OFF:
case MIDI_EVENT_NOTE_ON:
case MIDI_EVENT_AFTERTOUCH:
case MIDI_EVENT_CONTROLLER:
case MIDI_EVENT_PROGRAM_CHANGE:
case MIDI_EVENT_CHAN_AFTERTOUCH:
case MIDI_EVENT_PITCH_BEND:
printf("\tChannel: %i\n", event->data.channel.channel);
printf("\tParameter 1: %i\n", event->data.channel.param1);
printf("\tParameter 2: %i\n", event->data.channel.param2);
break;
case MIDI_EVENT_SYSEX:
case MIDI_EVENT_SYSEX_SPLIT:
printf("\tLength: %i\n", event->data.sysex.length);
break;
case MIDI_EVENT_META:
printf("\tMeta type: %i\n", event->data.meta.type);
printf("\tLength: %i\n", event->data.meta.length);
break;
}
}
}
int main(int argc, char *argv[])
{
midi_file_t *file;
unsigned int i;
if (argc < 2)
{
printf("Usage: %s <filename>\n", argv[0]);
exit(1);
}
file = MIDI_LoadFile(argv[1]);
if (file == NULL)
{
fprintf(stderr, "Failed to open %s\n", argv[1]);
exit(1);
}
for (i=0; i<file->num_tracks; ++i)
{
printf("\n== Track %i ==\n\n", i);
PrintTrack(&file->tracks[i]);
}
return 0;
}
#endif

171
src/midifile.h Normal file
View File

@@ -0,0 +1,171 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// MIDI file parsing.
//
#ifndef MIDIFILE_H
#define MIDIFILE_H
#include "doomtype.h"
typedef struct midi_file_s midi_file_t;
typedef struct midi_track_iter_s midi_track_iter_t;
#define MIDI_CHANNELS_PER_TRACK 16
typedef enum
{
MIDI_EVENT_NOTE_OFF = 0x80,
MIDI_EVENT_NOTE_ON = 0x90,
MIDI_EVENT_AFTERTOUCH = 0xa0,
MIDI_EVENT_CONTROLLER = 0xb0,
MIDI_EVENT_PROGRAM_CHANGE = 0xc0,
MIDI_EVENT_CHAN_AFTERTOUCH = 0xd0,
MIDI_EVENT_PITCH_BEND = 0xe0,
MIDI_EVENT_SYSEX = 0xf0,
MIDI_EVENT_SYSEX_SPLIT = 0xf7,
MIDI_EVENT_META = 0xff,
} midi_event_type_t;
typedef enum
{
MIDI_CONTROLLER_BANK_SELECT = 0x0,
MIDI_CONTROLLER_MODULATION = 0x1,
MIDI_CONTROLLER_BREATH_CONTROL = 0x2,
MIDI_CONTROLLER_FOOT_CONTROL = 0x3,
MIDI_CONTROLLER_PORTAMENTO = 0x4,
MIDI_CONTROLLER_DATA_ENTRY = 0x5,
MIDI_CONTROLLER_MAIN_VOLUME = 0x7,
MIDI_CONTROLLER_PAN = 0xa,
MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7b,
} midi_controller_t;
typedef enum
{
MIDI_META_SEQUENCE_NUMBER = 0x0,
MIDI_META_TEXT = 0x1,
MIDI_META_COPYRIGHT = 0x2,
MIDI_META_TRACK_NAME = 0x3,
MIDI_META_INSTR_NAME = 0x4,
MIDI_META_LYRICS = 0x5,
MIDI_META_MARKER = 0x6,
MIDI_META_CUE_POINT = 0x7,
MIDI_META_CHANNEL_PREFIX = 0x20,
MIDI_META_END_OF_TRACK = 0x2f,
MIDI_META_SET_TEMPO = 0x51,
MIDI_META_SMPTE_OFFSET = 0x54,
MIDI_META_TIME_SIGNATURE = 0x58,
MIDI_META_KEY_SIGNATURE = 0x59,
MIDI_META_SEQUENCER_SPECIFIC = 0x7f,
} midi_meta_event_type_t;
typedef struct
{
// Meta event type:
unsigned int type;
// Length:
unsigned int length;
// Meta event data:
byte *data;
} midi_meta_event_data_t;
typedef struct
{
// Length:
unsigned int length;
// Event data:
byte *data;
} midi_sysex_event_data_t;
typedef struct
{
// The channel number to which this applies:
unsigned int channel;
// Extra parameters:
unsigned int param1;
unsigned int param2;
} midi_channel_event_data_t;
typedef struct
{
// Time between the previous event and this event.
unsigned int delta_time;
// Type of event:
midi_event_type_t event_type;
union
{
midi_channel_event_data_t channel;
midi_meta_event_data_t meta;
midi_sysex_event_data_t sysex;
} data;
} midi_event_t;
// Load a MIDI file.
midi_file_t *MIDI_LoadFile(char *filename);
// Free a MIDI file.
void MIDI_FreeFile(midi_file_t *file);
// Get the time division value from the MIDI header.
unsigned int MIDI_GetFileTimeDivision(midi_file_t *file);
// Get the number of tracks in a MIDI file.
unsigned int MIDI_NumTracks(midi_file_t *file);
// Start iterating over the events in a track.
midi_track_iter_t *MIDI_IterateTrack(midi_file_t *file, unsigned int track_num);
// Free an iterator.
void MIDI_FreeIterator(midi_track_iter_t *iter);
// Get the time until the next MIDI event in a track.
unsigned int MIDI_GetDeltaTime(midi_track_iter_t *iter);
// Get a pointer to the next MIDI event.
int MIDI_GetNextEvent(midi_track_iter_t *iter, midi_event_t **event);
// Reset an iterator to the beginning of a track.
void MIDI_RestartIterator(midi_track_iter_t *iter);
#endif /* #ifndef MIDIFILE_H */

732
src/mus2mid.c Normal file
View File

@@ -0,0 +1,732 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
// Copyright(C) 2006 Ben Ryves 2006
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// mus2mid.c - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com
// Use to convert a MUS file into a single track, type 0 MIDI file.
#include "doomtype.h"
#include "i_swap.h"
#include "memio.h"
#include "mus2mid.h"
#define NUM_CHANNELS 16
#define MIDI_PERCUSSION_CHAN 9
#define MUS_PERCUSSION_CHAN 15
// MUS event codes
typedef enum
{
mus_releasekey = 0x00,
mus_presskey = 0x10,
mus_pitchwheel = 0x20,
mus_systemevent = 0x30,
mus_changecontroller = 0x40,
mus_scoreend = 0x60
} musevent;
// MIDI event codes
typedef enum
{
midi_releasekey = 0x80,
midi_presskey = 0x90,
midi_aftertouchkey = 0xA0,
midi_changecontroller = 0xB0,
midi_changepatch = 0xC0,
midi_aftertouchchannel = 0xD0,
midi_pitchwheel = 0xE0
} midievent;
// Structure to hold MUS file header
typedef PACKED_STRUCT (
{
byte id[4];
unsigned short scorelength;
unsigned short scorestart;
unsigned short primarychannels;
unsigned short secondarychannels;
unsigned short instrumentcount;
}) musheader;
// Standard MIDI type 0 header + track header
static const byte midiheader[] =
{
'M', 'T', 'h', 'd', // Main header
0x00, 0x00, 0x00, 0x06, // Header size
0x00, 0x00, // MIDI type (0)
0x00, 0x01, // Number of tracks
0x00, 0x46, // Resolution
'M', 'T', 'r', 'k', // Start of track
0x00, 0x00, 0x00, 0x00 // Placeholder for track length
};
// Cached channel velocities
static byte channelvelocities[] =
{
127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127
};
// Timestamps between sequences of MUS events
static unsigned int queuedtime = 0;
// Counter for the length of the track
static unsigned int tracksize;
static const byte controller_map[] =
{
0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D,
0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79
};
static int channel_map[NUM_CHANNELS];
// Write timestamp to a MIDI file.
static boolean WriteTime(unsigned int time, MEMFILE *midioutput)
{
unsigned int buffer = time & 0x7F;
byte writeval;
while ((time >>= 7) != 0)
{
buffer <<= 8;
buffer |= ((time & 0x7F) | 0x80);
}
for (;;)
{
writeval = (byte)(buffer & 0xFF);
if (mem_fwrite(&writeval, 1, 1, midioutput) != 1)
{
return true;
}
++tracksize;
if ((buffer & 0x80) != 0)
{
buffer >>= 8;
}
else
{
queuedtime = 0;
return false;
}
}
}
// Write the end of track marker
static boolean WriteEndTrack(MEMFILE *midioutput)
{
byte endtrack[] = {0xFF, 0x2F, 0x00};
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(endtrack, 1, 3, midioutput) != 3)
{
return true;
}
tracksize += 3;
return false;
}
// Write a key press event
static boolean WritePressKey(byte channel, byte key,
byte velocity, MEMFILE *midioutput)
{
byte working = midi_presskey | channel;
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = key & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = velocity & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
tracksize += 3;
return false;
}
// Write a key release event
static boolean WriteReleaseKey(byte channel, byte key,
MEMFILE *midioutput)
{
byte working = midi_releasekey | channel;
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = key & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = 0;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
tracksize += 3;
return false;
}
// Write a pitch wheel/bend event
static boolean WritePitchWheel(byte channel, short wheel,
MEMFILE *midioutput)
{
byte working = midi_pitchwheel | channel;
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = wheel & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = (wheel >> 7) & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
tracksize += 3;
return false;
}
// Write a patch change event
static boolean WriteChangePatch(byte channel, byte patch,
MEMFILE *midioutput)
{
byte working = midi_changepatch | channel;
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = patch & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
tracksize += 2;
return false;
}
// Write a valued controller change event
static boolean WriteChangeController_Valued(byte channel,
byte control,
byte value,
MEMFILE *midioutput)
{
byte working = midi_changecontroller | channel;
if (WriteTime(queuedtime, midioutput))
{
return true;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
working = control & 0x7F;
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
// Quirk in vanilla DOOM? MUS controller values should be
// 7-bit, not 8-bit.
working = value;// & 0x7F;
// Fix on said quirk to stop MIDI players from complaining that
// the value is out of range:
if (working & 0x80)
{
working = 0x7F;
}
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
{
return true;
}
tracksize += 3;
return false;
}
// Write a valueless controller change event
static boolean WriteChangeController_Valueless(byte channel,
byte control,
MEMFILE *midioutput)
{
return WriteChangeController_Valued(channel, control, 0,
midioutput);
}
// Allocate a free MIDI channel.
static int AllocateMIDIChannel(void)
{
int result;
int max;
int i;
// Find the current highest-allocated channel.
max = -1;
for (i=0; i<NUM_CHANNELS; ++i)
{
if (channel_map[i] > max)
{
max = channel_map[i];
}
}
// max is now equal to the highest-allocated MIDI channel. We can
// now allocate the next available channel. This also works if
// no channels are currently allocated (max=-1)
result = max + 1;
// Don't allocate the MIDI percussion channel!
if (result == MIDI_PERCUSSION_CHAN)
{
++result;
}
return result;
}
// Given a MUS channel number, get the MIDI channel number to use
// in the outputted file.
static int GetMIDIChannel(int mus_channel, MEMFILE *midioutput)
{
// Find the MIDI channel to use for this MUS channel.
// MUS channel 15 is the percusssion channel.
if (mus_channel == MUS_PERCUSSION_CHAN)
{
return MIDI_PERCUSSION_CHAN;
}
else
{
// If a MIDI channel hasn't been allocated for this MUS channel
// yet, allocate the next free MIDI channel.
if (channel_map[mus_channel] == -1)
{
channel_map[mus_channel] = AllocateMIDIChannel();
// First time using the channel, send an "all notes off"
// event. This fixes "The D_DDTBLU disease" described here:
// https://www.doomworld.com/vb/source-ports/66802-the
WriteChangeController_Valueless(channel_map[mus_channel], 0x7b,
midioutput);
}
return channel_map[mus_channel];
}
}
static boolean ReadMusHeader(MEMFILE *file, musheader *header)
{
boolean result;
result = mem_fread(&header->id, sizeof(byte), 4, file) == 4
&& mem_fread(&header->scorelength, sizeof(short), 1, file) == 1
&& mem_fread(&header->scorestart, sizeof(short), 1, file) == 1
&& mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1
&& mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1
&& mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1;
if (result)
{
header->scorelength = SHORT(header->scorelength);
header->scorestart = SHORT(header->scorestart);
header->primarychannels = SHORT(header->primarychannels);
header->secondarychannels = SHORT(header->secondarychannels);
header->instrumentcount = SHORT(header->instrumentcount);
}
return result;
}
// Read a MUS file from a stream (musinput) and output a MIDI file to
// a stream (midioutput).
//
// Returns 0 on success or 1 on failure.
boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput)
{
// Header for the MUS file
musheader musfileheader;
// Descriptor for the current MUS event
byte eventdescriptor;
int channel; // Channel number
musevent event;
// Bunch of vars read from MUS lump
byte key;
byte controllernumber;
byte controllervalue;
// Buffer used for MIDI track size record
byte tracksizebuffer[4];
// Flag for when the score end marker is hit.
int hitscoreend = 0;
// Temp working byte
byte working;
// Used in building up time delays
unsigned int timedelay;
// Initialise channel map to mark all channels as unused.
for (channel=0; channel<NUM_CHANNELS; ++channel)
{
channel_map[channel] = -1;
}
// Grab the header
if (!ReadMusHeader(musinput, &musfileheader))
{
return true;
}
#ifdef CHECK_MUS_HEADER
// Check MUS header
if (musfileheader.id[0] != 'M'
|| musfileheader.id[1] != 'U'
|| musfileheader.id[2] != 'S'
|| musfileheader.id[3] != 0x1A)
{
return true;
}
#endif
// Seek to where the data is held
if (mem_fseek(musinput, (long)musfileheader.scorestart,
MEM_SEEK_SET) != 0)
{
return true;
}
// So, we can assume the MUS file is faintly legit. Let's start
// writing MIDI data...
mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput);
tracksize = 0;
// Now, process the MUS file:
while (!hitscoreend)
{
// Handle a block of events:
while (!hitscoreend)
{
// Fetch channel number and event code:
if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1)
{
return true;
}
channel = GetMIDIChannel(eventdescriptor & 0x0F, midioutput);
event = eventdescriptor & 0x70;
switch (event)
{
case mus_releasekey:
if (mem_fread(&key, 1, 1, musinput) != 1)
{
return true;
}
if (WriteReleaseKey(channel, key, midioutput))
{
return true;
}
break;
case mus_presskey:
if (mem_fread(&key, 1, 1, musinput) != 1)
{
return true;
}
if (key & 0x80)
{
if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1)
{
return true;
}
channelvelocities[channel] &= 0x7F;
}
if (WritePressKey(channel, key,
channelvelocities[channel], midioutput))
{
return true;
}
break;
case mus_pitchwheel:
if (mem_fread(&key, 1, 1, musinput) != 1)
{
break;
}
if (WritePitchWheel(channel, (short)(key * 64), midioutput))
{
return true;
}
break;
case mus_systemevent:
if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
{
return true;
}
if (controllernumber < 10 || controllernumber > 14)
{
return true;
}
if (WriteChangeController_Valueless(channel,
controller_map[controllernumber],
midioutput))
{
return true;
}
break;
case mus_changecontroller:
if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
{
return true;
}
if (mem_fread(&controllervalue, 1, 1, musinput) != 1)
{
return true;
}
if (controllernumber == 0)
{
if (WriteChangePatch(channel, controllervalue,
midioutput))
{
return true;
}
}
else
{
if (controllernumber < 1 || controllernumber > 9)
{
return true;
}
if (WriteChangeController_Valued(channel,
controller_map[controllernumber],
controllervalue,
midioutput))
{
return true;
}
}
break;
case mus_scoreend:
hitscoreend = 1;
break;
default:
return true;
break;
}
if (eventdescriptor & 0x80)
{
break;
}
}
// Now we need to read the time code:
if (!hitscoreend)
{
timedelay = 0;
for (;;)
{
if (mem_fread(&working, 1, 1, musinput) != 1)
{
return true;
}
timedelay = timedelay * 128 + (working & 0x7F);
if ((working & 0x80) == 0)
{
break;
}
}
queuedtime += timedelay;
}
}
// End of track
if (WriteEndTrack(midioutput))
{
return true;
}
// Write the track size into the stream
if (mem_fseek(midioutput, 18, MEM_SEEK_SET))
{
return true;
}
tracksizebuffer[0] = (tracksize >> 24) & 0xff;
tracksizebuffer[1] = (tracksize >> 16) & 0xff;
tracksizebuffer[2] = (tracksize >> 8) & 0xff;
tracksizebuffer[3] = tracksize & 0xff;
if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4)
{
return true;
}
return false;
}
#ifdef STANDALONE
#include "m_misc.h"
#include "z_zone.h"
int main(int argc, char *argv[])
{
MEMFILE *src, *dst;
byte *infile;
long infile_len;
void *outfile;
size_t outfile_len;
if (argc != 3)
{
printf("Usage: %s <musfile> <midfile>\n", argv[0]);
exit(-1);
}
Z_Init();
infile_len = M_ReadFile(argv[1], &infile);
src = mem_fopen_read(infile, infile_len);
dst = mem_fopen_write();
if (mus2mid(src, dst))
{
fprintf(stderr, "mus2mid() failed\n");
exit(-1);
}
// Write result to output file:
mem_get_buf(dst, &outfile, &outfile_len);
M_WriteFile(argv[2], outfile, outfile_len);
return 0;
}
#endif

29
src/mus2mid.h Normal file
View File

@@ -0,0 +1,29 @@
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
// Copyright(C) 2006 Ben Ryves 2006
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// mus2mid.h - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com
// Use to convert a MUS file into a single track, type 0 MIDI file.
#ifndef MUS2MID_H
#define MUS2MID_H
#include "doomtype.h"
#include "memio.h"
boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput);
#endif /* #ifndef MUS2MID_H */

1084
src/net_client.c Normal file

File diff suppressed because it is too large Load Diff

50
src/net_client.h Normal file
View File

@@ -0,0 +1,50 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Network client code
//
#ifndef NET_CLIENT_H
#define NET_CLIENT_H
#include "doomtype.h"
#include "d_ticcmd.h"
#include "sha1.h"
#include "net_defs.h"
boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);
void NET_CL_Disconnect(void);
void NET_CL_Run(void);
void NET_CL_Init(void);
void NET_CL_LaunchGame(void);
void NET_CL_StartGame(net_gamesettings_t *settings);
void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);
boolean NET_CL_GetSettings(net_gamesettings_t *_settings);
void NET_Init(void);
void NET_BindVariables(void);
extern boolean net_client_connected;
extern boolean net_client_received_wait_data;
extern net_waitdata_t net_client_wait_data;
extern boolean net_waiting_for_launch;
extern char *net_player_name;
extern sha1_digest_t net_server_wad_sha1sum;
extern unsigned int net_server_is_freedoom;
extern sha1_digest_t net_local_wad_sha1sum;
extern unsigned int net_local_is_freedoom;
extern boolean drone;
#endif /* #ifndef NET_CLIENT_H */

534
src/net_common.c Normal file
View File

@@ -0,0 +1,534 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Common code shared between the client and server
//
#include <stdio.h>
#include <stdlib.h>
#include "doomtype.h"
#include "d_mode.h"
#include "i_timer.h"
#include "net_common.h"
#include "net_io.h"
#include "net_packet.h"
#include "net_structrw.h"
// connections time out after 30 seconds
#define CONNECTION_TIMEOUT_LEN 30
// maximum time between sending packets
#define KEEPALIVE_PERIOD 1
// reliable packet that is guaranteed to reach its destination
struct net_reliable_packet_s
{
net_packet_t *packet;
int last_send_time;
int seq;
net_reliable_packet_t *next;
};
static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr)
{
conn->last_send_time = -1;
conn->num_retries = 0;
conn->addr = addr;
conn->reliable_packets = NULL;
conn->reliable_send_seq = 0;
conn->reliable_recv_seq = 0;
}
// Initialize as a client connection
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr)
{
NET_Conn_Init(conn, addr);
conn->state = NET_CONN_STATE_CONNECTING;
}
// Initialize as a server connection
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr)
{
NET_Conn_Init(conn, addr);
conn->state = NET_CONN_STATE_WAITING_ACK;
}
// Send a packet to a connection
// All packets should be sent through this interface, as it maintains the
// keepalive_send_time counter.
void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet)
{
conn->keepalive_send_time = I_GetTimeMS();
NET_SendPacket(conn->addr, packet);
}
// parse an ACK packet from a client
static void NET_Conn_ParseACK(net_connection_t *conn, net_packet_t *packet)
{
net_packet_t *reply;
if (conn->state == NET_CONN_STATE_CONNECTING)
{
// We are a client
// received a response from the server to our SYN
conn->state = NET_CONN_STATE_CONNECTED;
// We must send an ACK reply to the server's ACK
reply = NET_NewPacket(10);
NET_WriteInt16(reply, NET_PACKET_TYPE_ACK);
NET_Conn_SendPacket(conn, reply);
NET_FreePacket(reply);
}
if (conn->state == NET_CONN_STATE_WAITING_ACK)
{
// We are a server
// Client is connected
conn->state = NET_CONN_STATE_CONNECTED;
}
}
static void NET_Conn_ParseDisconnect(net_connection_t *conn, net_packet_t *packet)
{
net_packet_t *reply;
// Other end wants to disconnect
// Send a DISCONNECT_ACK reply.
reply = NET_NewPacket(10);
NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK);
NET_Conn_SendPacket(conn, reply);
NET_FreePacket(reply);
conn->last_send_time = I_GetTimeMS();
conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP;
conn->disconnect_reason = NET_DISCONNECT_REMOTE;
}
// Parse a DISCONNECT_ACK packet
static void NET_Conn_ParseDisconnectACK(net_connection_t *conn,
net_packet_t *packet)
{
if (conn->state == NET_CONN_STATE_DISCONNECTING)
{
// We have received an acknowledgement to our disconnect
// request. We have been disconnected successfully.
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_LOCAL;
conn->last_send_time = -1;
}
}
static void NET_Conn_ParseReject(net_connection_t *conn, net_packet_t *packet)
{
char *msg;
msg = NET_ReadString(packet);
if (msg == NULL)
{
return;
}
if (conn->state == NET_CONN_STATE_CONNECTING)
{
// rejected by server
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_REMOTE;
printf("Rejected by server: ");
NET_SafePuts(msg);
}
}
static void NET_Conn_ParseReliableACK(net_connection_t *conn, net_packet_t *packet)
{
unsigned int seq;
if (!NET_ReadInt8(packet, &seq))
{
return;
}
if (conn->reliable_packets == NULL)
{
return;
}
// Is this an acknowledgement for the first packet in the list?
if (seq == (unsigned int)((conn->reliable_packets->seq + 1) & 0xff))
{
net_reliable_packet_t *rp;
// Discard it, then.
// Unlink from the list.
rp = conn->reliable_packets;
conn->reliable_packets = rp->next;
NET_FreePacket(rp->packet);
free(rp);
}
}
// Process the header of a reliable packet
//
// Returns true if the packet should be discarded (incorrect sequence)
static boolean NET_Conn_ReliablePacket(net_connection_t *conn,
net_packet_t *packet)
{
unsigned int seq;
net_packet_t *reply;
boolean result;
// Read the sequence number
if (!NET_ReadInt8(packet, &seq))
{
return true;
}
if (seq != (unsigned int)(conn->reliable_recv_seq & 0xff))
{
// This is not the next expected packet in the sequence!
//
// Discard the packet. If we were smart, we would use a proper
// sliding window protocol to do this, but I'm lazy.
result = true;
}
else
{
// Now we can receive the next packet in the sequence.
conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff;
result = false;
}
// Send an acknowledgement
// Note: this is braindead. It would be much more sensible to
// include this in the next packet, rather than the overhead of
// sending a complete packet just for one byte of information.
reply = NET_NewPacket(10);
NET_WriteInt16(reply, NET_PACKET_TYPE_RELIABLE_ACK);
NET_WriteInt8(reply, conn->reliable_recv_seq & 0xff);
NET_Conn_SendPacket(conn, reply);
NET_FreePacket(reply);
return result;
}
// Process a packet received by the server
//
// Returns true if eaten by common code
boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
unsigned int *packet_type)
{
conn->keepalive_recv_time = I_GetTimeMS();
// Is this a reliable packet?
if (*packet_type & NET_RELIABLE_PACKET)
{
if (NET_Conn_ReliablePacket(conn, packet))
{
// Invalid packet: eat it.
return true;
}
// Remove the reliable bit
*packet_type &= ~NET_RELIABLE_PACKET;
}
switch (*packet_type)
{
case NET_PACKET_TYPE_ACK:
NET_Conn_ParseACK(conn, packet);
break;
case NET_PACKET_TYPE_DISCONNECT:
NET_Conn_ParseDisconnect(conn, packet);
break;
case NET_PACKET_TYPE_DISCONNECT_ACK:
NET_Conn_ParseDisconnectACK(conn, packet);
break;
case NET_PACKET_TYPE_KEEPALIVE:
// No special action needed.
break;
case NET_PACKET_TYPE_REJECTED:
NET_Conn_ParseReject(conn, packet);
break;
case NET_PACKET_TYPE_RELIABLE_ACK:
NET_Conn_ParseReliableACK(conn, packet);
break;
default:
// Not a common packet
return false;
}
// We found a packet that we found interesting, and ate it.
return true;
}
void NET_Conn_Disconnect(net_connection_t *conn)
{
if (conn->state != NET_CONN_STATE_DISCONNECTED
&& conn->state != NET_CONN_STATE_DISCONNECTING
&& conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP)
{
conn->state = NET_CONN_STATE_DISCONNECTING;
conn->disconnect_reason = NET_DISCONNECT_LOCAL;
conn->last_send_time = -1;
conn->num_retries = 0;
}
}
void NET_Conn_Run(net_connection_t *conn)
{
net_packet_t *packet;
unsigned int nowtime;
nowtime = I_GetTimeMS();
if (conn->state == NET_CONN_STATE_CONNECTED)
{
// Check the keepalive counters
if (nowtime - conn->keepalive_recv_time > CONNECTION_TIMEOUT_LEN * 1000)
{
// Haven't received any packets from the other end in a long
// time. Assume disconnected.
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_TIMEOUT;
}
if (nowtime - conn->keepalive_send_time > KEEPALIVE_PERIOD * 1000)
{
// We have not sent anything in a long time.
// Send a keepalive.
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_KEEPALIVE);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
}
// Check the reliable packet list. Has the first packet in the
// list timed out?
//
// NB. This is braindead, we have a fixed time of one second.
if (conn->reliable_packets != NULL
&& (conn->reliable_packets->last_send_time < 0
|| nowtime - conn->reliable_packets->last_send_time > 1000))
{
// Packet timed out, time to resend
NET_Conn_SendPacket(conn, conn->reliable_packets->packet);
conn->reliable_packets->last_send_time = nowtime;
}
}
else if (conn->state == NET_CONN_STATE_WAITING_ACK)
{
if (conn->last_send_time < 0
|| nowtime - conn->last_send_time > 1000)
{
// it has been a second since the last ACK was sent, and
// still no reply.
if (conn->num_retries < MAX_RETRIES)
{
// send another ACK
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_ACK);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
conn->last_send_time = nowtime;
++conn->num_retries;
}
else
{
// no more retries allowed.
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_TIMEOUT;
}
}
}
else if (conn->state == NET_CONN_STATE_DISCONNECTING)
{
// Waiting for a reply to our DISCONNECT request.
if (conn->last_send_time < 0
|| nowtime - conn->last_send_time > 1000)
{
// it has been a second since the last disconnect packet
// was sent, and still no reply.
if (conn->num_retries < MAX_RETRIES)
{
// send another disconnect
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT);
NET_Conn_SendPacket(conn, packet);
NET_FreePacket(packet);
conn->last_send_time = nowtime;
++conn->num_retries;
}
else
{
// No more retries allowed.
// Force disconnect.
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_LOCAL;
}
}
}
else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP)
{
// We are disconnected, waiting in case we need to send
// a DISCONNECT_ACK to the server again.
if (nowtime - conn->last_send_time > 5000)
{
// Idle for 5 seconds, switch state
conn->state = NET_CONN_STATE_DISCONNECTED;
conn->disconnect_reason = NET_DISCONNECT_REMOTE;
}
}
}
net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type)
{
net_packet_t *packet;
net_reliable_packet_t *rp;
net_reliable_packet_t **listend;
// Generate a packet with the right header
packet = NET_NewPacket(100);
NET_WriteInt16(packet, packet_type | NET_RELIABLE_PACKET);
// write the low byte of the send sequence number
NET_WriteInt8(packet, conn->reliable_send_seq & 0xff);
// Add to the list of reliable packets
rp = malloc(sizeof(net_reliable_packet_t));
rp->packet = packet;
rp->next = NULL;
rp->seq = conn->reliable_send_seq;
rp->last_send_time = -1;
for (listend = &conn->reliable_packets;
*listend != NULL;
listend = &((*listend)->next));
*listend = rp;
// Count along the sequence
conn->reliable_send_seq = (conn->reliable_send_seq + 1) & 0xff;
// Finished
return packet;
}
// Used to expand the least significant byte of a tic number into
// the full tic number, from the current tic number
unsigned int NET_ExpandTicNum(unsigned int relative, unsigned int b)
{
unsigned int l, h;
unsigned int result;
h = relative & ~0xff;
l = relative & 0xff;
result = h | b;
if (l < 0x40 && b > 0xb0)
result -= 0x100;
if (l > 0xb0 && b < 0x40)
result += 0x100;
return result;
}
// Check that game settings are valid
boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission,
net_gamesettings_t *settings)
{
if (settings->ticdup <= 0)
return false;
if (settings->extratics < 0)
return false;
if (settings->deathmatch < 0 || settings->deathmatch > 2)
return false;
if (settings->skill < sk_noitems || settings->skill > sk_nightmare)
return false;
if (!D_ValidGameVersion(mission, settings->gameversion))
return false;
if (!D_ValidEpisodeMap(mission, mode, settings->episode, settings->map))
return false;
return true;
}

111
src/net_common.h Normal file
View File

@@ -0,0 +1,111 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Common code shared between the client and server
//
#ifndef NET_COMMON_H
#define NET_COMMON_H
#include "d_mode.h"
#include "doomtype.h"
#include "net_defs.h"
typedef enum
{
// sending syn packets, waiting for an ACK reply
// (client side)
NET_CONN_STATE_CONNECTING,
// received a syn, sent an ack, waiting for an ack reply
// (server side)
NET_CONN_STATE_WAITING_ACK,
// successfully connected
NET_CONN_STATE_CONNECTED,
// sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply
NET_CONN_STATE_DISCONNECTING,
// client successfully disconnected
NET_CONN_STATE_DISCONNECTED,
// We are disconnected, but in a sleep state, waiting for several
// seconds. This is in case the DISCONNECT_ACK we sent failed
// to arrive, and we need to send another one. We keep this as
// a valid connection for a few seconds until we are sure that
// the other end has successfully disconnected as well.
NET_CONN_STATE_DISCONNECTED_SLEEP,
} net_connstate_t;
// Reason a connection was terminated
typedef enum
{
// As the result of a local disconnect request
NET_DISCONNECT_LOCAL,
// As the result of a remote disconnect request
NET_DISCONNECT_REMOTE,
// Timeout (no data received in a long time)
NET_DISCONNECT_TIMEOUT,
} net_disconnect_reason_t;
#define MAX_RETRIES 5
typedef struct net_reliable_packet_s net_reliable_packet_t;
typedef struct
{
net_connstate_t state;
net_disconnect_reason_t disconnect_reason;
net_addr_t *addr;
int last_send_time;
int num_retries;
int keepalive_send_time;
int keepalive_recv_time;
net_reliable_packet_t *reliable_packets;
int reliable_send_seq;
int reliable_recv_seq;
} net_connection_t;
void NET_Conn_SendPacket(net_connection_t *conn, net_packet_t *packet);
void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr);
void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr);
boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
unsigned int *packet_type);
void NET_Conn_Disconnect(net_connection_t *conn);
void NET_Conn_Run(net_connection_t *conn);
net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type);
// Other miscellaneous common functions
unsigned int NET_ExpandTicNum(unsigned int relative, unsigned int b);
boolean NET_ValidGameSettings(GameMode_t mode, GameMission_t mission,
net_gamesettings_t *settings);
#endif /* #ifndef NET_COMMON_H */

73
src/net_dedicated.c Normal file
View File

@@ -0,0 +1,73 @@
//
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// Dedicated server code.
//
#include <stdio.h>
#include "doomtype.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_argv.h"
#include "net_sdl.h"
#include "net_server.h"
//
// People can become confused about how dedicated servers work. Game
// options are specified to the controlling player who is the first to
// join a game. Bomb out with an error message if game options are
// specified to a dedicated server.
//
static char *not_dedicated_options[] =
{
"-iwad", "-gameversion", "-nomonsters", "-respawn",
"-fast", "-altdeath", "-deathmatch", "-turbo", "-merge", "-af", "-as",
"-aa", "-file", "-wart", "-skill", "-episode", "-timer", "-avg", "-warp",
"-loadgame", "-longtics", "-extratics", "-dup", NULL,
};
static void CheckForClientOptions(void)
{
int i;
for (i=0; not_dedicated_options[i] != NULL; ++i)
{
if (M_CheckParm(not_dedicated_options[i]) > 0)
{
I_Error("The command line parameter '%s' was specified to a "
"dedicated server.\nGame parameters should be specified "
"to the first player to join a server, \nnot to the "
"server itself. ",
not_dedicated_options[i]);
}
}
}
void NET_DedicatedServer(void)
{
CheckForClientOptions();
NET_SV_Init();
NET_SV_AddModule(&net_sdl_module);
NET_SV_RegisterWithMaster();
while (true)
{
NET_SV_Run();
I_Sleep(10);
}
}

Some files were not shown because too many files have changed in this diff Show More