Merge remote-tracking branch 'origin' into openal

Conflicts:
	src/sound/fmodsound.cpp
This commit is contained in:
Chris Robinson 2013-06-29 19:02:04 -07:00
commit 18597a93a7
507 changed files with 30037 additions and 11065 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
/Debug
*.ncb
*.suo
*.pdb
*.ilk
*.aps
/Release
/wadsrc_wad
*.user
/debug
/release
*/debug
*/release
/release_gcc
/dumb/vc6/dumb_static/release
/dumb/vc6/dumb_static/debug
/DOOMSTATS.TXT
/src/gitinfo.h
/src/sc_man_scanner.h
/src/xlat/xlat_parser.c
/src/xlat/xlat_parser.h
/src/xlat/xlat_parser.out
/tools/*/debug
/tools/*/release
/tools/*/*.exe
/tools/lemon/build
/tools/re2c/build
/wadsrc/*.pk3

View File

@ -1,4 +1,5 @@
cmake_minimum_required( VERSION 2.4 ) cmake_minimum_required( VERSION 2.4 )
project(ZDoom)
IF( NOT CMAKE_BUILD_TYPE ) IF( NOT CMAKE_BUILD_TYPE )
SET( CMAKE_BUILD_TYPE Debug CACHE STRING SET( CMAKE_BUILD_TYPE Debug CACHE STRING
@ -9,9 +10,11 @@ ENDIF( NOT CMAKE_BUILD_TYPE )
set( ZDOOM_OUTPUT_DIR ${CMAKE_BINARY_DIR} CACHE PATH "Directory where zdoom.pk3 and the executable will be created." ) set( ZDOOM_OUTPUT_DIR ${CMAKE_BINARY_DIR} CACHE PATH "Directory where zdoom.pk3 and the executable will be created." )
set( ZDOOM_EXE_NAME "zdoom" CACHE FILEPATH "Name of the executable to create." ) set( ZDOOM_EXE_NAME "zdoom" CACHE FILEPATH "Name of the executable to create." )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set( PROFILE 0 CACHE BOOL "Enable profiling with gprof for Debug and RelWithDebInfo build types." ) set( PROFILE 0 CACHE BOOL "Enable profiling with gprof for Debug and RelWithDebInfo build types." )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
option( NO_FMOD "Disable FMODEx sound support" ) option( NO_FMOD "Disable FMODEx sound support" )
option( NO_OPENAL "Disable OpenAL sound support" ) option( NO_OPENAL "Disable OpenAL sound support" )

View File

@ -1,8 +1,8 @@
cmake_minimum_required( VERSION 2.4 ) cmake_minimum_required( VERSION 2.4 )
if( CMAKE_COMPILER_IS_GNUC ) if( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" )
endif( CMAKE_COMPILER_IS_GNUC ) endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
add_definitions( -DBZ_NO_STDIO ) add_definitions( -DBZ_NO_STDIO )
add_library( bz2 add_library( bz2

View File

@ -11,9 +11,9 @@ endif( NOT CMAKE_BUILD_TYPE MATCHES "Release" )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DDEBUGMODE=1" ) set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -DDEBUGMODE=1" )
if( CMAKE_COMPILER_IS_GNUC ) if( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-pointer-sign -Wno-uninitialized" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-pointer-sign -Wno-uninitialized" )
endif( CMAKE_COMPILER_IS_GNUC ) endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
CHECK_FUNCTION_EXISTS( itoa ITOA_EXISTS ) CHECK_FUNCTION_EXISTS( itoa ITOA_EXISTS )
if( NOT ITOA_EXISTS ) if( NOT ITOA_EXISTS )
@ -36,6 +36,7 @@ add_library( dumb
src/core/rendsig.c src/core/rendsig.c
src/core/unload.c src/core/unload.c
src/helpers/barray.c src/helpers/barray.c
src/helpers/blip_buf.c
src/helpers/clickrem.c src/helpers/clickrem.c
src/helpers/memfile.c src/helpers/memfile.c
src/helpers/resample.c src/helpers/resample.c
@ -60,6 +61,8 @@ add_library( dumb
src/it/loadmod2.c src/it/loadmod2.c
src/it/loadmtm.c src/it/loadmtm.c
src/it/loadmtm2.c src/it/loadmtm2.c
src/it/loadokt.c
src/it/loadokt2.c
src/it/loadoldpsm.c src/it/loadoldpsm.c
src/it/loadoldpsm2.c src/it/loadoldpsm2.c
src/it/loadpsm.c src/it/loadpsm.c
@ -83,6 +86,8 @@ add_library( dumb
src/it/readmod.c src/it/readmod.c
src/it/readmod2.c src/it/readmod2.c
src/it/readmtm.c src/it/readmtm.c
src/it/readokt.c
src/it/readokt2.c
src/it/readoldpsm.c src/it/readoldpsm.c
src/it/readpsm.c src/it/readpsm.c
src/it/readptm.c src/it/readptm.c
@ -96,6 +101,6 @@ add_library( dumb
src/it/xmeffect.c ) src/it/xmeffect.c )
target_link_libraries( dumb ) target_link_libraries( dumb )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse ) set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )

View File

@ -233,7 +233,6 @@ int32 DUMBEXPORT duh_get_length(DUH *duh);
const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key); const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key);
/* Signal Rendering Functions */ /* Signal Rendering Functions */
typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; typedef struct DUH_SIGRENDERER DUH_SIGRENDERER;
@ -396,11 +395,13 @@ void DUMBEXPORT dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sig
int DUMBCALLBACK dumb_it_callback_terminate(void *data); int DUMBCALLBACK dumb_it_callback_terminate(void *data);
int DUMBCALLBACK dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte); int DUMBCALLBACK dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte);
/* dumb_*_mod*: restrict |= 1-Don't read 15 sample files / 2-Use old pattern counting method */
DUH *DUMBEXPORT dumb_load_it(const char *filename); DUH *DUMBEXPORT dumb_load_it(const char *filename);
DUH *DUMBEXPORT dumb_load_xm(const char *filename); DUH *DUMBEXPORT dumb_load_xm(const char *filename);
DUH *DUMBEXPORT dumb_load_s3m(const char *filename); DUH *DUMBEXPORT dumb_load_s3m(const char *filename);
DUH *DUMBEXPORT dumb_load_stm(const char *filename); DUH *DUMBEXPORT dumb_load_stm(const char *filename);
DUH *DUMBEXPORT dumb_load_mod(const char *filename, int restrict); DUH *DUMBEXPORT dumb_load_mod(const char *filename, int rstrict);
DUH *DUMBEXPORT dumb_load_ptm(const char *filename); DUH *DUMBEXPORT dumb_load_ptm(const char *filename);
DUH *DUMBEXPORT dumb_load_669(const char *filename); DUH *DUMBEXPORT dumb_load_669(const char *filename);
DUH *DUMBEXPORT dumb_load_psm(const char *filename, int subsong); DUH *DUMBEXPORT dumb_load_psm(const char *filename, int subsong);
@ -408,12 +409,13 @@ DUH *DUMBEXPORT dumb_load_old_psm(const char * filename);
DUH *DUMBEXPORT dumb_load_mtm(const char *filename); DUH *DUMBEXPORT dumb_load_mtm(const char *filename);
DUH *DUMBEXPORT dumb_load_riff(const char *filename); DUH *DUMBEXPORT dumb_load_riff(const char *filename);
DUH *DUMBEXPORT dumb_load_asy(const char *filename); DUH *DUMBEXPORT dumb_load_asy(const char *filename);
DUH *DUMBEXPORT dumb_load_okt(const char *filename);
DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_it(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_xm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_s3m(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_s3m(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_stm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_stm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int restrict); DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int rstrict);
DUH *DUMBEXPORT dumb_read_ptm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_ptm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_669(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_669(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_psm(DUMBFILE *f, int subsong); DUH *DUMBEXPORT dumb_read_psm(DUMBFILE *f, int subsong);
@ -421,12 +423,13 @@ DUH *DUMBEXPORT dumb_read_old_psm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mtm(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_mtm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_riff(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_riff(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_asy(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_asy(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f);
DUH *DUMBEXPORT dumb_load_it_quick(const char *filename); DUH *DUMBEXPORT dumb_load_it_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename); DUH *DUMBEXPORT dumb_load_xm_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_s3m_quick(const char *filename); DUH *DUMBEXPORT dumb_load_s3m_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_stm_quick(const char *filename); DUH *DUMBEXPORT dumb_load_stm_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict); DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int rstrict);
DUH *DUMBEXPORT dumb_load_ptm_quick(const char *filename); DUH *DUMBEXPORT dumb_load_ptm_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_669_quick(const char *filename); DUH *DUMBEXPORT dumb_load_669_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_psm_quick(const char *filename, int subsong); DUH *DUMBEXPORT dumb_load_psm_quick(const char *filename, int subsong);
@ -434,12 +437,13 @@ DUH *DUMBEXPORT dumb_load_old_psm_quick(const char * filename);
DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename); DUH *DUMBEXPORT dumb_load_mtm_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_riff_quick(const char *filename); DUH *DUMBEXPORT dumb_load_riff_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename); DUH *DUMBEXPORT dumb_load_asy_quick(const char *filename);
DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename);
DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_xm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_s3m_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_s3m_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int restrict); DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int rstrict);
DUH *DUMBEXPORT dumb_read_ptm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_ptm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_669_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_669_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_psm_quick(DUMBFILE *f, int subsong); DUH *DUMBEXPORT dumb_read_psm_quick(DUMBFILE *f, int subsong);
@ -447,6 +451,7 @@ DUH *DUMBEXPORT dumb_read_old_psm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_mtm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_riff_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_riff_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f); DUH *DUMBEXPORT dumb_read_asy_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f);
int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder); int32 DUMBEXPORT dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder);
void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh); void DUMBEXPORT dumb_it_do_initial_runthrough(DUH *duh);
@ -606,6 +611,8 @@ sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type);
DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, int32 pos); DUH_SIGRENDERER *DUMBEXPORT duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, int32 pos);
sigrenderer_t *DUMBEXPORT duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type); sigrenderer_t *DUMBEXPORT duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, int32 type);
int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata);
/* Standard Signal Types */ /* Standard Signal Types */
@ -662,6 +669,8 @@ typedef struct DUMB_VOLUME_RAMP_INFO DUMB_VOLUME_RAMP_INFO;
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data); typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
#include "internal/blip_buf.h"
struct DUMB_RESAMPLER struct DUMB_RESAMPLER
{ {
void *src; void *src;
@ -679,6 +688,9 @@ struct DUMB_RESAMPLER
signed char x8[3*2]; signed char x8[3*2];
} x; } x;
int overshot; int overshot;
int last_clock;
int last_amp[2];
blip_t* blip_buffer[2];
}; };
struct DUMB_VOLUME_RAMP_INFO struct DUMB_VOLUME_RAMP_INFO

View File

@ -0,0 +1,77 @@
/** \file
Sample buffer that resamples from input clock rate to output sample rate */
/* blip_buf 1.1.0 */
#ifndef BLIP_BUF_H
#define BLIP_BUF_H
#ifdef __cplusplus
extern "C" {
#endif
/** First parameter of most functions is blip_t*, or const blip_t* if nothing
is changed. */
typedef struct blip_t blip_t;
/** Creates new buffer that can hold at most sample_count samples. Sets rates
so that there are blip_max_ratio clocks per sample. Returns pointer to new
buffer, or NULL if insufficient memory. */
blip_t* blip_new( int sample_count );
blip_t* blip_dup( blip_t* );
/** Sets approximate input clock rate and output sample rate. For every
clock_rate input clocks, approximately sample_rate samples are generated. */
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
clock_rate must not be greater than sample_rate*blip_max_ratio. */
blip_max_ratio = 1 << 20 };
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
void blip_clear( blip_t* );
/** Adds positive/negative delta into buffer at specified clock time. */
void blip_add_delta( blip_t*, unsigned int clock_time, int delta );
/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */
void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta );
/** Length of time frame, in clocks, needed to make sample_count additional
samples available. */
int blip_clocks_needed( const blip_t*, int sample_count );
enum { /** Maximum number of samples that can be generated from one time frame. */
blip_max_frame = 4000 };
/** Makes input clocks before clock_duration available for reading as output
samples. Also begins new time frame at clock_duration, so that clock time 0 in
the new time frame specifies the same clock as clock_duration in the old time
frame specified. Deltas can have been added slightly past clock_duration (up to
however many clocks there are in two output samples). */
void blip_end_frame( blip_t*, unsigned int clock_duration );
/** Number of buffered samples available for reading. */
int blip_samples_avail( const blip_t* );
/** Reads and removes at most 'count' samples and writes them to 'out'. If
'stereo' is true, writes output to every other element of 'out', allowing easy
interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed
samples. Returns number of samples actually read. */
int blip_read_samples( blip_t*, int out [], int count );
/** Reads the current integrator and returns it */
int blip_peek_sample( blip_t* );
/** Frees buffer. No effect if NULL is passed. */
void blip_delete( blip_t* );
/* Deprecated */
typedef blip_t blip_buffer_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -54,7 +54,7 @@ sigdata->flags & IT_COMPATIBLE_GXX
* handle ambiguities in the format specification. The correct code in each * handle ambiguities in the format specification. The correct code in each
* case will be determined most likely by experimentation. * case will be determined most likely by experimentation.
*/ */
#define STEREO_SAMPLES_COUNT_AS_TWO //#define STEREO_SAMPLES_COUNT_AS_TWO
#define INVALID_ORDERS_END_SONG #define INVALID_ORDERS_END_SONG
#define INVALID_NOTES_CAUSE_NOTE_CUT #define INVALID_NOTES_CAUSE_NOTE_CUT
#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP #define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP
@ -300,7 +300,18 @@ struct IT_SAMPLE
#define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36 #define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36
#define IT_PTM_NOTE_SLIDE_UP_RETRIG 37 #define IT_PTM_NOTE_SLIDE_UP_RETRIG 37
#define IT_N_EFFECTS 38 /* More effects needed for OKT compatibility */
#define IT_OKT_NOTE_SLIDE_DOWN 38
#define IT_OKT_NOTE_SLIDE_DOWN_ROW 39
#define IT_OKT_NOTE_SLIDE_UP 40
#define IT_OKT_NOTE_SLIDE_UP_ROW 41
#define IT_OKT_ARPEGGIO_3 42
#define IT_OKT_ARPEGGIO_4 43
#define IT_OKT_ARPEGGIO_5 44
#define IT_OKT_VOLUME_SLIDE_DOWN 45
#define IT_OKT_VOLUME_SLIDE_UP 46
#define IT_N_EFFECTS 47
/* These represent the top nibble of the command value. */ /* These represent the top nibble of the command value. */
#define IT_S_SET_FILTER 0 /* Greyed out in IT... */ #define IT_S_SET_FILTER 0 /* Greyed out in IT... */
@ -399,6 +410,10 @@ struct IT_PATTERN
#define IT_WAS_A_669 1024 #define IT_WAS_A_669 1024
#define IT_WAS_AN_OKT 2048
#define IT_WAS_AN_STM 4096
#define IT_ORDER_END 255 #define IT_ORDER_END 255
#define IT_ORDER_SKIP 254 #define IT_ORDER_SKIP 254
@ -452,6 +467,7 @@ struct IT_PLAYING_ENVELOPE
#define IT_PLAYING_SUSTAINOFF 2 #define IT_PLAYING_SUSTAINOFF 2
#define IT_PLAYING_FADING 4 #define IT_PLAYING_FADING 4
#define IT_PLAYING_DEAD 8 #define IT_PLAYING_DEAD 8
#define IT_PLAYING_REVERSE 16
struct IT_PLAYING struct IT_PLAYING
{ {
@ -586,7 +602,8 @@ struct IT_CHANNEL
unsigned char new_note_action; unsigned char new_note_action;
int arpeggio; unsigned int arpeggio;
int arpeggio_shift;
unsigned char retrig; unsigned char retrig;
unsigned char xm_retrig; unsigned char xm_retrig;
int retrig_tick; int retrig_tick;
@ -601,7 +618,7 @@ struct IT_CHANNEL
int portamento; int portamento;
int toneporta; int toneporta;
int toneslide; int toneslide;
unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide; unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide, okt_toneslide;
unsigned char destnote; unsigned char destnote;
unsigned char toneslide_retrig; unsigned char toneslide_retrig;
@ -643,6 +660,10 @@ struct IT_CHANNEL
unsigned char xm_lastX1; unsigned char xm_lastX1;
unsigned char xm_lastX2; unsigned char xm_lastX2;
unsigned char inv_loop_delay;
unsigned char inv_loop_speed;
int inv_loop_offset;
IT_PLAYING *playing; IT_PLAYING *playing;
#ifdef BIT_ARRAY_BULLSHIT #ifdef BIT_ARRAY_BULLSHIT
@ -802,6 +823,7 @@ extern DUH_SIGTYPE_DESC _dumb_sigtype_it;
#define XM_E_NOTE_CUT 0xC #define XM_E_NOTE_CUT 0xC
#define XM_E_NOTE_DELAY 0xD #define XM_E_NOTE_DELAY 0xD
#define XM_E_PATTERN_DELAY 0xE #define XM_E_PATTERN_DELAY 0xE
#define XM_E_SET_MIDI_MACRO 0xF
#define XM_X_EXTRAFINE_PORTA_UP 1 #define XM_X_EXTRAFINE_PORTA_UP 1
#define XM_X_EXTRAFINE_PORTA_DOWN 2 #define XM_X_EXTRAFINE_PORTA_DOWN 2
@ -880,4 +902,6 @@ void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry);
int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f); int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f);
void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample);
#endif /* INTERNAL_IT_H */ #endif /* INTERNAL_IT_H */

View File

@ -130,3 +130,22 @@ DUH *make_duh(
return duh; return duh;
} }
int DUMBEXPORT duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata)
{
DUH_SIGNAL **signal;
if ( !duh || !desc || !sigdata ) return -1;
signal = ( DUH_SIGNAL ** ) realloc( duh->signal, ( duh->n_signals + 1 ) * sizeof( *duh->signal ) );
if ( !signal ) return -1;
duh->signal = signal;
memmove( signal + 1, signal, duh->n_signals * sizeof( *signal ) );
duh->n_signals++;
signal[ 0 ] = make_signal( desc, sigdata );
if ( !signal[ 0 ] ) return -1;
return 0;
}

View File

@ -29,16 +29,30 @@
*/ */
sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type) sigdata_t *DUMBEXPORT duh_get_raw_sigdata(DUH *duh, int sig, int32 type)
{ {
int i;
DUH_SIGNAL *signal; DUH_SIGNAL *signal;
if (!duh) return NULL; if (!duh) return NULL;
if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL; if ( sig >= 0 )
{
if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
signal = duh->signal[sig]; signal = duh->signal[sig];
if (signal && signal->desc->type == type) if (signal && signal->desc->type == type)
return signal->sigdata; return signal->sigdata;
}
else
{
for ( i = 0; i < duh->n_signals; i++ )
{
signal = duh->signal[i];
if (signal && signal->desc->type == type)
return signal->sigdata;
}
}
return NULL; return NULL;
} }

354
dumb/src/helpers/blip_buf.c Normal file
View File

@ -0,0 +1,354 @@
/* blip_buf 1.1.0. http://www.slack.net/~ant/ */
#include "internal/blip_buf.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
/* Library Copyright (C) 2003-2009 Shay Green. This library is free software;
you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#if defined (BLARGG_TEST) && BLARGG_TEST
#include "blargg_test.h"
#endif
/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000.
Avoids constants that don't fit in 32 bits. */
#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF
typedef unsigned long fixed_t;
enum { pre_shift = 32 };
#elif defined(ULLONG_MAX)
typedef unsigned long long fixed_t;
enum { pre_shift = 32 };
#else
typedef unsigned fixed_t;
enum { pre_shift = 0 };
#endif
enum { time_bits = pre_shift + 20 };
static fixed_t const time_unit = (fixed_t) 1 << time_bits;
enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */
enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */
enum { half_width = 8 };
enum { buf_extra = half_width*2 + end_frame_extra };
enum { phase_bits = 5 };
enum { phase_count = 1 << phase_bits };
enum { delta_bits = 15 };
enum { delta_unit = 1 << delta_bits };
enum { frac_bits = time_bits - pre_shift };
/* We could eliminate avail and encode whole samples in offset, but that would
limit the total buffered samples to blip_max_frame. That could only be
increased by decreasing time_bits, which would reduce resample ratio accuracy.
*/
/** Sample buffer that resamples to output rate and accumulates samples
until they're read out */
struct blip_t
{
fixed_t factor;
fixed_t offset;
int avail;
int size;
int integrator;
};
typedef int buf_t;
/* probably not totally portable */
#define SAMPLES( buf ) ((buf_t*) ((buf) + 1))
/* Arithmetic (sign-preserving) right shift */
#define ARITH_SHIFT( n, shift ) \
((n) >> (shift))
enum { max_sample = +32767 };
enum { min_sample = -32768 };
#define CLAMP( n ) \
{\
if ( (short) n != n )\
n = ARITH_SHIFT( n, 16 ) ^ max_sample;\
}
static void check_assumptions( void )
{
int n;
#if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF
#error "int must be at least 32 bits"
#endif
assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */
n = max_sample * 2;
CLAMP( n );
assert( n == max_sample );
n = min_sample * 2;
CLAMP( n );
assert( n == min_sample );
assert( blip_max_ratio <= time_unit );
assert( blip_max_frame <= (fixed_t) -1 >> time_bits );
}
blip_t* blip_new( int size )
{
blip_t* m;
assert( size >= 0 );
m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) );
if ( m )
{
m->factor = time_unit / blip_max_ratio;
m->size = size;
blip_clear( m );
check_assumptions();
}
return m;
}
blip_t* blip_dup( blip_t* m )
{
size_t size = sizeof *m + (m->size + buf_extra) * sizeof(buf_t);
blip_t* r = (blip_t*) malloc( size );
if ( r ) memcpy( r, m, size );
return r;
}
void blip_delete( blip_t* m )
{
if ( m != NULL )
{
/* Clear fields in case user tries to use after freeing */
memset( m, 0, sizeof *m );
free( m );
}
}
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
{
double factor = time_unit * sample_rate / clock_rate;
m->factor = (fixed_t) factor;
/* Fails if clock_rate exceeds maximum, relative to sample_rate */
assert( 0 <= factor - m->factor && factor - m->factor < 1 );
/* Avoid requiring math.h. Equivalent to
m->factor = (int) ceil( factor ) */
if ( m->factor < factor )
m->factor++;
/* At this point, factor is most likely rounded up, but could still
have been rounded down in the floating-point calculation. */
}
void blip_clear( blip_t* m )
{
/* We could set offset to 0, factor/2, or factor-1. 0 is suitable if
factor is rounded up. factor-1 is suitable if factor is rounded down.
Since we don't know rounding direction, factor/2 accommodates either,
with the slight loss of showing an error in half the time. Since for
a 64-bit factor this is years, the halving isn't a problem. */
m->offset = m->factor / 2;
m->avail = 0;
m->integrator = 0;
memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) );
}
int blip_clocks_needed( const blip_t* m, int samples )
{
fixed_t needed;
/* Fails if buffer can't hold that many more samples */
assert( samples >= 0 && m->avail + samples <= m->size );
needed = (fixed_t) samples * time_unit;
if ( needed < m->offset )
return 0;
return (int)((needed - m->offset + m->factor - 1) / m->factor);
}
void blip_end_frame( blip_t* m, unsigned t )
{
fixed_t off = t * m->factor + m->offset;
m->avail += (int)(off >> time_bits);
m->offset = off & (time_unit - 1);
/* Fails if buffer size was exceeded */
assert( m->avail <= m->size );
}
int blip_samples_avail( const blip_t* m )
{
return m->avail;
}
static void remove_samples( blip_t* m, int count )
{
buf_t* buf = SAMPLES( m );
int remain = m->avail + buf_extra - count;
m->avail -= count;
memmove( &buf [0], &buf [count], remain * sizeof buf [0] );
memset( &buf [remain], 0, count * sizeof buf [0] );
}
int blip_read_samples( blip_t* m, int out [], int count )
{
assert( count >= 0 );
if ( count > m->avail )
count = m->avail;
if ( count )
{
buf_t const* in = SAMPLES( m );
buf_t const* end = in + count;
int sum = m->integrator;
do
{
/* Eliminate fraction */
int s = ARITH_SHIFT( sum, delta_bits - 8 );
sum += *in++;
*out = s;
out++;
/* High-pass filter */
sum -= s >> (8 - (delta_bits - bass_shift)); //<< (delta_bits - bass_shift - 8);
}
while ( in != end );
m->integrator = sum;
remove_samples( m, count );
}
return count;
}
int blip_peek_sample( blip_t* m )
{
return ARITH_SHIFT( m->integrator, delta_bits - 8 );
}
/* Things that didn't help performance on x86:
__attribute__((aligned(128)))
#define short int
restrict
*/
/* Sinc_Generator( 0.9, 0.55, 4.5 ) */
static short const bl_step [phase_count + 1] [half_width] =
{
{ 43, -115, 350, -488, 1136, -914, 5861,21022},
{ 44, -118, 348, -473, 1076, -799, 5274,21001},
{ 45, -121, 344, -454, 1011, -677, 4706,20936},
{ 46, -122, 336, -431, 942, -549, 4156,20829},
{ 47, -123, 327, -404, 868, -418, 3629,20679},
{ 47, -122, 316, -375, 792, -285, 3124,20488},
{ 47, -120, 303, -344, 714, -151, 2644,20256},
{ 46, -117, 289, -310, 634, -17, 2188,19985},
{ 46, -114, 273, -275, 553, 117, 1758,19675},
{ 44, -108, 255, -237, 471, 247, 1356,19327},
{ 43, -103, 237, -199, 390, 373, 981,18944},
{ 42, -98, 218, -160, 310, 495, 633,18527},
{ 40, -91, 198, -121, 231, 611, 314,18078},
{ 38, -84, 178, -81, 153, 722, 22,17599},
{ 36, -76, 157, -43, 80, 824, -241,17092},
{ 34, -68, 135, -3, 8, 919, -476,16558},
{ 32, -61, 115, 34, -60, 1006, -683,16001},
{ 29, -52, 94, 70, -123, 1083, -862,15422},
{ 27, -44, 73, 106, -184, 1152,-1015,14824},
{ 25, -36, 53, 139, -239, 1211,-1142,14210},
{ 22, -27, 34, 170, -290, 1261,-1244,13582},
{ 20, -20, 16, 199, -335, 1301,-1322,12942},
{ 18, -12, -3, 226, -375, 1331,-1376,12293},
{ 15, -4, -19, 250, -410, 1351,-1408,11638},
{ 13, 3, -35, 272, -439, 1361,-1419,10979},
{ 11, 9, -49, 292, -464, 1362,-1410,10319},
{ 9, 16, -63, 309, -483, 1354,-1383, 9660},
{ 7, 22, -75, 322, -496, 1337,-1339, 9005},
{ 6, 26, -85, 333, -504, 1312,-1280, 8355},
{ 4, 31, -94, 341, -507, 1278,-1205, 7713},
{ 3, 35, -102, 347, -506, 1238,-1119, 7082},
{ 1, 40, -110, 350, -499, 1190,-1021, 6464},
{ 0, 43, -115, 350, -488, 1136, -914, 5861}
};
/* Shifting by pre_shift allows calculation using unsigned int rather than
possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient.
And by having pre_shift 32, a 32-bit platform can easily do the shift by
simply ignoring the low half. */
void blip_add_delta( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int const phase_shift = frac_bits - phase_bits;
int phase = fixed >> phase_shift & (phase_count - 1);
short const* in = bl_step [phase];
short const* rev = bl_step [phase_count - phase];
int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1);
int delta2 = (delta * interp) >> delta_bits;
delta -= delta2;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [0] += in[0]*delta + in[half_width+0]*delta2;
out [1] += in[1]*delta + in[half_width+1]*delta2;
out [2] += in[2]*delta + in[half_width+2]*delta2;
out [3] += in[3]*delta + in[half_width+3]*delta2;
out [4] += in[4]*delta + in[half_width+4]*delta2;
out [5] += in[5]*delta + in[half_width+5]*delta2;
out [6] += in[6]*delta + in[half_width+6]*delta2;
out [7] += in[7]*delta + in[half_width+7]*delta2;
in = rev;
out [ 8] += in[7]*delta + in[7-half_width]*delta2;
out [ 9] += in[6]*delta + in[6-half_width]*delta2;
out [10] += in[5]*delta + in[5-half_width]*delta2;
out [11] += in[4]*delta + in[4-half_width]*delta2;
out [12] += in[3]*delta + in[3-half_width]*delta2;
out [13] += in[2]*delta + in[2-half_width]*delta2;
out [14] += in[1]*delta + in[1-half_width]*delta2;
out [15] += in[0]*delta + in[0-half_width]*delta2;
}
void blip_add_delta_fast( blip_t* m, unsigned time, int delta )
{
unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift);
buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits);
int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1);
int delta2 = delta * interp;
/* Fails if buffer size was exceeded */
assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] );
out [7] += delta * delta_unit - delta2;
out [8] += delta2;
}

View File

@ -95,7 +95,8 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES #define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES #define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO #define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
#define MIX_ALIAS(op, upd, offset) MONO_DEST_MIX_ALIAS(op, upd, offset) #define MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS
#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1) #define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) #define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) *dst++ op 0 #define MIX_ZEROS(op) *dst++ op 0
@ -137,7 +138,8 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
} }
#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) #define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MIX_ALIAS(op, upd, offset) STEREO_DEST_MIX_ALIAS(op, upd, offset) #define MIX_ALIAS(count) STEREO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS
#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) #define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) #define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } #define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
@ -157,6 +159,9 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
#undef MONO_DEST_VOLUME_ZEROS #undef MONO_DEST_VOLUME_ZEROS
#undef MONO_DEST_VOLUME_VARIABLES #undef MONO_DEST_VOLUME_VARIABLES
#undef MONO_DEST_VOLUME_PARAMETERS #undef MONO_DEST_VOLUME_PARAMETERS
#undef STEREO_DEST_PEEK_ALIAS
#undef MONO_DEST_PEEK_ALIAS
#undef POKE_ALIAS
#undef COPYSRC2 #undef COPYSRC2
#undef COPYSRC #undef COPYSRC
#undef DIVIDE_BY_SRC_CHANNELS #undef DIVIDE_BY_SRC_CHANNELS

View File

@ -46,12 +46,13 @@
int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VOLUME_PARAMETERS, double delta) int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VOLUME_PARAMETERS, double delta)
{ {
int dt; int dt, inv_dt;
int VOLUME_VARIABLES; int VOLUME_VARIABLES;
long done; long done;
long todo; long todo;
LONG_LONG todo64; LONG_LONG todo64;
int quality; int quality;
int blip_samples[256*SRC_CHANNELS];
if (!resampler || resampler->dir == 0) return 0; if (!resampler || resampler->dir == 0) return 0;
ASSERT(resampler->dir == -1 || resampler->dir == 1); ASSERT(resampler->dir == -1 || resampler->dir == 1);
@ -59,6 +60,7 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
done = 0; done = 0;
dt = (int)(delta * 65536.0 + 0.5); dt = (int)(delta * 65536.0 + 0.5);
if (dt == 0 || dt == 0x80000000) return 0; if (dt == 0 || dt == 0x80000000) return 0;
inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
SET_VOLUME_VARIABLES; SET_VOLUME_VARIABLES;
if (VOLUMES_ARE_ZERO) dst = NULL; if (VOLUMES_ARE_ZERO) dst = NULL;
@ -104,29 +106,33 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
subpos = (long)new_subpos & 65535; subpos = (long)new_subpos & 65535;
} else if (quality <= DUMB_RQ_ALIASING) { } else if (quality <= DUMB_RQ_ALIASING) {
/* Aliasing, backwards */ /* Aliasing, backwards */
SRCTYPE xbuf[2*SRC_CHANNELS]; int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
SRCTYPE xbuf[2*SRC_CHANNELS];
SRCTYPE *x = &xbuf[0]; SRCTYPE *x = &xbuf[0];
SRCTYPE *xstart;
COPYSRC(xbuf, 0, resampler->X, 1); COPYSRC(xbuf, 0, resampler->X, 1);
COPYSRC(xbuf, 1, resampler->X, 2); COPYSRC(xbuf, 1, resampler->X, 2);
while (todo && x < &xbuf[2*SRC_CHANNELS]) { if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
// TODO: check what happens when multiple tempo slides occur per row // TODO: check what happens when multiple tempo slides occur per row
HEAVYASSERT(pos >= resampler->start); HEAVYASSERT(pos >= resampler->start);
MIX_ALIAS(+=, 1, 0); POKE_ALIAS(0);
subpos += dt; pos--;
pos += subpos >> 16; x += SRC_CHANNELS;
x -= (subpos >> 16) * SRC_CHANNELS; }
subpos &= 65535; x = &src[pos*SRC_CHANNELS];
todo--; while ( todo_clocks ) {
todo_clocks_set = todo_clocks;
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
todo_clocks -= todo_clocks_set;
while ( resampler->last_clock < todo_clocks_set )
{
POKE_ALIAS(2);
pos--;
x -= SRC_CHANNELS;
}
todo = todo_clocks_set >> 16;
MIX_ALIAS( todo );
} }
x = xstart = &src[pos*SRC_CHANNELS];
LOOP4(todo,
MIX_ALIAS(+=, 1, 2);
subpos += dt;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
pos += DIVIDE_BY_SRC_CHANNELS((long)(x - xstart));
} else if (quality <= DUMB_RQ_LINEAR) { } else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */ /* Linear interpolation, backwards */
SRCTYPE xbuf[3*SRC_CHANNELS]; SRCTYPE xbuf[3*SRC_CHANNELS];
@ -205,28 +211,32 @@ int32 dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, int32 dst_size, VO
subpos = (long)new_subpos & 65535; subpos = (long)new_subpos & 65535;
} else if (quality <= DUMB_RQ_ALIASING) { } else if (quality <= DUMB_RQ_ALIASING) {
/* Aliasing, forwards */ /* Aliasing, forwards */
int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
SRCTYPE xbuf[2*SRC_CHANNELS]; SRCTYPE xbuf[2*SRC_CHANNELS];
SRCTYPE *x = &xbuf[0]; SRCTYPE *x = &xbuf[0];
SRCTYPE *xstart;
COPYSRC(xbuf, 0, resampler->X, 1); COPYSRC(xbuf, 0, resampler->X, 1);
COPYSRC(xbuf, 1, resampler->X, 2); COPYSRC(xbuf, 1, resampler->X, 2);
while (todo && x < &xbuf[2*SRC_CHANNELS]) { if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
HEAVYASSERT(pos < resampler->end); HEAVYASSERT(pos < resampler->end);
MIX_ALIAS(+=, 1, 0); POKE_ALIAS(0);
subpos += dt; pos++;
pos += subpos >> 16; x += SRC_CHANNELS;
x += (subpos >> 16) * SRC_CHANNELS; }
subpos &= 65535; x = &src[pos*SRC_CHANNELS];
todo--; while ( todo_clocks ) {
todo_clocks_set = todo_clocks;
if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
todo_clocks -= todo_clocks_set;
while ( resampler->last_clock < todo_clocks_set )
{
POKE_ALIAS(-2);
pos++;
x += SRC_CHANNELS;
}
todo = todo_clocks_set >> 16;
MIX_ALIAS( todo );
} }
x = xstart = &src[pos*SRC_CHANNELS];
LOOP4(todo,
MIX_ALIAS(+=, 1, -2);
subpos += dt;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
pos += DIVIDE_BY_SRC_CHANNELS((long)(x - xstart));
} else if (quality <= DUMB_RQ_LINEAR) { } else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */ /* Linear interpolation, forwards */
SRCTYPE xbuf[3*SRC_CHANNELS]; SRCTYPE xbuf[3*SRC_CHANNELS];
@ -339,7 +349,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos >= resampler->start); HEAVYASSERT(pos >= resampler->start);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) { if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing, backwards */ /* Aliasing, backwards */
MIX_ALIAS(=, 0, 1); PEEK_ALIAS;
} else if (quality <= DUMB_RQ_LINEAR) { } else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */ /* Linear interpolation, backwards */
MIX_LINEAR(=, 0, 2, 1); MIX_LINEAR(=, 0, 2, 1);
@ -351,7 +361,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos < resampler->end); HEAVYASSERT(pos < resampler->end);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) { if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing */ /* Aliasing */
MIX_ALIAS(=, 0, 1); PEEK_ALIAS;
} else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) { } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */ /* Linear interpolation, forwards */
MIX_LINEAR(=, 0, 1, 2); MIX_LINEAR(=, 0, 1, 2);
@ -368,6 +378,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
#undef MIX_CUBIC #undef MIX_CUBIC
#undef MIX_LINEAR #undef MIX_LINEAR
#undef MIX_ALIAS #undef MIX_ALIAS
#undef PEEK_ALIAS
#undef VOLUMES_ARE_ZERO #undef VOLUMES_ARE_ZERO
#undef SET_VOLUME_VARIABLES #undef SET_VOLUME_VARIABLES
#undef RETURN_VOLUME_VARIABLES #undef RETURN_VOLUME_VARIABLES

View File

@ -117,9 +117,9 @@ int dumb_resampling_quality = DUMB_RQ_CUBIC;
*/ */
#define LOOP4(iterator, CONTENT) \ #define LOOP4(iterator, CONTENT) \
{ \ { \
while (iterator) { \ while ( (iterator)-- ) \
{ \
CONTENT; \ CONTENT; \
(iterator)--; \
} \ } \
} }
#endif #endif
@ -189,7 +189,7 @@ static void init_cubic(void)
#define SRCTYPE sample_t #define SRCTYPE sample_t
#define SRCBITS 24 #define SRCBITS 24
#define ALIAS(x, vol) MULSC(x, vol) #define ALIAS(x) (x >> 8)
#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos)) #define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
/* /*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -225,7 +225,7 @@ static void init_cubic(void)
#define SUFFIX _16 #define SUFFIX _16
#define SRCTYPE short #define SRCTYPE short
#define SRCBITS 16 #define SRCBITS 16
#define ALIAS(x, vol) (x * vol >> 8) #define ALIAS(x) (x)
#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos)) #define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
/* /*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -247,7 +247,7 @@ static void init_cubic(void)
#define SUFFIX _8 #define SUFFIX _8
#define SRCTYPE signed char #define SRCTYPE signed char
#define SRCBITS 8 #define SRCBITS 8
#define ALIAS(x, vol) (x * vol) #define ALIAS(x) (x << 8)
#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos) #define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
/* /*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \

View File

@ -69,6 +69,11 @@ void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_chann
} }
for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0; for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0;
resampler->overshot = -1; resampler->overshot = -1;
resampler->last_clock = 0;
resampler->last_amp[0] = 0;
resampler->last_amp[1] = 0;
blip_clear(resampler->blip_buffer[0]);
blip_clear(resampler->blip_buffer[1]);
} }
@ -77,6 +82,21 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
{ {
DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
if (!resampler) return NULL; if (!resampler) return NULL;
resampler->blip_buffer[0] = blip_new( 256 );
if (!resampler->blip_buffer[0])
{
free(resampler);
return NULL;
}
resampler->blip_buffer[1] = blip_new( 256 );
if (!resampler->blip_buffer[1])
{
free(resampler->blip_buffer[0]);
free(resampler);
return NULL;
}
blip_set_rates(resampler->blip_buffer[0], 65536, 1);
blip_set_rates(resampler->blip_buffer[1], 65536, 1);
dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality); dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
return resampler; return resampler;
} }
@ -123,16 +143,41 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
} }
#define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f #define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f
#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0) #define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \ #define POKE_ALIAS(offset) { \
*dst++ op ALIAS(x[offset], vol); \ int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \
if ( upd ) UPDATE_VOLUME( volume, vol ); \ resampler->last_amp[0] += delta; \
if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \
resampler->last_clock += inv_dt; \
} }
#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \ #define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
int xm = x[offset]; \ #define MONO_DEST_MIX_ALIAS(count) { \
*dst++ op ALIAS(xm, lvol); \ int n = 0; \
*dst++ op ALIAS(xm, rvol); \ resampler->last_clock -= count * 65536; \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], vol ); \
n++; \
UPDATE_VOLUME( volume, vol ); \
); \
}
#define STEREO_DEST_PEEK_ALIAS { \
int sample = blip_peek_sample( resampler->blip_buffer[0] ); \
*dst++ = MULSC( sample, lvol ); \
*dst++ = MULSC( sample, rvol ); \
}
#define STEREO_DEST_MIX_ALIAS(count) { \
int sample, n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
LOOP4( count, \
sample = blip_samples[n++]; \
*dst++ += MULSC( sample, lvol ); \
*dst++ += MULSC( sample, rvol ); \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
} }
#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ #define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
*dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \ *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \
@ -208,16 +253,51 @@ DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, int32 pos,
if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
} }
#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) #define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \ #define POKE_ALIAS(offset) { \
*dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol); \ int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ resampler->last_amp[0] += deltal; \
resampler->last_amp[1] += deltar; \
if ( deltal ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, deltal ); \
if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \
resampler->last_clock += inv_dt; \
} }
#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \ #define MONO_DEST_PEEK_ALIAS { \
*dst++ op ALIAS(x[(offset)*2], lvol); \ *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
*dst++ op ALIAS(x[(offset)*2+1], rvol); \ MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ }
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ #define MONO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], lvol ) + MULSC( blip_samples[256+n], rvol ); \
n++; \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
}
#define STEREO_DEST_PEEK_ALIAS { \
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
}
#define STEREO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
LOOP4( count, \
*dst++ += MULSC( blip_samples[n], lvol); \
*dst++ += MULSC( blip_samples[256+n], rvol); \
n++; \
UPDATE_VOLUME( volume_left, lvol ); \
UPDATE_VOLUME( volume_right, rvol ); \
); \
} }
#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ #define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
*dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \ *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \

View File

@ -24,7 +24,7 @@
DUMB_IT_SIGDATA *DUMBEXPORT duh_get_it_sigdata(DUH *duh) DUMB_IT_SIGDATA *DUMBEXPORT duh_get_it_sigdata(DUH *duh)
{ {
return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT); return duh_get_raw_sigdata(duh, -1, SIGTYPE_IT);
} }

View File

@ -31,18 +31,71 @@
//#define INVESTIGATE_OLD_INSTRUMENTS //#define INVESTIGATE_OLD_INSTRUMENTS
typedef struct tdumbfile_mem_status
static int it_seek(DUMBFILE *f, int32 offset)
{ {
int32 pos = dumbfile_pos(f); const unsigned char * ptr;
unsigned offset, size;
} dumbfile_mem_status;
if (pos > offset) static int dumbfile_mem_skip(void * f, int32 n)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
s->offset += n;
if (s->offset > s->size)
{
s->offset = s->size;
return 1;
}
return 0;
}
static int dumbfile_mem_getc(void * f)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
if (s->offset < s->size)
{
return *(s->ptr + s->offset++);
}
return -1;
}
static int32 dumbfile_mem_getnc(char * ptr, int32 n, void * f)
{
dumbfile_mem_status * s = (dumbfile_mem_status *) f;
int32 max = s->size - s->offset;
if (max > n) max = n;
if (max)
{
memcpy(ptr, s->ptr + s->offset, max);
s->offset += max;
}
return max;
}
static DUMBFILE_SYSTEM mem_dfs = {
NULL, // open
&dumbfile_mem_skip,
&dumbfile_mem_getc,
&dumbfile_mem_getnc,
NULL // close
};
static int it_seek(dumbfile_mem_status * s, int32 offset)
{
if ( (unsigned)offset > s->size )
return -1; return -1;
if (pos < offset) s->offset = offset;
if (dumbfile_skip(f, offset - pos))
return -1;
return 0; return 0;
} }
@ -306,6 +359,8 @@ static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
envelope->loop_end = dumbfile_getc(f); envelope->loop_end = dumbfile_getc(f);
envelope->sus_loop_start = dumbfile_getc(f); envelope->sus_loop_start = dumbfile_getc(f);
envelope->sus_loop_end = dumbfile_getc(f); envelope->sus_loop_end = dumbfile_getc(f);
if (envelope->n_nodes > 25)
envelope->n_nodes = 25;
for (n = 0; n < envelope->n_nodes; n++) { for (n = 0; n < envelope->n_nodes; n++) {
envelope->node_y[n] = dumbfile_getc(f); envelope->node_y[n] = dumbfile_getc(f);
envelope->node_t[n] = dumbfile_igetw(f); envelope->node_t[n] = dumbfile_igetw(f);
@ -629,7 +684,7 @@ int32 _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f)
signed char * ptr, * end; signed char * ptr, * end;
signed char compression_table[16]; signed char compression_table[16];
if (dumbfile_getnc(compression_table, 16, f) != 16) if (dumbfile_getnc(compression_table, 16, f) != 16)
return -1; return -1;
ptr = (signed char *) sample->data; ptr = (signed char *) sample->data;
delta = 0; delta = 0;
@ -682,15 +737,36 @@ static int32 it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char conv
else else
decompress8(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4))); decompress8(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4)));
} else if (sample->flags & IT_SAMPLE_16BIT) { } else if (sample->flags & IT_SAMPLE_16BIT) {
if (convert & 2) if (sample->flags & IT_SAMPLE_STEREO) {
if (convert & 2) {
for (n = 0; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_mgetw(f);
for (n = 1; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_mgetw(f);
} else {
for (n = 0; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_igetw(f);
for (n = 1; n < datasize; n += 2)
((short *)sample->data)[n] = dumbfile_igetw(f);
}
} else {
if (convert & 2)
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_mgetw(f);
else
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_igetw(f);
}
} else {
if (sample->flags & IT_SAMPLE_STEREO) {
for (n = 0; n < datasize; n += 2)
((signed char *)sample->data)[n] = dumbfile_getc(f);
for (n = 1; n < datasize; n += 2)
((signed char *)sample->data)[n] = dumbfile_getc(f);
} else
for (n = 0; n < datasize; n++) for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_mgetw(f); ((signed char *)sample->data)[n] = dumbfile_getc(f);
else }
for (n = 0; n < datasize; n++)
((short *)sample->data)[n] = dumbfile_igetw(f);
} else
for (n = 0; n < datasize; n++)
((signed char *)sample->data)[n] = dumbfile_getc(f);
if (dumbfile_error(f)) if (dumbfile_error(f))
return -1; return -1;
@ -934,13 +1010,58 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
unsigned char *buffer; unsigned char *buffer;
unsigned char *file_buffer = NULL;
unsigned int file_size = 0;
int block_size;
dumbfile_mem_status memdata;
do
{
void * temp = realloc( file_buffer, file_size + 32768 );
if ( !temp )
{
if ( file_buffer ) free( file_buffer );
return NULL;
}
file_buffer = temp;
block_size = dumbfile_getnc( file_buffer + file_size, 32768, f );
if ( block_size < 0 )
{
free( file_buffer );
return NULL;
}
file_size += block_size;
}
while ( block_size == 32768 );
memdata.ptr = file_buffer;
memdata.offset = 0;
memdata.size = file_size;
f = dumbfile_open_ex(&memdata, &mem_dfs);
if ( !f )
{
free( file_buffer );
return NULL;
}
if (dumbfile_mgetl(f) != IT_SIGNATURE) if (dumbfile_mgetl(f) != IT_SIGNATURE)
{
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
}
sigdata = malloc(sizeof(*sigdata)); sigdata = malloc(sizeof(*sigdata));
if (!sigdata) if (!sigdata)
{
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
}
sigdata->song_message = NULL; sigdata->song_message = NULL;
sigdata->order = NULL; sigdata->order = NULL;
@ -989,12 +1110,16 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
// XXX sample count // XXX sample count
if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) { if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
sigdata->order = malloc(sigdata->n_orders); sigdata->order = malloc(sigdata->n_orders);
if (!sigdata->order) { if (!sigdata->order) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1002,6 +1127,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
if (!sigdata->instrument) { if (!sigdata->instrument) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
} }
@ -1010,6 +1137,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) { if (!sigdata->sample) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
for (n = 0; n < sigdata->n_samples; n++) for (n = 0; n < sigdata->n_samples; n++)
@ -1020,6 +1149,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
if (!sigdata->pattern) { if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
for (n = 0; n < sigdata->n_patterns; n++) for (n = 0; n < sigdata->n_patterns; n++)
@ -1032,6 +1163,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
component = malloc(769 * sizeof(*component)); component = malloc(769 * sizeof(*component));
if (!component) { if (!component) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1076,6 +1209,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_error(f)) { if (dumbfile_error(f)) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1096,6 +1231,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (!sigdata->midi) { if (!sigdata->midi) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
// Should we be happy with this outcome in some situations? // Should we be happy with this outcome in some situations?
} }
@ -1104,6 +1241,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) { if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
/* Read embedded MIDI configuration */ /* Read embedded MIDI configuration */
@ -1111,6 +1250,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_skip(f, 32*9)) { if (dumbfile_skip(f, 32*9)) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
@ -1119,6 +1260,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_getnc(mididata, 32, f) < 32) { if (dumbfile_getnc(mididata, 32, f) < 32) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
sigdata->midi->SFmacroz[i] = 0; sigdata->midi->SFmacroz[i] = 0;
@ -1174,12 +1317,14 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->flags &= IT_REAL_FLAGS; sigdata->flags &= IT_REAL_FLAGS;
qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare); qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
buffer = malloc(65536); buffer = malloc(65536);
if (!buffer) { if (!buffer) {
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1208,10 +1353,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
continue; continue;
} }
if (it_seek(f, component[n].offset)) { if (it_seek(&memdata, component[n].offset)) {
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1227,6 +1374,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
sigdata->song_message[message_length] = 0; sigdata->song_message[message_length] = 0;
@ -1243,6 +1392,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
break; break;
@ -1252,6 +1403,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
break; break;
@ -1261,6 +1414,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1287,10 +1442,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
m = component[n].sampfirst; m = component[n].sampfirst;
while (m >= 0) { while (m >= 0) {
if (it_seek(f, component[m].offset)) { if (it_seek(&memdata, component[m].offset)) {
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
@ -1298,16 +1455,79 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer); free(buffer);
free(component); free(component);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL; return NULL;
} }
m = component[m].sampnext; m = component[m].sampnext;
} }
} }
free(buffer); for ( n = 0; n < 10; n++ )
{
if ( dumbfile_getc( f ) == 'X' )
{
if ( dumbfile_getc( f ) == 'T' )
{
if ( dumbfile_getc( f ) == 'P' )
{
if ( dumbfile_getc( f ) == 'M' )
{
break;
}
}
}
}
}
if ( !dumbfile_error( f ) && n < 10 )
{
unsigned int mptx_id = dumbfile_igetl( f );
while ( !dumbfile_error( f ) && mptx_id != DUMB_ID('M','P','T','S') )
{
unsigned int size = dumbfile_igetw( f );
switch (mptx_id)
{
/* TODO: Add instrument extension readers */
default:
dumbfile_skip(f, size * sigdata->n_instruments);
break;
}
mptx_id = dumbfile_igetl( f );
}
mptx_id = dumbfile_igetl( f );
while ( memdata.offset < file_size )
{
unsigned int size = dumbfile_igetw( f );
switch (mptx_id)
{
/* TODO: Add more song extension readers */
case DUMB_ID('D','T','.','.'):
if ( size == 2 )
sigdata->tempo = dumbfile_igetw( f );
else if ( size == 4 )
sigdata->tempo = dumbfile_igetl( f );
break;
default:
dumbfile_skip(f, size);
break;
}
mptx_id = dumbfile_igetl( f );
}
}
free(buffer);
free(component); free(component);
dumbfile_close(f);
free(file_buffer);
_dumb_it_fix_invalid_orders(sigdata); _dumb_it_fix_invalid_orders(sigdata);
return sigdata; return sigdata;
@ -1327,9 +1547,11 @@ DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f)
return NULL; return NULL;
{ {
const char *tag[1][2]; const char *tag[2][2];
tag[0][0] = "TITLE"; tag[0][0] = "TITLE";
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); tag[1][0] = "FORMAT";
tag[1][1] = "IT";
return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
* pointer to the DUH struct. When you have finished with it, you must * pointer to the DUH struct. When you have finished with it, you must
* pass the pointer to unload_duh() so that the memory can be freed. * pass the pointer to unload_duh() so that the memory can be freed.
*/ */
DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict) DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int rstrict)
{ {
DUH *duh; DUH *duh;
DUMBFILE *f = dumbfile_open(filename); DUMBFILE *f = dumbfile_open(filename);
@ -34,7 +34,7 @@ DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict)
if (!f) if (!f)
return NULL; return NULL;
duh = dumb_read_mod_quick(f, restrict); duh = dumb_read_mod_quick(f, rstrict);
dumbfile_close(f); dumbfile_close(f);

View File

@ -21,9 +21,9 @@
DUH *DUMBEXPORT dumb_load_mod(const char *filename, int restrict) DUH *DUMBEXPORT dumb_load_mod(const char *filename, int rstrict)
{ {
DUH *duh = dumb_load_mod_quick(filename, restrict); DUH *duh = dumb_load_mod_quick(filename, rstrict);
dumb_it_do_initial_runthrough(duh); dumb_it_do_initial_runthrough(duh);
return duh; return duh;
} }

42
dumb/src/it/loadokt.c Normal file
View File

@ -0,0 +1,42 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* loadokt.c - Code to read an Oktalyzer module / / \ \
* file, opening and closing it for | < / \_
* you. | \/ /\ /
* \_ / > /
* By Chris Moeller. | \ / /
* | ' /
* \__/
*/
#include "dumb.h"
#include "internal/it.h"
/* dumb_load_okt_quick(): loads an OKT file into a DUH struct, returning a
* pointer to the DUH struct. When you have finished with it, you must
* pass the pointer to unload_duh() so that the memory can be freed.
*/
DUH *DUMBEXPORT dumb_load_okt_quick(const char *filename)
{
DUH *duh;
DUMBFILE *f = dumbfile_open(filename);
if (!f)
return NULL;
duh = dumb_read_okt_quick(f);
dumbfile_close(f);
return duh;
}

29
dumb/src/it/loadokt2.c Normal file
View File

@ -0,0 +1,29 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* loadokt2.c - Function to read an Oktalyzer / / \ \
* module file, opening and closing | < / \_
* it for you, and do an initial run- | \/ /\ /
* through. \_ / > /
* | \ / /
* By Chris Moeller. | ' /
* \__/
*/
#include "dumb.h"
DUH *DUMBEXPORT dumb_load_okt(const char *filename)
{
DUH *duh = dumb_load_okt_quick(filename);
dumb_it_do_initial_runthrough(duh);
return duh;
}

View File

@ -331,6 +331,7 @@ static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0; sigdata->n_instruments = 0;
sigdata->n_orders = 0; sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
@ -553,6 +554,7 @@ static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0; sigdata->n_instruments = 0;
sigdata->n_orders = 0; sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);

View File

@ -71,7 +71,7 @@ static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->mask |= IT_ENTRY_INSTRUMENT; entry->mask |= IT_ENTRY_INSTRUMENT;
} }
_dumb_it_xm_convert_effect( buffer[ pos + 2 ] & 0x0F, buffer[ pos + 3 ], entry, 1 ); _dumb_it_xm_convert_effect( buffer[ pos + 2 ], buffer[ pos + 3 ], entry, 1 );
if ( entry->mask ) ++entry; if ( entry->mask ) ++entry;
} }
@ -90,7 +90,7 @@ static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f ) static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
{ {
int finetune; int finetune, key_offset;
/** /**
21 22 Chars Sample 1 name. If the name is not a full 21 22 Chars Sample 1 name. If the name is not a full
@ -111,7 +111,7 @@ assumed not to be an instrument name, and is probably a message.
sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead? sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead?
sample->global_volume = 64; sample->global_volume = 64;
if ( sample->default_volume > 64 ) sample->default_volume = 64; if ( sample->default_volume > 64 ) sample->default_volume = 64;
dumbfile_skip( f, 1 ); /* XXX unknown */ key_offset = ( signed char ) dumbfile_getc( f ); /* base key offset */
sample->length = dumbfile_igetl( f ); sample->length = dumbfile_igetl( f );
sample->loop_start = dumbfile_igetl( f ); sample->loop_start = dumbfile_igetl( f );
sample->loop_end = sample->loop_start + dumbfile_igetl( f ); sample->loop_end = sample->loop_start + dumbfile_igetl( f );
@ -124,7 +124,7 @@ assumed not to be an instrument name, and is probably a message.
sample->flags = IT_SAMPLE_EXISTS; sample->flags = IT_SAMPLE_EXISTS;
sample->default_pan = 0; sample->default_pan = 0;
sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//( int32 )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) ); sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) );
sample->finetune = finetune * 32; sample->finetune = finetune * 32;
// the above line might be wrong // the above line might be wrong

View File

@ -265,6 +265,7 @@ static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata( struct riff * stream )
sigdata->n_instruments = 0; sigdata->n_instruments = 0;
sigdata->n_orders = 0; sigdata->n_orders = 0;
sigdata->restart_position = 0;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);

View File

@ -393,10 +393,12 @@ static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, uint32 *fft)
return dumbfile_open_ex(bm, &buffer_mod_dfs); return dumbfile_open_ex(bm, &buffer_mod_dfs);
} }
static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain) static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int n_samples, IT_SAMPLE *sample, int32 *total_sample_size, int32 *remain)
{ {
int32 read; int32 read;
int sample_number;
BUFFERED_MOD *bm = malloc(sizeof(*bm)); BUFFERED_MOD *bm = malloc(sizeof(*bm));
unsigned char *ptr;
if (!bm) return NULL; if (!bm) return NULL;
bm->buffered = malloc(32768); bm->buffered = malloc(32768);
@ -430,6 +432,21 @@ static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain)
if (*remain) { if (*remain) {
bm->ptr = 0; bm->ptr = 0;
ptr = bm->buffered + *remain;
sample_number = n_samples - 1;
*total_sample_size = 0;
while (ptr > bm->buffered && sample_number >= 0) {
if (sample[sample_number].flags & IT_SAMPLE_EXISTS) {
ptr -= (sample[sample_number].length + 1) / 2 + 5 + 16;
if (ptr >= bm->buffered && !memcmp(ptr, "ADPCM", 5)) { /* BAH */
*total_sample_size += (sample[sample_number].length + 1) / 2 + 5 + 16;
} else {
*total_sample_size += sample[sample_number].length;
ptr -= sample[sample_number].length - ((sample[sample_number].length + 1) / 2 + 5 + 16);
}
}
sample_number--;
}
} else { } else {
free(bm->buffered); free(bm->buffered);
bm->buffered = NULL; bm->buffered = NULL;
@ -441,13 +458,13 @@ static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain)
} }
static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict) static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int rstrict)
{ {
DUMB_IT_SIGDATA *sigdata; DUMB_IT_SIGDATA *sigdata;
int n_channels; int n_channels;
int i; int i;
uint32 fft = 0; uint32 fft = 0;
DUMBFILE *rem; DUMBFILE *rem = NULL;
f = dumbfile_buffer_mod(f, &fft); f = dumbfile_buffer_mod(f, &fft);
if (!f) if (!f)
@ -550,7 +567,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
} }
// moo // moo
if ( restrict && sigdata->n_samples == 15 ) if ( ( rstrict & 1 ) && sigdata->n_samples == 15 )
{ {
free(sigdata); free(sigdata);
dumbfile_close(f); dumbfile_close(f);
@ -629,12 +646,48 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
if (sigdata->n_samples == 31) if (sigdata->n_samples == 31)
dumbfile_skip(f, 4); dumbfile_skip(f, 4);
/* Work out how many patterns there are. */
sigdata->n_patterns = -1; sigdata->n_patterns = -1;
for (i = 0; i < 128; i++)
if (sigdata->n_patterns < sigdata->order[i]) if ( ( rstrict & 2 ) )
sigdata->n_patterns = sigdata->order[i]; {
sigdata->n_patterns++; long total_sample_size;
long remain;
rem = f;
f = dumbfile_buffer_mod_2(rem, sigdata->n_samples, sigdata->sample, &total_sample_size, &remain);
if (!f) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(rem);
return NULL;
}
if (remain > total_sample_size) {
sigdata->n_patterns = ( remain - total_sample_size + 4 ) / ( 256 * sigdata->n_pchannels );
if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
remain -= sigdata->n_patterns * 256 * sigdata->n_pchannels;
if (dumbfile_skip(f, remain - total_sample_size)) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
dumbfile_close(rem);
return NULL;
}
}
}
}
else
{
for (i = 0; i < 128; i++)
{
if (sigdata->order[i] > sigdata->n_patterns)
sigdata->n_patterns = sigdata->order[i];
}
sigdata->n_patterns++;
}
if ( sigdata->n_patterns <= 0 ) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
/* May as well try to save a tiny bit of memory. */ /* May as well try to save a tiny bit of memory. */
if (sigdata->n_orders < 128) { if (sigdata->n_orders < 128) {
@ -646,6 +699,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
if (!sigdata->pattern) { if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f); dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL; return NULL;
} }
for (i = 0; i < sigdata->n_patterns; i++) for (i = 0; i < sigdata->n_patterns; i++)
@ -653,10 +707,11 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
/* Read in the patterns */ /* Read in the patterns */
{ {
unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */ unsigned char *buffer = malloc(256 * sigdata->n_pchannels); /* 64 rows * 4 bytes */
if (!buffer) { if (!buffer) {
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f); dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL; return NULL;
} }
for (i = 0; i < sigdata->n_patterns; i++) { for (i = 0; i < sigdata->n_patterns; i++) {
@ -664,40 +719,13 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
free(buffer); free(buffer);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
dumbfile_close(f); dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL; return NULL;
} }
} }
free(buffer); free(buffer);
} }
rem = NULL;
/* uggly */
if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
int32 skip;
int32 remain;
rem = f;
f = dumbfile_buffer_mod_2(rem, &remain);
if (!f) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(rem);
return NULL;
}
for (skip = 0, i = 0; i < sigdata->n_samples; i++) {
if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
skip += sigdata->sample[i].length;
}
}
if (remain - skip) {
if (dumbfile_skip(f, remain - skip)) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
dumbfile_close(rem);
return NULL;
}
}
}
/* And finally, the sample data */ /* And finally, the sample data */
for (i = 0; i < sigdata->n_samples; i++) { for (i = 0; i < sigdata->n_samples; i++) {
if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) { if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) {
@ -727,8 +755,8 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
}*/ }*/
dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */ dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
if (rem) dumbfile_close(rem); /* And the BUFFERED_MOD DUMBFILE used to pre-read the signature. */
/* The DUMBFILE originally passed to our function is intact. */ /* The DUMBFILE originally passed to our function is intact. */
if (rem) dumbfile_close(rem);
/* Now let's initialise the remaining variables, and we're done! */ /* Now let's initialise the remaining variables, and we're done! */
sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
@ -758,13 +786,13 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int restrict) DUH *DUMBEXPORT dumb_read_mod_quick(DUMBFILE *f, int rstrict)
{ {
sigdata_t *sigdata; sigdata_t *sigdata;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_mod_load_sigdata(f, restrict); sigdata = it_mod_load_sigdata(f, rstrict);
if (!sigdata) if (!sigdata)
return NULL; return NULL;

View File

@ -21,9 +21,9 @@
DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int restrict) DUH *DUMBEXPORT dumb_read_mod(DUMBFILE *f, int rstrict)
{ {
DUH *duh = dumb_read_mod_quick(f, restrict); DUH *duh = dumb_read_mod_quick(f, rstrict);
dumb_it_do_initial_runthrough(duh); dumb_it_do_initial_runthrough(duh);
return duh; return duh;
} }

558
dumb/src/it/readokt.c Normal file
View File

@ -0,0 +1,558 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* readokt.c - Code to read an Oktalyzer module / / \ \
* from an open file. | < / \_
* | \/ /\ /
* By Chris Moeller. \_ / > /
* | \ / /
* | ' /
* \__/
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "dumb.h"
#include "internal/it.h"
static int it_okt_read_pattern(IT_PATTERN *pattern, const unsigned char *data, int length, int n_channels)
{
int pos;
int channel;
int row;
int n_rows;
IT_ENTRY *entry;
if (length < 2) return -1;
n_rows = (data[0] << 8) | data[1];
if (!n_rows) n_rows = 64;
if (length < 2 + (n_rows * n_channels * 4)) return -1;
pattern->n_rows = n_rows;
/* compute number of entries */
pattern->n_entries = n_rows; /* Account for the row end markers */
pos = 2;
for (row = 0; row < pattern->n_rows; row++) {
for (channel = 0; channel < n_channels; channel++) {
if (data[pos+0] | data[pos+2])
pattern->n_entries++;
pos += 4;
}
}
pattern->entry = (IT_ENTRY *) malloc(pattern->n_entries * sizeof(*pattern->entry));
if (!pattern->entry)
return -1;
entry = pattern->entry;
pos = 2;
for (row = 0; row < n_rows; row++) {
for (channel = 0; channel < n_channels; channel++) {
if (data[pos+0] | data[pos+2]) {
entry->channel = channel;
entry->mask = 0;
if (data[pos+0] > 0 && data[pos+0] <= 36) {
entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT;
entry->note = data[pos+0] + 35;
entry->instrument = data[pos+1] + 1;
}
entry->effect = 0;
entry->effectvalue = data[pos+3];
switch (data[pos+2]) {
case 2: if (data[pos+3]) entry->effect = IT_PORTAMENTO_DOWN; break; // XXX code calls this rs_portu, but it's adding to the period, which decreases the pitch
case 13: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN; break;
case 21: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN_ROW; break;
case 1: if (data[pos+3]) entry->effect = IT_PORTAMENTO_UP; break; // XXX same deal here, increasing the pitch
case 17: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP; break;
case 30: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP_ROW; break;
case 10: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_3; break;
case 11: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_4; break;
case 12: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_5; break;
case 15: entry->effect = IT_S; entry->effectvalue = EFFECT_VALUE(IT_S_SET_FILTER, data[pos+3] & 0x0F); break;
case 25: entry->effect = IT_JUMP_TO_ORDER; break;
case 27: entry->note = IT_NOTE_OFF; entry->mask |= IT_ENTRY_NOTE; break;
case 28: entry->effect = IT_SET_SPEED; break;
case 31:
if ( data[pos+3] <= 0x40 ) entry->effect = IT_SET_CHANNEL_VOLUME;
else if ( data[pos+3] <= 0x50 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x40; }
else if ( data[pos+3] <= 0x60 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x50; }
else if ( data[pos+3] <= 0x70 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x50; }
else if ( data[pos+3] <= 0x80 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x60; }
break;
}
if ( entry->effect ) entry->mask |= IT_ENTRY_EFFECT;
entry++;
}
pos += 4;
}
IT_SET_END_ROW(entry);
entry++;
}
return 0;
}
static void it_okt_read_sample_header(IT_SAMPLE *sample, const unsigned char * data)
{
int loop_start, loop_length;
memcpy(sample->name, data, 20);
sample->name[20] = 0;
sample->filename[0] = 0;
sample->length = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23];
sample->global_volume = 64;
sample->default_volume = data[29];
loop_start = ((data[24] << 8) | data[25]) << 1;
loop_length = ((data[26] << 8) | data[27]) << 1;
sample->sus_loop_start = loop_start;
sample->sus_loop_end = loop_start + loop_length;
if (sample->length <= 0) {
sample->flags = 0;
return;
}
sample->flags = IT_SAMPLE_EXISTS;
sample->default_pan = 0;
sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
sample->finetune = 0;
if (sample->sus_loop_end > sample->length)
sample->sus_loop_end = sample->length;
if (loop_length > 2)
sample->flags |= IT_SAMPLE_SUS_LOOP;
sample->vibrato_speed = 0;
sample->vibrato_depth = 0;
sample->vibrato_rate = 0;
sample->vibrato_waveform = 0; // do we have to set _all_ these?
sample->max_resampling_quality = -1;
}
static int it_okt_read_sample_data(IT_SAMPLE *sample, const char * data, int length)
{
if (length && sample->length) {
if (length < sample->length) {
sample->length = length;
if (length < sample->sus_loop_end) sample->sus_loop_end = length;
}
sample->data = malloc(length);
if (!sample->data)
return -1;
memcpy(sample->data, data, length);
}
return 0;
}
typedef struct IFF_CHUNK IFF_CHUNK;
typedef struct IFF_CHUNKED IFF_CHUNKED;
struct IFF_CHUNK
{
unsigned type;
unsigned char * data;
unsigned size;
};
struct IFF_CHUNKED
{
unsigned chunk_count;
IFF_CHUNK * chunks;
};
static IFF_CHUNKED *dumbfile_read_okt(DUMBFILE *f)
{
IFF_CHUNKED *mod = (IFF_CHUNKED *) malloc(sizeof(*mod));
if (!mod) return NULL;
mod->chunk_count = 0;
mod->chunks = 0;
for (;;)
{
long bytes_read;
IFF_CHUNK * chunk = ( IFF_CHUNK * ) realloc( mod->chunks, ( mod->chunk_count + 1 ) * sizeof( IFF_CHUNK ) );
if ( !chunk )
{
if ( mod->chunks ) free( mod->chunks );
free( mod );
return NULL;
}
mod->chunks = chunk;
chunk += mod->chunk_count;
bytes_read = dumbfile_mgetl( f );
if ( bytes_read < 0 ) break;
chunk->type = bytes_read;
chunk->size = dumbfile_mgetl( f );
if ( dumbfile_error( f ) ) break;
chunk->data = (unsigned char *) malloc( chunk->size );
if ( !chunk->data )
{
free( mod->chunks );
free( mod );
return NULL;
}
bytes_read = dumbfile_getnc( ( char * ) chunk->data, chunk->size, f );
if ( bytes_read < (long)chunk->size )
{
if ( bytes_read <= 0 ) {
free( chunk->data );
break;
} else {
chunk->size = bytes_read;
mod->chunk_count++;
break;
}
}
mod->chunk_count++;
}
if ( !mod->chunk_count ) {
if ( mod->chunks ) free(mod->chunks);
free(mod);
mod = NULL;
}
return mod;
}
void free_okt(IFF_CHUNKED * mod)
{
unsigned i;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].data) free(mod->chunks[i].data);
}
free(mod->chunks);
}
free(mod);
}
}
const IFF_CHUNK * get_chunk_by_type(IFF_CHUNKED * mod, unsigned type, unsigned offset)
{
unsigned i;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].type == type)
{
if (!offset) return &mod->chunks[i];
else offset--;
}
}
}
}
return NULL;
}
unsigned get_chunk_count(IFF_CHUNKED *mod, unsigned type)
{
unsigned i, count = 0;
if (mod)
{
if (mod->chunks)
{
for (i = 0; i < mod->chunk_count; i++)
{
if (mod->chunks[i].type == type) count++;
}
}
}
return count;
}
static DUMB_IT_SIGDATA *it_okt_load_sigdata(DUMBFILE *f)
{
DUMB_IT_SIGDATA *sigdata;
unsigned n_channels;
unsigned i, j, k, l;
IFF_CHUNKED *mod;
const IFF_CHUNK *chunk;
char signature[8];
if (dumbfile_getnc(signature, 8, f) < 8 ||
memcmp(signature, "OKTASONG", 8)) {
return NULL;
}
mod = dumbfile_read_okt(f);
if (!mod)
return NULL;
sigdata = (DUMB_IT_SIGDATA *) malloc(sizeof(*sigdata));
if (!sigdata) {
free_okt(mod);
return NULL;
}
sigdata->name[0] = 0;
chunk = get_chunk_by_type(mod, DUMB_ID('S','P','E','E'), 0);
if (!chunk || chunk->size < 2) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->speed = (chunk->data[0] << 8) | chunk->data[1];
chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0);
if (!chunk || chunk->size < 32) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_samples = chunk->size / 32;
chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0);
if (!chunk || chunk->size < 8) {
free(sigdata);
free_okt(mod);
return NULL;
}
n_channels = 0;
for (i = 0; i < 4; i++) {
j = (chunk->data[i * 2] << 8) | chunk->data[i * 2 + 1];
if (!j) n_channels++;
else if (j == 1) n_channels += 2;
}
if (!n_channels) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_pchannels = n_channels;
sigdata->sample = (IT_SAMPLE *) malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) {
free(sigdata);
free_okt(mod);
return NULL;
}
sigdata->song_message = NULL;
sigdata->order = NULL;
sigdata->instrument = NULL;
sigdata->pattern = NULL;
sigdata->midi = NULL;
sigdata->checkpoint = NULL;
sigdata->n_instruments = 0;
for (i = 0; i < (unsigned)sigdata->n_samples; i++)
sigdata->sample[i].data = NULL;
chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0);
for (i = 0; i < (unsigned)sigdata->n_samples; i++) {
it_okt_read_sample_header(&sigdata->sample[i], chunk->data + 32 * i);
}
sigdata->restart_position = 0;
chunk = get_chunk_by_type(mod, DUMB_ID('P','L','E','N'), 0);
if (!chunk || chunk->size < 2) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_orders = (chunk->data[0] << 8) | chunk->data[1];
// what if this is > 128?
if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
chunk = get_chunk_by_type(mod, DUMB_ID('P','A','T','T'), 0);
if (!chunk || chunk->size < (unsigned)sigdata->n_orders) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->order = (unsigned char *) malloc(sigdata->n_orders);
if (!sigdata->order) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
memcpy(sigdata->order, chunk->data, sigdata->n_orders);
/* Work out how many patterns there are. */
chunk = get_chunk_by_type(mod, DUMB_ID('S','L','E','N'), 0);
if (!chunk || chunk->size < 2) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->n_patterns = (chunk->data[0] << 8) | chunk->data[1];
j = get_chunk_count(mod, DUMB_ID('P','B','O','D'));
if (sigdata->n_patterns > (int)j) sigdata->n_patterns = (int)j;
if (!sigdata->n_patterns) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
sigdata->pattern = (IT_PATTERN *) malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
for (i = 0; i < (unsigned)sigdata->n_patterns; i++)
sigdata->pattern[i].entry = NULL;
/* Read in the patterns */
for (i = 0; i < (unsigned)sigdata->n_patterns; i++) {
chunk = get_chunk_by_type(mod, DUMB_ID('P','B','O','D'), i);
if (it_okt_read_pattern(&sigdata->pattern[i], chunk->data, chunk->size, n_channels) != 0) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
}
/* And finally, the sample data */
k = get_chunk_count(mod, DUMB_ID('S','B','O','D'));
for (i = 0, j = 0; i < (unsigned)sigdata->n_samples, j < k; i++) {
if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
chunk = get_chunk_by_type(mod, DUMB_ID('S','B','O','D'), j);
if (it_okt_read_sample_data(&sigdata->sample[i], (const char *)chunk->data, chunk->size)) {
_dumb_it_unload_sigdata(sigdata);
free_okt(mod);
return NULL;
}
j++;
}
}
for (; i < (unsigned)sigdata->n_samples; i++) {
sigdata->sample[i].flags = 0;
}
chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0);
for (i = 0, j = 0; i < n_channels, j < 4; j++) {
k = (chunk->data[j * 2] << 8) | chunk->data[j * 2 + 1];
l = (j == 1 || j == 2) ? 48 : 16;
if (k == 0) {
sigdata->channel_pan[i++] = l;
}
else if (k == 1) {
sigdata->channel_pan[i++] = l;
sigdata->channel_pan[i++] = l;
}
}
free_okt(mod);
/* Now let's initialise the remaining variables, and we're done! */
sigdata->flags = IT_WAS_AN_OKT | IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
sigdata->global_volume = 128;
sigdata->mixing_volume = 48;
/* We want 50 ticks per second; 50/6 row advances per second;
* 50*10=500 row advances per minute; 500/4=125 beats per minute.
*/
sigdata->tempo = 125;
sigdata->pan_separation = 128;
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
memset(sigdata->channel_pan + n_channels, 32, DUMB_IT_N_CHANNELS - n_channels);
_dumb_it_fix_invalid_orders(sigdata);
return sigdata;
}
DUH *DUMBEXPORT dumb_read_okt_quick(DUMBFILE *f)
{
sigdata_t *sigdata;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_okt_load_sigdata(f);
if (!sigdata)
return NULL;
{
const char *tag[1][2];
tag[0][0] = "FORMAT";
tag[0][1] = "Oktalyzer";
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
}
}

29
dumb/src/it/readokt2.c Normal file
View File

@ -0,0 +1,29 @@
/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* readokt2.c - Function to read an Oktalyzer / / \ \
* module from an open file and do | < / \_
* an initial run-through. | \/ /\ /
* \_ / > /
* | \ / /
* By Chris Moeller. | ' /
* \__/
*/
#include "dumb.h"
DUH *DUMBEXPORT dumb_read_okt(DUMBFILE *f)
{
DUH *duh = dumb_read_okt_quick(f);
dumb_it_do_initial_runthrough(duh);
return duh;
}

View File

@ -296,7 +296,7 @@ static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num,
if (flags & 0x80) { if (flags & 0x80) {
if ((*ptr < 60) && (channel < pchans)) { if ((*ptr < 60) && (channel < pchans)) {
entry->mask |= IT_ENTRY_NOTE; entry->mask |= IT_ENTRY_NOTE;
entry->note = *ptr + 36; entry->note = *ptr + 35;
} }
ptr++; ptr++;
if (*ptr) { if (*ptr) {

View File

@ -557,8 +557,8 @@ static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
return NULL; return NULL;
} }
sigdata->global_volume = dumbfile_getc(f) << 1; sigdata->global_volume = dumbfile_getc(f) * 16 / 11;
if ( !sigdata->global_volume || sigdata->global_volume > 128 ) sigdata->global_volume = 128; if ( !sigdata->global_volume || sigdata->global_volume > 93 ) sigdata->global_volume = 93;
sigdata->speed = dumbfile_getc(f); sigdata->speed = dumbfile_getc(f);
if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
sigdata->tempo = dumbfile_getc(f); sigdata->tempo = dumbfile_getc(f);

View File

@ -20,6 +20,7 @@
// IT_STEREO... :o // IT_STEREO... :o
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h>
#include "dumb.h" #include "dumb.h"
#include "internal/it.h" #include "internal/it.h"
@ -28,14 +29,16 @@
#define strnicmp strncasecmp #define strnicmp strncasecmp
#endif #endif
static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f ) static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, unsigned short *offset )
{ {
dumbfile_getnc( sample->filename, 12, f ); dumbfile_getnc( sample->filename, 12, f );
sample->filename[12] = 0; sample->filename[12] = 0;
memcpy( sample->name, sample->filename, 13 ); memcpy( sample->name, sample->filename, 13 );
dumbfile_skip( f, 2 + 2 ); dumbfile_skip( f, 2 );
*offset = dumbfile_igetw( f );
sample->length = dumbfile_igetw( f ); sample->length = dumbfile_igetw( f );
sample->loop_start = dumbfile_igetw( f ); sample->loop_start = dumbfile_igetw( f );
@ -78,24 +81,15 @@ static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f )
return dumbfile_error(f); return dumbfile_error(f);
} }
static int it_stm_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f ) static int it_stm_read_sample_data( IT_SAMPLE *sample, void *data_block, long offset )
{ {
int32 n;
if ( ! sample->length ) return 0; if ( ! sample->length ) return 0;
n = dumbfile_pos( f );
if ( n & 15 ) {
if ( dumbfile_skip( f, 16 - ( n & 15 ) ) )
return -1;
}
sample->data = malloc( sample->length ); sample->data = malloc( sample->length );
if (!sample->data) if (!sample->data)
return -1; return -1;
if ( dumbfile_getnc( sample->data, sample->length, f ) != sample->length ) memcpy( sample->data, (unsigned char*)data_block + offset, sample->length );
return -1;
return 0; return 0;
} }
@ -141,10 +135,6 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->effectvalue = buffer[ pos + 3 ]; entry->effectvalue = buffer[ pos + 3 ];
if ( entry->instrument && entry->instrument < 32 ) if ( entry->instrument && entry->instrument < 32 )
entry->mask |= IT_ENTRY_INSTRUMENT; entry->mask |= IT_ENTRY_INSTRUMENT;
if ( note == 0xFC || note == 0xFE ) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = IT_NOTE_CUT;
}
if ( note < 251 ) { if ( note < 251 ) {
entry->mask |= IT_ENTRY_NOTE; entry->mask |= IT_ENTRY_NOTE;
entry->note = ( note >> 4 ) * 12 + ( note & 0x0F ); entry->note = ( note >> 4 ) * 12 + ( note & 0x0F );
@ -153,9 +143,9 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->mask |= IT_ENTRY_VOLPAN; entry->mask |= IT_ENTRY_VOLPAN;
entry->mask |= IT_ENTRY_EFFECT; entry->mask |= IT_ENTRY_EFFECT;
switch ( entry->effect ) { switch ( entry->effect ) {
case IT_SET_SPEED: case IT_SET_SPEED:
entry->effectvalue >>= 4; /* taken care of in the renderer */
break; break;
case IT_BREAK_TO_ROW: case IT_BREAK_TO_ROW:
entry->effectvalue -= (entry->effectvalue >> 4) * 6; entry->effectvalue -= (entry->effectvalue >> 4) * 6;
@ -192,14 +182,20 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/) static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f, int * version)
{ {
DUMB_IT_SIGDATA *sigdata; DUMB_IT_SIGDATA *sigdata;
char tracker_name[ 8 ]; char tracker_name[ 8 ];
unsigned short sample_offset[ 31 ];
void *data_block;
int n; int n;
long o, p, q;
sigdata = malloc(sizeof(*sigdata)); sigdata = malloc(sizeof(*sigdata));
if (!sigdata) return NULL; if (!sigdata) return NULL;
@ -227,8 +223,7 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
return NULL; return NULL;
} }
/* *version = dumbfile_mgetw(f); */ *version = dumbfile_mgetw(f);
dumbfile_skip( f, 2 );
sigdata->song_message = NULL; sigdata->song_message = NULL;
sigdata->order = NULL; sigdata->order = NULL;
@ -242,16 +237,17 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
sigdata->n_samples = 31; sigdata->n_samples = 31;
sigdata->n_pchannels = 4; sigdata->n_pchannels = 4;
sigdata->tempo = 125; sigdata->tempo = 125;
sigdata->mixing_volume = 48; sigdata->mixing_volume = 48;
sigdata->pan_separation = 128; sigdata->pan_separation = 128;
/** WARNING: which ones? */ /** WARNING: which ones? */
sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M | IT_WAS_AN_STM | IT_STEREO;
sigdata->speed = dumbfile_getc(f) >> 4; n = dumbfile_getc(f);
if ( sigdata->speed < 1 ) sigdata->speed = 1; if ( n < 32 ) n = 32;
sigdata->n_patterns = dumbfile_getc(f); sigdata->speed = n;
sigdata->n_patterns = dumbfile_getc(f);
sigdata->global_volume = dumbfile_getc(f) << 1; sigdata->global_volume = dumbfile_getc(f) << 1;
if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128; if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128;
@ -287,7 +283,7 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
sigdata->channel_pan[ 3 ] = 16; sigdata->channel_pan[ 3 ] = 16;
for ( n = 0; n < sigdata->n_samples; ++n ) { for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_header( &sigdata->sample[ n ], f ) ) { if ( it_stm_read_sample_header( &sigdata->sample[ n ], f, &sample_offset[ n ] ) ) {
_dumb_it_unload_sigdata( sigdata ); _dumb_it_unload_sigdata( sigdata );
return NULL; return NULL;
} }
@ -300,7 +296,8 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
} }
/* Orders, byte each, length = sigdata->n_orders (should be even) */ /* Orders, byte each, length = sigdata->n_orders (should be even) */
dumbfile_getnc( sigdata->order, 128, f ); dumbfile_getnc( sigdata->order, *version >= 0x200 ? 128 : 64, f );
if (*version < 0x200) memset( sigdata->order + 64, 0xFF, 64 );
sigdata->restart_position = 0; sigdata->restart_position = 0;
for ( n = 127; n >= 0; --n ) { for ( n = 127; n >= 0; --n ) {
@ -332,54 +329,105 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
free( buffer ); free( buffer );
} }
o = LONG_MAX;
p = 0;
for ( n = 0; n < sigdata->n_samples; ++n ) { for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_data( &sigdata->sample[ n ], f ) ) { if ((sigdata->sample[ n ].flags & IT_SAMPLE_EXISTS) && sample_offset[ n ]) {
q = ((long)sample_offset[ n ]) * 16;
if (q < o) {
o = q;
}
if (q + sigdata->sample[ n ].length > p) {
p = q + sigdata->sample[ n ].length;
}
}
else {
sigdata->sample[ n ].flags = 0;
sigdata->sample[ n ].length = 0;
}
}
data_block = malloc( p - o );
if ( !data_block ) {
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
for ( n = 0, q = o / 16; n < sigdata->n_samples; ++n ) {
if ( sample_offset[ n ] ) {
sample_offset[ n ] = (unsigned short)(sample_offset[ n ] - q);
}
}
q = o - dumbfile_pos( f );
p -= o;
o = 0;
if ( q >= 0 ) dumbfile_skip( f, q );
else {
o = -q;
memset ( data_block, 0, o );
}
if ( dumbfile_getnc( (char*)data_block + o, p - o, f ) != p - o ) {
free( data_block );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
for ( n = 0; n < sigdata->n_samples; ++n ) {
if ( it_stm_read_sample_data( &sigdata->sample[ n ], data_block, ((long)sample_offset[ n ]) * 16 ) ) {
free( data_block );
_dumb_it_unload_sigdata( sigdata ); _dumb_it_unload_sigdata( sigdata );
return NULL; return NULL;
} }
} }
free( data_block );
_dumb_it_fix_invalid_orders(sigdata); _dumb_it_fix_invalid_orders(sigdata);
return sigdata; return sigdata;
} }
/*static char hexdigit(int in)
{
if (in < 10) return in + '0';
else return in + 'A' - 10;
}*/
DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f) DUH *DUMBEXPORT dumb_read_stm_quick(DUMBFILE *f)
{ {
sigdata_t *sigdata; sigdata_t *sigdata;
/*int ver;*/ int ver;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_stm_load_sigdata(f /*, &ver*/); sigdata = it_stm_load_sigdata(f , &ver);
if (!sigdata) if (!sigdata)
return NULL; return NULL;
{ {
/*char version[16];*/ char version[16];
const char *tag[2][2]; const char *tag[2][2];
tag[0][0] = "TITLE"; tag[0][0] = "TITLE";
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
tag[1][0] = "FORMAT"; tag[1][0] = "FORMAT";
tag[1][1] = "STM"; version[0] = 'S';
/*version[0] = 'S';
version[1] = 'T'; version[1] = 'T';
version[2] = 'M'; version[2] = 'M';
version[3] = ' '; version[3] = ' ';
version[4] = 'v'; version[4] = 'v';
version[5] = hexdigit((ver >> 8) & 15); version[5] = '0' + ((ver >> 8) & 15);
version[6] = '.'; version[6] = '.';
version[7] = hexdigit((ver >> 4) & 15); if ((ver & 255) > 99)
version[8] = hexdigit(ver & 15); {
version[9] = 0; version[7] = '0' + ((ver & 255) / 100 );
tag[1][1] = (const char *) &version;*/ version[8] = '0' + (((ver & 255) / 10) % 10);
version[9] = '0' + ((ver & 255) % 10);
version[10] = 0;
}
else
{
version[7] = '0' + ((ver & 255) / 10);
version[8] = '0' + ((ver & 255) % 10);
version[9] = 0;
}
tag[1][1] = (const char *) &version;
return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
} }
} }

View File

@ -111,11 +111,20 @@ typedef struct XM_INSTRUMENT_EXTRA
int vibrato_sweep; /* 0-0xFF */ int vibrato_sweep; /* 0-0xFF */
int vibrato_depth; /* 0-0x0F */ int vibrato_depth; /* 0-0x0F */
int vibrato_speed; /* 0-0x3F */ int vibrato_speed; /* 0-0x3F */
int sample_header_size;
} }
XM_INSTRUMENT_EXTRA; XM_INSTRUMENT_EXTRA;
/* Trims off trailing white space, usually added by the tracker on file creation
*/
static void trim_whitespace(char *ptr, size_t size)
{
char *p = ptr + size - 1;
while (p >= ptr && *p <= 0x20) *p-- = '\0';
}
/* Frees the original block if it can't resize it or if size is 0, and acts /* Frees the original block if it can't resize it or if size is 0, and acts
* as malloc if ptr is NULL. * as malloc if ptr is NULL.
*/ */
@ -329,7 +338,7 @@ static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels,
static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset)
{ {
int i, pos; int i, pos, val;
if (envelope->n_nodes > 12) { if (envelope->n_nodes > 12) {
/* XXX /* XXX
@ -346,12 +355,13 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data
pos = 0; pos = 0;
for (i = 0; i < envelope->n_nodes; i++) { for (i = 0; i < envelope->n_nodes; i++) {
envelope->node_t[i] = data[pos++]; envelope->node_t[i] = data[pos++];
if (data[pos] > 64) { val = data[pos++];
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]); if (val > 64) {
envelope->n_nodes = 0; TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val);
return -1; /* FT2 seems to simply clip the value */
val = 64;
} }
envelope->node_y[i] = (signed char)(data[pos++] + y_offset); envelope->node_y[i] = (signed char)(val + y_offset);
} }
return 0; return 0;
@ -359,21 +369,153 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data
typedef struct LIMITED_XM LIMITED_XM;
struct LIMITED_XM
{
unsigned char *buffered;
long ptr, limit, allocated;
DUMBFILE *remaining;
};
/* XXX */
struct DUMBFILE
{
DUMBFILE_SYSTEM *dfs;
void *file;
long pos;
};
static int limit_xm_resize(void *f, long n)
{
DUMBFILE *df = f;
LIMITED_XM *lx = df->file;
if (lx->buffered || n) {
if (n > lx->allocated) {
unsigned char *buffered = realloc( lx->buffered, n );
if ( !buffered ) return -1;
lx->buffered = buffered;
memset( buffered + lx->allocated, 0, n - lx->allocated );
lx->allocated = n;
}
if ( dumbfile_getnc( lx->buffered, n, lx->remaining ) < n ) return -1;
} else if (!n) {
if ( lx->buffered ) free( lx->buffered );
lx->buffered = NULL;
lx->allocated = 0;
}
lx->limit = n;
lx->ptr = 0;
return 0;
}
static int limit_xm_skip_end(void *f, int32 n)
{
DUMBFILE *df = f;
LIMITED_XM *lx = df->file;
return dumbfile_skip( lx->remaining, n );
}
static int limit_xm_skip(void *f, int32 n)
{
LIMITED_XM *lx = f;
lx->ptr += n;
return 0;
}
static int limit_xm_getc(void *f)
{
LIMITED_XM *lx = f;
if (lx->ptr >= lx->allocated) {
return 0;
}
return lx->buffered[lx->ptr++];
}
static long limit_xm_getnc(char *ptr, int32 n, void *f)
{
LIMITED_XM *lx = f;
int left;
left = lx->allocated - lx->ptr;
if (n > left) {
if (left > 0) {
memcpy( ptr, lx->buffered + lx->ptr, left );
memset( ptr + left, 0, n - left );
} else {
memset( ptr, 0, n );
}
} else {
memcpy( ptr, lx->buffered + lx->ptr, n );
}
lx->ptr += n;
return n;
}
static void limit_xm_close(void *f)
{
LIMITED_XM *lx = f;
if (lx->buffered) free(lx->buffered);
/* Do NOT close lx->remaining */
free(f);
}
DUMBFILE_SYSTEM limit_xm_dfs = {
NULL,
&limit_xm_skip,
&limit_xm_getc,
&limit_xm_getnc,
&limit_xm_close
};
static DUMBFILE *dumbfile_limit_xm(DUMBFILE *f)
{
LIMITED_XM * lx = malloc(sizeof(*lx));
lx->remaining = f;
lx->buffered = NULL;
lx->ptr = 0;
lx->limit = 0;
lx->allocated = 0;
return dumbfile_open_ex( lx, &limit_xm_dfs );
}
static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f) static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
{ {
uint32 size, bytes_read; uint32 size, bytes_read;
unsigned short vol_points[24]; unsigned short vol_points[24];
unsigned short pan_points[24]; unsigned short pan_points[24];
int i, type; int i, type;
const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2;
unsigned long skip_end = 0;
/* Header size. Tends to be more than the actual size of the structure. /* Header size. Tends to be more than the actual size of the structure.
* So unread bytes must be skipped before reading the first sample * So unread bytes must be skipped before reading the first sample
* header. * header.
*/ */
if ( limit_xm_resize( f, 4 ) < 0 ) return -1;
size = dumbfile_igetl(f); size = dumbfile_igetl(f);
if ( size == 0 ) size = max_size;
else if ( size > max_size )
{
skip_end = size - max_size;
size = max_size;
}
if ( limit_xm_resize( f, size - 4 ) < 0 ) return -1;
dumbfile_getnc(instrument->name, 22, f); dumbfile_getnc(instrument->name, 22, f);
instrument->name[22] = 0; instrument->name[22] = 0;
trim_whitespace(instrument->name, 22);
instrument->filename[0] = 0; instrument->filename[0] = 0;
dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */
extra->n_samples = dumbfile_igetw(f); extra->n_samples = dumbfile_igetw(f);
@ -385,12 +527,11 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA
if (extra->n_samples) { if (extra->n_samples) {
/* sample header size */ /* sample header size */
dumbfile_skip(f, 4); // XXX can't be trusted, as there are trackers that write the wrong value here
/*i = dumbfile_igetl(f); /*i = dumbfile_igetl(f);
if (i && i != 0x28) { // XXX some crap with 0 here if (!i || i > 0x28) i = 0x28;*/
TRACE("XM error: unexpected sample header size\n"); dumbfile_skip(f, 4);
return -1; i = 0x28;
}*/ extra->sample_header_size = i;
/* sample map */ /* sample map */
for (i = 0; i < 96; i++) { for (i = 0; i < 96; i++) {
@ -476,7 +617,10 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA
for (i = 0; i < 96; i++) for (i = 0; i < 96; i++)
instrument->map_sample[i] = 0; instrument->map_sample[i] = 0;
if (dumbfile_skip(f, size - bytes_read)) if (size > bytes_read && dumbfile_skip(f, size - bytes_read))
return -1;
if (skip_end && limit_xm_skip_end(f, skip_end))
return -1; return -1;
instrument->new_note_action = NNA_NOTE_CUT; instrument->new_note_action = NNA_NOTE_CUT;
@ -531,6 +675,7 @@ static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
dumbfile_getnc(sample->name, 22, f); dumbfile_getnc(sample->name, 22, f);
sample->name[22] = 0; sample->name[22] = 0;
trim_whitespace(sample->name, 22);
sample->filename[0] = 0; sample->filename[0] = 0;
@ -785,6 +930,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
return NULL; return NULL;
} }
sigdata->name[20] = 0; sigdata->name[20] = 0;
trim_whitespace(sigdata->name, 20);
if (dumbfile_getc(f) != 0x1A) { if (dumbfile_getc(f) != 0x1A) {
TRACE("XM error: 0x1A not found\n"); TRACE("XM error: 0x1A not found\n");
@ -924,16 +1070,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (i = 0; i < sigdata->n_instruments; i++) { for (i = 0; i < sigdata->n_instruments; i++) {
XM_INSTRUMENT_EXTRA extra; XM_INSTRUMENT_EXTRA extra;
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) { DUMBFILE * lf = dumbfile_limit_xm( f );
if ( !lf ) {
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
// XXX // XXX
if ( ! i ) if ( ! i )
{ {
TRACE("XM error: instrument %d\n", i+1); TRACE("XM error: instrument %d\n", i+1);
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
} }
else else
{ {
dumbfile_close( lf );
sigdata->n_instruments = i; sigdata->n_instruments = i;
break; break;
} }
@ -948,17 +1102,31 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
if (!sigdata->sample) { if (!sigdata->sample) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
} }
for (j = total_samples; j < total_samples+extra.n_samples; j++) for (j = total_samples; j < total_samples+extra.n_samples; j++)
sigdata->sample[j].data = NULL; sigdata->sample[j].data = NULL;
if ( limit_xm_resize( lf, 0 ) < 0 ) {
dumbfile_close( lf );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
/* read instrument's samples */ /* read instrument's samples */
for (j = 0; j < extra.n_samples; j++) { for (j = 0; j < extra.n_samples; j++) {
IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
int b = it_xm_read_sample_header(sample, f); int b;
if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
dumbfile_close( lf );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
b = it_xm_read_sample_header(sample, lf);
if (b < 0) { if (b < 0) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
} }
@ -975,12 +1143,15 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
} }
for (j = 0; j < extra.n_samples; j++) { for (j = 0; j < extra.n_samples; j++) {
if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) { if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
} }
} }
total_samples += extra.n_samples; total_samples += extra.n_samples;
} }
dumbfile_close( lf );
} }
sigdata->n_samples = total_samples; sigdata->n_samples = total_samples;
@ -1012,8 +1183,16 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (i = 0; i < sigdata->n_instruments; i++) { for (i = 0; i < sigdata->n_instruments; i++) {
XM_INSTRUMENT_EXTRA extra; XM_INSTRUMENT_EXTRA extra;
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) { DUMBFILE * lf = dumbfile_limit_xm( f );
if ( !lf ) {
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
TRACE("XM error: instrument %d\n", i+1); TRACE("XM error: instrument %d\n", i+1);
dumbfile_close(lf);
free(roguebytes); free(roguebytes);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
@ -1026,6 +1205,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
if (!sigdata->sample) { if (!sigdata->sample) {
dumbfile_close( lf );
free(roguebytes); free(roguebytes);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
return NULL; return NULL;
@ -1033,10 +1213,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
for (j = total_samples; j < total_samples+extra.n_samples; j++) for (j = total_samples; j < total_samples+extra.n_samples; j++)
sigdata->sample[j].data = NULL; sigdata->sample[j].data = NULL;
if ( limit_xm_resize( lf, 0 ) < 0 ) {
dumbfile_close( lf );
free( roguebytes );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
/* read instrument's samples */ /* read instrument's samples */
for (j = 0; j < extra.n_samples; j++) { for (j = 0; j < extra.n_samples; j++) {
IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
int b = it_xm_read_sample_header(sample, f); int b;
if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
dumbfile_close( lf );
free( roguebytes );
_dumb_it_unload_sigdata( sigdata );
return NULL;
}
b = it_xm_read_sample_header(sample, lf);
if (b < 0) { if (b < 0) {
free(roguebytes); free(roguebytes);
_dumb_it_unload_sigdata(sigdata); _dumb_it_unload_sigdata(sigdata);
@ -1055,6 +1249,8 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
} }
total_samples += extra.n_samples; total_samples += extra.n_samples;
} }
dumbfile_close( lf );
} }
sigdata->n_samples = total_samples; sigdata->n_samples = total_samples;

View File

@ -167,6 +167,7 @@ if (log) printf(" - %2d %02X", effect, value);
case XM_SET_GLOBAL_VOLUME: case XM_SET_GLOBAL_VOLUME:
effect = IT_SET_GLOBAL_VOLUME; effect = IT_SET_GLOBAL_VOLUME;
value *= 2; value *= 2;
if (value > 128) value = 128;
break; break;
case XM_KEY_OFF: case XM_KEY_OFF:
@ -187,6 +188,7 @@ if (log) printf(" - %2d %02X", effect, value);
case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break; case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break;
case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break; case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break;
case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break; case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break;
case EBASE+XM_E_SET_MIDI_MACRO: effect = SBASE+IT_S_SET_MIDI_MACRO; break;
case EBASE + XM_E_FINE_PORTA_UP: case EBASE + XM_E_FINE_PORTA_UP:
effect = IT_PORTAMENTO_UP; effect = IT_PORTAMENTO_UP;

View File

@ -877,6 +877,10 @@
RelativePath="..\..\src\helpers\barray.c" RelativePath="..\..\src\helpers\barray.c"
> >
</File> </File>
<File
RelativePath="..\..\src\helpers\blip_buf.c"
>
</File>
<File <File
RelativePath="..\..\src\helpers\clickrem.c" RelativePath="..\..\src\helpers\clickrem.c"
> >
@ -1560,6 +1564,14 @@
RelativePath="..\..\src\it\loadmtm2.c" RelativePath="..\..\src\it\loadmtm2.c"
> >
</File> </File>
<File
RelativePath="..\..\src\it\loadokt.c"
>
</File>
<File
RelativePath="..\..\src\it\loadokt2.c"
>
</File>
<File <File
RelativePath="..\..\src\it\loadoldpsm.c" RelativePath="..\..\src\it\loadoldpsm.c"
> >
@ -1778,6 +1790,14 @@
RelativePath="..\..\src\it\readmtm.c" RelativePath="..\..\src\it\readmtm.c"
> >
</File> </File>
<File
RelativePath="..\..\src\it\readokt.c"
>
</File>
<File
RelativePath="..\..\src\it\readokt2.c"
>
</File>
<File <File
RelativePath="..\..\src\it\readoldpsm.c" RelativePath="..\..\src\it\readoldpsm.c"
> >
@ -1960,6 +1980,10 @@
<Filter <Filter
Name="internal" Name="internal"
> >
<File
RelativePath="..\..\include\internal\blip_buf.h"
>
</File>
<File <File
RelativePath="..\..\include\internal\dumb.h" RelativePath="..\..\include\internal\dumb.h"
> >

View File

@ -6,7 +6,7 @@ if( NOT CMAKE_BUILD_TYPE MATCHES "Release" )
set( CMAKE_BUILD_TYPE "RelWithDebInfo" ) set( CMAKE_BUILD_TYPE "RelWithDebInfo" )
endif( NOT CMAKE_BUILD_TYPE MATCHES "Release" ) endif( NOT CMAKE_BUILD_TYPE MATCHES "Release" )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" )
if( NOT PROFILE ) if( NOT PROFILE )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" )
@ -15,7 +15,7 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( HAVE_NO_ARRAY_BOUNDS ) if( HAVE_NO_ARRAY_BOUNDS )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds" )
endif( HAVE_NO_ARRAY_BOUNDS ) endif( HAVE_NO_ARRAY_BOUNDS )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
add_library( gme add_library( gme
gme/Blip_Buffer.cpp gme/Blip_Buffer.cpp

View File

@ -62,10 +62,10 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out )
assert( blip_buf.samples_avail() == pair_count ); assert( blip_buf.samples_avail() == pair_count );
resampler.write( new_count ); resampler.write( new_count );
long count = resampler.read( sample_buf.begin(), sample_buf_size ); long count = resampler.read( sample_buf.begin(), sample_buf_size );
assert( count == (long) sample_buf_size ); assert( count == (long) sample_buf_size );
mix_samples( blip_buf, out ); mix_samples( blip_buf, out );
blip_buf.remove_samples( pair_count ); blip_buf.remove_samples( pair_count );
} }

View File

@ -41,7 +41,7 @@ public:
public: public:
// deprecated // deprecated
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }

View File

@ -34,7 +34,7 @@ public:
public: public:
// deprecated // deprecated
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }
enum { gym_rate = 60 }; enum { gym_rate = 60 };

View File

@ -58,7 +58,7 @@ public:
void ignore_silence( bool disable = true ); void ignore_silence( bool disable = true );
// Info for current track // Info for current track
Gme_File::track_info; using Gme_File::track_info;
blargg_err_t track_info( track_info_t* out ) const; blargg_err_t track_info( track_info_t* out ) const;
// Sound customization // Sound customization

View File

@ -44,7 +44,7 @@ public:
public: public:
// deprecated // deprecated
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }

View File

@ -46,7 +46,7 @@ public:
public: public:
// deprecated // deprecated
struct header_t { char tag [4]; }; struct header_t { char tag [4]; };
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }
void disable_playlist( bool = true ); // use clear_playlist() void disable_playlist( bool = true ); // use clear_playlist()

View File

@ -47,7 +47,7 @@ public:
public: public:
// deprecated // deprecated
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }
byte const* trailer() const; // use track_info() byte const* trailer() const; // use track_info()

View File

@ -51,7 +51,7 @@ public:
public: public:
// deprecated // deprecated
Music_Emu::load; using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); } { return load_remaining_( &h, sizeof h, in ); }
byte const* gd3_data( int* size_out = 0 ) const; // use track_info() byte const* gd3_data( int* size_out = 0 ) const; // use track_info()

View File

@ -8,9 +8,9 @@ if( MSVC )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4554 /wd4102" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4554 /wd4102" )
endif( MSVC ) endif( MSVC )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
add_definitions( -DINFNAN_CHECK -DMULTIPLE_THREADS ) add_definitions( -DINFNAN_CHECK -DMULTIPLE_THREADS )

View File

@ -1,8 +1,8 @@
cmake_minimum_required( VERSION 2.4 ) cmake_minimum_required( VERSION 2.4 )
if( CMAKE_COMPILER_IS_GNUC ) if( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" )
endif( CMAKE_COMPILER_IS_GNUC ) endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
add_library( jpeg add_library( jpeg
jcomapi.c jcomapi.c

View File

@ -1,8 +1,8 @@
cmake_minimum_required( VERSION 2.4 ) cmake_minimum_required( VERSION 2.4 )
if( CMAKE_COMPILER_IS_GNUC ) if( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" )
endif( CMAKE_COMPILER_IS_GNUC ) endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( LZMA_FILES set( LZMA_FILES
C/Archive/7z/7zDecode.c C/Archive/7z/7zDecode.c

View File

@ -1,3 +1,3 @@
This version of ZDoom must be compiled with any version between 4.22 and 4.28 inclusive. This version of ZDoom must be compiled with any version between 4.22 and 4.28 inclusive or 4.34.
Use of the latest 4.26 is recommended though due to technical issues with 4.28. Use of the latest 4.26 is recommended though due to technical issues with 4.28.

View File

@ -39,7 +39,7 @@ between the TEXTMAP and ENDMAP lumps:
BEHAVIOR = contains compiled ACS code BEHAVIOR = contains compiled ACS code
DIALOGUE = contains compiled Strife conversation scripts. DIALOGUE = contains compiled Strife conversation scripts.
ZNODES = Nodes (must be stored as extended GL nodes. Compression is allowed ZNODES = Nodes (must be stored as extended GL nodes. Compression is allowed
but deprecated for portability reasons.) but deprecated for portability reasons.)
BLOCKMAP = blockmap. It is recommended not to include this lump in UDMF maps. BLOCKMAP = blockmap. It is recommended not to include this lump in UDMF maps.
REJECT = reject table. Recommended use is for special effects only. REJECT = reject table. Recommended use is for special effects only.
@ -111,11 +111,17 @@ Note: All <bool> fields default to false unless mentioned otherwise.
midtex3d = <bool>; // Actors can walk on mid texture. midtex3d = <bool>; // Actors can walk on mid texture.
checkswitchrange = <bool>;// Switches can only be activated when vertically reachable. checkswitchrange = <bool>;// Switches can only be activated when vertically reachable.
blockprojectiles = <bool>;// Line blocks all projectiles blockprojectiles = <bool>;// Line blocks all projectiles
blockuse = <bool>; // Line blocks all use actions blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight blocksight = <bool>; // Line blocks monster line of sight
locknumber = <int>; // Line special is locked
arg0str = <string>; // Alternate string-based version of arg0
* Note about arg0str
For lines with ACS specials (80-86 and 226), if arg0str is present and non-null, it
will be used as the name of the script to execute, and arg0 will be ignored.
} }
sidedef sidedef
{ {
scalex_top = <float>; // X scale for upper texture, Default = 1.0. scalex_top = <float>; // X scale for upper texture, Default = 1.0.
@ -135,16 +141,19 @@ Note: All <bool> fields default to false unless mentioned otherwise.
light = <integer>; // This side's light level. Default is 0. light = <integer>; // This side's light level. Default is 0.
lightabsolute = <bool>; // true = 'light' is an absolute value. Default is lightabsolute = <bool>; // true = 'light' is an absolute value. Default is
// relative to the owning sector's light level. // relative to the owning sector's light level.
nofakecontrast = <bool>; // Disables use of fake contrast on this sidedef. lightfog = <bool>; // true = This side's relative lighting is used even in
smoothlighting = <bool>; // Use smooth fake contrast. // foggy sectors. Default is to disable relative
// lighting in foggy sectors.
nofakecontrast = <bool>; // Disables use of fake contrast on this sidedef.
smoothlighting = <bool>; // Use smooth fake contrast.
clipmidtex = <bool>; // Side's mid textures are clipped to floor and ceiling. clipmidtex = <bool>; // Side's mid textures are clipped to floor and ceiling.
wrapmidtex = <bool>; // Side's mid textures are wrapped. wrapmidtex = <bool>; // Side's mid textures are wrapped.
nodecals = <bool>; // Disables decals on the sidedef. nodecals = <bool>; // Disables decals on the sidedef.
} }
sector sector
{ {
xpanningfloor = <float>; // X texture offset of floor texture, Default = 0.0. xpanningfloor = <float>; // X texture offset of floor texture, Default = 0.0.
ypanningfloor = <float>; // Y texture offset of floor texture, Default = 0.0. ypanningfloor = <float>; // Y texture offset of floor texture, Default = 0.0.
xpanningceiling = <float>; // X texture offset of ceiling texture, Default = 0.0. xpanningceiling = <float>; // X texture offset of ceiling texture, Default = 0.0.
ypanningceiling = <float>; // Y texture offset of ceiling texture, Default = 0.0. ypanningceiling = <float>; // Y texture offset of ceiling texture, Default = 0.0.
@ -163,9 +172,9 @@ Note: All <bool> fields default to false unless mentioned otherwise.
alphafloor = <float>; // translucency of floor plane (only has meaning with Sector_SetPortal) Default is 1.0. alphafloor = <float>; // translucency of floor plane (only has meaning with Sector_SetPortal) Default is 1.0.
alphaceiling = <float>; // translucency of ceiling plane (only has meaning with Sector_SetPortal) Default is 1.0. alphaceiling = <float>; // translucency of ceiling plane (only has meaning with Sector_SetPortal) Default is 1.0.
renderstylefloor = <string>; // floor plane renderstyle (only has meaning with Sector_SetPortal); not implemented yet in software renderer renderstylefloor = <string>; // floor plane renderstyle (only has meaning with Sector_SetPortal); not implemented yet in software renderer
// can be "translucent" or "add", default is "translucent". // can be "translucent" or "add", default is "translucent".
renderstyleceiling = <string>; // ceiling plane renderstyle (only has meaning with Sector_SetPortal); not implemented yet in software renderer renderstyleceiling = <string>; // ceiling plane renderstyle (only has meaning with Sector_SetPortal); not implemented yet in software renderer
// can be "translucent" or "add", default is "translucent". // can be "translucent" or "add", default is "translucent".
gravity = <float>; // Sector's gravity. Default is 1.0. gravity = <float>; // Sector's gravity. Default is 1.0.
lightcolor = <integer>; // Sector's light color as RRGGBB value, default = 0xffffff. lightcolor = <integer>; // Sector's light color as RRGGBB value, default = 0xffffff.
fadecolor = <integer>; // Sector's fog color as RRGGBB value, default = 0x000000. fadecolor = <integer>; // Sector's fog color as RRGGBB value, default = 0x000000.
@ -173,29 +182,35 @@ Note: All <bool> fields default to false unless mentioned otherwise.
silent = <bool>; // Actors in this sector make no sound, silent = <bool>; // Actors in this sector make no sound,
nofallingdamage = <bool>; // Falling damage is disabled in this sector nofallingdamage = <bool>; // Falling damage is disabled in this sector
dropactors = <bool>; // Actors drop with instantly moving floors (*) dropactors = <bool>; // Actors drop with instantly moving floors (*)
norespawn = <bool>; // Players can not respawn in this sector norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
// sound sequence thing in the sector will override this property. // sound sequence thing in the sector will override this property.
hidden = <bool>; // if true this sector will not be drawn on the textured automap. hidden = <bool>; // if true this sector will not be drawn on the textured automap.
* Note about dropactors * Note about dropactors
The spec requires this to be false by default. Currently, however, ZDoom assumes this to be true The spec requires this to be false by default. Currently, however, ZDoom assumes this to be true
for Doom format maps so any map converter converting to the ZDoomTranslated namespace should for Doom format maps so any map converter converting to the ZDoomTranslated namespace should
set this flag for each tagged sector. set this flag for each tagged sector.
} }
thing thing
{ {
skill# = <bool> // Unlike the base spec, # can range from 1-16. skill# = <bool> // Unlike the base spec, # can range from 1-16.
class# = <bool> // Unlike the base spec, # can range from 1-16. class# = <bool> // Unlike the base spec, # can range from 1-16.
conversation = <int> // Assigns a conversation dialogue to this thing. conversation = <int> // Assigns a conversation dialogue to this thing.
// Parameter is the conversation ID, 0 meaning none. // Parameter is the conversation ID, 0 meaning none.
countsecret = <bool>; // Picking up this actor counts as a secret. countsecret = <bool>; // Picking up this actor counts as a secret.
arg0str = <string>; // Alternate string-based version of arg0
* Note about arg0str
For things with ACS specials (80-86 and 226), if arg0str is present and non-null, it
will be used as the name of the script to execute, and arg0 will be ignored.
} }
*** Special notes for map format conversions: *** Special notes for map format conversions:
@ -204,7 +219,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
Unless mentioned differently the arg being used to define the line ID Unless mentioned differently the arg being used to define the line ID
should be set to 0. should be set to 0.
The following line specials are affected: The following line specials are affected:
121: Line_SetIdentification, arg 0 121: Line_SetIdentification, arg 0
208: TranslucentLine, arg0 (arg0 must be preserved) 208: TranslucentLine, arg0 (arg0 must be preserved)
1: Polyobj_StartLine, arg3 1: Polyobj_StartLine, arg3
@ -213,16 +228,16 @@ Note: All <bool> fields default to false unless mentioned otherwise.
215: Teleport_Line, arg0 215: Teleport_Line, arg0
222: Scroll_Texture_Model, arg0 (arg0 must be preserved) 222: Scroll_Texture_Model, arg0 (arg0 must be preserved)
160: Sector_3DFloor, arg4 (both uses as high-byte of tag and line ID are not supported in UDMF and must be remapped) 160: Sector_3DFloor, arg4 (both uses as high-byte of tag and line ID are not supported in UDMF and must be remapped)
Some specials also allow setting the extended flags. These must also be Some specials also allow setting the extended flags. These must also be
converted to explicitly setting the flags through the defined map fields. converted to explicitly setting the flags through the defined map fields.
This affects the following specials: This affects the following specials:
121: Line_SetIdentification, arg1 121: Line_SetIdentification, arg1
208: TranslucentLine, arg3 208: TranslucentLine, arg3
These args are to be converted as follows to flags, bit by bit: These args are to be converted as follows to flags, bit by bit:
Bit 0 (Value 1): zoneboundary Bit 0 (Value 1): zoneboundary
Bit 1 (Value 2): jumpover Bit 1 (Value 2): jumpover
Bit 2 (Value 4): blockfloaters Bit 2 (Value 4): blockfloaters
@ -231,13 +246,13 @@ Note: All <bool> fields default to false unless mentioned otherwise.
Bit 5 (Value 32): midtex3d Bit 5 (Value 32): midtex3d
Bit 6 (Value 64): checkswitchrange Bit 6 (Value 64): checkswitchrange
Bit 7 (Value 128): firstsideonly Bit 7 (Value 128): firstsideonly
When used in special 208 this arg should be cleared afterward. When used in special 208 this arg should be cleared afterward.
Special 121 is not being used by UDMF maps in ZDoom and should be completely Special 121 is not being used by UDMF maps in ZDoom and should be completely
deleted after conversion. deleted after conversion.
======================================= =======================================
Changelog Changelog
======================================= =======================================
@ -303,6 +318,16 @@ Removed remarks of 8 being the maximum number of player classes/skill levels the
Added renderstyleceiling and renderstylefloor sector properties Added renderstyleceiling and renderstylefloor sector properties
Added Sector_Set3DFloor to list of specials that need to be handled for line ID remapping Added Sector_Set3DFloor to list of specials that need to be handled for line ID remapping
1.18 17.02.2012
Added arg0str linedef property.
Standardized whitespace.
1.19 24.02.2012
Added back locknumber property.
1.20 25.02.2012
Added arg0str thing property.
=============================================================================== ===============================================================================
EOF EOF
=============================================================================== ===============================================================================

View File

@ -1,5 +1,5 @@
=============================================================================== ===============================================================================
Universal Strife Dialog Format Specification v2.0 - 08/20/10 Universal Strife Dialog Format Specification v2.1 - 01/06/13
Written by Braden "Blzut3" Obrzut - admin@maniacsvault.net Written by Braden "Blzut3" Obrzut - admin@maniacsvault.net
@ -11,7 +11,7 @@ Graf Zahl
Quasar Quasar
et al. et al.
Copyright (c) 2010 Braden Obrzut. Copyright (c) 2013 Braden Obrzut.
Permission is granted to copy, distribute and/or modify this document Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2 under the terms of the GNU Free Documentation License, Version 1.2
or any later version published by the Free Software Foundation; or any later version published by the Free Software Foundation;
@ -19,6 +19,14 @@ et al.
=============================================================================== ===============================================================================
=======================================
Changes in v2.1
=======================================
* Pages are specified as starting as being indexed from 1 instead of 0. While
this technically renders the spec incompatible, all known implementations
used this convention as it was inline with the binary format.
======================================= =======================================
I. Grammar / Syntax I. Grammar / Syntax
======================================= =======================================
@ -77,7 +85,7 @@ conversation // Starts a dialog.
actor = <integer>; // mobj for this conversation's actor. If previously actor = <integer>; // mobj for this conversation's actor. If previously
// used, this will override the previous conversation. // used, this will override the previous conversation.
page // Starts a new page. Pages are automatically numbered starting at 0. page // Starts a new page. Pages are automatically numbered starting at 1.
{ {
name = <string>; // Name that goes in the upper left hand corner name = <string>; // Name that goes in the upper left hand corner
panel = <string>; // Name of lump to render as the background. panel = <string>; // Name of lump to render as the background.
@ -105,14 +113,14 @@ conversation // Starts a dialog.
// The amount of an item needed to successfully pick this option. // The amount of an item needed to successfully pick this option.
// This can be repeated, but only the first will be shown (provided // This can be repeated, but only the first will be shown (provided
// diaplaycost is true). All costs must be satisfied for success. // displaycost is true). All costs must be satisfied for success.
cost cost
{ {
item = <integer>; // Item that is required for this option. item = <integer>; // Item that is required for this option.
amount = <integer>; // Minimum amount of the item needed. amount = <integer>; // Minimum amount of the item needed.
} }
displaycost = <bool>; // Weather the cost should be displaycost = <bool>; // Whether the cost should be
// displayed with the option. // displayed with the option.
// If no cost is specified this should // If no cost is specified this should
// be ignored. // be ignored.

View File

@ -10,14 +10,14 @@ include( CheckCXXCompilerFlag )
include( FindPkgConfig ) include( FindPkgConfig )
option( NO_ASM "Disable assembly code" ) option( NO_ASM "Disable assembly code" )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
option( NO_STRIP "Do not strip Release or MinSizeRel builds" ) option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
# At least some versions of Xcode fail if you strip with the linker # At least some versions of Xcode fail if you strip with the linker
# instead of the separate strip utility. # instead of the separate strip utility.
if( APPLE ) if( APPLE )
set( NO_STRIP ON ) set( NO_STRIP ON )
endif( APPLE ) endif( APPLE )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" )
@ -44,7 +44,7 @@ set( MINOR_VERSIONS "50" "49" "48" "47" "46" "45" "44" "43" "42" "41"
"27" "26" "25" "24" "23" "22" "21" "20" "21" "19" "18" "17" "16" "27" "26" "25" "24" "23" "22" "21" "20" "21" "19" "18" "17" "16"
"15" "14" "13" "12" "11" "10" "09" "08" "07" "06" "05" "04" "03" "15" "14" "13" "12" "11" "10" "09" "08" "07" "06" "05" "04" "03"
"02" "01" "00" ) "02" "01" "00" )
set( MAJOR_VERSIONS "30" "28" "26" "24" "22" "20" ) set( MAJOR_VERSIONS "44" "34" "28" "26" "24" "22" "20" )
set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "../fmod" ) set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "../fmod" )
foreach( majver ${MAJOR_VERSIONS} ) foreach( majver ${MAJOR_VERSIONS} )
foreach( minver ${MINOR_VERSIONS} ) foreach( minver ${MINOR_VERSIONS} )
@ -401,7 +401,7 @@ endif( NO_OPENAL )
# Search for FluidSynth # Search for FluidSynth
include( ../FindFluidSynth.cmake ) find_package( FluidSynth )
# Search for NASM # Search for NASM
@ -503,7 +503,7 @@ endif( NOT NO_ASM )
set( SSE_MATTERS NO ) set( SSE_MATTERS NO )
# SSE only matters on 32-bit targets. We check compiler flags to know if we can do it. # SSE only matters on 32-bit targets. We check compiler flags to know if we can do it.
if( CMAKE_SIZEOF_VOID_P MATCHES "4" ) if( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc )
CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH ) CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH )
CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 ) CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 )
if( CAN_DO_MFPMATH ) if( CAN_DO_MFPMATH )
@ -515,7 +515,7 @@ if( CMAKE_SIZEOF_VOID_P MATCHES "4" )
set( SSE2_ENABLE -arch:SSE2 ) set( SSE2_ENABLE -arch:SSE2 )
set( SSE_MATTERS YES ) set( SSE_MATTERS YES )
endif( CAN_DO_MFPMATH ) endif( CAN_DO_MFPMATH )
endif( CMAKE_SIZEOF_VOID_P MATCHES "4" ) endif( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc )
if( SSE_MATTERS ) if( SSE_MATTERS )
if( WIN32 ) if( WIN32 )
@ -535,7 +535,7 @@ endif( SSE_MATTERS )
# Set up flags for GCC # Set up flags for GCC
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
if( PROFILE ) if( PROFILE )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
@ -547,24 +547,24 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( NOT PROFILE ) if( NOT PROFILE )
set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" ) set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" )
endif( NOT PROFILE ) endif( NOT PROFILE )
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${REL_CXX_FLAGS}" ) set( CMAKE_CXX_FLAGS_RELEASE "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}" )
set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_CXX_FLAGS}" ) set( CMAKE_CXX_FLAGS_MINSIZEREL "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_CXX_FLAGS}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused -Wextra -Wno-missing-field-initializers" ) set( CMAKE_CXX_FLAGS "-Wall -Wno-unused -Wextra -Wno-missing-field-initializers ${CMAKE_CXX_FLAGS}" )
# Remove extra warnings when using the official DirectX headers. # Remove extra warnings when using the official DirectX headers.
# Also, TDM-GCC 4.4.0 no longer accepts glibc-style printf formats as valid, # Also, TDM-GCC 4.4.0 no longer accepts glibc-style printf formats as valid,
# which is a royal pain. The previous version I had been using was fine with them. # which is a royal pain. The previous version I had been using was fine with them.
if( WIN32 ) if( WIN32 )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-comment -Wno-format" ) set( CMAKE_CXX_FLAGS "-Wno-unknown-pragmas -Wno-comment -Wno-format ${CMAKE_CXX_FLAGS}" )
endif( WIN32 ) endif( WIN32 )
if( NOT NO_STRIP ) if( NOT NO_STRIP )
set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s" ) set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s" )
set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s" ) set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s" )
endif( NOT NO_STRIP ) endif( NOT NO_STRIP )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
# Check for functions that may or may not exist. # Check for functions that may or may not exist.
@ -588,13 +588,6 @@ if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp ) add_definitions( -Dstrnicmp=strncasecmp )
endif( NOT STRNICMP_EXISTS ) endif( NOT STRNICMP_EXISTS )
if( NOT WIN32 )
CHECK_FUNCTION_EXISTS( sigtimedwait SIGTIMEDWAIT_EXISTS )
if( SIGTIMEDWAIT_EXISTS )
add_definitions( -DHAVE_SIGTIMEDWAIT )
endif( SIGTIMEDWAIT_EXISTS )
endif( NOT WIN32)
if( NOT MSVC ) if( NOT MSVC )
add_definitions( -D__forceinline=inline ) add_definitions( -D__forceinline=inline )
endif( NOT MSVC ) endif( NOT MSVC )
@ -634,10 +627,12 @@ if( BACKPATCH )
add_definitions( -DBACKPATCH ) add_definitions( -DBACKPATCH )
endif( BACKPATCH ) endif( BACKPATCH )
# Update svnrevision.h # Update gitinfo.h
get_target_property( UPDATEREVISION_EXE updaterevision LOCATION )
add_custom_target( revision_check ALL add_custom_target( revision_check ALL
COMMAND ${CMAKE_BINARY_DIR}/tools/updaterevision/updaterevision . src/svnrevision.h COMMAND ${UPDATEREVISION_EXE} src/gitinfo.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
DEPENDS updaterevision ) DEPENDS updaterevision )
@ -678,15 +673,15 @@ if( WIN32 )
win32/i_system.cpp win32/i_system.cpp
win32/st_start.cpp win32/st_start.cpp
win32/win32video.cpp ) win32/win32video.cpp )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
# CMake is not set up to compile and link rc files with GCC. :( # CMake is not set up to compile and link rc files with GCC. :(
add_custom_command( OUTPUT zdoom-rc.o add_custom_command( OUTPUT zdoom-rc.o
COMMAND windres -o zdoom-rc.o -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zdoom.rc COMMAND windres -o zdoom-rc.o -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zdoom.rc
DEPENDS win32/zdoom.rc ) DEPENDS win32/zdoom.rc )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} zdoom-rc.o ) set( SYSTEM_SOURCES ${SYSTEM_SOURCES} zdoom-rc.o )
else( CMAKE_COMPILER_IS_GNUCXX ) else( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc ) set( SYSTEM_SOURCES ${SYSTEM_SOURCES} win32/zdoom.rc )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
else( WIN32 ) else( WIN32 )
set( SYSTEM_SOURCES_DIR sdl ) set( SYSTEM_SOURCES_DIR sdl )
set( SYSTEM_SOURCES set( SYSTEM_SOURCES
@ -701,7 +696,7 @@ else( WIN32 )
sdl/sdlvideo.cpp sdl/sdlvideo.cpp
sdl/st_start.cpp ) sdl/st_start.cpp )
if( APPLE ) if( APPLE )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES} sdl/SDLMain.m sdl/iwadpicker_cocoa.mm ) set( SYSTEM_SOURCES ${SYSTEM_SOURCES} sdl/SDLMain.m sdl/iwadpicker_cocoa.mm sdl/i_system_cocoa.mm )
endif( APPLE ) endif( APPLE )
endif( WIN32 ) endif( WIN32 )
@ -717,21 +712,19 @@ else( NO_ASM )
ADD_ASM_FILE( asm_ia32 tmap2 ) ADD_ASM_FILE( asm_ia32 tmap2 )
ADD_ASM_FILE( asm_ia32 tmap3 ) ADD_ASM_FILE( asm_ia32 tmap3 )
endif( X64 ) endif( X64 )
if( WIN32 )
if( NOT X64 )
ADD_ASM_FILE( win32 wrappers )
endif( NOT X64 )
endif( WIN32 )
endif( NO_ASM ) endif( NO_ASM )
get_target_property( LEMON_EXE lemon LOCATION )
get_target_property( RE2C_EXE re2c LOCATION )
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.h add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.h
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y . COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y .
COMMAND ${CMAKE_BINARY_DIR}/tools/lemon/lemon xlat_parser.y COMMAND ${LEMON_EXE} xlat_parser.y
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y ) DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y )
add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h
COMMAND ${CMAKE_BINARY_DIR}/tools/re2c/re2c --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re COMMAND ${RE2C_EXE} --no-generation-date -s -o ${CMAKE_CURRENT_BINARY_DIR}/sc_man_scanner.h ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re
DEPENDS re2c ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re ) DEPENDS re2c ${CMAKE_CURRENT_SOURCE_DIR}/sc_man_scanner.re )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
@ -754,7 +747,37 @@ elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_FLUIDSYNTH ) add_definitions( -DHAVE_FLUIDSYNTH )
endif( DYN_FLUIDSYNTH ) endif( DYN_FLUIDSYNTH )
# Project files should be aware of the header files. We can GLOB these since
# there's generally a new cpp for every header so this file will get changed
if( WIN32 )
set( EXTRA_HEADER_DIRS win32/*.h )
else( WIN32 )
set( EXTRA_HEADER_DIRS sdl/*.h )
endif( WIN32 )
file( GLOB HEADER_FILES
${EXTRA_HEADER_DIRS}
fragglescript/*.h
g_doom/*.h
g_heretic/*.h
g_hexen/*.h
g_raven/*.h
g_shared/*.h
g_strife/*.h
intermission/*.h
menu/*.h
oplsynth/*.h
r_data/*.h
resourcefiles/*.h
sfmt/*.h
sound/*.h
textures/*.h
thingdef/*.h
xlat/*.h
*.h
)
add_executable( zdoom WIN32 add_executable( zdoom WIN32
${HEADER_FILES}
autostart.cpp autostart.cpp
${ASM_SOURCES} ${ASM_SOURCES}
${SYSTEM_SOURCES} ${SYSTEM_SOURCES}
@ -803,6 +826,7 @@ add_executable( zdoom WIN32
g_skill.cpp g_skill.cpp
gameconfigfile.cpp gameconfigfile.cpp
gi.cpp gi.cpp
gitinfo.cpp
hu_scores.cpp hu_scores.cpp
i_net.cpp i_net.cpp
info.cpp info.cpp
@ -892,6 +916,7 @@ add_executable( zdoom WIN32
tables.cpp tables.cpp
teaminfo.cpp teaminfo.cpp
tempfiles.cpp tempfiles.cpp
v_blend.cpp
v_collection.cpp v_collection.cpp
v_draw.cpp v_draw.cpp
v_font.cpp v_font.cpp
@ -967,6 +992,8 @@ add_executable( zdoom WIN32
oplsynth/music_opldumper_mididevice.cpp oplsynth/music_opldumper_mididevice.cpp
oplsynth/music_opl_mididevice.cpp oplsynth/music_opl_mididevice.cpp
oplsynth/opl_mus_player.cpp oplsynth/opl_mus_player.cpp
oplsynth/dosbox/opl.cpp
oplsynth/OPL3.cpp
resourcefiles/ancientzip.cpp resourcefiles/ancientzip.cpp
resourcefiles/file_7z.cpp resourcefiles/file_7z.cpp
resourcefiles/file_grp.cpp resourcefiles/file_grp.cpp
@ -1025,6 +1052,7 @@ add_executable( zdoom WIN32
thingdef/thingdef_data.cpp thingdef/thingdef_data.cpp
thingdef/thingdef_exp.cpp thingdef/thingdef_exp.cpp
thingdef/thingdef_expression.cpp thingdef/thingdef_expression.cpp
thingdef/thingdef_function.cpp
thingdef/thingdef_parse.cpp thingdef/thingdef_parse.cpp
thingdef/thingdef_properties.cpp thingdef/thingdef_properties.cpp
thingdef/thingdef_states.cpp thingdef/thingdef_states.cpp
@ -1098,7 +1126,7 @@ if( NOT WIN32 )
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make ) COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
endif( NOT WIN32 ) endif( NOT WIN32 )
if( CMAKE_COMPILER_IS_GNUCXX ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
# GCC misoptimizes this file # GCC misoptimizes this file
set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" ) set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
@ -1106,4 +1134,8 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( SSE_MATTERS ) if( SSE_MATTERS )
set_source_files_properties( x86.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mmmx" ) set_source_files_properties( x86.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mmmx" )
endif( SSE_MATTERS ) endif( SSE_MATTERS )
endif( CMAKE_COMPILER_IS_GNUCXX ) endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
if( MSVC )
set_target_properties(zdoom PROPERTIES LINK_FLAGS "/MANIFEST:NO")
endif( MSVC )

View File

@ -36,7 +36,7 @@ DEFINE_SPECIAL(ClearForceField, 34, 1, 1, 1) // [RH] Remove Strife's forcefie
DEFINE_SPECIAL(Floor_RaiseByValueTimes8, 35, 3, 3, 3) DEFINE_SPECIAL(Floor_RaiseByValueTimes8, 35, 3, 3, 3)
DEFINE_SPECIAL(Floor_LowerByValueTimes8, 36, 3, 3, 3) DEFINE_SPECIAL(Floor_LowerByValueTimes8, 36, 3, 3, 3)
DEFINE_SPECIAL(Floor_MoveToValue, 37, 3, 4, 4) DEFINE_SPECIAL(Floor_MoveToValue, 37, 3, 4, 4)
DEFINE_SPECIAL(Ceiling_Waggle, 38, 5, 5, 4) // [RH] Complement of Floor_Waggle DEFINE_SPECIAL(Ceiling_Waggle, 38, 5, 5, 5) // [RH] Complement of Floor_Waggle
DEFINE_SPECIAL(Teleport_ZombieChanger, 39, 2, 2, 2) // [RH] Needed for Strife DEFINE_SPECIAL(Teleport_ZombieChanger, 39, 2, 2, 2) // [RH] Needed for Strife
DEFINE_SPECIAL(Ceiling_LowerByValue, 40, 3, 3, 3) DEFINE_SPECIAL(Ceiling_LowerByValue, 40, 3, 3, 3)
DEFINE_SPECIAL(Ceiling_RaiseByValue, 41, 3, 3, 3) DEFINE_SPECIAL(Ceiling_RaiseByValue, 41, 3, 3, 3)
@ -69,7 +69,7 @@ DEFINE_SPECIAL(Floor_RaiseInstant, 67, 3, 3, 3)
DEFINE_SPECIAL(Floor_MoveToValueTimes8, 68, 4, 4, 4) DEFINE_SPECIAL(Floor_MoveToValueTimes8, 68, 4, 4, 4)
DEFINE_SPECIAL(Ceiling_MoveToValueTimes8, 69, 4, 4, 4) DEFINE_SPECIAL(Ceiling_MoveToValueTimes8, 69, 4, 4, 4)
DEFINE_SPECIAL(Teleport, 70, 1, 3, 3) DEFINE_SPECIAL(Teleport, 70, 1, 3, 3)
DEFINE_SPECIAL(Teleport_NoFog, 71, 1, 3, 3) DEFINE_SPECIAL(Teleport_NoFog, 71, 1, 4, 4)
DEFINE_SPECIAL(ThrustThing, 72, 2, 4, 4) DEFINE_SPECIAL(ThrustThing, 72, 2, 4, 4)
DEFINE_SPECIAL(DamageThing, 73, 1, 2, 2) DEFINE_SPECIAL(DamageThing, 73, 1, 2, 2)
DEFINE_SPECIAL(Teleport_NewMap, 74, 2, 3, 3) DEFINE_SPECIAL(Teleport_NewMap, 74, 2, 3, 3)
@ -82,7 +82,7 @@ DEFINE_SPECIAL(ACS_Execute, 80, 1, 5, 5)
DEFINE_SPECIAL(ACS_Suspend, 81, 2, 2, 2) DEFINE_SPECIAL(ACS_Suspend, 81, 2, 2, 2)
DEFINE_SPECIAL(ACS_Terminate, 82, 2, 2, 2) DEFINE_SPECIAL(ACS_Terminate, 82, 2, 2, 2)
DEFINE_SPECIAL(ACS_LockedExecute, 83, 5, 5, 5) DEFINE_SPECIAL(ACS_LockedExecute, 83, 5, 5, 5)
DEFINE_SPECIAL(ACS_ExecuteWithResult, 84, 1, 4, 4) DEFINE_SPECIAL(ACS_ExecuteWithResult, 84, 1, 5, 5)
DEFINE_SPECIAL(ACS_LockedExecuteDoor, 85, 5, 5, 5) DEFINE_SPECIAL(ACS_LockedExecuteDoor, 85, 5, 5, 5)
DEFINE_SPECIAL(Polyobj_MoveToSpot, 86, 3, 3, 3) DEFINE_SPECIAL(Polyobj_MoveToSpot, 86, 3, 3, 3)
DEFINE_SPECIAL(Polyobj_Stop, 87, 1, 1, 1) DEFINE_SPECIAL(Polyobj_Stop, 87, 1, 1, 1)
@ -97,7 +97,7 @@ DEFINE_SPECIAL(FloorAndCeiling_LowerByValue, 95, 3, 3, 3)
DEFINE_SPECIAL(FloorAndCeiling_RaiseByValue, 96, 3, 3, 3) DEFINE_SPECIAL(FloorAndCeiling_RaiseByValue, 96, 3, 3, 3)
DEFINE_SPECIAL(Ceiling_LowerAndCrushDist, 97, 3, 5, 5) DEFINE_SPECIAL(Ceiling_LowerAndCrushDist, 97, 3, 5, 5)
DEFINE_SPECIAL(Sector_SetTranslucent, 98, 3, 4, 4) DEFINE_SPECIAL(Sector_SetTranslucent, 98, 3, 4, 4)
DEFINE_SPECIAL(Floor_RaiseAndCrushDoom, 99, 3, 4, 4)
DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Up, 102, -1, -1, 2) DEFINE_SPECIAL(Scroll_Texture_Up, 102, -1, -1, 2)
@ -146,6 +146,7 @@ DEFINE_SPECIAL(Sector_Set3DFloor, 160, -1, -1, 5)
DEFINE_SPECIAL(Sector_SetContents, 161, -1, -1, 3) DEFINE_SPECIAL(Sector_SetContents, 161, -1, -1, 3)
// [RH] Begin new specials for ZDoom // [RH] Begin new specials for ZDoom
DEFINE_SPECIAL(Ceiling_CrushAndRaiseDist, 168, 3, 5, 5)
DEFINE_SPECIAL(Generic_Crusher2, 169, 5, 5, 5) DEFINE_SPECIAL(Generic_Crusher2, 169, 5, 5, 5)
DEFINE_SPECIAL(Sector_SetCeilingScale2, 170, 3, 3, 3) DEFINE_SPECIAL(Sector_SetCeilingScale2, 170, 3, 3, 3)
DEFINE_SPECIAL(Sector_SetFloorScale2, 171, 3, 3, 3) DEFINE_SPECIAL(Sector_SetFloorScale2, 171, 3, 3, 3)
@ -205,7 +206,7 @@ DEFINE_SPECIAL(Scroll_Ceiling, 224, 4, 4, 5)
DEFINE_SPECIAL(Scroll_Texture_Offsets, 225, -1, -1, 1) DEFINE_SPECIAL(Scroll_Texture_Offsets, 225, -1, -1, 1)
DEFINE_SPECIAL(ACS_ExecuteAlways, 226, 1, 5, 5) DEFINE_SPECIAL(ACS_ExecuteAlways, 226, 1, 5, 5)
DEFINE_SPECIAL(PointPush_SetForce, 227, -1, -1, 4) DEFINE_SPECIAL(PointPush_SetForce, 227, -1, -1, 4)
DEFINE_SPECIAL(Plat_RaiseAndStayTx0, 228, 2, 2, 2) DEFINE_SPECIAL(Plat_RaiseAndStayTx0, 228, 2, 3, 3)
DEFINE_SPECIAL(Thing_SetGoal, 229, 3, 4, 4) DEFINE_SPECIAL(Thing_SetGoal, 229, 3, 4, 4)
DEFINE_SPECIAL(Plat_UpByValueStayTx, 230, 3, 3, 3) DEFINE_SPECIAL(Plat_UpByValueStayTx, 230, 3, 3, 3)
DEFINE_SPECIAL(Plat_ToggleCeiling, 231, 1, 1, 1) DEFINE_SPECIAL(Plat_ToggleCeiling, 231, 1, 1, 1)
@ -219,7 +220,7 @@ DEFINE_SPECIAL(Floor_RaiseToLowestCeiling, 238, 2, 2, 2)
DEFINE_SPECIAL(Floor_RaiseByValueTxTy, 239, 3, 3, 3) DEFINE_SPECIAL(Floor_RaiseByValueTxTy, 239, 3, 3, 3)
DEFINE_SPECIAL(Floor_RaiseByTexture, 240, 2, 2, 2) DEFINE_SPECIAL(Floor_RaiseByTexture, 240, 2, 2, 2)
DEFINE_SPECIAL(Floor_LowerToLowestTxTy, 241, 2, 2, 2) DEFINE_SPECIAL(Floor_LowerToLowestTxTy, 241, 2, 2, 2)
DEFINE_SPECIAL(Floor_LowerToHighest, 242, 3, 3, 3) DEFINE_SPECIAL(Floor_LowerToHighest, 242, 3, 4, 4)
DEFINE_SPECIAL(Exit_Normal, 243, 1, 1, 1) DEFINE_SPECIAL(Exit_Normal, 243, 1, 1, 1)
DEFINE_SPECIAL(Exit_Secret, 244, 1, 1, 1) DEFINE_SPECIAL(Exit_Secret, 244, 1, 1, 1)
DEFINE_SPECIAL(Elevator_RaiseToNearest, 245, 2, 2, 2) DEFINE_SPECIAL(Elevator_RaiseToNearest, 245, 2, 2, 2)
@ -228,7 +229,7 @@ DEFINE_SPECIAL(Elevator_LowerToNearest, 247, 2, 2, 2)
DEFINE_SPECIAL(HealThing, 248, 1, 2, 2) DEFINE_SPECIAL(HealThing, 248, 1, 2, 2)
DEFINE_SPECIAL(Door_CloseWaitOpen, 249, 3, 4, 4) DEFINE_SPECIAL(Door_CloseWaitOpen, 249, 3, 4, 4)
DEFINE_SPECIAL(Floor_Donut, 250, 3, 3, 3) DEFINE_SPECIAL(Floor_Donut, 250, 3, 3, 3)
DEFINE_SPECIAL(FloorAndCeiling_LowerRaise, 251, 3, 3, 4) DEFINE_SPECIAL(FloorAndCeiling_LowerRaise, 251, 3, 4, 4)
DEFINE_SPECIAL(Ceiling_RaiseToNearest, 252, 2, 2, 2) DEFINE_SPECIAL(Ceiling_RaiseToNearest, 252, 2, 2, 2)
DEFINE_SPECIAL(Ceiling_LowerToLowest, 253, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerToLowest, 253, 2, 2, 2)
DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2) DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2)

View File

@ -30,9 +30,7 @@
#include "dthinker.h" #include "dthinker.h"
// States are tied to finite states are // States are tied to finite states are tied to animation frames.
// tied to animation frames.
// Needs precompiled tables/data structures.
#include "info.h" #include "info.h"
#include "doomdef.h" #include "doomdef.h"
@ -40,6 +38,7 @@
#include "r_data/renderstyle.h" #include "r_data/renderstyle.h"
#include "s_sound.h" #include "s_sound.h"
#include "memarena.h" #include "memarena.h"
#include "g_level.h"
struct subsector_t; struct subsector_t;
// //
@ -266,8 +265,8 @@ enum
// --- mobj.flags5 --- // --- mobj.flags5 ---
MF5_FASTER = 0x00000001, // moves faster when DF_FAST_MONSTERS or nightmare is on. MF5_DONTDRAIN = 0x00000001, // cannot be drained health from.
MF5_FASTMELEE = 0x00000002, // has a faster melee attack when DF_FAST_MONSTERS or nightmare is on. /* = 0x00000002, */
MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances.
/* = 0x00000008, */ /* = 0x00000008, */
MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret
@ -330,6 +329,8 @@ enum
MF6_DONTCORPSE = 0x02000000, // [RC] Don't autoset MF_CORPSE upon death and don't force Crash state change. MF6_DONTCORPSE = 0x02000000, // [RC] Don't autoset MF_CORPSE upon death and don't force Crash state change.
MF6_POISONALWAYS = 0x04000000, // Always apply poison, even when target can't take the damage. MF6_POISONALWAYS = 0x04000000, // Always apply poison, even when target can't take the damage.
MF6_DOHARMSPECIES = 0x08000000, // Do hurt one's own species with projectiles. MF6_DOHARMSPECIES = 0x08000000, // Do hurt one's own species with projectiles.
MF6_INTRYMOVE = 0x10000000, // Executing P_TryMove
MF6_NOTAUTOAIMED = 0x20000000, // Do not subject actor to player autoaim.
// --- mobj.renderflags --- // --- mobj.renderflags ---
@ -400,7 +401,7 @@ enum EBounceFlags
BOUNCE_Ceilings = 1<<2, // bounces off of ceilings BOUNCE_Ceilings = 1<<2, // bounces off of ceilings
BOUNCE_Actors = 1<<3, // bounces off of some actors BOUNCE_Actors = 1<<3, // bounces off of some actors
BOUNCE_AllActors = 1<<4, // bounces off of all actors (requires BOUNCE_Actors to be set, too) BOUNCE_AllActors = 1<<4, // bounces off of all actors (requires BOUNCE_Actors to be set, too)
BOUNCE_AutoOff = 1<<5, // when bouncing off a floor, if the new Z velocity is below 3.0, disable further bouncing BOUNCE_AutoOff = 1<<5, // when bouncing off a sector plane, if the new Z velocity is below 3.0, disable further bouncing
BOUNCE_HereticType = 1<<6, // goes into Death state when bouncing on floors or ceilings BOUNCE_HereticType = 1<<6, // goes into Death state when bouncing on floors or ceilings
BOUNCE_UseSeeSound = 1<<7, // compatibility fallback. This will only be set by BOUNCE_UseSeeSound = 1<<7, // compatibility fallback. This will only be set by
@ -412,6 +413,8 @@ enum EBounceFlags
// MBF bouncing is a bit different from other modes as Killough coded many special behavioral cases // MBF bouncing is a bit different from other modes as Killough coded many special behavioral cases
// for them that are not present in ZDoom, so it is necessary to identify it properly. // for them that are not present in ZDoom, so it is necessary to identify it properly.
BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag. BOUNCE_MBF = 1<<12, // This in itself is not a valid mode, but replaces MBF's MF_BOUNCE flag.
BOUNCE_AutoOffFloorOnly = 1<<13, // like BOUNCE_AutoOff, but only on floors
BOUNCE_UseBounceState = 1<<14, // Use Bounce[.*] states
BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF, BOUNCE_TypeMask = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff | BOUNCE_HereticType | BOUNCE_MBF,
@ -581,15 +584,14 @@ public:
bool AdjustReflectionAngle (AActor *thing, angle_t &angle); bool AdjustReflectionAngle (AActor *thing, angle_t &angle);
// Returns true if this actor is within melee range of its target // Returns true if this actor is within melee range of its target
bool CheckMeleeRange (); bool CheckMeleeRange();
// BeginPlay: Called just after the actor is created virtual void BeginPlay(); // Called immediately after the actor is created
virtual void BeginPlay (); virtual void PostBeginPlay(); // Called immediately before the actor's first tick
virtual void PostBeginPlay (); virtual void LevelSpawned(); // Called after BeginPlay if this actor was spawned by the world
// LevelSpawned: Called after BeginPlay if this actor was spawned by the world virtual void HandleSpawnFlags(); // Translates SpawnFlags into in-game flags.
virtual void LevelSpawned ();
// Translates SpawnFlags into in-game flags. virtual void MarkPrecacheSounds() const; // Marks sounds used by this actor for precaching.
virtual void HandleSpawnFlags ();
virtual void Activate (AActor *activator); virtual void Activate (AActor *activator);
virtual void Deactivate (AActor *activator); virtual void Deactivate (AActor *activator);
@ -597,11 +599,11 @@ public:
virtual void Tick (); virtual void Tick ();
// Called when actor dies // Called when actor dies
virtual void Die (AActor *source, AActor *inflictor); virtual void Die (AActor *source, AActor *inflictor, int dmgflags = 0);
// Perform some special damage action. Returns the amount of damage to do. // Perform some special damage action. Returns the amount of damage to do.
// Returning -1 signals the damage routine to exit immediately // Returning -1 signals the damage routine to exit immediately
virtual int DoSpecialDamage (AActor *target, int damage); virtual int DoSpecialDamage (AActor *target, int damage, FName damagetype);
// Like DoSpecialDamage, but called on the actor receiving the damage. // Like DoSpecialDamage, but called on the actor receiving the damage.
virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype);
@ -656,6 +658,9 @@ public:
// Tosses an item out of the inventory. // Tosses an item out of the inventory.
virtual AInventory *DropInventory (AInventory *item); virtual AInventory *DropInventory (AInventory *item);
// Removes all items from the inventory.
void ClearInventory();
// Returns true if this view is considered "local" for the player. // Returns true if this view is considered "local" for the player.
bool CheckLocalView (int playernum) const; bool CheckLocalView (int playernum) const;
@ -696,7 +701,7 @@ public:
virtual bool Massacre (); virtual bool Massacre ();
// Transforms the actor into a finely-ground paste // Transforms the actor into a finely-ground paste
bool Grind(bool items); virtual bool Grind(bool items);
// Is the other actor on my team? // Is the other actor on my team?
bool IsTeammate (AActor *other); bool IsTeammate (AActor *other);
@ -707,9 +712,20 @@ public:
// Do I hate the other actor? // Do I hate the other actor?
bool IsHostile (AActor *other); bool IsHostile (AActor *other);
inline bool IsNoClip2() const;
// What species am I? // What species am I?
virtual FName GetSpecies(); virtual FName GetSpecies();
fixed_t GetBobOffset(fixed_t ticfrac=0) const
{
if (!(flags2 & MF2_FLOATBOB))
{
return 0;
}
return finesine[MulScale22(((FloatBobPhase + level.maptime) << FRACBITS) + ticfrac, FINEANGLES) & FINEMASK] * 8;
}
// Enter the crash state // Enter the crash state
void Crash(); void Crash();
@ -763,6 +779,10 @@ public:
return bloodcls; return bloodcls;
} }
inline void SetFriendPlayer(player_t *player);
bool IsVisibleToPlayer() const;
// Calculate amount of missile damage // Calculate amount of missile damage
virtual int GetMissileDamage(int mask, int add); virtual int GetMissileDamage(int mask, int add);
@ -773,6 +793,8 @@ public:
const char *GetTag(const char *def = NULL) const; const char *GetTag(const char *def = NULL) const;
void SetTag(const char *def); void SetTag(const char *def);
// Triggers SECSPAC_Exit/SECSPAC_Enter and related events if oldsec != current sector
void CheckSectorTransition(sector_t *oldsec);
// info for drawing // info for drawing
// NOTE: The first member variable *must* be x. // NOTE: The first member variable *must* be x.
@ -814,6 +836,10 @@ public:
DWORD flags4; // [RH] Even more flags! DWORD flags4; // [RH] Even more flags!
DWORD flags5; // OMG! We need another one. DWORD flags5; // OMG! We need another one.
DWORD flags6; // Shit! Where did all the flags go? DWORD flags6; // Shit! Where did all the flags go?
// [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it.
DWORD VisibleToTeam;
int special1; // Special info int special1; // Special info
int special2; // Special info int special2; // Special info
int health; int health;
@ -845,6 +871,8 @@ public:
int special; // special int special; // special
int args[5]; // special arguments int args[5]; // special arguments
int accuracy, stamina; // [RH] Strife stats -- [XA] moved here for DECORATE/ACS access.
AActor *inext, **iprev;// Links to other mobjs in same bucket AActor *inext, **iprev;// Links to other mobjs in same bucket
TObjPtr<AActor> goal; // Monster's goal if not chasing anything TObjPtr<AActor> goal; // Monster's goal if not chasing anything
int waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes int waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes
@ -950,6 +978,7 @@ private:
static FSharedStringArena mStringPropertyData; static FSharedStringArena mStringPropertyData;
friend class FActorIterator; friend class FActorIterator;
friend bool P_IsTIDUsed(int tid);
sector_t *LinkToWorldForMapThing (); sector_t *LinkToWorldForMapThing ();
@ -978,6 +1007,11 @@ public:
return GetClass()->ActorInfo->FindState(2, names, exact); return GetClass()->ActorInfo->FindState(2, names, exact);
} }
FState *FindState(int numnames, FName *names, bool exact = false) const
{
return GetClass()->ActorInfo->FindState(numnames, names, exact);
}
bool HasSpecialDeathStates () const; bool HasSpecialDeathStates () const;
}; };
@ -1044,6 +1078,9 @@ public:
} }
}; };
bool P_IsTIDUsed(int tid);
int P_FindUniqueTID(int start_tid, int limit);
inline AActor *Spawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) inline AActor *Spawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement)
{ {
return AActor::StaticSpawn (type, x, y, z, allowreplacement); return AActor::StaticSpawn (type, x, y, z, allowreplacement);

View File

@ -40,6 +40,8 @@
#include "c_bind.h" #include "c_bind.h"
#include "farchive.h" #include "farchive.h"
#include "r_renderer.h" #include "r_renderer.h"
#include "r_sky.h"
#include "sbar.h"
#include "m_cheat.h" #include "m_cheat.h"
#include "i_system.h" #include "i_system.h"
@ -308,6 +310,7 @@ struct islope_t
static TArray<mline_t> MapArrow; static TArray<mline_t> MapArrow;
static TArray<mline_t> CheatMapArrow; static TArray<mline_t> CheatMapArrow;
static TArray<mline_t> CheatKey; static TArray<mline_t> CheatKey;
static TArray<mline_t> EasyKey;
#define R (MAPUNIT) #define R (MAPUNIT)
// [RH] Avoid lots of warnings without compiler-specific #pragmas // [RH] Avoid lots of warnings without compiler-specific #pragmas
@ -536,10 +539,12 @@ void AM_StaticInit()
MapArrow.Clear(); MapArrow.Clear();
CheatMapArrow.Clear(); CheatMapArrow.Clear();
CheatKey.Clear(); CheatKey.Clear();
EasyKey.Clear();
if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow); if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow);
if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow); if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow);
AM_ParseArrow(CheatKey, "maparrows/key.txt"); AM_ParseArrow(CheatKey, "maparrows/key.txt");
AM_ParseArrow(EasyKey, "maparrows/ravenkey.txt");
if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined"); if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined");
char namebuf[9]; char namebuf[9];
@ -1077,7 +1082,7 @@ void AM_Stop ()
{ {
automapactive = false; automapactive = false;
stopped = true; stopped = true;
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
viewactive = true; viewactive = true;
} }
@ -1155,7 +1160,10 @@ void AM_NewResolution()
CCMD (togglemap) CCMD (togglemap)
{ {
gameaction = ga_togglemap; if (gameaction == ga_nothing)
{
gameaction = ga_togglemap;
}
} }
//============================================================================= //=============================================================================
@ -1173,7 +1181,7 @@ void AM_ToggleMap ()
if (dmflags2 & DF2_NO_AUTOMAP) if (dmflags2 & DF2_NO_AUTOMAP)
return; return;
SB_state = screen->GetPageCount (); ST_SetNeedRefresh();
if (!automapactive) if (!automapactive)
{ {
AM_Start (); AM_Start ();
@ -1184,7 +1192,7 @@ void AM_ToggleMap ()
if (am_overlay==1 && viewactive) if (am_overlay==1 && viewactive)
{ {
viewactive = false; viewactive = false;
SB_state = screen->GetPageCount (); ST_SetNeedRefresh();
} }
else else
{ {
@ -1605,9 +1613,10 @@ void AM_drawSubsectors()
angle_t rotation; angle_t rotation;
sector_t tempsec; sector_t tempsec;
int floorlight, ceilinglight; int floorlight, ceilinglight;
fixed_t scalex, scaley;
double originx, originy; double originx, originy;
FDynamicColormap *colormap; FDynamicColormap *colormap;
mpoint_t originpt;
for (int i = 0; i < numsubsectors; ++i) for (int i = 0; i < numsubsectors; ++i)
{ {
@ -1636,27 +1645,17 @@ void AM_drawSubsectors()
// For lighting and texture determination // For lighting and texture determination
sector_t *sec = Renderer->FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false); sector_t *sec = Renderer->FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false);
// Find texture origin. // Find texture origin.
mpoint_t originpt = { -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS, originpt.x = -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS;
sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS }; originpt.y = sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS;
rotation = 0 - sec->GetAngle(sector_t::floor); rotation = 0 - sec->GetAngle(sector_t::floor);
// Apply the floor's rotation to the texture origin.
if (rotation != 0)
{
AM_rotate(&originpt.x, &originpt.y, rotation);
}
// Apply the automap's rotation to the texture origin.
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
rotation += ANG90 - players[consoleplayer].camera->angle;
AM_rotatePoint(&originpt.x, &originpt.y);
}
originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
// Coloring for the polygon // Coloring for the polygon
colormap = sec->ColorMap; colormap = sec->ColorMap;
FTextureID maptex = sec->GetTexture(sector_t::floor); FTextureID maptex = sec->GetTexture(sector_t::floor);
scalex = sec->GetXScale(sector_t::floor);
scaley = sec->GetYScale(sector_t::floor);
#ifdef _3DFLOORS #ifdef _3DFLOORS
if (sec->e->XFloor.ffloors.Size()) if (sec->e->XFloor.ffloors.Size())
@ -1670,6 +1669,7 @@ void AM_drawSubsectors()
// (Make the comparison in floating point to avoid overflows and improve performance.) // (Make the comparison in floating point to avoid overflows and improve performance.)
double secx; double secx;
double secy; double secy;
double seczb, seczt;
double cmpz = FIXED2DBL(viewz); double cmpz = FIXED2DBL(viewz);
if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector) if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector)
@ -1683,17 +1683,33 @@ void AM_drawSubsectors()
secx = FIXED2DBL(sec->soundorg[0]); secx = FIXED2DBL(sec->soundorg[0]);
secy = FIXED2DBL(sec->soundorg[1]); secy = FIXED2DBL(sec->soundorg[1]);
} }
seczb = floorplane->ZatPoint(secx, secy);
seczt = sec->ceilingplane.ZatPoint(secx, secy);
for (unsigned int i = 0; i < sec->e->XFloor.ffloors.Size(); ++i) for (unsigned int i = 0; i < sec->e->XFloor.ffloors.Size(); ++i)
{ {
F3DFloor *rover = sec->e->XFloor.ffloors[i]; F3DFloor *rover = sec->e->XFloor.ffloors[i];
if (!(rover->flags & FF_EXISTS)) continue; if (!(rover->flags & FF_EXISTS)) continue;
if (rover->flags & FF_FOG) continue; if (rover->flags & FF_FOG) continue;
if (!(rover->flags & FF_RENDERPLANES)) continue;
if (rover->alpha == 0) continue; if (rover->alpha == 0) continue;
if (rover->top.plane->ZatPoint(secx, secy) < cmpz) double roverz = rover->top.plane->ZatPoint(secx, secy);
// Ignore 3D floors that are above or below the sector itself:
// they are hidden. Since 3D floors are sorted top to bottom,
// if we get below the sector floor, we can stop.
if (roverz > seczt) continue;
if (roverz < seczb) break;
if (roverz < cmpz)
{ {
maptex = *(rover->top.texture); maptex = *(rover->top.texture);
floorplane = rover->top.plane; floorplane = rover->top.plane;
sector_t *model = rover->top.model;
int selector = (rover->flags & FF_INVERTPLANES) ? sector_t::floor : sector_t::ceiling;
rotation = 0 - model->GetAngle(selector);
scalex = model->GetXScale(selector);
scaley = model->GetYScale(selector);
originpt.x = -model->GetXOffset(selector) >> FRACTOMAPBITS;
originpt.y = model->GetYOffset(selector) >> FRACTOMAPBITS;
break; break;
} }
} }
@ -1703,6 +1719,24 @@ void AM_drawSubsectors()
colormap = light->extra_colormap; colormap = light->extra_colormap;
} }
#endif #endif
if (maptex == skyflatnum)
{
continue;
}
// Apply the floor's rotation to the texture origin.
if (rotation != 0)
{
AM_rotate(&originpt.x, &originpt.y, rotation);
}
// Apply the automap's rotation to the texture origin.
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
rotation += ANG90 - players[consoleplayer].camera->angle;
AM_rotatePoint(&originpt.x, &originpt.y);
}
originx = f_x + ((originpt.x - m_x) * scale / float(1 << 24));
originy = f_y + (f_h - (originpt.y - m_y) * scale / float(1 << 24));
// If this subsector has not actually been seen yet (because you are cheating // If this subsector has not actually been seen yet (because you are cheating
// to see it on the map), tint and desaturate it. // to see it on the map), tint and desaturate it.
@ -1719,15 +1753,19 @@ void AM_drawSubsectors()
} }
// Draw the polygon. // Draw the polygon.
screen->FillSimplePoly(TexMan(maptex), FTexture *pic = TexMan(maptex);
&points[0], points.Size(), if (pic != NULL && pic->UseType != FTexture::TEX_Null)
originx, originy, {
scale / (FIXED2FLOAT(sec->GetXScale(sector_t::floor)) * float(1 << MAPBITS)), screen->FillSimplePoly(TexMan(maptex),
scale / (FIXED2FLOAT(sec->GetYScale(sector_t::floor)) * float(1 << MAPBITS)), &points[0], points.Size(),
rotation, originx, originy,
colormap, scale / (FIXED2DBL(scalex) * float(1 << MAPBITS)),
floorlight scale / (FIXED2DBL(scaley) * float(1 << MAPBITS)),
); rotation,
colormap,
floorlight
);
}
} }
} }
@ -1907,6 +1945,7 @@ void AM_drawWalls (bool allmap)
{ {
int i; int i;
static mline_t l; static mline_t l;
int lock, color;
for (i = 0; i < numlines; i++) for (i = 0; i < numlines; i++)
{ {
@ -1942,8 +1981,16 @@ void AM_drawWalls (bool allmap)
AM_drawMline(&l, SecretWallColor); AM_drawMline(&l, SecretWallColor);
else else
AM_drawMline(&l, WallColor); AM_drawMline(&l, WallColor);
} } else if (lines[i].locknumber > 0) { // [Dusk] specials w/ locknumbers
else if ((lines[i].special == Teleport || lock = lines[i].locknumber;
color = P_GetMapColorForLock(lock);
AMColor c;
if (color >= 0) c.FromRGB(RPART(color), GPART(color), BPART(color));
else c = LockedColor;
AM_drawMline (&l, c);
} else if ((lines[i].special == Teleport ||
lines[i].special == Teleport_NoFog || lines[i].special == Teleport_NoFog ||
lines[i].special == Teleport_ZombieChanger || lines[i].special == Teleport_ZombieChanger ||
lines[i].special == Teleport_Line) && lines[i].special == Teleport_Line) &&
@ -1969,13 +2016,12 @@ void AM_drawWalls (bool allmap)
if (am_colorset == 0 || am_colorset == 3) // Raven games show door colors if (am_colorset == 0 || am_colorset == 3) // Raven games show door colors
{ {
int P_GetMapColorForLock(int lock); int P_GetMapColorForLock(int lock);
int lock;
if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated) if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated)
lock=lines[i].args[3]; lock=lines[i].args[3];
else lock=lines[i].args[4]; else lock=lines[i].args[4];
int color = P_GetMapColorForLock(lock); color = P_GetMapColorForLock(lock);
AMColor c; AMColor c;
@ -2240,6 +2286,49 @@ void AM_drawPlayers ()
// //
//============================================================================= //=============================================================================
void AM_drawKeys ()
{
AMColor color;
mpoint_t p;
angle_t angle;
TThinkerIterator<AKey> it;
AKey *key;
while ((key = it.Next()) != NULL)
{
p.x = key->x >> FRACTOMAPBITS;
p.y = key->y >> FRACTOMAPBITS;
angle = key->angle;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint (&p.x, &p.y);
angle += ANG90 - players[consoleplayer].camera->angle;
}
color = ThingColor;
if (key->flags & MF_SPECIAL)
{
// Find the key's own color.
// Only works correctly if single-key locks have lower numbers than any-key locks.
// That is the case for all default keys, however.
int P_GetMapColorForKey (AInventory * key);
int c = P_GetMapColorForKey(key);
if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
else color = ThingColor_CountItem;
AM_drawLineCharacter(&EasyKey[0], EasyKey.Size(), 0, 0, color, p.x, p.y);
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void AM_drawThings () void AM_drawThings ()
{ {
AMColor color; AMColor color;
@ -2278,7 +2367,12 @@ void AM_drawThings ()
// That is the case for all default keys, however. // That is the case for all default keys, however.
if (t->IsKindOf(RUNTIME_CLASS(AKey))) if (t->IsKindOf(RUNTIME_CLASS(AKey)))
{ {
if (am_showkeys) if (G_SkillProperty(SKILLP_EasyKey))
{
// Already drawn by AM_drawKeys(), so don't draw again
color.Index = -1;
}
else if (am_showkeys)
{ {
int P_GetMapColorForKey (AInventory * key); int P_GetMapColorForKey (AInventory * key);
int c = P_GetMapColorForKey(static_cast<AKey *>(t)); int c = P_GetMapColorForKey(static_cast<AKey *>(t));
@ -2500,6 +2594,8 @@ void AM_Drawer ()
AM_drawWalls(allmap); AM_drawWalls(allmap);
AM_drawPlayers(); AM_drawPlayers();
if (G_SkillProperty(SKILLP_EasyKey))
AM_drawKeys();
if (am_cheat >= 2 || allthings) if (am_cheat >= 2 || allthings)
AM_drawThings(); AM_drawThings();

View File

@ -51,15 +51,16 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
players[i].mo = NULL; players[i].mo = NULL;
} }
botinfo_t *bot = botinfo; botinfo_t *bot = botinfo;
while (bot && stricmp (players[i].userinfo.netname, bot->name)) while (bot && stricmp (players[i].userinfo.GetName(), bot->name))
bot = bot->next; bot = bot->next;
if (bot) if (bot)
{ {
bot->inuse = false; bot->inuse = false;
bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_NONE; bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE;
} }
players[i].~player_t(); players[i].~player_t();
::new(&players[i]) player_t; ::new(&players[i]) player_t;
players[i].userinfo.Reset();
playeringame[i] = false; playeringame[i] = false;
} }

View File

@ -140,7 +140,7 @@ void FCajunMaster::Main (int buf)
//Check if player should go observer. Or un observe //Check if player should go observer. Or un observe
if (bot_observer && !observer && !netgame) if (bot_observer && !observer && !netgame)
{ {
Printf ("%s is now observer\n", players[consoleplayer].userinfo.netname); Printf ("%s is now observer\n", players[consoleplayer].userinfo.GetName());
observer = true; observer = true;
players[consoleplayer].mo->UnlinkFromWorld (); players[consoleplayer].mo->UnlinkFromWorld ();
players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY; players[consoleplayer].mo->flags = MF_DROPOFF|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOTDMATCH|MF_NOGRAVITY|MF_FRIENDLY;
@ -149,7 +149,7 @@ void FCajunMaster::Main (int buf)
} }
else if (!bot_observer && observer && !netgame) //Go back else if (!bot_observer && observer && !netgame) //Go back
{ {
Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.netname); Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName());
observer = false; observer = false;
players[consoleplayer].mo->UnlinkFromWorld (); players[consoleplayer].mo->UnlinkFromWorld ();
players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY; players[consoleplayer].mo->flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH|MF_FRIENDLY;
@ -218,7 +218,7 @@ void FCajunMaster::End ()
{ {
if (deathmatch) if (deathmatch)
{ {
getspawned.Push(players[i].userinfo.netname); getspawned.Push(players[i].userinfo.GetName());
} }
CleanBotstuff (&players[i]); CleanBotstuff (&players[i]);
} }
@ -353,7 +353,7 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
if (!deathmatch && playerstarts[bnum].type == 0) if (!deathmatch && playerstarts[bnum].type == 0)
{ {
Printf ("%s tried to join, but there was no player %d start\n", Printf ("%s tried to join, but there was no player %d start\n",
players[bnum].userinfo.netname, bnum+1); players[bnum].userinfo.GetName(), bnum+1);
ClearPlayer (bnum, false); // Make the bot inactive again ClearPlayer (bnum, false); // Make the bot inactive again
if (botnum > 0) if (botnum > 0)
{ {
@ -370,9 +370,9 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
botingame[bnum] = true; botingame[bnum] = true;
if (teamplay) if (teamplay)
Printf ("%s joined the %s team\n", players[bnum].userinfo.netname,Teams[players[bnum].userinfo.team].GetName ()); Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
else else
Printf ("%s joined the game\n", players[bnum].userinfo.netname); Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
G_DoReborn (bnum, true); G_DoReborn (bnum, true);
if (StatusBar != NULL) if (StatusBar != NULL)
@ -474,6 +474,7 @@ static void appendinfo (char *&front, const char *back)
{ {
size_t newlen = strlen (back) + 2; size_t newlen = strlen (back) + 2;
newstr = new char[newlen]; newstr = new char[newlen];
newstr[0] = 0;
} }
strcat (newstr, "\\"); strcat (newstr, "\\");
strcat (newstr, back); strcat (newstr, back);

View File

@ -231,7 +231,7 @@ bool AnnounceKill (AActor *killer, AActor *killee)
if (killer == NULL) if (killer == NULL)
{ // The world killed the player { // The world killed the player
if (killee->player->userinfo.gender == GENDER_MALE) if (killee->player->userinfo.GetGender() == GENDER_MALE)
{ // Only males have scrotums to separate { // Only males have scrotums to separate
choice = &WorldKillSounds[rannum % 3]; choice = &WorldKillSounds[rannum % 3];
} }
@ -244,11 +244,11 @@ bool AnnounceKill (AActor *killer, AActor *killee)
else if (killer == killee) else if (killer == killee)
{ // The player killed self { // The player killed self
choice = &SuicideSounds[rannum & 3]; choice = &SuicideSounds[rannum & 3];
killerName = killer->player->userinfo.netname; killerName = killer->player->userinfo.GetName();
} }
else else
{ // Another player did the killing { // Another player did the killing
if (killee->player->userinfo.gender == GENDER_MALE) if (killee->player->userinfo.GetGender() == GENDER_MALE)
{ // Only males can be castrated { // Only males can be castrated
choice = &KillSounds[rannum % countof(KillSounds)]; choice = &KillSounds[rannum % countof(KillSounds)];
} }
@ -256,7 +256,7 @@ bool AnnounceKill (AActor *killer, AActor *killee)
{ {
choice = &KillSounds[rannum % (countof(KillSounds) - 1)]; choice = &KillSounds[rannum % (countof(KillSounds) - 1)];
} }
killerName = killer->player->userinfo.netname; killerName = killer->player->userinfo.GetName();
// Blood only plays the announcement sound on the killer's // Blood only plays the announcement sound on the killer's
// computer. I think it sounds neater to also hear it on // computer. I think it sounds neater to also hear it on
@ -269,8 +269,8 @@ bool AnnounceKill (AActor *killer, AActor *killee)
{ {
char assembled[1024]; char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.gender, SexMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.netname, killerName); killee->player->userinfo.GetName(), killerName);
Printf (PRINT_MEDIUM, "%s\n", assembled); Printf (PRINT_MEDIUM, "%s\n", assembled);
} }
if (playSound) if (playSound)
@ -301,8 +301,8 @@ bool AnnounceTelefrag (AActor *killer, AActor *killee)
{ {
char assembled[1024]; char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.gender, SexMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.netname, killer->player->userinfo.netname); killee->player->userinfo.GetName(), killer->player->userinfo.GetName());
Printf (PRINT_MEDIUM, "%s\n", assembled); Printf (PRINT_MEDIUM, "%s\n", assembled);
} }
if (killee->CheckLocalView (consoleplayer) || if (killee->CheckLocalView (consoleplayer) ||

View File

@ -829,6 +829,7 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
bool dclick; bool dclick;
int dclickspot; int dclickspot;
BYTE dclickmask; BYTE dclickmask;
unsigned int nowtime;
if (ev->type != EV_KeyDown && ev->type != EV_KeyUp) if (ev->type != EV_KeyDown && ev->type != EV_KeyUp)
return false; return false;
@ -841,10 +842,11 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
dclick = false; dclick = false;
// This used level.time which didn't work outside a level. // This used level.time which didn't work outside a level.
if (DClickTime[ev->data1] > I_MSTime() && ev->type == EV_KeyDown) nowtime = I_MSTime();
if (doublebinds != NULL && DClickTime[ev->data1] > nowtime && ev->type == EV_KeyDown)
{ {
// Key pressed for a double click // Key pressed for a double click
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1); binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] |= dclickmask; DClicked[dclickspot] |= dclickmask;
dclick = true; dclick = true;
} }
@ -853,11 +855,11 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
if (ev->type == EV_KeyDown) if (ev->type == EV_KeyDown)
{ // Key pressed for a normal press { // Key pressed for a normal press
binding = binds->GetBinding(ev->data1); binding = binds->GetBinding(ev->data1);
DClickTime[ev->data1] = I_MSTime() + 571; DClickTime[ev->data1] = nowtime + 571;
} }
else if (DClicked[dclickspot] & dclickmask) else if (doublebinds != NULL && DClicked[dclickspot] & dclickmask)
{ // Key released from a double click { // Key released from a double click
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1); binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] &= ~dclickmask; DClicked[dclickspot] &= ~dclickmask;
DClickTime[ev->data1] = 0; DClickTime[ev->data1] = 0;
dclick = true; dclick = true;

View File

@ -175,6 +175,15 @@ CCMD (noclip)
Net_WriteByte (CHT_NOCLIP); Net_WriteByte (CHT_NOCLIP);
} }
CCMD (noclip2)
{
if (CheckCheatmode())
return;
Net_WriteByte (DEM_GENERICCHEAT);
Net_WriteByte (CHT_NOCLIP2);
}
CCMD (powerup) CCMD (powerup)
{ {
if (CheckCheatmode ()) if (CheckCheatmode ())
@ -245,7 +254,7 @@ CCMD (chase)
else else
{ {
// Check if we're allowed to use chasecam. // Check if we're allowed to use chasecam.
if (gamestate != GS_LEVEL || (!(dmflags2 & DF2_CHASECAM) && CheckCheatmode ())) if (gamestate != GS_LEVEL || (!(dmflags2 & DF2_CHASECAM) && deathmatch && CheckCheatmode ()))
return; return;
Net_WriteByte (DEM_GENERICCHEAT); Net_WriteByte (DEM_GENERICCHEAT);
@ -320,7 +329,7 @@ CCMD (hxvisit)
CCMD (changemap) CCMD (changemap)
{ {
if (who == NULL) if (who == NULL || !usergame)
{ {
Printf ("Use the map command when not in a game.\n"); Printf ("Use the map command when not in a game.\n");
return; return;
@ -386,7 +395,7 @@ CCMD (take)
CCMD (gameversion) CCMD (gameversion)
{ {
Printf ("%s : " __DATE__ "\n", DOTVERSIONSTR); Printf ("%s @ %s\nCommit %s", GetVersionString(), GetGitTime(), GetGitHash());
} }
CCMD (print) CCMD (print)
@ -451,9 +460,9 @@ CCMD (puke)
{ {
int argc = argv.argc(); int argc = argv.argc();
if (argc < 2 || argc > 5) if (argc < 2 || argc > 6)
{ {
Printf ("Usage: puke <script> [arg1] [arg2] [arg3]\n"); Printf ("Usage: puke <script> [arg1] [arg2] [arg3] [arg4]\n");
} }
else else
{ {
@ -463,8 +472,8 @@ CCMD (puke)
{ // Script 0 is reserved for Strife support. It is not pukable. { // Script 0 is reserved for Strife support. It is not pukable.
return; return;
} }
int arg[3] = { 0, 0, 0 }; int arg[4] = { 0, 0, 0, 0 };
int argn = MIN (argc - 2, 3), i; int argn = MIN<int>(argc - 2, countof(arg)), i;
for (i = 0; i < argn; ++i) for (i = 0; i < argn; ++i)
{ {
@ -489,6 +498,44 @@ CCMD (puke)
} }
} }
CCMD (pukename)
{
int argc = argv.argc();
if (argc < 2 || argc > 7)
{
Printf ("Usage: pukename \"<script>\" [\"always\"] [arg1] [arg2] [arg3] [arg4]\n");
}
else
{
bool always = false;
int argstart = 2;
int arg[4] = { 0, 0, 0, 0 };
int argn = 0, i;
if (argc > 2)
{
if (stricmp(argv[2], "always") == 0)
{
always = true;
argstart = 3;
}
argn = MIN<int>(argc - argstart, countof(arg));
for (i = 0; i < argn; ++i)
{
arg[i] = atoi(argv[argstart + i]);
}
}
Net_WriteByte(DEM_RUNNAMEDSCRIPT);
Net_WriteString(argv[1]);
Net_WriteByte(argn | (always << 7));
for (i = 0; i < argn; ++i)
{
Net_WriteLong(arg[i]);
}
}
}
CCMD (special) CCMD (special)
{ {
int argc = argv.argc(); int argc = argv.argc();
@ -911,9 +958,16 @@ CCMD(thaw)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
CCMD(nextmap) CCMD(nextmap)
{ {
char * next=NULL; if (netgame)
{
Printf ("Use "TEXTCOLOR_BOLD"changemap"TEXTCOLOR_NORMAL" instead. "TEXTCOLOR_BOLD"Nextmap"
TEXTCOLOR_NORMAL" is for single-player only.\n");
return;
}
char *next = NULL;
if (*level.nextmap) next = level.nextmap; if (*level.nextmap)
next = level.nextmap;
if (next != NULL && strncmp(next, "enDSeQ", 6)) if (next != NULL && strncmp(next, "enDSeQ", 6))
{ {
@ -932,9 +986,16 @@ CCMD(nextmap)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
CCMD(nextsecret) CCMD(nextsecret)
{ {
char * next=NULL; if (netgame)
{
Printf ("Use "TEXTCOLOR_BOLD"changemap"TEXTCOLOR_NORMAL" instead. "TEXTCOLOR_BOLD"Nextsecret"
TEXTCOLOR_NORMAL" is for single-player only.\n");
return;
}
char *next = NULL;
if (*level.secretmap) next = level.secretmap; if (*level.secretmap)
next = level.secretmap;
if (next != NULL && strncmp(next, "enDSeQ", 6)) if (next != NULL && strncmp(next, "enDSeQ", 6))
{ {
@ -1050,7 +1111,8 @@ CCMD(secret)
{ {
FString levelname; FString levelname;
level_info_t *info = FindLevelInfo(mapname); level_info_t *info = FindLevelInfo(mapname);
levelname.Format("%s - %s\n", mapname, info->LevelName.GetChars()); const char *ln = !(info->flags & LEVEL_LOOKUPLEVELNAME)? info->LevelName.GetChars() : GStrings[info->LevelName.GetChars()];
levelname.Format("%s - %s\n", mapname, ln);
size_t llen = levelname.Len() - 1; size_t llen = levelname.Len() - 1;
for(size_t ii=0; ii<llen; ii++) levelname += '-'; for(size_t ii=0; ii<llen; ii++) levelname += '-';
Printf(TEXTCOLOR_YELLOW"%s\n", levelname.GetChars()); Printf(TEXTCOLOR_YELLOW"%s\n", levelname.GetChars());

View File

@ -768,7 +768,7 @@ void AddToConsole (int printlevel, const char *text)
// The line start is outside the buffer. // The line start is outside the buffer.
// Make space for the newly inserted stuff. // Make space for the newly inserted stuff.
size_t movesize = work-linestart; size_t movesize = work-linestart;
memmove(work + movesize, work, strlen(work)); memmove(work + movesize, work, strlen(work)+1);
work_p += movesize; work_p += movesize;
linestart = work; linestart = work;
} }
@ -1134,7 +1134,7 @@ void C_DrawConsole (bool hw2d)
(viewwindowx || viewwindowy) && (viewwindowx || viewwindowy) &&
viewactive) viewactive)
{ {
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
} }
oldbottom = ConBottom; oldbottom = ConBottom;
@ -1167,9 +1167,9 @@ void C_DrawConsole (bool hw2d)
if (ConBottom >= 12) if (ConBottom >= 12)
{ {
screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 - screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 -
ConFont->StringWidth ("v" DOTVERSIONSTR), ConFont->StringWidth (GetVersionString()),
ConBottom - ConFont->GetHeight() - 4, ConBottom - ConFont->GetHeight() - 4,
"v" DOTVERSIONSTR, TAG_DONE); GetVersionString(), TAG_DONE);
if (TickerMax) if (TickerMax)
{ {
char tickstr[256]; char tickstr[256];
@ -1224,8 +1224,8 @@ void C_DrawConsole (bool hw2d)
{ {
screen->Dim (PalEntry ((unsigned char)(player->BlendR*255), (unsigned char)(player->BlendG*255), (unsigned char)(player->BlendB*255)), screen->Dim (PalEntry ((unsigned char)(player->BlendR*255), (unsigned char)(player->BlendG*255), (unsigned char)(player->BlendB*255)),
player->BlendA, 0, ConBottom, screen->GetWidth(), screen->GetHeight() - ConBottom); player->BlendA, 0, ConBottom, screen->GetWidth(), screen->GetHeight() - ConBottom);
SB_state = screen->GetPageCount (); ST_SetNeedRefresh();
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
} }
} }
} }

View File

@ -132,10 +132,10 @@ FBaseCVar::~FBaseCVar ()
} }
} }
void FBaseCVar::ForceSet (UCVarValue value, ECVarType type) void FBaseCVar::ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend)
{ {
DoSet (value, type); DoSet (value, type);
if (Flags & CVAR_USERINFO) if ((Flags & CVAR_USERINFO) && !nouserinfosend && !(Flags & CVAR_IGNORE))
D_UserInfoChanged (this); D_UserInfoChanged (this);
if (m_UseCallback) if (m_UseCallback)
Callback (); Callback ();
@ -266,7 +266,7 @@ static GUID cGUID;
static char truestr[] = "true"; static char truestr[] = "true";
static char falsestr[] = "false"; static char falsestr[] = "false";
char *FBaseCVar::ToString (UCVarValue value, ECVarType type) const char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
{ {
switch (type) switch (type)
{ {
@ -849,9 +849,7 @@ UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const
void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
{ {
if (DefaultValue) ReplaceString(&DefaultValue, ToString(value, type));
delete[] DefaultValue;
DefaultValue = ToString (value, type);
if (Flags & CVAR_ISDEFAULT) if (Flags & CVAR_ISDEFAULT)
{ {
SetGenericRep (value, type); SetGenericRep (value, type);
@ -1274,52 +1272,56 @@ static int STACK_ARGS sortcvars (const void *a, const void *b)
void FilterCompactCVars (TArray<FBaseCVar *> &cvars, DWORD filter) void FilterCompactCVars (TArray<FBaseCVar *> &cvars, DWORD filter)
{ {
FBaseCVar *cvar = CVars; // Accumulate all cvars that match the filter flags.
while (cvar) for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{ {
if (cvar->Flags & filter) if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_IGNORE))
cvars.Push (cvar); cvars.Push(cvar);
cvar = cvar->m_Next;
} }
if (cvars.Size () > 0) // Now sort them, so they're in a deterministic order and not whatever
// order the linker put them in.
if (cvars.Size() > 0)
{ {
cvars.ShrinkToFit (); qsort(&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars);
qsort (&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars);
} }
} }
void C_WriteCVars (BYTE **demo_p, DWORD filter, bool compact) void C_WriteCVars (BYTE **demo_p, DWORD filter, bool compact)
{ {
FBaseCVar *cvar = CVars; FString dump = C_GetMassCVarString(filter, compact);
BYTE *ptr = *demo_p; size_t dumplen = dump.Len() + 1; // include terminating \0
memcpy(*demo_p, dump.GetChars(), dumplen);
*demo_p += dumplen;
}
FString C_GetMassCVarString (DWORD filter, bool compact)
{
FBaseCVar *cvar;
FString dump;
if (compact) if (compact)
{ {
TArray<FBaseCVar *> cvars; TArray<FBaseCVar *> cvars;
ptr += sprintf ((char *)ptr, "\\\\%ux", filter); dump.AppendFormat("\\\\%ux", filter);
FilterCompactCVars (cvars, filter); FilterCompactCVars(cvars, filter);
while (cvars.Pop (cvar)) while (cvars.Pop (cvar))
{ {
UCVarValue val = cvar->GetGenericRep (CVAR_String); UCVarValue val = cvar->GetGenericRep(CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s", val.String); dump << '\\' << val.String;
} }
} }
else else
{ {
cvar = CVars; for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
while (cvar)
{ {
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_NOSAVE)) if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
{ {
UCVarValue val = cvar->GetGenericRep (CVAR_String); UCVarValue val = cvar->GetGenericRep(CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s\\%s", dump << '\\' << cvar->GetName() << '\\' << val.String;
cvar->GetName (), val.String);
} }
cvar = cvar->m_Next;
} }
} }
return dump;
*demo_p = ptr + 1;
} }
void C_ReadCVars (BYTE **demo_p) void C_ReadCVars (BYTE **demo_p)
@ -1390,58 +1392,42 @@ void C_ReadCVars (BYTE **demo_p)
*demo_p += strlen (*((char **)demo_p)) + 1; *demo_p += strlen (*((char **)demo_p)) + 1;
} }
static struct backup_s struct FCVarBackup
{ {
char *name, *string; FString Name, String;
} CVarBackups[MAX_DEMOCVARS]; };
static TArray<FCVarBackup> CVarBackups;
static int numbackedup = 0;
void C_BackupCVars (void) void C_BackupCVars (void)
{ {
struct backup_s *backup = CVarBackups; assert(CVarBackups.Size() == 0);
FBaseCVar *cvar = CVars; CVarBackups.Clear();
while (cvar) FCVarBackup backup;
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{ {
if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) && !(cvar->Flags & CVAR_LATCH))
&& !(cvar->Flags & CVAR_LATCH))
{ {
if (backup == &CVarBackups[MAX_DEMOCVARS]) backup.Name = cvar->GetName();
I_Error ("C_BackupDemoCVars: Too many cvars to save (%d)", MAX_DEMOCVARS); backup.String = cvar->GetGenericRep(CVAR_String).String;
backup->name = copystring (cvar->GetName()); CVarBackups.Push(backup);
backup->string = copystring (cvar->GetGenericRep (CVAR_String).String);
backup++;
} }
cvar = cvar->m_Next;
} }
numbackedup = int(backup - CVarBackups);
} }
void C_RestoreCVars (void) void C_RestoreCVars (void)
{ {
struct backup_s *backup = CVarBackups; for (unsigned int i = 0; i < CVarBackups.Size(); ++i)
int i;
for (i = numbackedup; i; i--, backup++)
{ {
cvar_set (backup->name, backup->string); cvar_set(CVarBackups[i].Name, CVarBackups[i].String);
} }
C_ForgetCVars(); C_ForgetCVars();
} }
void C_ForgetCVars (void) void C_ForgetCVars (void)
{ {
struct backup_s *backup = CVarBackups; CVarBackups.Clear();
int i;
for (i = numbackedup; i; i--, backup++)
{
delete[] backup->name;
delete[] backup->string;
backup->name = backup->string = NULL;
}
numbackedup = 0;
} }
FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev) FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev)
@ -1489,6 +1475,30 @@ FBaseCVar *FindCVarSub (const char *var_name, int namelen)
return var; return var;
} }
//===========================================================================
//
// C_CreateCVar
//
// Create a new cvar with the specified name and type. It should not already
// exist.
//
//===========================================================================
FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags)
{
assert(FindCVar(var_name, NULL) == NULL);
flags |= CVAR_AUTO;
switch (var_type)
{
case CVAR_Bool: return new FBoolCVar(var_name, 0, flags);
case CVAR_Int: return new FIntCVar(var_name, 0, flags);
case CVAR_Float: return new FFloatCVar(var_name, 0, flags);
case CVAR_String: return new FStringCVar(var_name, NULL, flags);
case CVAR_Color: return new FColorCVar(var_name, 0, flags);
default: return NULL;
}
}
void UnlatchCVars (void) void UnlatchCVars (void)
{ {
FLatchedValue var; FLatchedValue var;
@ -1522,33 +1532,14 @@ void C_SetCVarsToDefaults (void)
} }
} }
void C_ArchiveCVars (FConfigFile *f, int type) void C_ArchiveCVars (FConfigFile *f, uint32 filter)
{ {
// type 0: Game-specific cvars
// type 1: Global cvars
// type 2: Unknown cvars
// type 3: Unknown global cvars
// type 4: User info cvars
// type 5: Server info cvars
static const DWORD filters[6] =
{
CVAR_ARCHIVE,
CVAR_ARCHIVE|CVAR_GLOBALCONFIG,
CVAR_ARCHIVE|CVAR_AUTO,
CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_AUTO,
CVAR_ARCHIVE|CVAR_USERINFO,
CVAR_ARCHIVE|CVAR_SERVERINFO
};
FBaseCVar *cvar = CVars; FBaseCVar *cvar = CVars;
DWORD filter;
filter = filters[type];
while (cvar) while (cvar)
{ {
if ((cvar->Flags & if ((cvar->Flags &
(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE)) (CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_MOD|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
== filter) == filter)
{ {
UCVarValue val; UCVarValue val;
@ -1679,14 +1670,16 @@ void FBaseCVar::ListVars (const char *filter, bool plain)
else else
{ {
++count; ++count;
Printf ("%c%c%c %s : :%s\n", Printf ("%c%c%c%c%c %s = %s\n",
flags & CVAR_ARCHIVE ? 'A' : ' ', flags & CVAR_ARCHIVE ? 'A' : ' ',
flags & CVAR_USERINFO ? 'U' : flags & CVAR_USERINFO ? 'U' :
flags & CVAR_SERVERINFO ? 'S' : flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ', flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_NOSET ? '-' : flags & CVAR_NOSET ? '-' :
flags & CVAR_LATCH ? 'L' : flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ', flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_MOD ? 'M' : ' ',
flags & CVAR_IGNORE ? 'X' : ' ',
var->GetName(), var->GetName(),
var->GetGenericRep (CVAR_String).String); var->GetGenericRep (CVAR_String).String);
} }

View File

@ -61,6 +61,8 @@ enum
CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section
CVAR_VIDEOCONFIG = 2048, // cvar is saved to video config section (not implemented) CVAR_VIDEOCONFIG = 2048, // cvar is saved to video config section (not implemented)
CVAR_NOSAVE = 4096, // when used with CVAR_SERVERINFO, do not save var to savegame CVAR_NOSAVE = 4096, // when used with CVAR_SERVERINFO, do not save var to savegame
CVAR_MOD = 8192, // cvar was defined by a mod
CVAR_IGNORE = 16384,// do not send cvar across the network/inaccesible from ACS (dummy mod cvar)
}; };
union UCVarValue union UCVarValue
@ -68,7 +70,7 @@ union UCVarValue
bool Bool; bool Bool;
int Int; int Int;
float Float; float Float;
char *String; const char *String;
const GUID *pGUID; const GUID *pGUID;
}; };
@ -96,9 +98,10 @@ public:
inline const char *GetName () const { return Name; } inline const char *GetName () const { return Name; }
inline uint32 GetFlags () const { return Flags; } inline uint32 GetFlags () const { return Flags; }
inline FBaseCVar *GetNext() const { return m_Next; }
void CmdSet (const char *newval); void CmdSet (const char *newval);
void ForceSet (UCVarValue value, ECVarType type); void ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend=false);
void SetGenericRep (UCVarValue value, ECVarType type); void SetGenericRep (UCVarValue value, ECVarType type);
void ResetToDefault (); void ResetToDefault ();
void SetArchiveBit () { Flags |= CVAR_ARCHIVE; } void SetArchiveBit () { Flags |= CVAR_ARCHIVE; }
@ -129,7 +132,7 @@ protected:
static bool ToBool (UCVarValue value, ECVarType type); static bool ToBool (UCVarValue value, ECVarType type);
static int ToInt (UCVarValue value, ECVarType type); static int ToInt (UCVarValue value, ECVarType type);
static float ToFloat (UCVarValue value, ECVarType type); static float ToFloat (UCVarValue value, ECVarType type);
static char *ToString (UCVarValue value, ECVarType type); static const char *ToString (UCVarValue value, ECVarType type);
static const GUID *ToGUID (UCVarValue value, ECVarType type); static const GUID *ToGUID (UCVarValue value, ECVarType type);
static UCVarValue FromBool (bool value, ECVarType type); static UCVarValue FromBool (bool value, ECVarType type);
static UCVarValue FromInt (int value, ECVarType type); static UCVarValue FromInt (int value, ECVarType type);
@ -150,18 +153,22 @@ private:
static bool m_UseCallback; static bool m_UseCallback;
static bool m_DoNoSet; static bool m_DoNoSet;
friend void C_WriteCVars (BYTE **demo_p, uint32 filter, bool compact); friend FString C_GetMassCVarString (uint32 filter, bool compact);
friend void C_ReadCVars (BYTE **demo_p); friend void C_ReadCVars (BYTE **demo_p);
friend void C_BackupCVars (void); friend void C_BackupCVars (void);
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen); friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
friend void UnlatchCVars (void); friend void UnlatchCVars (void);
friend void C_ArchiveCVars (FConfigFile *f, int type); friend void C_ArchiveCVars (FConfigFile *f, uint32 filter);
friend void C_SetCVarsToDefaults (void); friend void C_SetCVarsToDefaults (void);
friend void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32 filter); friend void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32 filter);
friend void C_DeinitConsole(); friend void C_DeinitConsole();
}; };
// Returns a string with all cvars whose flags match filter. In compact mode,
// the cvar names are omitted to save space.
FString C_GetMassCVarString (uint32 filter, bool compact=false);
// Writes all cvars that could effect demo sync to *demo_p. These are // Writes all cvars that could effect demo sync to *demo_p. These are
// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set. // cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set.
void C_WriteCVars (BYTE **demo_p, uint32 filter, bool compact=false); void C_WriteCVars (BYTE **demo_p, uint32 filter, bool compact=false);
@ -177,11 +184,14 @@ void C_BackupCVars (void);
FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
FBaseCVar *FindCVarSub (const char *var_name, int namelen); FBaseCVar *FindCVarSub (const char *var_name, int namelen);
// Create a new cvar with the specified name and type
FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags);
// Called from G_InitNew() // Called from G_InitNew()
void UnlatchCVars (void); void UnlatchCVars (void);
// archive cvars to FILE f // archive cvars to FILE f
void C_ArchiveCVars (FConfigFile *f, int type); void C_ArchiveCVars (FConfigFile *f, uint32 filter);
// initialize cvars to default values after they are created // initialize cvars to default values after they are created
void C_SetCVarsToDefaults (void); void C_SetCVarsToDefaults (void);
@ -404,10 +414,6 @@ inline FBaseCVar *cvar_forceset (const char *var_name, const BYTE *value) { retu
// Maximum number of cvars that can be saved across a demo. If you need
// to save more, bump this up.
#define MAX_DEMOCVARS 32
// Restore demo cvars. Called after demo playback to restore all cvars // Restore demo cvars. Called after demo playback to restore all cvars
// that might possibly have been changed during the course of demo playback. // that might possibly have been changed during the course of demo playback.
void C_RestoreCVars (void); void C_RestoreCVars (void);
@ -425,5 +431,6 @@ void C_ForgetCVars (void);
#define EXTERN_CVAR(type,name) extern F##type##CVar name; #define EXTERN_CVAR(type,name) extern F##type##CVar name;
extern FBaseCVar *CVars;
#endif //__C_CVARS_H__ #endif //__C_CVARS_H__

View File

@ -1019,32 +1019,7 @@ FConsoleAlias::~FConsoleAlias ()
m_Command[1] = m_Command[0] = FString(); m_Command[1] = m_Command[0] = FString();
} }
FString BuildString (int argc, char **argv) // Given an argument vector, reconstitute the command line it could have been produced from.
{
if (argc == 1)
{
return *argv;
}
else
{
FString buf;
int arg;
for (arg = 0; arg < argc; arg++)
{
if (strchr (argv[arg], ' '))
{
buf.AppendFormat ("\"%s\" ", argv[arg]);
}
else
{
buf.AppendFormat ("%s ", argv[arg]);
}
}
return buf;
}
}
FString BuildString (int argc, FString *argv) FString BuildString (int argc, FString *argv)
{ {
if (argc == 1) if (argc == 1)
@ -1058,8 +1033,23 @@ FString BuildString (int argc, FString *argv)
for (arg = 0; arg < argc; arg++) for (arg = 0; arg < argc; arg++)
{ {
if (strchr (argv[arg], ' ')) if (strchr(argv[arg], '"'))
{ { // If it contains one or more quotes, we need to escape them.
buf << '"';
long substr_start = 0, quotepos;
while ((quotepos = argv[arg].IndexOf('"', substr_start)) >= 0)
{
if (substr_start < quotepos)
{
buf << argv[arg].Mid(substr_start, quotepos - substr_start);
}
buf << "\\\"";
substr_start = quotepos + 1;
}
buf << argv[arg].Mid(substr_start) << "\" ";
}
else if (strchr(argv[arg], ' '))
{ // If it contains a space, it needs to be quoted.
buf << '"' << argv[arg] << "\" "; buf << '"' << argv[arg] << "\" ";
} }
else else

View File

@ -61,7 +61,6 @@ void C_SetAlias (const char *name, const char *cmd);
void C_ClearAliases (); void C_ClearAliases ();
// build a single string out of multiple strings // build a single string out of multiple strings
FString BuildString (int argc, char **argv);
FString BuildString (int argc, FString *argv); FString BuildString (int argc, FString *argv);
// Class that can parse command lines // Class that can parse command lines

View File

@ -537,6 +537,7 @@ void CreatePath(const char *fn)
} }
if (mkdir(copy, 0755) == -1) if (mkdir(copy, 0755) == -1)
{ // failed { // failed
free(copy);
return; return;
} }
exists: if (p != NULL) exists: if (p != NULL)
@ -550,7 +551,7 @@ exists: if (p != NULL)
//========================================================================== //==========================================================================
// //
// strbin1 -- In-place version // strbin -- In-place version
// //
// [RH] Replaces the escape sequences in a string with actual escaped characters. // [RH] Replaces the escape sequences in a string with actual escaped characters.
// This operation is done in-place. The result is the new length of the string. // This operation is done in-place. The result is the new length of the string.
@ -600,18 +601,20 @@ int strbin (char *str)
case 'x': case 'x':
case 'X': case 'X':
c = 0; c = 0;
p++; for (i = 0; i < 2; i++)
for (i = 0; i < 2; i++) { {
c <<= 4;
if (*p >= '0' && *p <= '9')
c += *p-'0';
else if (*p >= 'a' && *p <= 'f')
c += 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c += 10 + *p-'A';
else
break;
p++; p++;
if (*p >= '0' && *p <= '9')
c = (c << 4) + *p-'0';
else if (*p >= 'a' && *p <= 'f')
c = (c << 4) + 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c = (c << 4) + 10 + *p-'A';
else
{
p--;
break;
}
} }
*str++ = c; *str++ = c;
break; break;
@ -650,7 +653,7 @@ int strbin (char *str)
// strbin1 -- String-creating version // strbin1 -- String-creating version
// //
// [RH] Replaces the escape sequences in a string with actual escaped characters. // [RH] Replaces the escape sequences in a string with actual escaped characters.
// This operation is done in-place. // The result is a new string.
// //
//========================================================================== //==========================================================================
@ -698,18 +701,20 @@ FString strbin1 (const char *start)
case 'x': case 'x':
case 'X': case 'X':
c = 0; c = 0;
p++; for (i = 0; i < 2; i++)
for (i = 0; i < 2; i++) { {
c <<= 4;
if (*p >= '0' && *p <= '9')
c += *p-'0';
else if (*p >= 'a' && *p <= 'f')
c += 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c += 10 + *p-'A';
else
break;
p++; p++;
if (*p >= '0' && *p <= '9')
c = (c << 4) + *p-'0';
else if (*p >= 'a' && *p <= 'f')
c = (c << 4) + 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c = (c << 4) + 10 + *p-'A';
else
{
p--;
break;
}
} }
result << c; result << c;
break; break;
@ -751,7 +756,7 @@ FString strbin1 (const char *start)
// //
//========================================================================== //==========================================================================
void CleanseString(char *str) char *CleanseString(char *str)
{ {
char *escape = strrchr(str, TEXTCOLOR_ESCAPE); char *escape = strrchr(str, TEXTCOLOR_ESCAPE);
if (escape != NULL) if (escape != NULL)
@ -769,6 +774,7 @@ void CleanseString(char *str)
} }
} }
} }
return str;
} }
//========================================================================== //==========================================================================

View File

@ -47,7 +47,7 @@ const char *myasctime ();
int strbin (char *str); int strbin (char *str);
FString strbin1 (const char *start); FString strbin1 (const char *start);
void CleanseString (char *str); char *CleanseString (char *str);
void CreatePath(const char * fn); void CreatePath(const char * fn);

View File

@ -50,6 +50,7 @@
#include "g_level.h" #include "g_level.h"
#include "p_lnspec.h" #include "p_lnspec.h"
#include "r_state.h" #include "r_state.h"
#include "w_wad.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -58,8 +59,15 @@
struct FCompatOption struct FCompatOption
{ {
const char *Name; const char *Name;
int CompatFlags; DWORD CompatFlags;
int BCompatFlags; int WhichSlot;
};
enum
{
SLOT_COMPAT,
SLOT_COMPAT2,
SLOT_BCOMPAT
}; };
enum enum
@ -67,7 +75,12 @@ enum
CP_END, CP_END,
CP_CLEARFLAGS, CP_CLEARFLAGS,
CP_SETFLAGS, CP_SETFLAGS,
CP_SETSPECIAL CP_SETSPECIAL,
CP_CLEARSPECIAL,
CP_SETACTIVATION,
CP_SECTORFLOOROFFSET,
CP_SETWALLYSCALE,
CP_SETTHINGZ,
}; };
// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -77,6 +90,7 @@ enum
// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern TArray<FMapThing> MapThingsConverted;
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -86,44 +100,58 @@ TMap<FMD5Holder, FCompatValues, FMD5HashTraits> BCompatMap;
static FCompatOption Options[] = static FCompatOption Options[] =
{ {
{ "setslopeoverflow", 0, BCOMPATF_SETSLOPEOVERFLOW }, { "setslopeoverflow", BCOMPATF_SETSLOPEOVERFLOW, SLOT_BCOMPAT },
{ "resetplayerspeed", 0, BCOMPATF_RESETPLAYERSPEED }, { "resetplayerspeed", BCOMPATF_RESETPLAYERSPEED, SLOT_BCOMPAT },
{ "vileghosts", 0, BCOMPATF_VILEGHOSTS }, { "vileghosts", BCOMPATF_VILEGHOSTS, SLOT_BCOMPAT },
{ "ignoreteleporttags", 0, BCOMPATF_BADTELEPORTERS }, { "ignoreteleporttags", BCOMPATF_BADTELEPORTERS, SLOT_BCOMPAT },
{ "rebuildnodes", BCOMPATF_REBUILDNODES, SLOT_BCOMPAT },
// list copied from g_mapinfo.cpp // list copied from g_mapinfo.cpp
{ "shorttex", COMPATF_SHORTTEX, 0 }, { "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT },
{ "stairs", COMPATF_STAIRINDEX, 0 }, { "stairs", COMPATF_STAIRINDEX, SLOT_COMPAT },
{ "limitpain", COMPATF_LIMITPAIN, 0 }, { "limitpain", COMPATF_LIMITPAIN, SLOT_COMPAT },
{ "nopassover", COMPATF_NO_PASSMOBJ, 0 }, { "nopassover", COMPATF_NO_PASSMOBJ, SLOT_COMPAT },
{ "notossdrops", COMPATF_NOTOSSDROPS, 0 }, { "notossdrops", COMPATF_NOTOSSDROPS, SLOT_COMPAT },
{ "useblocking", COMPATF_USEBLOCKING, 0 }, { "useblocking", COMPATF_USEBLOCKING, SLOT_COMPAT },
{ "nodoorlight", COMPATF_NODOORLIGHT, 0 }, { "nodoorlight", COMPATF_NODOORLIGHT, SLOT_COMPAT },
{ "ravenscroll", COMPATF_RAVENSCROLL, 0 }, { "ravenscroll", COMPATF_RAVENSCROLL, SLOT_COMPAT },
{ "soundtarget", COMPATF_SOUNDTARGET, 0 }, { "soundtarget", COMPATF_SOUNDTARGET, SLOT_COMPAT },
{ "dehhealth", COMPATF_DEHHEALTH, 0 }, { "dehhealth", COMPATF_DEHHEALTH, SLOT_COMPAT },
{ "trace", COMPATF_TRACE, 0 }, { "trace", COMPATF_TRACE, SLOT_COMPAT },
{ "dropoff", COMPATF_DROPOFF, 0 }, { "dropoff", COMPATF_DROPOFF, SLOT_COMPAT },
{ "boomscroll", COMPATF_BOOMSCROLL, 0 }, { "boomscroll", COMPATF_BOOMSCROLL, SLOT_COMPAT },
{ "invisibility", COMPATF_INVISIBILITY, 0 }, { "invisibility", COMPATF_INVISIBILITY, SLOT_COMPAT },
{ "silentinstantfloors", COMPATF_SILENT_INSTANT_FLOORS, 0 }, { "silentinstantfloors", COMPATF_SILENT_INSTANT_FLOORS, SLOT_COMPAT },
{ "sectorsounds", COMPATF_SECTORSOUNDS, 0 }, { "sectorsounds", COMPATF_SECTORSOUNDS, SLOT_COMPAT },
{ "missileclip", COMPATF_MISSILECLIP, 0 }, { "missileclip", COMPATF_MISSILECLIP, SLOT_COMPAT },
{ "crossdropoff", COMPATF_CROSSDROPOFF, 0 }, { "crossdropoff", COMPATF_CROSSDROPOFF, SLOT_COMPAT },
{ "wallrun", COMPATF_WALLRUN, 0 }, // [GZ] Added for CC MAP29 { "wallrun", COMPATF_WALLRUN, SLOT_COMPAT }, // [GZ] Added for CC MAP29
{ "anybossdeath", COMPATF_ANYBOSSDEATH, 0}, // [GZ] Added for UAC_DEAD { "anybossdeath", COMPATF_ANYBOSSDEATH, SLOT_COMPAT },// [GZ] Added for UAC_DEAD
{ "mushroom", COMPATF_MUSHROOM, 0}, { "mushroom", COMPATF_MUSHROOM, SLOT_COMPAT },
{ "mbfmonstermove", COMPATF_MBFMONSTERMOVE, 0 }, { "mbfmonstermove", COMPATF_MBFMONSTERMOVE, SLOT_COMPAT },
{ "corpsegibs", COMPATF_CORPSEGIBS, 0 }, { "corpsegibs", COMPATF_CORPSEGIBS, SLOT_COMPAT },
{ "noblockfriends", COMPATF_NOBLOCKFRIENDS, 0 }, { "noblockfriends", COMPATF_NOBLOCKFRIENDS, SLOT_COMPAT },
{ "spritesort", COMPATF_SPRITESORT, 0 }, { "spritesort", COMPATF_SPRITESORT, SLOT_COMPAT },
{ "hitscan", COMPATF_HITSCAN, 0 }, { "hitscan", COMPATF_HITSCAN, SLOT_COMPAT },
{ "lightlevel", COMPATF_LIGHT, 0 }, { "lightlevel", COMPATF_LIGHT, SLOT_COMPAT },
{ "polyobj", COMPATF_POLYOBJ, 0 }, { "polyobj", COMPATF_POLYOBJ, SLOT_COMPAT },
{ "maskedmidtex", COMPATF_MASKEDMIDTEX, 0 }, { "maskedmidtex", COMPATF_MASKEDMIDTEX, SLOT_COMPAT },
{ "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 },
{ "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 },
{ NULL, 0, 0 } { NULL, 0, 0 }
}; };
static const char *const LineSides[] =
{
"Front", "Back", NULL
};
static const char *const WallTiers[] =
{
"Top", "Mid", "Bot", NULL
};
static TArray<int> CompatParams; static TArray<int> CompatParams;
static int ii_compatparams; static int ii_compatparams;
@ -189,15 +217,13 @@ void ParseCompatibility()
md5array.Push(md5); md5array.Push(md5);
sc.MustGetString(); sc.MustGetString();
} while (!sc.Compare("{")); } while (!sc.Compare("{"));
flags.CompatFlags = 0; memset(flags.CompatFlags, 0, sizeof(flags.CompatFlags));
flags.BCompatFlags = 0;
flags.ExtCommandIndex = ~0u; flags.ExtCommandIndex = ~0u;
while (sc.GetString()) while (sc.GetString())
{ {
if ((i = sc.MatchString(&Options[0].Name, sizeof(*Options))) >= 0) if ((i = sc.MatchString(&Options[0].Name, sizeof(*Options))) >= 0)
{ {
flags.CompatFlags |= Options[i].CompatFlags; flags.CompatFlags[Options[i].WhichSlot] |= Options[i].CompatFlags;
flags.BCompatFlags |= Options[i].BCompatFlags;
} }
else if (sc.Compare("clearlineflags")) else if (sc.Compare("clearlineflags"))
{ {
@ -226,12 +252,59 @@ void ParseCompatibility()
sc.MustGetString(); sc.MustGetString();
CompatParams.Push(P_FindLineSpecial(sc.String, NULL, NULL)); CompatParams.Push(P_FindLineSpecial(sc.String, NULL, NULL));
for(int i=0;i<5;i++) for (int i = 0; i < 5; i++)
{ {
sc.MustGetNumber(); sc.MustGetNumber();
CompatParams.Push(sc.Number); CompatParams.Push(sc.Number);
} }
} }
else if (sc.Compare("clearlinespecial"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_CLEARSPECIAL);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else if (sc.Compare("setactivation"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETACTIVATION);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else if (sc.Compare("sectorflooroffset"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SECTORFLOOROFFSET);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else if (sc.Compare("setwallyscale"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETWALLYSCALE);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetString();
CompatParams.Push(sc.MustMatchString(LineSides));
sc.MustGetString();
CompatParams.Push(sc.MustMatchString(WallTiers));
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else if (sc.Compare("setthingz"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETTHINGZ);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else else
{ {
sc.UnGet(); sc.UnGet();
@ -261,6 +334,7 @@ void CheckCompatibility(MapData *map)
{ {
FMD5Holder md5; FMD5Holder md5;
FCompatValues *flags; FCompatValues *flags;
bool onlyparams = true;
// When playing Doom IWAD levels force COMPAT_SHORTTEX and COMPATF_LIGHT. // When playing Doom IWAD levels force COMPAT_SHORTTEX and COMPATF_LIGHT.
// I'm not sure if the IWAD maps actually need COMPATF_LIGHT but it certainly does not hurt. // I'm not sure if the IWAD maps actually need COMPATF_LIGHT but it certainly does not hurt.
@ -269,53 +343,74 @@ void CheckCompatibility(MapData *map)
{ {
ii_compatflags = COMPATF_SHORTTEX|COMPATF_LIGHT; ii_compatflags = COMPATF_SHORTTEX|COMPATF_LIGHT;
if (gameinfo.flags & GI_COMPATSTAIRS) ii_compatflags |= COMPATF_STAIRINDEX; if (gameinfo.flags & GI_COMPATSTAIRS) ii_compatflags |= COMPATF_STAIRINDEX;
ii_compatflags2 = 0;
ib_compatflags = 0; ib_compatflags = 0;
ii_compatparams = -1; ii_compatparams = -1;
} }
else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPOLY1) && Wads.CheckLumpName(map->lumpnum, "MAP36")) else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPOLY1) && Wads.CheckLumpName(map->lumpnum, "MAP36"))
{ {
ii_compatflags = COMPATF_POLYOBJ; ii_compatflags = COMPATF_POLYOBJ;
ii_compatflags2 = 0;
ib_compatflags = 0; ib_compatflags = 0;
ii_compatparams = -1; ii_compatparams = -1;
} }
else if (Wads.GetLumpFile(map->lumpnum) == 2 && (gameinfo.flags & GI_COMPATPOLY2) && Wads.CheckLumpName(map->lumpnum, "MAP47")) else if (Wads.GetLumpFile(map->lumpnum) == 2 && (gameinfo.flags & GI_COMPATPOLY2) && Wads.CheckLumpName(map->lumpnum, "MAP47"))
{ {
ii_compatflags = COMPATF_POLYOBJ; ii_compatflags = COMPATF_POLYOBJ;
ii_compatflags2 = 0;
ib_compatflags = 0; ib_compatflags = 0;
ii_compatparams = -1; ii_compatparams = -1;
} }
else else
{ {
map->GetChecksum(md5.Bytes); onlyparams = false;
}
flags = BCompatMap.CheckKey(md5); map->GetChecksum(md5.Bytes);
if (developer) flags = BCompatMap.CheckKey(md5);
if (developer)
{
Printf("MD5 = ");
for (size_t j = 0; j < sizeof(md5.Bytes); ++j)
{ {
Printf("MD5 = "); Printf("%02X", md5.Bytes[j]);
for (size_t j = 0; j < sizeof(md5.Bytes); ++j)
{
Printf("%02X", md5.Bytes[j]);
}
if (flags != NULL) Printf(", cflags = %08x, bflags = %08x\n", flags->CompatFlags, flags->BCompatFlags);
else Printf("\n");
} }
if (flags != NULL) if (flags != NULL)
{ {
ii_compatflags = flags->CompatFlags; Printf(", cflags = %08x, cflags2 = %08x, bflags = %08x\n",
ib_compatflags = flags->BCompatFlags; flags->CompatFlags[SLOT_COMPAT], flags->CompatFlags[SLOT_COMPAT2], flags->CompatFlags[SLOT_BCOMPAT]);
ii_compatparams = flags->ExtCommandIndex;
} }
else else
{ {
ii_compatflags = 0; Printf("\n");
ib_compatflags = 0;
ii_compatparams = -1;
} }
} }
if (flags != NULL)
{
if (!onlyparams)
{
ii_compatflags = flags->CompatFlags[SLOT_COMPAT];
ii_compatflags2 = flags->CompatFlags[SLOT_COMPAT2];
ib_compatflags = flags->CompatFlags[SLOT_BCOMPAT];
}
ii_compatparams = flags->ExtCommandIndex;
}
else
{
if (!onlyparams)
{
ii_compatflags = 0;
ii_compatflags2 = 0;
ib_compatflags = 0;
}
ii_compatparams = -1;
}
// Reset i_compatflags // Reset i_compatflags
compatflags.Callback(); compatflags.Callback();
compatflags2.Callback();
} }
//========================================================================== //==========================================================================
@ -368,6 +463,61 @@ void SetCompatibilityParams()
i+=8; i+=8;
break; break;
} }
case CP_CLEARSPECIAL:
{
if (CompatParams[i+1] < numlines)
{
line_t *line = &lines[CompatParams[i+1]];
line->special = 0;
memset(line->args, 0, sizeof(line->args));
}
i += 2;
break;
}
case CP_SETACTIVATION:
{
if (CompatParams[i+1] < numlines)
{
line_t *line = &lines[CompatParams[i+1]];
line->activation = CompatParams[i+2];
}
i += 3;
break;
}
case CP_SECTORFLOOROFFSET:
{
if (CompatParams[i+1] < numsectors)
{
sector_t *sec = &sectors[CompatParams[i+1]];
sec->floorplane.ChangeHeight(CompatParams[i+2]);
sec->ChangePlaneTexZ(sector_t::floor, CompatParams[i+2]);
}
i += 3;
break;
}
case CP_SETWALLYSCALE:
{
if (CompatParams[i+1] < numlines)
{
side_t *side = lines[CompatParams[i+1]].sidedef[CompatParams[i+2]];
if (side != NULL)
{
side->SetTextureYScale(CompatParams[i+3], CompatParams[i+4]);
}
}
i += 5;
break;
}
case CP_SETTHINGZ:
{
// When this is called, the things haven't been spawned yet so we can alter the position inside the MapThings array.
if ((unsigned)CompatParams[i+1] < MapThingsConverted.Size())
{
MapThingsConverted[CompatParams[i+1]].z = CompatParams[i+2];
}
i += 3;
break;
}
} }
} }
} }
@ -416,5 +566,5 @@ CCMD (mapchecksum)
CCMD (hiddencompatflags) CCMD (hiddencompatflags)
{ {
Printf("%08x %08x\n", ii_compatflags, ib_compatflags); Printf("%08x %08x %08x\n", ii_compatflags, ii_compatflags2, ib_compatflags);
} }

View File

@ -14,8 +14,7 @@ union FMD5Holder
struct FCompatValues struct FCompatValues
{ {
int CompatFlags; int CompatFlags[3];
int BCompatFlags;
unsigned int ExtCommandIndex; unsigned int ExtCommandIndex;
}; };

View File

@ -38,9 +38,12 @@
#include "doomtype.h" #include "doomtype.h"
#include "configfile.h" #include "configfile.h"
#include "m_random.h"
#define READBUFFERSIZE 256 #define READBUFFERSIZE 256
static FRandom pr_endtag;
//==================================================================== //====================================================================
// //
// FConfigFile Constructor // FConfigFile Constructor
@ -53,6 +56,8 @@ FConfigFile::FConfigFile ()
LastSectionPtr = &Sections; LastSectionPtr = &Sections;
CurrentEntry = NULL; CurrentEntry = NULL;
PathName = ""; PathName = "";
OkayToWrite = true;
FileExisted = true;
} }
//==================================================================== //====================================================================
@ -70,6 +75,8 @@ FConfigFile::FConfigFile (const char *pathname,
CurrentEntry = NULL; CurrentEntry = NULL;
ChangePathName (pathname); ChangePathName (pathname);
LoadConfigFile (nosechandler, userdata); LoadConfigFile (nosechandler, userdata);
OkayToWrite = true;
FileExisted = true;
} }
//==================================================================== //====================================================================
@ -85,6 +92,8 @@ FConfigFile::FConfigFile (const FConfigFile &other)
CurrentEntry = NULL; CurrentEntry = NULL;
ChangePathName (other.PathName); ChangePathName (other.PathName);
*this = other; *this = other;
OkayToWrite = other.OkayToWrite;
FileExisted = other.FileExisted;
} }
//==================================================================== //====================================================================
@ -587,11 +596,15 @@ void FConfigFile::LoadConfigFile (void (*nosechandler)(const char *pathname, FCo
FILE *file = fopen (PathName, "r"); FILE *file = fopen (PathName, "r");
bool succ; bool succ;
FileExisted = false;
if (file == NULL) if (file == NULL)
{
return; return;
}
succ = ReadConfig (file); succ = ReadConfig (file);
fclose (file); fclose (file);
FileExisted = succ;
if (!succ) if (!succ)
{ // First valid line did not define a section { // First valid line did not define a section
@ -669,13 +682,68 @@ bool FConfigFile::ReadConfig (void *file)
whiteprobe++; whiteprobe++;
} }
*(whiteprobe - 1) = 0; *(whiteprobe - 1) = 0;
NewConfigEntry (section, start, whiteprobe); // Check for multi-line value
if (whiteprobe[0] == '<' && whiteprobe[1] == '<' && whiteprobe[2] == '<' && whiteprobe[3] != '\0')
{
ReadMultiLineValue (file, section, start, whiteprobe + 3);
}
else
{
NewConfigEntry (section, start, whiteprobe);
}
} }
} }
} }
return true; return true;
} }
//====================================================================
//
// FConfigFile :: ReadMultiLineValue
//
// Reads a multi-line value, with format as follows:
//
// key=<<<ENDTAG
// ... blah blah blah ...
// >>>ENDTAG
//
// The final ENDTAG must be on a line all by itself.
//
//====================================================================
FConfigFile::FConfigEntry *FConfigFile::ReadMultiLineValue(void *file, FConfigSection *section, const char *key, const char *endtag)
{
char readbuf[READBUFFERSIZE];
FString value;
size_t endlen = strlen(endtag);
// Keep on reading lines until we reach a line that matches >>>endtag
while (ReadLine(readbuf, READBUFFERSIZE, file) != NULL)
{
// Does the start of this line match the endtag?
if (readbuf[0] == '>' && readbuf[1] == '>' && readbuf[2] == '>' &&
strncmp(readbuf + 3, endtag, endlen) == 0)
{ // Is there nothing but line break characters after the match?
size_t i;
for (i = endlen + 3; readbuf[i] != '\0'; ++i)
{
if (readbuf[i] != '\n' && readbuf[i] != '\r')
{ // Not a line break character
break;
}
}
if (readbuf[i] == '\0')
{ // We're done; strip the previous line's line breaks, since it's not part of the value.
value.StripRight("\n\r");
}
break;
}
// Append this line to the value.
value << readbuf;
}
return NewConfigEntry(section, key, value);
}
//==================================================================== //====================================================================
// //
// FConfigFile :: ReadLine // FConfigFile :: ReadLine
@ -695,6 +763,13 @@ char *FConfigFile::ReadLine (char *string, int n, void *file) const
bool FConfigFile::WriteConfigFile () const bool FConfigFile::WriteConfigFile () const
{ {
if (!OkayToWrite && FileExisted)
{ // Pretend it was written anyway so that the user doesn't get
// any "config not written" notifications, but only if the file
// already existed. Otherwise, let it write out a default one.
return true;
}
FILE *file = fopen (PathName, "w"); FILE *file = fopen (PathName, "w");
FConfigSection *section; FConfigSection *section;
FConfigEntry *entry; FConfigEntry *entry;
@ -715,7 +790,16 @@ bool FConfigFile::WriteConfigFile () const
fprintf (file, "[%s]\n", section->Name); fprintf (file, "[%s]\n", section->Name);
while (entry != NULL) while (entry != NULL)
{ {
fprintf (file, "%s=%s\n", entry->Key, entry->Value); if (strpbrk(entry->Value, "\r\n") == NULL)
{ // Single-line value
fprintf (file, "%s=%s\n", entry->Key, entry->Value);
}
else
{ // Multi-line value
const char *endtag = GenerateEndTag(entry->Value);
fprintf (file, "%s=<<<%s\n%s\n>>>%s\n", entry->Key,
endtag, entry->Value, endtag);
}
entry = entry->Next; entry = entry->Next;
} }
section = section->Next; section = section->Next;
@ -725,6 +809,44 @@ bool FConfigFile::WriteConfigFile () const
return true; return true;
} }
//====================================================================
//
// FConfigFile :: GenerateEndTag
//
// Generates a terminator sequence for multi-line values that does
// not appear anywhere in the value.
//
//====================================================================
const char *FConfigFile::GenerateEndTag(const char *value)
{
static const char Base64Table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
static char EndTag[25] = "EOV-";
// Try different 20-character sequences until we find one that
// isn't in the value. We create the sequences by generating two
// 64-bit random numbers and Base64 encoding the first 15 bytes
// from them.
union { QWORD rand_num[2]; BYTE rand_bytes[16]; };
do
{
rand_num[0] = pr_endtag.GenRand64();
rand_num[1] = pr_endtag.GenRand64();
for (int i = 0; i < 5; ++i)
{
DWORD three_bytes = (rand_bytes[i*3] << 16) | (rand_bytes[i*3+1] << 8) | (rand_bytes[i*3+2]);
EndTag[4+i*4 ] = Base64Table[rand_bytes[i*3] >> 2];
EndTag[4+i*4+1] = Base64Table[((rand_bytes[i*3] & 3) << 4) | (rand_bytes[i*3+1] >> 4)];
EndTag[4+i*4+2] = Base64Table[((rand_bytes[i*3+1] & 15) << 2) | (rand_bytes[i*3+2] >> 6)];
EndTag[4+i*4+3] = Base64Table[rand_bytes[i*3+2] & 63];
}
}
while (strstr(value, EndTag) != NULL);
return EndTag;
}
//==================================================================== //====================================================================
// //
// FConfigFile :: WriteCommentHeader // FConfigFile :: WriteCommentHeader

View File

@ -78,6 +78,10 @@ protected:
virtual char *ReadLine (char *string, int n, void *file) const; virtual char *ReadLine (char *string, int n, void *file) const;
bool ReadConfig (void *file); bool ReadConfig (void *file);
static const char *GenerateEndTag(const char *value);
bool OkayToWrite;
bool FileExisted;
private: private:
struct FConfigEntry struct FConfigEntry
@ -107,6 +111,7 @@ private:
FConfigEntry *FindEntry (FConfigSection *section, const char *key) const; FConfigEntry *FindEntry (FConfigSection *section, const char *key) const;
FConfigSection *NewConfigSection (const char *name); FConfigSection *NewConfigSection (const char *name);
FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value); FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value);
FConfigEntry *ReadMultiLineValue (void *file, FConfigSection *section, const char *key, const char *terminator);
void SetSectionNote (FConfigSection *section, const char *note); void SetSectionNote (FConfigSection *section, const char *note);
public: public:

View File

@ -273,7 +273,9 @@ void CT_Drawer (void)
if (players[consoleplayer].camera != NULL && if (players[consoleplayer].camera != NULL &&
(Button_ShowScores.bDown || (Button_ShowScores.bDown ||
players[consoleplayer].camera->health <= 0)) players[consoleplayer].camera->health <= 0) &&
// Don't draw during intermission, since it has its own scoreboard in wi_stuff.cpp.
gamestate != GS_INTERMISSION)
{ {
HU_DrawScores (&players[consoleplayer]); HU_DrawScores (&players[consoleplayer]);
} }

View File

@ -162,6 +162,41 @@ struct CodePointerAlias
}; };
static TArray<CodePointerAlias> MBFCodePointers; static TArray<CodePointerAlias> MBFCodePointers;
struct AmmoPerAttack
{
actionf_p func;
int ammocount;
};
DECLARE_ACTION(A_Punch)
DECLARE_ACTION(A_FirePistol)
DECLARE_ACTION(A_FireShotgun)
DECLARE_ACTION(A_FireShotgun2)
DECLARE_ACTION(A_FireCGun)
DECLARE_ACTION(A_FireMissile)
DECLARE_ACTION_PARAMS(A_Saw)
DECLARE_ACTION(A_FirePlasma)
DECLARE_ACTION(A_FireBFG)
DECLARE_ACTION(A_FireOldBFG)
DECLARE_ACTION(A_FireRailgun)
// Default ammo use of the various weapon attacks
static AmmoPerAttack AmmoPerAttacks[] = {
{ AF_A_Punch, 0},
{ AF_A_FirePistol, 1},
{ AF_A_FireShotgun, 1},
{ AF_A_FireShotgun2, 2},
{ AF_A_FireCGun, 1},
{ AF_A_FireMissile, 1},
{ AFP_A_Saw, 0},
{ AF_A_FirePlasma, 1},
{ AF_A_FireBFG, -1}, // uses deh.BFGCells
{ AF_A_FireOldBFG, 1},
{ AF_A_FireRailgun, 1},
{ NULL, 0}
};
// Miscellaneous info that used to be constant // Miscellaneous info that used to be constant
DehInfo deh = DehInfo deh =
{ {
@ -183,6 +218,7 @@ DehInfo deh =
255, // Rocket explosion style, 255=use cvar 255, // Rocket explosion style, 255=use cvar
FRACUNIT*2/3, // Rocket explosion alpha FRACUNIT*2/3, // Rocket explosion alpha
false, // .NoAutofreeze false, // .NoAutofreeze
40, // BFG cells per shot
}; };
// Doom identified pickup items by their sprites. ZDoom prefers to use their // Doom identified pickup items by their sprites. ZDoom prefers to use their
@ -691,8 +727,11 @@ void SetDehParams(FState * state, int codepointer)
if (value2) StateParams.Set(ParamIndex+1, new FxConstant(SoundMap[value2-1], *pos)); // hit sound if (value2) StateParams.Set(ParamIndex+1, new FxConstant(SoundMap[value2-1], *pos)); // hit sound
break; break;
case MBF_PlaySound: case MBF_PlaySound:
StateParams.Set(ParamIndex+0, new FxConstant(SoundMap[value1-1], *pos)); // soundid StateParams.Set(ParamIndex+0, new FxConstant(SoundMap[value1-1], *pos)); // soundid
StateParams.Set(ParamIndex+4, new FxConstant((value2?ATTN_NONE:ATTN_NORM), *pos)); // attenuation StateParams.Set(ParamIndex+1, new FxConstant(CHAN_BODY, *pos)); // channel
StateParams.Set(ParamIndex+2, new FxConstant(1.0, *pos)); // volume
StateParams.Set(ParamIndex+3, new FxConstant(false, *pos)); // looping
StateParams.Set(ParamIndex+4, new FxConstant((value2 ? ATTN_NONE : ATTN_NORM), *pos)); // attenuation
break; break;
case MBF_RandomJump: case MBF_RandomJump:
StateParams.Set(ParamIndex+0, new FxConstant(2, *pos)); // count StateParams.Set(ParamIndex+0, new FxConstant(2, *pos)); // count
@ -1087,6 +1126,15 @@ static int PatchThing (int thingy)
value[0] &= ~MF_TRANSLUCENT; // clean the slot value[0] &= ~MF_TRANSLUCENT; // clean the slot
vchanged[2] = true; value[2] |= 2; // let the TRANSLUCxx code below handle it vchanged[2] = true; value[2] |= 2; // let the TRANSLUCxx code below handle it
} }
if ((info->flags & MF_MISSILE) && (info->flags2 & MF2_NOTELEPORT)
&& !(value[0] & MF_MISSILE))
{
// ZDoom gives missiles flags that did not exist in Doom: MF2_NOTELEPORT,
// MF2_IMPACT, and MF2_PCROSS. The NOTELEPORT one can be a problem since
// some projectile actors (those new to Doom II) were not excluded from
// triggering line effects and can teleport when the missile flag is removed.
info->flags2 &= ~MF2_NOTELEPORT;
}
info->flags = value[0]; info->flags = value[0];
} }
if (vchanged[1]) if (vchanged[1])
@ -1182,15 +1230,24 @@ static int PatchThing (int thingy)
PushTouchedActor(const_cast<PClass *>(type)); PushTouchedActor(const_cast<PClass *>(type));
} }
// Make MF3_ISMONSTER match MF_COUNTKILL // If MF_COUNTKILL is set, make sure the other standard monster flags are
if (info->flags & MF_COUNTKILL) // set, too. And vice versa.
if (thingy != 1) // don't mess with the player's flags
{ {
info->flags3 |= MF3_ISMONSTER; if (info->flags & MF_COUNTKILL)
} {
else info->flags2 |= MF2_PUSHWALL | MF2_MCROSS | MF2_PASSMOBJ;
{ info->flags3 |= MF3_ISMONSTER;
info->flags3 &= ~MF3_ISMONSTER; }
else
{
info->flags2 &= ~(MF2_PUSHWALL | MF2_MCROSS);
info->flags3 &= ~MF3_ISMONSTER;
}
} }
// Everything that's altered here gets the CANUSEWALLS flag, just in case
// it calls P_Move().
info->flags4 |= MF4_CANUSEWALLS;
if (patchedStates) if (patchedStates)
{ {
statedef.InstallStates(type->ActorInfo, info); statedef.InstallStates(type->ActorInfo, info);
@ -1296,7 +1353,7 @@ static int PatchFrame (int frameNum)
if (keylen == 8 && stricmp (Line1, "Duration") == 0) if (keylen == 8 && stricmp (Line1, "Duration") == 0)
{ {
tics = clamp (val, -1, 65534); tics = clamp (val, -1, SHRT_MAX);
} }
else if (keylen == 9 && stricmp (Line1, "Unknown 1") == 0) else if (keylen == 9 && stricmp (Line1, "Unknown 1") == 0)
{ {
@ -1571,6 +1628,7 @@ static int PatchWeapon (int weapNum)
else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0) else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0)
{ {
info->AmmoUse1 = val; info->AmmoUse1 = val;
info->flags6 |= MF6_INTRYMOVE; // flag the weapon for postprocessing (reuse a flag that can't be set by external means)
} }
else if (stricmp (Line1, "Min ammo") == 0) else if (stricmp (Line1, "Min ammo") == 0)
{ {
@ -1724,7 +1782,7 @@ static int PatchMisc (int dummy)
{ {
if (stricmp (Line1, "BFG Cells/Shot") == 0) if (stricmp (Line1, "BFG Cells/Shot") == 0)
{ {
((AWeapon*)GetDefaultByName ("BFG9000"))->AmmoUse1 = atoi (Line2); deh.BFGCells = atoi (Line2);
} }
else if (stricmp (Line1, "Rocket Explosion Style") == 0) else if (stricmp (Line1, "Rocket Explosion Style") == 0)
{ {
@ -2279,6 +2337,28 @@ int D_LoadDehLumps()
{ {
count += D_LoadDehLump(lumpnum); count += D_LoadDehLump(lumpnum);
} }
if (0 == PatchSize)
{
// No DEH/BEX patch is loaded yet, try to find lump(s) with specific extensions
for (lumpnum = 0, lastlump = Wads.GetNumLumps();
lumpnum < lastlump;
++lumpnum)
{
const char* const fullName = Wads.GetLumpFullName(lumpnum);
const char* const extension = strrchr(fullName, '.');
const bool isDehOrBex = NULL != extension
&& (0 == stricmp(extension, ".deh") || 0 == stricmp(extension, ".bex"));
if (isDehOrBex)
{
count += D_LoadDehLump(lumpnum);
}
}
}
return count; return count;
} }
@ -2482,8 +2562,6 @@ static void UnloadDehSupp ()
BitNames.ShrinkToFit(); BitNames.ShrinkToFit();
StyleNames.Clear(); StyleNames.Clear();
StyleNames.ShrinkToFit(); StyleNames.ShrinkToFit();
WeaponNames.Clear();
WeaponNames.ShrinkToFit();
AmmoNames.Clear(); AmmoNames.Clear();
AmmoNames.ShrinkToFit(); AmmoNames.ShrinkToFit();
@ -2776,6 +2854,7 @@ static bool LoadDehSupp ()
} }
else if (sc.Compare("WeaponNames")) else if (sc.Compare("WeaponNames"))
{ {
WeaponNames.Clear(); // This won't be cleared by UnloadDEHSupp so we need to do it here explicitly
sc.MustGetStringName("{"); sc.MustGetStringName("{");
while (!sc.CheckString("}")) while (!sc.CheckString("}"))
{ {
@ -2882,6 +2961,55 @@ void FinishDehPatch ()
StateMap.ShrinkToFit(); StateMap.ShrinkToFit();
TouchedActors.Clear(); TouchedActors.Clear();
TouchedActors.ShrinkToFit(); TouchedActors.ShrinkToFit();
// Now it gets nasty: We have to fiddle around with the weapons' ammo use info to make Doom's original
// ammo consumption work as intended.
for(unsigned i = 0; i < WeaponNames.Size(); i++)
{
AWeapon *weap = (AWeapon*)GetDefaultByType(WeaponNames[i]);
bool found = false;
if (weap->flags6 & MF6_INTRYMOVE)
{
// Weapon sets an explicit amount of ammo to use so we won't need any special processing here
weap->flags6 &= ~MF6_INTRYMOVE;
}
else
{
weap->WeaponFlags |= WIF_DEHAMMO;
weap->AmmoUse1 = 0;
// to allow proper checks in CheckAmmo we have to find the first attack pointer in the Fire sequence
// and set its default ammo use as the weapon's AmmoUse1.
TMap<FState*, bool> StateVisited;
FState *state = WeaponNames[i]->ActorInfo->FindState(NAME_Fire);
while (state != NULL)
{
bool *check = StateVisited.CheckKey(state);
if (check != NULL && *check)
{
break; // State has already been checked so we reached a loop
}
StateVisited[state] = true;
for(unsigned j = 0; AmmoPerAttacks[j].func != NULL; j++)
{
if (state->ActionFunc == AmmoPerAttacks[j].func)
{
found = true;
int use = AmmoPerAttacks[j].ammocount;
if (use < 0) use = deh.BFGCells;
weap->AmmoUse1 = use;
break;
}
}
if (found) break;
state = state->GetNextState();
}
}
}
WeaponNames.Clear();
WeaponNames.ShrinkToFit();
} }
void ModifyDropAmount(AInventory *inv, int dropamount); void ModifyDropAmount(AInventory *inv, int dropamount);

View File

@ -63,6 +63,7 @@ typedef enum
ga_newgame2, ga_newgame2,
ga_loadgame, ga_loadgame,
ga_loadgamehidecon, ga_loadgamehidecon,
ga_loadgameplaydemo,
ga_autoloadgame, ga_autoloadgame,
ga_savegame, ga_savegame,
ga_autosave, ga_autosave,
@ -89,8 +90,8 @@ typedef enum
BT_CROUCH = 1<<3, BT_CROUCH = 1<<3,
BT_TURN180 = 1<<4, BT_TURN180 = 1<<4,
BT_ALTATTACK = 1<<5, // Press your other "Fire". BT_ALTATTACK = 1<<5, // Press your other "Fire".
BT_RELOAD = 1<<6, // Not connected to anything at the moment. BT_RELOAD = 1<<6, // [XA] Reload key. Causes state jump in A_WeaponReady.
BT_ZOOM = 1<<7, // Neither is this. BT_ZOOM = 1<<7, // [XA] Zoom key. Ditto.
// The rest are all ignored by the play simulation and are for scripts. // The rest are all ignored by the play simulation and are for scripts.
BT_SPEED = 1<<8, BT_SPEED = 1<<8,

View File

@ -413,7 +413,7 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
} }
} }
if (iwadparm == NULL || wads[0].Path.IsEmpty()) if (iwadparm == NULL || wads[0].Path.IsEmpty() || mIWads[wads[0].Type].Required.IsNotEmpty())
{ {
if (GameConfig->SetSection ("IWADSearch.Directories")) if (GameConfig->SetSection ("IWADSearch.Directories"))
{ {
@ -441,7 +441,8 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
"heretic shadow of the serpent riders/base", "heretic shadow of the serpent riders/base",
"hexen/base", "hexen/base",
"hexen deathkings of the dark citadel/base", "hexen deathkings of the dark citadel/base",
"ultimate doom/base" "ultimate doom/base",
"DOOM 3 BFG Edition/base/wads"
}; };
steam_path += "/SteamApps/common/"; steam_path += "/SteamApps/common/";
for (i = 0; i < countof(steam_dirs); ++i) for (i = 0; i < countof(steam_dirs); ++i)

View File

@ -106,6 +106,7 @@
#include "po_man.h" #include "po_man.h"
#include "resourcefiles/resourcefile.h" #include "resourcefiles/resourcefile.h"
#include "r_renderer.h" #include "r_renderer.h"
#include "p_local.h"
#ifdef USE_POLYMOST #ifdef USE_POLYMOST
#include "r_polymost.h" #include "r_polymost.h"
@ -157,7 +158,6 @@ EXTERN_CVAR (Bool, sv_unlimited_pickup)
extern int testingmode; extern int testingmode;
extern bool setmodeneeded; extern bool setmodeneeded;
extern bool netdemo;
extern int NewWidth, NewHeight, NewBits, DisplayBits; extern int NewWidth, NewHeight, NewBits, DisplayBits;
EXTERN_CVAR (Bool, st_scale) EXTERN_CVAR (Bool, st_scale)
extern bool gameisdead; extern bool gameisdead;
@ -515,8 +515,8 @@ CVAR (Flag, sv_nocountendmonst, dmflags2, DF2_NOCOUNTENDMONST);
// //
//========================================================================== //==========================================================================
int i_compatflags; // internal compatflags composed from the compatflags CVAR and MAPINFO settings int i_compatflags, i_compatflags2; // internal compatflags composed from the compatflags CVAR and MAPINFO settings
int ii_compatflags, ib_compatflags; int ii_compatflags, ii_compatflags2, ib_compatflags;
EXTERN_CVAR(Int, compatmode) EXTERN_CVAR(Int, compatmode)
@ -526,19 +526,30 @@ static int GetCompatibility(int mask)
else return (mask & ~level.info->compatmask) | (level.info->compatflags & level.info->compatmask); else return (mask & ~level.info->compatmask) | (level.info->compatflags & level.info->compatmask);
} }
static int GetCompatibility2(int mask)
{
return (level.info == NULL) ? mask
: (mask & ~level.info->compatmask2) | (level.info->compatflags2 & level.info->compatmask2);
}
CUSTOM_CVAR (Int, compatflags, 0, CVAR_ARCHIVE|CVAR_SERVERINFO) CUSTOM_CVAR (Int, compatflags, 0, CVAR_ARCHIVE|CVAR_SERVERINFO)
{ {
int old = i_compatflags; int old = i_compatflags;
i_compatflags = GetCompatibility(self) | ii_compatflags; i_compatflags = GetCompatibility(self) | ii_compatflags;
if ((old ^i_compatflags) & COMPATF_POLYOBJ) if ((old ^ i_compatflags) & COMPATF_POLYOBJ)
{ {
FPolyObj::ClearAllSubsectorLinks(); FPolyObj::ClearAllSubsectorLinks();
} }
} }
CUSTOM_CVAR (Int, compatflags2, 0, CVAR_ARCHIVE|CVAR_SERVERINFO)
{
i_compatflags2 = GetCompatibility2(self) | ii_compatflags2;
}
CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
{ {
int v; int v, w = 0;
switch (self) switch (self)
{ {
@ -551,6 +562,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT| v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT|
COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_DEHHEALTH|COMPATF_CROSSDROPOFF| COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_DEHHEALTH|COMPATF_CROSSDROPOFF|
COMPATF_LIGHT; COMPATF_LIGHT;
w= COMPATF2_FLOORMOVE;
break; break;
case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set) case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set)
@ -558,6 +570,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN| COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN|
COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS|COMPATF_HITSCAN| COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|
COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_LIGHT|COMPATF_MASKEDMIDTEX; COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_LIGHT|COMPATF_MASKEDMIDTEX;
w = COMPATF2_BADANGLES|COMPATF2_FLOORMOVE;
break; break;
case 3: // Boom compat mode case 3: // Boom compat mode
@ -573,47 +586,50 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
COMPATF_MBFMONSTERMOVE|COMPATF_NOBLOCKFRIENDS; COMPATF_MBFMONSTERMOVE|COMPATF_NOBLOCKFRIENDS;
break; break;
case 6: // Boom with some added settings to reenable spme 'broken' behavior case 6: // Boom with some added settings to reenable some 'broken' behavior
v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_NO_PASSMOBJ| v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_NO_PASSMOBJ|
COMPATF_INVISIBILITY|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|COMPATF_WALLRUN|COMPATF_NOTOSSDROPS; COMPATF_INVISIBILITY|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|COMPATF_WALLRUN|COMPATF_NOTOSSDROPS;
break; break;
} }
compatflags = v; compatflags = v;
compatflags2 = w;
} }
CVAR (Flag, compat_shortTex, compatflags, COMPATF_SHORTTEX); CVAR (Flag, compat_shortTex, compatflags, COMPATF_SHORTTEX);
CVAR (Flag, compat_stairs, compatflags, COMPATF_STAIRINDEX); CVAR (Flag, compat_stairs, compatflags, COMPATF_STAIRINDEX);
CVAR (Flag, compat_limitpain, compatflags, COMPATF_LIMITPAIN); CVAR (Flag, compat_limitpain, compatflags, COMPATF_LIMITPAIN);
CVAR (Flag, compat_silentpickup,compatflags, COMPATF_SILENTPICKUP); CVAR (Flag, compat_silentpickup, compatflags, COMPATF_SILENTPICKUP);
CVAR (Flag, compat_nopassover, compatflags, COMPATF_NO_PASSMOBJ); CVAR (Flag, compat_nopassover, compatflags, COMPATF_NO_PASSMOBJ);
CVAR (Flag, compat_soundslots, compatflags, COMPATF_MAGICSILENCE); CVAR (Flag, compat_soundslots, compatflags, COMPATF_MAGICSILENCE);
CVAR (Flag, compat_wallrun, compatflags, COMPATF_WALLRUN); CVAR (Flag, compat_wallrun, compatflags, COMPATF_WALLRUN);
CVAR (Flag, compat_notossdrops, compatflags, COMPATF_NOTOSSDROPS); CVAR (Flag, compat_notossdrops, compatflags, COMPATF_NOTOSSDROPS);
CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING); CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
CVAR (Flag, compat_nodoorlight, compatflags, COMPATF_NODOORLIGHT); CVAR (Flag, compat_nodoorlight, compatflags, COMPATF_NODOORLIGHT);
CVAR (Flag, compat_ravenscroll, compatflags, COMPATF_RAVENSCROLL); CVAR (Flag, compat_ravenscroll, compatflags, COMPATF_RAVENSCROLL);
CVAR (Flag, compat_soundtarget, compatflags, COMPATF_SOUNDTARGET); CVAR (Flag, compat_soundtarget, compatflags, COMPATF_SOUNDTARGET);
CVAR (Flag, compat_dehhealth, compatflags, COMPATF_DEHHEALTH); CVAR (Flag, compat_dehhealth, compatflags, COMPATF_DEHHEALTH);
CVAR (Flag, compat_trace, compatflags, COMPATF_TRACE); CVAR (Flag, compat_trace, compatflags, COMPATF_TRACE);
CVAR (Flag, compat_dropoff, compatflags, COMPATF_DROPOFF); CVAR (Flag, compat_dropoff, compatflags, COMPATF_DROPOFF);
CVAR (Flag, compat_boomscroll, compatflags, COMPATF_BOOMSCROLL); CVAR (Flag, compat_boomscroll, compatflags, COMPATF_BOOMSCROLL);
CVAR (Flag, compat_invisibility,compatflags, COMPATF_INVISIBILITY); CVAR (Flag, compat_invisibility, compatflags, COMPATF_INVISIBILITY);
CVAR (Flag, compat_silentinstantfloors,compatflags, COMPATF_SILENT_INSTANT_FLOORS); CVAR (Flag, compat_silentinstantfloors, compatflags, COMPATF_SILENT_INSTANT_FLOORS);
CVAR (Flag, compat_sectorsounds,compatflags, COMPATF_SECTORSOUNDS); CVAR (Flag, compat_sectorsounds, compatflags, COMPATF_SECTORSOUNDS);
CVAR (Flag, compat_missileclip, compatflags, COMPATF_MISSILECLIP); CVAR (Flag, compat_missileclip, compatflags, COMPATF_MISSILECLIP);
CVAR (Flag, compat_crossdropoff,compatflags, COMPATF_CROSSDROPOFF); CVAR (Flag, compat_crossdropoff, compatflags, COMPATF_CROSSDROPOFF);
CVAR (Flag, compat_anybossdeath,compatflags, COMPATF_ANYBOSSDEATH); CVAR (Flag, compat_anybossdeath, compatflags, COMPATF_ANYBOSSDEATH);
CVAR (Flag, compat_minotaur, compatflags, COMPATF_MINOTAUR); CVAR (Flag, compat_minotaur, compatflags, COMPATF_MINOTAUR);
CVAR (Flag, compat_mushroom, compatflags, COMPATF_MUSHROOM); CVAR (Flag, compat_mushroom, compatflags, COMPATF_MUSHROOM);
CVAR (Flag, compat_mbfmonstermove,compatflags, COMPATF_MBFMONSTERMOVE); CVAR (Flag, compat_mbfmonstermove, compatflags, COMPATF_MBFMONSTERMOVE);
CVAR (Flag, compat_corpsegibs, compatflags, COMPATF_CORPSEGIBS); CVAR (Flag, compat_corpsegibs, compatflags, COMPATF_CORPSEGIBS);
CVAR (Flag, compat_noblockfriends,compatflags,COMPATF_NOBLOCKFRIENDS); CVAR (Flag, compat_noblockfriends, compatflags, COMPATF_NOBLOCKFRIENDS);
CVAR (Flag, compat_spritesort, compatflags,COMPATF_SPRITESORT); CVAR (Flag, compat_spritesort, compatflags, COMPATF_SPRITESORT);
CVAR (Flag, compat_hitscan, compatflags,COMPATF_HITSCAN); CVAR (Flag, compat_hitscan, compatflags, COMPATF_HITSCAN);
CVAR (Flag, compat_light, compatflags,COMPATF_LIGHT); CVAR (Flag, compat_light, compatflags, COMPATF_LIGHT);
CVAR (Flag, compat_polyobj, compatflags,COMPATF_POLYOBJ); CVAR (Flag, compat_polyobj, compatflags, COMPATF_POLYOBJ);
CVAR (Flag, compat_maskedmidtex,compatflags,COMPATF_MASKEDMIDTEX); CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX);
CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES);
CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE);
//========================================================================== //==========================================================================
// //
@ -665,6 +681,8 @@ void D_Display ()
// Reload crosshair if transitioned to a different size // Reload crosshair if transitioned to a different size
ST_LoadCrosshair (true); ST_LoadCrosshair (true);
AM_NewResolution (); AM_NewResolution ();
// Reset the mouse cursor in case the bit depth changed
vid_cursor.Callback();
} }
} }
@ -679,21 +697,21 @@ void D_Display ()
if (screen->Lock (false)) if (screen->Lock (false))
{ {
SB_state = screen->GetPageCount (); ST_SetNeedRefresh();
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
} }
// [RH] Allow temporarily disabling wipes // [RH] Allow temporarily disabling wipes
if (NoWipe) if (NoWipe)
{ {
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
NoWipe--; NoWipe--;
wipe = false; wipe = false;
wipegamestate = gamestate; wipegamestate = gamestate;
} }
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL) else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL)
{ // save the current screen if about to wipe { // save the current screen if about to wipe
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
switch (wipegamestate) switch (wipegamestate)
{ {
default: default:
@ -756,13 +774,14 @@ void D_Display ()
} }
screen->SetBlendingRect(viewwindowx, viewwindowy, screen->SetBlendingRect(viewwindowx, viewwindowy,
viewwindowx + viewwidth, viewwindowy + viewheight); viewwindowx + viewwidth, viewwindowy + viewheight);
P_CheckPlayerSprites(); P_PredictPlayer(&players[consoleplayer]);
Renderer->RenderView(&players[consoleplayer]); Renderer->RenderView(&players[consoleplayer]);
P_UnPredictPlayer();
if ((hw2d = screen->Begin2D(viewactive))) if ((hw2d = screen->Begin2D(viewactive)))
{ {
// Redraw everything every frame when using 2D accel // Redraw everything every frame when using 2D accel
SB_state = screen->GetPageCount(); ST_SetNeedRefresh();
BorderNeedRefresh = screen->GetPageCount(); V_SetBorderNeedRefresh();
} }
Renderer->DrawRemainingPlayerSprites(); Renderer->DrawRemainingPlayerSprites();
screen->DrawBlendingRect(); screen->DrawBlendingRect();
@ -783,17 +802,22 @@ void D_Display ()
if (hud_althud && viewheight == SCREENHEIGHT && screenblocks > 10) if (hud_althud && viewheight == SCREENHEIGHT && screenblocks > 10)
{ {
StatusBar->DrawBottomStuff (HUD_AltHud);
if (DrawFSHUD || automapactive) DrawHUD(); if (DrawFSHUD || automapactive) DrawHUD();
StatusBar->DrawTopStuff (HUD_None); StatusBar->Draw (HUD_AltHud);
StatusBar->DrawTopStuff (HUD_AltHud);
} }
else else
if (viewheight == SCREENHEIGHT && viewactive && screenblocks > 10) if (viewheight == SCREENHEIGHT && viewactive && screenblocks > 10)
{ {
StatusBar->Draw (DrawFSHUD ? HUD_Fullscreen : HUD_None); EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None;
StatusBar->DrawTopStuff (DrawFSHUD ? HUD_Fullscreen : HUD_None); StatusBar->DrawBottomStuff (state);
StatusBar->Draw (state);
StatusBar->DrawTopStuff (state);
} }
else else
{ {
StatusBar->DrawBottomStuff (HUD_StatusBar);
StatusBar->Draw (HUD_StatusBar); StatusBar->Draw (HUD_StatusBar);
StatusBar->DrawTopStuff (HUD_StatusBar); StatusBar->DrawTopStuff (HUD_StatusBar);
} }
@ -912,6 +936,7 @@ void D_Display ()
void D_ErrorCleanup () void D_ErrorCleanup ()
{ {
savegamerestore = false;
screen->Unlock (); screen->Unlock ();
bglobal.RemoveAllBots (true); bglobal.RemoveAllBots (true);
D_QuitNetGame (); D_QuitNetGame ();
@ -1210,12 +1235,17 @@ void D_DoAdvanceDemo (void)
static int pagecount; static int pagecount;
const char *pagename = NULL; const char *pagename = NULL;
advancedemo = false;
if (gameaction != ga_nothing)
{
return;
}
V_SetBlend (0,0,0,0); V_SetBlend (0,0,0,0);
players[consoleplayer].playerstate = PST_LIVE; // not reborn players[consoleplayer].playerstate = PST_LIVE; // not reborn
advancedemo = false;
usergame = false; // no save / end game here usergame = false; // no save / end game here
paused = 0; paused = 0;
gameaction = ga_nothing;
// [RH] If you want something more dynamic for your title, create a map // [RH] If you want something more dynamic for your title, create a map
// and name it TITLEMAP. That map will be loaded and used as the title. // and name it TITLEMAP. That map will be loaded and used as the title.
@ -1248,7 +1278,7 @@ void D_DoAdvanceDemo (void)
Advisory = NULL; Advisory = NULL;
if (!M_DemoNoPlay) if (!M_DemoNoPlay)
{ {
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
democount++; democount++;
mysnprintf (demoname + 4, countof(demoname) - 4, "%d", democount); mysnprintf (demoname + 4, countof(demoname) - 4, "%d", democount);
if (Wads.CheckNumForName (demoname) < 0) if (Wads.CheckNumForName (demoname) < 0)
@ -1328,6 +1358,136 @@ CCMD (endgame)
} }
} }
//==========================================================================
//
// ParseCVarInfo
//
//==========================================================================
void ParseCVarInfo()
{
int lump, lastlump = 0;
bool addedcvars = false;
while ((lump = Wads.FindLump("CVARINFO", &lastlump)) != -1)
{
FScanner sc(lump);
sc.SetCMode(true);
while (sc.GetToken())
{
FString cvarname;
char *cvardefault = NULL;
ECVarType cvartype = CVAR_Dummy;
int cvarflags = CVAR_MOD|CVAR_ARCHIVE;
FBaseCVar *cvar;
// Check for flag tokens.
while (sc.TokenType == TK_Identifier)
{
if (stricmp(sc.String, "server") == 0)
{
cvarflags |= CVAR_SERVERINFO;
}
else if (stricmp(sc.String, "user") == 0)
{
cvarflags |= CVAR_USERINFO;
}
else if (stricmp(sc.String, "noarchive") == 0)
{
cvarflags &= ~CVAR_ARCHIVE;
}
else
{
sc.ScriptError("Unknown cvar attribute '%s'", sc.String);
}
sc.MustGetAnyToken();
}
// Do some sanity checks.
if ((cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == 0 ||
(cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == (CVAR_SERVERINFO|CVAR_USERINFO))
{
sc.ScriptError("One of 'server' or 'user' must be specified");
}
// The next token must be the cvar type.
if (sc.TokenType == TK_Bool)
{
cvartype = CVAR_Bool;
}
else if (sc.TokenType == TK_Int)
{
cvartype = CVAR_Int;
}
else if (sc.TokenType == TK_Float)
{
cvartype = CVAR_Float;
}
else if (sc.TokenType == TK_Color)
{
cvartype = CVAR_Color;
}
else if (sc.TokenType == TK_String)
{
cvartype = CVAR_String;
}
else
{
sc.ScriptError("Bad cvar type '%s'", sc.String);
}
// The next token must be the cvar name.
sc.MustGetToken(TK_Identifier);
if (FindCVar(sc.String, NULL) != NULL)
{
sc.ScriptError("cvar '%s' already exists", sc.String);
}
cvarname = sc.String;
// A default value is optional and signalled by a '=' token.
if (sc.CheckToken('='))
{
switch (cvartype)
{
case CVAR_Bool:
if (!sc.CheckToken(TK_True) && !sc.CheckToken(TK_False))
{
sc.ScriptError("Expected true or false");
}
cvardefault = sc.String;
break;
case CVAR_Int:
sc.MustGetNumber();
cvardefault = sc.String;
break;
case CVAR_Float:
sc.MustGetFloat();
cvardefault = sc.String;
break;
default:
sc.MustGetString();
cvardefault = sc.String;
break;
}
}
// Now create the cvar.
cvar = C_CreateCVar(cvarname, cvartype, cvarflags);
if (cvardefault != NULL)
{
UCVarValue val;
val.String = cvardefault;
cvar->SetGenericRepDefault(val, CVAR_String);
}
// To be like C and ACS, require a semicolon after everything.
sc.MustGetToken(';');
addedcvars = true;
}
}
// Only load mod cvars from the config if we defined some, so we don't
// clutter up the cvar space when not playing mods with custom cvars.
if (addedcvars)
{
GameConfig->DoModSetup (gameinfo.ConfigName);
}
}
//========================================================================== //==========================================================================
// //
// D_AddFile // D_AddFile
@ -1681,6 +1841,25 @@ static FString ParseGameInfo(TArray<FString> &pwads, const char *fn, const char
sc.MustGetString(); sc.MustGetString();
DoomStartupInfo.BkColor = V_GetColor(NULL, sc.String); DoomStartupInfo.BkColor = V_GetColor(NULL, sc.String);
} }
else if (!nextKey.CompareNoCase("STARTUPTYPE"))
{
sc.MustGetString();
FString sttype = sc.String;
if (!sttype.CompareNoCase("DOOM"))
DoomStartupInfo.Type = FStartupInfo::DoomStartup;
else if (!sttype.CompareNoCase("HERETIC"))
DoomStartupInfo.Type = FStartupInfo::HereticStartup;
else if (!sttype.CompareNoCase("HEXEN"))
DoomStartupInfo.Type = FStartupInfo::HexenStartup;
else if (!sttype.CompareNoCase("STRIFE"))
DoomStartupInfo.Type = FStartupInfo::StrifeStartup;
else DoomStartupInfo.Type = FStartupInfo::DefaultStartup;
}
else if (!nextKey.CompareNoCase("STARTUPSONG"))
{
sc.MustGetString();
DoomStartupInfo.Song = sc.String;
}
} }
return iwad; return iwad;
} }
@ -2010,6 +2189,21 @@ static void CheckCmdLine()
} }
} }
//==========================================================================
//
// FinalGC
//
// If this doesn't free everything, the debug CRT will let us know.
//
//==========================================================================
static void FinalGC()
{
Args = NULL;
GC::FullGC();
GC::DelSoftRootHead(); // the soft root head will not be collected by a GC so we have to do it explicitly
}
//========================================================================== //==========================================================================
// //
// D_DoomMain // D_DoomMain
@ -2028,6 +2222,7 @@ void D_DoomMain (void)
D_DoomInit(); D_DoomInit();
PClass::StaticInit (); PClass::StaticInit ();
atterm(FinalGC);
// [RH] Make sure zdoom.pk3 is always loaded, // [RH] Make sure zdoom.pk3 is always loaded,
// as it contains magic stuff we need. // as it contains magic stuff we need.
@ -2099,7 +2294,10 @@ void D_DoomMain (void)
allwads.Clear(); allwads.Clear();
allwads.ShrinkToFit(); allwads.ShrinkToFit();
SetMapxxFlag(); SetMapxxFlag();
// Now that wads are loaded, define mod-specific cvars.
ParseCVarInfo();
// [RH] Initialize localizable strings. // [RH] Initialize localizable strings.
GStrings.LoadStrings (false); GStrings.LoadStrings (false);
@ -2127,8 +2325,14 @@ void D_DoomMain (void)
S_Init (); S_Init ();
Printf ("ST_Init: Init startup screen.\n"); Printf ("ST_Init: Init startup screen.\n");
if (!restart) StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5); if (!restart)
else StartScreen = new FStartupScreen(0); {
StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5);
}
else
{
StartScreen = new FStartupScreen(0);
}
ParseCompatibility(); ParseCompatibility();
@ -2137,14 +2341,17 @@ void D_DoomMain (void)
// [RH] Load sound environments // [RH] Load sound environments
S_ParseReverbDef (); S_ParseReverbDef ();
// [RH] Parse any SNDINFO lumps
Printf ("S_InitData: Load sound definitions.\n");
S_InitData ();
// [RH] Parse through all loaded mapinfo lumps // [RH] Parse through all loaded mapinfo lumps
Printf ("G_ParseMapInfo: Load map definitions.\n"); Printf ("G_ParseMapInfo: Load map definitions.\n");
G_ParseMapInfo (iwad_info->MapInfo); G_ParseMapInfo (iwad_info->MapInfo);
ReadStatistics(); ReadStatistics();
// [RH] Parse any SNDINFO lumps // MUSINFO must be parsed after MAPINFO
Printf ("S_InitData: Load sound definitions.\n"); S_ParseMusInfo();
S_InitData ();
Printf ("Texman.Init: Init texture manager.\n"); Printf ("Texman.Init: Init texture manager.\n");
TexMan.Init(); TexMan.Init();
@ -2275,6 +2482,7 @@ void D_DoomMain (void)
delete StartScreen; delete StartScreen;
StartScreen = NULL; StartScreen = NULL;
S_Sound (CHAN_BODY, "misc/startupdone", 1, ATTN_NONE);
if (Args->CheckParm("-norun")) if (Args->CheckParm("-norun"))
{ {
@ -2282,6 +2490,16 @@ void D_DoomMain (void)
} }
V_Init2(); V_Init2();
UpdateJoystickMenu(NULL);
v = Args->CheckValue ("-loadgame");
if (v)
{
FString file(v);
FixPathSeperator (file);
DefaultExtension (file, ".zds");
G_LoadGame (file);
}
v = Args->CheckValue("-playdemo"); v = Args->CheckValue("-playdemo");
if (v != NULL) if (v != NULL)
@ -2297,15 +2515,6 @@ void D_DoomMain (void)
G_TimeDemo (v); G_TimeDemo (v);
D_DoomLoop (); // never returns D_DoomLoop (); // never returns
} }
v = Args->CheckValue ("-loadgame");
if (v)
{
FString file(v);
FixPathSeperator (file);
DefaultExtension (file, ".zds");
G_LoadGame (file);
}
if (gameaction != ga_loadgame && gameaction != ga_loadgamehidecon) if (gameaction != ga_loadgame && gameaction != ga_loadgamehidecon)
{ {

View File

@ -94,6 +94,17 @@ struct FStartupInfo
FString Name; FString Name;
DWORD FgColor; // Foreground color for title banner DWORD FgColor; // Foreground color for title banner
DWORD BkColor; // Background color for title banner DWORD BkColor; // Background color for title banner
FString Song;
int Type;
enum
{
DefaultStartup,
DoomStartup,
HereticStartup,
HexenStartup,
StrifeStartup,
};
}; };
extern FStartupInfo DoomStartupInfo; extern FStartupInfo DoomStartupInfo;

View File

@ -59,9 +59,9 @@
#include "m_argv.h" #include "m_argv.h"
#include "p_lnspec.h" #include "p_lnspec.h"
#include "v_video.h" #include "v_video.h"
#include "p_spec.h"
int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide, #include "hardware.h"
int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net); #include "intermission/intermission.h"
EXTERN_CVAR (Int, disableautosave) EXTERN_CVAR (Int, disableautosave)
EXTERN_CVAR (Int, autosavecount) EXTERN_CVAR (Int, autosavecount)
@ -108,6 +108,8 @@ int resendcount[MAXNETNODES];
unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings
unsigned int currrecvtime[MAXPLAYERS]; unsigned int currrecvtime[MAXPLAYERS];
unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved.
bool hadlate;
int nodeforplayer[MAXPLAYERS]; int nodeforplayer[MAXPLAYERS];
int playerfornode[MAXNETNODES]; int playerfornode[MAXNETNODES];
@ -121,6 +123,7 @@ void G_BuildTiccmd (ticcmd_t *cmd);
void D_DoAdvanceDemo (void); void D_DoAdvanceDemo (void);
static void SendSetup (DWORD playersdetected[MAXNETNODES], BYTE gotsetup[MAXNETNODES], int len); static void SendSetup (DWORD playersdetected[MAXNETNODES], BYTE gotsetup[MAXNETNODES], int len);
static void RunScript(BYTE **stream, APlayerPawn *pawn, int snum, int argn, int always);
int reboundpacket; int reboundpacket;
BYTE reboundstore[MAX_MSGLEN]; BYTE reboundstore[MAX_MSGLEN];
@ -135,7 +138,18 @@ static int oldentertics;
extern bool advancedemo; extern bool advancedemo;
CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
// Do not use the separate FPS limit timer if we are limiting FPS with this.
if (self)
{
I_SetFPSLimit(0);
}
else
{
I_SetFPSLimit(-1);
}
}
// [RH] Special "ticcmds" get stored in here // [RH] Special "ticcmds" get stored in here
static struct TicSpecial static struct TicSpecial
@ -299,6 +313,8 @@ void Net_ClearBuffers ()
oldentertics = entertic; oldentertics = entertic;
gametic = 0; gametic = 0;
maketic = 0; maketic = 0;
lastglobalrecvtime = 0;
} }
// //
@ -584,21 +600,24 @@ void PlayerIsGone (int netnode, int netconsole)
if (deathmatch) if (deathmatch)
{ {
Printf ("%s left the game with %d frags\n", Printf ("%s left the game with %d frags\n",
players[netconsole].userinfo.netname, players[netconsole].userinfo.GetName(),
players[netconsole].fragcount); players[netconsole].fragcount);
} }
else else
{ {
Printf ("%s left the game\n", players[netconsole].userinfo.netname); Printf ("%s left the game\n", players[netconsole].userinfo.GetName());
} }
// [RH] Revert to your own view if spying through the player who left // [RH] Revert each player to their own view if spying through the player who left
if (players[consoleplayer].camera == players[netconsole].mo) for (int ii = 0; ii < MAXPLAYERS; ++ii)
{ {
players[consoleplayer].camera = players[consoleplayer].mo; if (playeringame[ii] && players[ii].camera == players[netconsole].mo)
if (StatusBar != NULL)
{ {
StatusBar->AttachToPlayer (&players[consoleplayer]); players[ii].camera = players[ii].mo;
if (ii == consoleplayer && StatusBar != NULL)
{
StatusBar->AttachToPlayer (&players[ii]);
}
} }
} }
@ -609,6 +628,11 @@ void PlayerIsGone (int netnode, int netconsole)
P_DisconnectEffect (players[netconsole].mo); P_DisconnectEffect (players[netconsole].mo);
players[netconsole].mo->player = NULL; players[netconsole].mo->player = NULL;
players[netconsole].mo->Destroy (); players[netconsole].mo->Destroy ();
if (!(players[netconsole].mo->ObjectFlags & OF_EuthanizeMe))
{ // We just destroyed a morphed player, so now the original player
// has taken their place. Destroy that one too.
players[netconsole].mo->Destroy();
}
players[netconsole].mo = NULL; players[netconsole].mo = NULL;
players[netconsole].camera = NULL; players[netconsole].camera = NULL;
} }
@ -626,7 +650,7 @@ void PlayerIsGone (int netnode, int netconsole)
{ {
Net_Arbitrator = i; Net_Arbitrator = i;
players[i].settings_controller = true; players[i].settings_controller = true;
Printf ("%s is the new arbitrator\n", players[i].userinfo.netname); Printf ("%s is the new arbitrator\n", players[i].userinfo.GetName());
break; break;
} }
} }
@ -680,6 +704,8 @@ void GetPackets (void)
} }
continue; // extra setup packet continue; // extra setup packet
} }
lastglobalrecvtime = I_GetTime (false); //Update the last time a packet was recieved
netnode = doomcom.remotenode; netnode = doomcom.remotenode;
netconsole = playerfornode[netnode] & ~PL_DRONE; netconsole = playerfornode[netnode] & ~PL_DRONE;
@ -748,6 +774,7 @@ void GetPackets (void)
} }
if (netbuffer[0] & NCMD_QUITTERS) if (netbuffer[0] & NCMD_QUITTERS)
{ {
numplayers = netbuffer[k++]; numplayers = netbuffer[k++];
for (int i = 0; i < numplayers; ++i) for (int i = 0; i < numplayers; ++i)
@ -1340,7 +1367,7 @@ bool DoArbitrate (void *userdata)
data->playersdetected[0] |= 1 << netbuffer[1]; data->playersdetected[0] |= 1 << netbuffer[1];
StartScreen->NetMessage ("Found %s (node %d, player %d)", StartScreen->NetMessage ("Found %s (node %d, player %d)",
players[netbuffer[1]].userinfo.netname, players[netbuffer[1]].userinfo.GetName(),
node, netbuffer[1]+1); node, netbuffer[1]+1);
} }
} }
@ -1799,6 +1826,33 @@ void TryRunTics (void)
if (lowtic < gametic) if (lowtic < gametic)
I_Error ("TryRunTics: lowtic < gametic"); I_Error ("TryRunTics: lowtic < gametic");
// [Ed850] Check to see the last time a packet was recieved.
// If it's longer then 3 seconds, a node has likely stalled. Check which one and re-request its last packet.
if(I_GetTime(false) - lastglobalrecvtime >= TICRATE*3)
{
int latenode = 0; // Node 0 is the local player, and should always be the highest
lastglobalrecvtime = I_GetTime(false); //Bump the count
if(NetMode == NET_PeerToPeer || consoleplayer == Net_Arbitrator)
{
for (i = 0; i < doomcom.numnodes; i++)
if (nodeingame[i] && nettics[i] < nettics[latenode])
latenode = i;
}
else if (nodeingame[nodeforplayer[Net_Arbitrator]] &&
nettics[nodeforplayer[Net_Arbitrator]] < nettics[0])
{ // Likely a packet server game. Only check the packet host.
latenode = Net_Arbitrator;
}
if (debugfile)
fprintf (debugfile, "lost tics from %i (%i to %i)\n",
latenode, nettics[latenode], gametic);
if(latenode != 0) // Send resend request to late node (if not yourself... somehow). Also mark the node as waiting to display it in the hud.
remoteresend[latenode] = players[playerfornode[latenode]].waiting = hadlate = true;
}
// don't stay in here forever -- give the menu a chance to work // don't stay in here forever -- give the menu a chance to work
if (I_GetTime (false) - entertic >= TICRATE/3) if (I_GetTime (false) - entertic >= TICRATE/3)
{ {
@ -1808,6 +1862,13 @@ void TryRunTics (void)
} }
} }
if (hadlate)
{
hadlate = false;
for (i = 0; i < MAXPLAYERS; i++)
players[i].waiting = false;
}
// run the count tics // run the count tics
if (counts > 0) if (counts > 0)
{ {
@ -1947,12 +2008,12 @@ void Net_DoCommand (int type, BYTE **stream, int player)
{ {
case DEM_SAY: case DEM_SAY:
{ {
const char *name = players[player].userinfo.netname; const char *name = players[player].userinfo.GetName();
BYTE who = ReadByte (stream); BYTE who = ReadByte (stream);
s = ReadString (stream); s = ReadString (stream);
CleanseString (s); CleanseString (s);
if (((who & 1) == 0) || players[player].userinfo.team == TEAM_NONE) if (((who & 1) == 0) || players[player].userinfo.GetTeam() == TEAM_NONE)
{ // Said to everyone { // Said to everyone
if (who & 2) if (who & 2)
{ {
@ -1964,7 +2025,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
} }
S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE); S_Sound (CHAN_VOICE | CHAN_UI, gameinfo.chatSound, 1, ATTN_NONE);
} }
else if (players[player].userinfo.team == players[consoleplayer].userinfo.team) else if (players[player].userinfo.GetTeam() == players[consoleplayer].userinfo.GetTeam())
{ // Said only to members of the player's team { // Said only to members of the player's team
if (who & 2) if (who & 2)
{ {
@ -2058,10 +2119,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break; break;
case DEM_CENTERVIEW: case DEM_CENTERVIEW:
if (players[player].mo != NULL) players[player].centering = true;
{
players[player].mo->pitch = 0;
}
break; break;
case DEM_INVUSEALL: case DEM_INVUSEALL:
@ -2227,7 +2285,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
paused = player + 1; paused = player + 1;
S_PauseSound (false, false); S_PauseSound (false, false);
} }
BorderNeedRefresh = screen->GetPageCount (); V_SetBorderNeedRefresh();
} }
break; break;
@ -2313,18 +2371,17 @@ void Net_DoCommand (int type, BYTE **stream, int player)
{ {
int snum = ReadWord (stream); int snum = ReadWord (stream);
int argn = ReadByte (stream); int argn = ReadByte (stream);
int arg[3] = { 0, 0, 0 };
RunScript(stream, players[player].mo, snum, argn, (type == DEM_RUNSCRIPT2) ? ACS_ALWAYS : 0);
for (i = 0; i < argn; ++i) }
{ break;
int argval = ReadLong(stream);
if ((unsigned)i < countof(arg)) case DEM_RUNNAMEDSCRIPT:
{ {
arg[i] = argval; char *sname = ReadString(stream);
} int argn = ReadByte(stream);
}
P_StartScript (players[player].mo, NULL, snum, level.mapname, false, RunScript(stream, players[player].mo, -FName(sname), argn & 127, (argn & 128) ? ACS_ALWAYS : 0);
arg[0], arg[1], arg[2], type == DEM_RUNSCRIPT2, false, true);
} }
break; break;
@ -2351,9 +2408,10 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_CROUCH: case DEM_CROUCH:
if (gamestate == GS_LEVEL && players[player].mo != NULL && if (gamestate == GS_LEVEL && players[player].mo != NULL &&
players[player].health > 0 && !(players[player].oldbuttons & BT_JUMP)) players[player].health > 0 && !(players[player].oldbuttons & BT_JUMP) &&
!P_IsPlayerTotallyFrozen(&players[player]))
{ {
players[player].crouching = players[player].crouchdir<0? 1 : -1; players[player].crouching = players[player].crouchdir < 0 ? 1 : -1;
} }
break; break;
@ -2374,7 +2432,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = true; players[playernum].settings_controller = true;
if (consoleplayer == playernum || consoleplayer == Net_Arbitrator) if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
Printf ("%s has been added to the controller list.\n", players[playernum].userinfo.netname); Printf ("%s has been added to the controller list.\n", players[playernum].userinfo.GetName());
} }
break; break;
@ -2384,7 +2442,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = false; players[playernum].settings_controller = false;
if (consoleplayer == playernum || consoleplayer == Net_Arbitrator) if (consoleplayer == playernum || consoleplayer == Net_Arbitrator)
Printf ("%s has been removed from the controller list.\n", players[playernum].userinfo.netname); Printf ("%s has been removed from the controller list.\n", players[playernum].userinfo.GetName());
} }
break; break;
@ -2419,17 +2477,27 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break; break;
case DEM_SETSLOT: case DEM_SETSLOT:
case DEM_SETSLOTPNUM:
{ {
int pnum;
if (type == DEM_SETSLOTPNUM)
{
pnum = ReadByte(stream);
}
else
{
pnum = player;
}
unsigned int slot = ReadByte(stream); unsigned int slot = ReadByte(stream);
int count = ReadByte(stream); int count = ReadByte(stream);
if (slot < NUM_WEAPON_SLOTS) if (slot < NUM_WEAPON_SLOTS)
{ {
players[player].weapons.Slots[slot].Clear(); players[pnum].weapons.Slots[slot].Clear();
} }
for(i = 0; i < count; ++i) for(i = 0; i < count; ++i)
{ {
const PClass *wpn = Net_ReadWeapon(stream); const PClass *wpn = Net_ReadWeapon(stream);
players[player].weapons.AddSlot(slot, wpn, player == consoleplayer); players[pnum].weapons.AddSlot(slot, wpn, pnum == consoleplayer);
} }
} }
break; break;
@ -2450,6 +2518,19 @@ void Net_DoCommand (int type, BYTE **stream, int player)
} }
break; break;
case DEM_SETPITCHLIMIT:
players[player].MinPitch = ReadByte(stream) * -ANGLE_1; // up
players[player].MaxPitch = ReadByte(stream) * ANGLE_1; // down
break;
case DEM_ADVANCEINTER:
F_AdvanceIntermission();
break;
case DEM_REVERTCAMERA:
players[player].camera = players[player].mo;
break;
default: default:
I_Error ("Unknown net command: %d", type); I_Error ("Unknown net command: %d", type);
break; break;
@ -2459,6 +2540,23 @@ void Net_DoCommand (int type, BYTE **stream, int player)
delete[] s; delete[] s;
} }
// Used by DEM_RUNSCRIPT, DEM_RUNSCRIPT2, and DEM_RUNNAMEDSCRIPT
static void RunScript(BYTE **stream, APlayerPawn *pawn, int snum, int argn, int always)
{
int arg[4] = { 0, 0, 0, 0 };
int i;
for (i = 0; i < argn; ++i)
{
int argval = ReadLong(stream);
if ((unsigned)i < countof(arg))
{
arg[i] = argval;
}
}
P_StartScript(pawn, NULL, snum, level.mapname, arg, MIN<int>(countof(arg), argn), ACS_NET | always);
}
void Net_SkipCommand (int type, BYTE **stream) void Net_SkipCommand (int type, BYTE **stream)
{ {
BYTE t; BYTE t;
@ -2547,6 +2645,11 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 3 + *(*stream + 2) * 4; skip = 3 + *(*stream + 2) * 4;
break; break;
case DEM_RUNNAMEDSCRIPT:
skip = strlen((char *)(*stream)) + 2;
skip += ((*(*stream + skip - 1)) & 127) * 4;
break;
case DEM_RUNSPECIAL: case DEM_RUNSPECIAL:
skip = 2 + *(*stream + 1) * 4; skip = 2 + *(*stream + 1) * 4;
break; break;
@ -2556,9 +2659,10 @@ void Net_SkipCommand (int type, BYTE **stream)
break; break;
case DEM_SETSLOT: case DEM_SETSLOT:
case DEM_SETSLOTPNUM:
{ {
skip = 2; skip = 2 + (type == DEM_SETSLOTPNUM);
for(int numweapons = (*stream)[1]; numweapons > 0; numweapons--) for(int numweapons = (*stream)[skip-1]; numweapons > 0; numweapons--)
{ {
skip += 1 + ((*stream)[skip] >> 7); skip += 1 + ((*stream)[skip] >> 7);
} }
@ -2570,6 +2674,9 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 2 + ((*stream)[1] >> 7); skip = 2 + ((*stream)[1] >> 7);
break; break;
case DEM_SETPITCHLIMIT:
skip = 2;
break;
default: default:
return; return;
@ -2586,7 +2693,7 @@ CCMD (pings)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i])
Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i], Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],
players[i].userinfo.netname); players[i].userinfo.GetName());
} }
//========================================================================== //==========================================================================
@ -2608,13 +2715,13 @@ static void Network_Controller (int playernum, bool add)
if (players[playernum].settings_controller && add) if (players[playernum].settings_controller && add)
{ {
Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.netname); Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.GetName());
return; return;
} }
if (!players[playernum].settings_controller && !add) if (!players[playernum].settings_controller && !add)
{ {
Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.netname); Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.GetName());
return; return;
} }
@ -2713,7 +2820,7 @@ CCMD (net_listcontrollers)
if (players[i].settings_controller) if (players[i].settings_controller)
{ {
Printf ("- %s\n", players[i].userinfo.netname); Printf ("- %s\n", players[i].userinfo.GetName());
} }
} }
} }

View File

@ -92,22 +92,6 @@ enum
const char *GenderNames[3] = { "male", "female", "other" }; const char *GenderNames[3] = { "male", "female", "other" };
static const char *UserInfoStrings[] =
{
"name",
"autoaim",
"color",
"skin",
"team",
"gender",
"neverswitchonpickup",
"movebob",
"stillbob",
"playerclass",
"colorset",
NULL
};
// Replace \ with %/ and % with %% // Replace \ with %/ and % with %%
FString D_EscapeUserInfo (const char *str) FString D_EscapeUserInfo (const char *str)
{ {
@ -192,11 +176,12 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet
{ {
userinfo_t *info = &players[player].userinfo; userinfo_t *info = &players[player].userinfo;
FPlayerColorSet *colorset = NULL; FPlayerColorSet *colorset = NULL;
int color; uint32 color;
int team;
if (players[player].mo != NULL) if (players[player].mo != NULL)
{ {
colorset = P_GetPlayerColorSet(players[player].mo->GetClass()->TypeName, info->colorset); colorset = P_GetPlayerColorSet(players[player].mo->GetClass()->TypeName, info->GetColorSet());
} }
if (colorset != NULL) if (colorset != NULL)
{ {
@ -204,25 +189,28 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet
} }
else else
{ {
color = info->color; color = info->GetColor();
} }
RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f, RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f,
h, s, v); h, s, v);
if (teamplay && TeamLibrary.IsValidTeam(info->team) && !Teams[info->team].GetAllowCustomPlayerColor ()) if (teamplay && TeamLibrary.IsValidTeam((team = info->GetTeam())) && !Teams[team].GetAllowCustomPlayerColor())
{ {
// In team play, force the player to use the team's hue // In team play, force the player to use the team's hue
// and adjust the saturation and value so that the team // and adjust the saturation and value so that the team
// hue is visible in the final color. // hue is visible in the final color.
float ts, tv; float ts, tv;
int tcolor = Teams[info->team].GetPlayerColor (); int tcolor = Teams[team].GetPlayerColor ();
RGBtoHSV (RPART(tcolor)/255.f, GPART(tcolor)/255.f, BPART(tcolor)/255.f, RGBtoHSV (RPART(tcolor)/255.f, GPART(tcolor)/255.f, BPART(tcolor)/255.f,
h, &ts, &tv); h, &ts, &tv);
*s = clamp(ts + *s * 0.15f - 0.075f, 0.f, 1.f); *s = clamp(ts + *s * 0.15f - 0.075f, 0.f, 1.f);
*v = clamp(tv + *v * 0.5f - 0.25f, 0.f, 1.f); *v = clamp(tv + *v * 0.5f - 0.25f, 0.f, 1.f);
// Make sure not to pass back any colorset in teamplay.
colorset = NULL;
} }
if (set != NULL) if (set != NULL)
{ {
@ -261,9 +249,10 @@ int D_PickRandomTeam ()
{ {
if (playeringame[i]) if (playeringame[i])
{ {
if (TeamLibrary.IsValidTeam (players[i].userinfo.team)) team = players[i].userinfo.GetTeam();
if (TeamLibrary.IsValidTeam(team))
{ {
if (Teams[players[i].userinfo.team].m_iPresent++ == 0) if (Teams[team].m_iPresent++ == 0)
{ {
numTeams++; numTeams++;
} }
@ -275,7 +264,7 @@ int D_PickRandomTeam ()
{ {
do do
{ {
team = pr_pickteam() % Teams.Size (); team = pr_pickteam() % Teams.Size();
} while (Teams[team].m_iPresent != 0); } while (Teams[team].m_iPresent != 0);
} }
else else
@ -316,7 +305,7 @@ static void UpdateTeam (int pnum, int team, bool update)
{ {
userinfo_t *info = &players[pnum].userinfo; userinfo_t *info = &players[pnum].userinfo;
if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TeamLibrary.IsValidTeam (info->team)) if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TeamLibrary.IsValidTeam (info->GetTeam()))
{ {
Printf ("Team changing has been disabled!\n"); Printf ("Team changing has been disabled!\n");
return; return;
@ -328,19 +317,15 @@ static void UpdateTeam (int pnum, int team, bool update)
{ {
team = TEAM_NONE; team = TEAM_NONE;
} }
oldteam = info->team; oldteam = info->GetTeam();
info->team = team; team = info->TeamChanged(team);
if (teamplay && !TeamLibrary.IsValidTeam (info->team)) if (update && oldteam != team)
{ // Force players onto teams in teamplay mode
info->team = D_PickRandomTeam ();
}
if (update && oldteam != info->team)
{ {
if (TeamLibrary.IsValidTeam (info->team)) if (TeamLibrary.IsValidTeam (team))
Printf ("%s joined the %s team\n", info->netname, Teams[info->team].GetName ()); Printf ("%s joined the %s team\n", info->GetName(), Teams[team].GetName ());
else else
Printf ("%s is now a loner\n", info->netname); Printf ("%s is now a loner\n", info->GetName());
} }
// Let the player take on the team's color // Let the player take on the team's color
R_BuildPlayerTranslation (pnum); R_BuildPlayerTranslation (pnum);
@ -348,25 +333,28 @@ static void UpdateTeam (int pnum, int team, bool update)
{ {
StatusBar->AttachToPlayer (&players[pnum]); StatusBar->AttachToPlayer (&players[pnum]);
} }
if (!TeamLibrary.IsValidTeam (info->team)) // Double-check
info->team = TEAM_NONE; if (!TeamLibrary.IsValidTeam (team))
{
*static_cast<FIntCVar *>((*info)[NAME_Team]) = TEAM_NONE;
}
} }
int D_GetFragCount (player_t *player) int D_GetFragCount (player_t *player)
{ {
if (!teamplay || !TeamLibrary.IsValidTeam (player->userinfo.team)) const int team = player->userinfo.GetTeam();
if (!teamplay || !TeamLibrary.IsValidTeam(team))
{ {
return player->fragcount; return player->fragcount;
} }
else else
{ {
// Count total frags for this player's team // Count total frags for this player's team
const int team = player->userinfo.team;
int count = 0; int count = 0;
for (int i = 0; i < MAXPLAYERS; ++i) for (int i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i] && players[i].userinfo.team == team) if (playeringame[i] && players[i].userinfo.GetTeam() == team)
{ {
count += players[i].fragcount; count += players[i].fragcount;
} }
@ -378,37 +366,146 @@ int D_GetFragCount (player_t *player)
void D_SetupUserInfo () void D_SetupUserInfo ()
{ {
int i; int i;
userinfo_t *coninfo = &players[consoleplayer].userinfo; userinfo_t *coninfo;
// Reset everybody's userinfo to a default state.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
memset (&players[i].userinfo, 0, sizeof(userinfo_t)); {
players[i].userinfo.Reset();
}
// Initialize the console player's user info
coninfo = &players[consoleplayer].userinfo;
strncpy (coninfo->netname, name, MAXPLAYERNAME); for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
if (teamplay && !TeamLibrary.IsValidTeam (team))
{ {
coninfo->team = D_PickRandomTeam (); if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_IGNORE)) == CVAR_USERINFO)
{
FBaseCVar **newcvar;
FName cvarname(cvar->GetName());
switch (cvarname.GetIndex())
{
// Some cvars don't copy their original value directly.
case NAME_Team: coninfo->TeamChanged(team); break;
case NAME_Skin: coninfo->SkinChanged(skin); break;
case NAME_Gender: coninfo->GenderChanged(gender); break;
case NAME_PlayerClass: coninfo->PlayerClassChanged(playerclass); break;
// The rest do.
default:
newcvar = coninfo->CheckKey(cvarname);
(*newcvar)->SetGenericRep(cvar->GetGenericRep(CVAR_String), CVAR_String);
break;
}
}
} }
else R_BuildPlayerTranslation(consoleplayer);
}
void userinfo_t::Reset()
{
// Clear this player's userinfo.
TMapIterator<FName, FBaseCVar *> it(*this);
TMap<FName, FBaseCVar *>::Pair *pair;
while (it.NextPair(pair))
{ {
coninfo->team = team; delete pair->Value;
} }
if (autoaim > 35.f || autoaim < 0.f) Clear();
// Create userinfo vars for this player, initialized to their defaults.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{ {
coninfo->aimdist = ANGLE_1*35; if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_IGNORE)) == CVAR_USERINFO)
{
ECVarType type;
FName cvarname(cvar->GetName());
FBaseCVar *newcvar;
// Some cvars have different types for their shadow copies.
switch (cvarname.GetIndex())
{
case NAME_Skin: type = CVAR_Int; break;
case NAME_Gender: type = CVAR_Int; break;
case NAME_PlayerClass: type = CVAR_Int; break;
default: type = cvar->GetRealType(); break;
}
newcvar = C_CreateCVar(NULL, type, cvar->GetFlags() & CVAR_MOD);
newcvar->SetGenericRepDefault(cvar->GetGenericRepDefault(CVAR_String), CVAR_String);
Insert(cvarname, newcvar);
}
} }
else }
{
coninfo->aimdist = abs ((int)(autoaim * (float)ANGLE_1)); int userinfo_t::TeamChanged(int team)
{
if (teamplay && !TeamLibrary.IsValidTeam(team))
{ // Force players onto teams in teamplay mode
team = D_PickRandomTeam();
} }
coninfo->color = color; *static_cast<FIntCVar *>((*this)[NAME_Team]) = team;
coninfo->colorset = colorset; return team;
coninfo->skin = R_FindSkin (skin, 0); }
coninfo->gender = D_GenderToInt (gender);
coninfo->neverswitch = neverswitchonpickup; int userinfo_t::SkinChanged(const char *skinname)
coninfo->MoveBob = (fixed_t)(65536.f * movebob); {
coninfo->StillBob = (fixed_t)(65536.f * stillbob); int skinnum = R_FindSkin(skinname, 0);
coninfo->PlayerClass = D_PlayerClassToInt (playerclass); *static_cast<FIntCVar *>((*this)[NAME_Skin]) = skinnum;
R_BuildPlayerTranslation (consoleplayer); return skinnum;
}
int userinfo_t::SkinNumChanged(int skinnum)
{
*static_cast<FIntCVar *>((*this)[NAME_Skin]) = skinnum;
return skinnum;
}
int userinfo_t::GenderChanged(const char *gendername)
{
int gendernum = D_GenderToInt(gendername);
*static_cast<FIntCVar *>((*this)[NAME_Gender]) = gendernum;
return gendernum;
}
int userinfo_t::PlayerClassChanged(const char *classname)
{
int classnum = D_PlayerClassToInt(classname);
*static_cast<FIntCVar *>((*this)[NAME_PlayerClass]) = classnum;
return classnum;
}
int userinfo_t::PlayerClassNumChanged(int classnum)
{
*static_cast<FIntCVar *>((*this)[NAME_PlayerClass]) = classnum;
return classnum;
}
int userinfo_t::ColorSetChanged(int setnum)
{
*static_cast<FIntCVar *>((*this)[NAME_ColorSet]) = setnum;
return setnum;
}
uint32 userinfo_t::ColorChanged(const char *colorname)
{
FColorCVar *color = static_cast<FColorCVar *>((*this)[NAME_Color]);
assert(color != NULL);
UCVarValue val;
val.String = const_cast<char *>(colorname);
color->SetGenericRep(val, CVAR_String);
*static_cast<FIntCVar *>((*this)[NAME_ColorSet]) = -1;
return *color;
}
uint32 userinfo_t::ColorChanged(uint32 colorval)
{
FColorCVar *color = static_cast<FColorCVar *>((*this)[NAME_Color]);
assert(color != NULL);
UCVarValue val;
val.Int = colorval;
color->SetGenericRep(val, CVAR_Int);
// This version is called by the menu code. Do not implicitly set colorset.
return colorval;
} }
void D_UserInfoChanged (FBaseCVar *cvar) void D_UserInfoChanged (FBaseCVar *cvar)
@ -500,7 +597,7 @@ static const char *SetServerVar (char *name, ECVarType type, BYTE **stream, bool
{ {
if (playeringame[i]) if (playeringame[i])
{ {
UpdateTeam (i, players[i].userinfo.team, true); UpdateTeam (i, players[i].userinfo.GetTeam(), true);
} }
} }
} }
@ -569,113 +666,131 @@ void D_DoServerInfoChange (BYTE **stream, bool singlebit)
} }
} }
void D_WriteUserInfoStrings (int i, BYTE **stream, bool compact) static int STACK_ARGS userinfosortfunc(const void *a, const void *b)
{ {
if (i >= MAXPLAYERS) TMap<FName, FBaseCVar *>::ConstPair *pair1 = *(TMap<FName, FBaseCVar *>::ConstPair **)a;
{ TMap<FName, FBaseCVar *>::ConstPair *pair2 = *(TMap<FName, FBaseCVar *>::ConstPair **)b;
WriteByte (0, stream); return stricmp(pair1->Key.GetChars(), pair2->Key.GetChars());
}
else
{
userinfo_t *info = &players[i].userinfo;
const PClass *type = PlayerClasses[info->PlayerClass].Type;
if (!compact)
{
sprintf (*((char **)stream),
"\\name\\%s"
"\\autoaim\\%g"
"\\color\\%x %x %x"
"\\colorset\\%d"
"\\skin\\%s"
"\\team\\%d"
"\\gender\\%s"
"\\neverswitchonpickup\\%d"
"\\movebob\\%g"
"\\stillbob\\%g"
"\\playerclass\\%s"
,
D_EscapeUserInfo(info->netname).GetChars(),
(double)info->aimdist / (float)ANGLE_1,
info->colorset,
RPART(info->color), GPART(info->color), BPART(info->color),
D_EscapeUserInfo(skins[info->skin].name).GetChars(),
info->team,
info->gender == GENDER_FEMALE ? "female" :
info->gender == GENDER_NEUTER ? "other" : "male",
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
info->PlayerClass == -1 ? "Random" :
D_EscapeUserInfo(type->Meta.GetMetaString (APMETA_DisplayName)).GetChars()
);
}
else
{
sprintf (*((char **)stream),
"\\"
"\\%s" // name
"\\%g" // autoaim
"\\%x %x %x" // color
"\\%s" // skin
"\\%d" // team
"\\%s" // gender
"\\%d" // neverswitchonpickup
"\\%g" // movebob
"\\%g" // stillbob
"\\%s" // playerclass
"\\%d" // colorset
,
D_EscapeUserInfo(info->netname).GetChars(),
(double)info->aimdist / (float)ANGLE_1,
RPART(info->color), GPART(info->color), BPART(info->color),
D_EscapeUserInfo(skins[info->skin].name).GetChars(),
info->team,
info->gender == GENDER_FEMALE ? "female" :
info->gender == GENDER_NEUTER ? "other" : "male",
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
info->PlayerClass == -1 ? "Random" :
D_EscapeUserInfo(type->Meta.GetMetaString (APMETA_DisplayName)).GetChars(),
info->colorset
);
}
}
*stream += strlen (*((char **)stream)) + 1;
} }
void D_ReadUserInfoStrings (int i, BYTE **stream, bool update) static int STACK_ARGS namesortfunc(const void *a, const void *b)
{ {
userinfo_t *info = &players[i].userinfo; FName *name1 = (FName *)a;
FName *name2 = (FName *)b;
return stricmp(name1->GetChars(), name2->GetChars());
}
void D_WriteUserInfoStrings (int pnum, BYTE **stream, bool compact)
{
if (pnum >= MAXPLAYERS)
{
WriteByte (0, stream);
return;
}
userinfo_t *info = &players[pnum].userinfo;
TArray<TMap<FName, FBaseCVar *>::Pair *> userinfo_pairs(info->CountUsed());
TMap<FName, FBaseCVar *>::Iterator it(*info);
TMap<FName, FBaseCVar *>::Pair *pair;
UCVarValue cval;
// Create a simple array of all userinfo cvars
while (it.NextPair(pair))
{
userinfo_pairs.Push(pair);
}
// For compact mode, these need to be sorted. Verbose mode doesn't matter.
if (compact)
{
qsort(&userinfo_pairs[0], userinfo_pairs.Size(), sizeof(pair), userinfosortfunc);
// Compact mode is signified by starting the string with two backslash characters.
// We output one now. The second will be output as part of the first value.
*(*stream)++ = '\\';
}
for (unsigned int i = 0; i < userinfo_pairs.Size(); ++i)
{
pair = userinfo_pairs[i];
if (!compact)
{ // In verbose mode, prepend the cvar's name
*stream += sprintf(*((char **)stream), "\\%s\\", pair->Key.GetChars());
}
// A few of these need special handling for compatibility reasons.
switch (pair->Key.GetIndex())
{
case NAME_Gender:
*stream += sprintf(*((char **)stream), "\\%s",
*static_cast<FIntCVar *>(pair->Value) == GENDER_FEMALE ? "female" :
*static_cast<FIntCVar *>(pair->Value) == GENDER_NEUTER ? "other" : "male");
break;
case NAME_PlayerClass:
*stream += sprintf(*((char **)stream), "\\%s", info->GetPlayerClassNum() == -1 ? "Random" :
D_EscapeUserInfo(info->GetPlayerClassType()->Meta.GetMetaString(APMETA_DisplayName)).GetChars());
break;
case NAME_Skin:
*stream += sprintf(*((char **)stream), "\\%s", D_EscapeUserInfo(skins[info->GetSkin()].name).GetChars());
break;
default:
cval = pair->Value->GetGenericRep(CVAR_String);
*stream += sprintf(*((char **)stream), "\\%s", cval.String);
break;
}
}
*(*stream)++ = '\0';
}
void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update)
{
userinfo_t *info = &players[pnum].userinfo;
TArray<FName> compact_names(info->CountUsed());
FBaseCVar **cvar_ptr;
const char *ptr = *((const char **)stream); const char *ptr = *((const char **)stream);
const char *breakpt; const char *breakpt;
FString value; FString value;
bool compact; bool compact;
int infotype = -1; FName keyname;
unsigned int infotype = 0;
if (*ptr++ != '\\') if (*ptr++ != '\\')
return; return;
compact = (*ptr == '\\') ? ptr++, true : false; compact = (*ptr == '\\') ? ptr++, true : false;
if (i < MAXPLAYERS) // We need the cvar names in sorted order for compact mode
if (compact)
{ {
for (;;) TMap<FName, FBaseCVar *>::Iterator it(*info);
{ TMap<FName, FBaseCVar *>::Pair *pair;
int j;
breakpt = strchr (ptr, '\\'); while (it.NextPair(pair))
{
compact_names.Push(pair->Key);
}
qsort(&compact_names[0], compact_names.Size(), sizeof(FName), namesortfunc);
}
if (pnum < MAXPLAYERS)
{
for (breakpt = ptr; breakpt != NULL; ptr = breakpt + 1)
{
breakpt = strchr(ptr, '\\');
if (compact) if (compact)
{ {
// Compact has just the value.
if (infotype >= compact_names.Size())
{ // Too many entries! OMG!
break;
}
keyname = compact_names[infotype++];
value = D_UnescapeUserInfo(ptr, breakpt != NULL ? breakpt - ptr : strlen(ptr)); value = D_UnescapeUserInfo(ptr, breakpt != NULL ? breakpt - ptr : strlen(ptr));
infotype++;
} }
else else
{ {
// Verbose has both the key name and its value.
assert(breakpt != NULL); assert(breakpt != NULL);
// A malicious remote machine could invalidate the above assert. // A malicious remote machine could invalidate the above assert.
if (breakpt == NULL) if (breakpt == NULL)
@ -691,154 +806,187 @@ void D_ReadUserInfoStrings (int i, BYTE **stream, bool update)
{ {
value = D_UnescapeUserInfo(valstart, strlen(valstart)); value = D_UnescapeUserInfo(valstart, strlen(valstart));
} }
keyname = FName(ptr, valstart - ptr - 1, true);
for (j = 0;
UserInfoStrings[j] && strnicmp (UserInfoStrings[j], ptr, valstart - ptr - 1) != 0;
++j)
{ }
if (UserInfoStrings[j] == NULL)
{
infotype = -1;
}
else
{
infotype = j;
}
} }
switch (infotype)
// A few of these need special handling.
switch (keyname)
{ {
case INFO_Autoaim: { case NAME_Gender:
double angles; info->GenderChanged(value);
angles = atof (value);
if (angles > 35.f || angles < 0.f)
{
info->aimdist = ANGLE_1*35;
}
else
{
info->aimdist = abs ((int)(angles * (float)ANGLE_1));
}
}
break; break;
case INFO_Name: case NAME_PlayerClass:
{ info->PlayerClassChanged(value);
char oldname[MAXPLAYERNAME+1];
strcpy (oldname, info->netname);
strncpy (info->netname, value, MAXPLAYERNAME);
info->netname[MAXPLAYERNAME] = 0;
CleanseString(info->netname);
if (update && strcmp (oldname, info->netname) != 0)
{
Printf ("%s is now known as %s\n", oldname, info->netname);
}
}
break; break;
case INFO_Team: case NAME_Skin:
UpdateTeam (i, atoi(value), update); info->SkinChanged(value);
break; if (players[pnum].mo != NULL)
case INFO_Color:
case INFO_ColorSet:
if (infotype == INFO_Color)
{ {
info->color = V_GetColorFromString (NULL, value); if (players[pnum].cls != NULL &&
} !(players[pnum].mo->flags4 & MF4_NOSKIN) &&
else players[pnum].mo->state->sprite ==
{ GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
info->colorset = atoi(value);
}
R_BuildPlayerTranslation (i);
if (StatusBar != NULL && i == StatusBar->GetPlayer())
{
StatusBar->AttachToPlayer (&players[i]);
}
break;
case INFO_Skin:
info->skin = R_FindSkin (value, players[i].CurrentPlayerClass);
if (players[i].mo != NULL)
{
if (players[i].cls != NULL &&
players[i].mo->state->sprite ==
GetDefaultByType (players[i].cls)->SpawnState->sprite)
{ // Only change the sprite if the player is using a standard one { // Only change the sprite if the player is using a standard one
players[i].mo->sprite = skins[info->skin].sprite; players[pnum].mo->sprite = skins[info->GetSkin()].sprite;
players[i].mo->scaleX = skins[info->skin].ScaleX;
players[i].mo->scaleY = skins[info->skin].ScaleY;
} }
} }
// Rebuild translation in case the new skin uses a different range // Rebuild translation in case the new skin uses a different range
// than the old one. // than the old one.
R_BuildPlayerTranslation (i); R_BuildPlayerTranslation(pnum);
break; break;
case INFO_Gender: case NAME_Team:
info->gender = D_GenderToInt (value); UpdateTeam(pnum, atoi(value), update);
break; break;
case INFO_NeverSwitchOnPickup: case NAME_Color:
if (value[0] >= '0' && value[0] <= '9') info->ColorChanged(value);
{
info->neverswitch = atoi (value) ? true : false;
}
else if (stricmp (value, "true") == 0)
{
info->neverswitch = 1;
}
else
{
info->neverswitch = 0;
}
break;
case INFO_MoveBob:
info->MoveBob = (fixed_t)(atof (value) * 65536.f);
break;
case INFO_StillBob:
info->StillBob = (fixed_t)(atof (value) * 65536.f);
break;
case INFO_PlayerClass:
info->PlayerClass = D_PlayerClassToInt (value);
break; break;
default: default:
cvar_ptr = info->CheckKey(keyname);
if (cvar_ptr != NULL)
{
assert(*cvar_ptr != NULL);
UCVarValue val;
FString oldname;
if (keyname == NAME_Name)
{
val = (*cvar_ptr)->GetGenericRep(CVAR_String);
oldname = val.String;
}
val.String = CleanseString(value.LockBuffer());
(*cvar_ptr)->SetGenericRep(val, CVAR_String);
value.UnlockBuffer();
if (keyname == NAME_Name && update && oldname != value)
{
Printf("%s is now known as %s\n", oldname.GetChars(), value.GetChars());
}
}
break; break;
} }
if (keyname == NAME_Color || keyname == NAME_ColorSet)
if (breakpt)
{ {
ptr = breakpt + 1; R_BuildPlayerTranslation(pnum);
if (StatusBar != NULL && pnum == StatusBar->GetPlayer())
{
StatusBar->AttachToPlayer(&players[pnum]);
}
} }
else }
}
*stream += strlen (*((char **)stream)) + 1;
}
void ReadCompatibleUserInfo(FArchive &arc, userinfo_t &info)
{
char netname[MAXPLAYERNAME + 1];
BYTE team;
int aimdist, color, colorset, skin, gender;
bool neverswitch;
//fixed_t movebob, stillbob; These were never serialized!
//int playerclass; "
info.Reset();
arc.Read(&netname, sizeof(netname));
arc << team << aimdist << color << skin << gender << neverswitch << colorset;
*static_cast<FStringCVar *>(info[NAME_Name]) = netname;
*static_cast<FIntCVar *>(info[NAME_Team]) = team;
*static_cast<FFloatCVar *>(info[NAME_Autoaim]) = (float)aimdist / ANGLE_1;
*static_cast<FIntCVar *>(info[NAME_Skin]) = skin;
*static_cast<FIntCVar *>(info[NAME_Gender]) = gender;
*static_cast<FBoolCVar *>(info[NAME_NeverSwitchOnPickup]) = neverswitch;
*static_cast<FIntCVar *>(info[NAME_ColorSet]) = colorset;
UCVarValue val;
val.Int = color;
static_cast<FColorCVar *>(info[NAME_Color])->SetGenericRep(val, CVAR_Int);
}
void WriteUserInfo(FArchive &arc, userinfo_t &info)
{
TMapIterator<FName, FBaseCVar *> it(info);
TMap<FName, FBaseCVar *>::Pair *pair;
FName name;
UCVarValue val;
int i;
while (it.NextPair(pair))
{
name = pair->Key;
arc << name;
switch (name.GetIndex())
{
case NAME_Skin:
arc.WriteString(skins[info.GetSkin()].name);
break;
case NAME_PlayerClass:
i = info.GetPlayerClassNum();
arc.WriteString(i == -1 ? "Random" : PlayerClasses[i].Type->Meta.GetMetaString(APMETA_DisplayName));
break;
default:
val = pair->Value->GetGenericRep(CVAR_String);
arc.WriteString(val.String);
break;
}
}
name = NAME_None;
arc << name;
}
void ReadUserInfo(FArchive &arc, userinfo_t &info)
{
FName name;
FBaseCVar **cvar;
char *str = NULL;
UCVarValue val;
info.Reset();
for (arc << name; name != NAME_None; arc << name)
{
cvar = info.CheckKey(name);
arc << str;
if (cvar != NULL && *cvar != NULL)
{
switch (name)
{ {
case NAME_Team: info.TeamChanged(atoi(str)); break;
case NAME_Skin: info.SkinChanged(str); break;
case NAME_PlayerClass: info.PlayerClassChanged(str); break;
default:
val.String = str;
(*cvar)->SetGenericRep(val, CVAR_String);
break; break;
} }
} }
} }
if (str != NULL)
*stream += strlen (*((char **)stream)) + 1; {
delete[] str;
}
} }
FArchive &operator<< (FArchive &arc, userinfo_t &info) FArchive &operator<< (FArchive &arc, userinfo_t &info)
{ {
if (arc.IsStoring ()) if (SaveVersion < 4253)
{ {
arc.Write (&info.netname, sizeof(info.netname)); ReadCompatibleUserInfo(arc, info);
}
else if (arc.IsStoring())
{
WriteUserInfo(arc, info);
} }
else else
{ {
arc.Read (&info.netname, sizeof(info.netname)); ReadUserInfo(arc, info);
} }
arc << info.team << info.aimdist << info.color
<< info.skin << info.gender << info.neverswitch
<< info.colorset;
return arc; return arc;
} }
@ -852,27 +1000,51 @@ CCMD (playerinfo)
{ {
if (playeringame[i]) if (playeringame[i])
{ {
Printf ("%d. %s\n", i, players[i].userinfo.netname); Printf("%d. %s\n", i, players[i].userinfo.GetName());
} }
} }
} }
else else
{ {
int i = atoi (argv[1]); int i = atoi(argv[1]);
if (i < 0 || i >= MAXPLAYERS)
{
Printf("Bad player number\n");
return;
}
userinfo_t *ui = &players[i].userinfo; userinfo_t *ui = &players[i].userinfo;
Printf ("Name: %s\n", ui->netname);
Printf ("Team: %s (%d)\n", ui->team == TEAM_NONE ? "None" : Teams[ui->team].GetName (), ui->team); if (!playeringame[i])
Printf ("Aimdist: %d\n", ui->aimdist); {
Printf ("Color: %06x\n", ui->color); Printf(TEXTCOLOR_ORANGE "Player %d is not in the game\n", i);
Printf ("ColorSet: %d\n", ui->colorset); }
Printf ("Skin: %s (%d)\n", skins[ui->skin].name, ui->skin);
Printf ("Gender: %s (%d)\n", GenderNames[ui->gender], ui->gender); // Print special info
Printf ("NeverSwitch: %d\n", ui->neverswitch); Printf("%20s: %s\n", "Name", ui->GetName());
Printf ("MoveBob: %g\n", ui->MoveBob/65536.f); Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam());
Printf ("StillBob: %g\n", ui->StillBob/65536.f); Printf("%20s: %s (%d)\n", "Skin", skins[ui->GetSkin()].name, ui->GetSkin());
Printf ("PlayerClass: %s (%d)\n", Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender());
ui->PlayerClass == -1 ? "Random" : PlayerClasses[ui->PlayerClass].Type->Meta.GetMetaString (APMETA_DisplayName), Printf("%20s: %s (%d)\n", "PlayerClass",
ui->PlayerClass); ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->Meta.GetMetaString (APMETA_DisplayName),
if (argv.argc() > 2) PrintMiscActorInfo(players[i].mo); ui->GetPlayerClassNum());
// Print generic info
TMapIterator<FName, FBaseCVar *> it(*ui);
TMap<FName, FBaseCVar *>::Pair *pair;
while (it.NextPair(pair))
{
if (pair->Key != NAME_Name && pair->Key != NAME_Team && pair->Key != NAME_Skin &&
pair->Key != NAME_Gender && pair->Key != NAME_PlayerClass)
{
UCVarValue val = pair->Value->GetGenericRep(CVAR_String);
Printf("%20s: %s\n", pair->Key.GetChars(), val.String);
}
}
if (argv.argc() > 2)
{
PrintMiscActorInfo(players[i].mo);
}
} }
} }

View File

@ -90,6 +90,7 @@ public:
virtual void AddInventory (AInventory *item); virtual void AddInventory (AInventory *item);
virtual void RemoveInventory (AInventory *item); virtual void RemoveInventory (AInventory *item);
virtual bool UseInventory (AInventory *item); virtual bool UseInventory (AInventory *item);
virtual void MarkPrecacheSounds () const;
virtual void PlayIdle (); virtual void PlayIdle ();
virtual void PlayRunning (); virtual void PlayRunning ();
@ -107,7 +108,7 @@ public:
void GiveDefaultInventory (); void GiveDefaultInventory ();
void PlayAttacking (); void PlayAttacking ();
void PlayAttacking2 (); void PlayAttacking2 ();
const char *GetSoundClass (); const char *GetSoundClass () const;
enum EInvulState enum EInvulState
{ {
@ -118,7 +119,7 @@ public:
}; };
void BeginPlay (); void BeginPlay ();
void Die (AActor *source, AActor *inflictor); void Die (AActor *source, AActor *inflictor, int dmgflags);
int crouchsprite; int crouchsprite;
int MaxHealth; int MaxHealth;
@ -130,6 +131,8 @@ public:
// [GRB] Player class properties // [GRB] Player class properties
fixed_t JumpZ; fixed_t JumpZ;
fixed_t GruntSpeed;
fixed_t FallingScreamMinSpeed, FallingScreamMaxSpeed;
fixed_t ViewHeight; fixed_t ViewHeight;
fixed_t ForwardMove1, ForwardMove2; fixed_t ForwardMove1, ForwardMove2;
fixed_t SideMove1, SideMove2; fixed_t SideMove1, SideMove2;
@ -159,6 +162,8 @@ class APlayerChunk : public APlayerPawn
enum enum
{ {
PPF_NOTHRUSTWHENINVUL = 1, // Attacks do not thrust the player if they are invulnerable. PPF_NOTHRUSTWHENINVUL = 1, // Attacks do not thrust the player if they are invulnerable.
PPF_CANSUPERMORPH = 2, // Being remorphed into this class can give you a Tome of Power
PPF_CROUCHABLEMORPH = 4, // This morphed player can crouch
}; };
// //
@ -191,22 +196,29 @@ typedef enum
CF_INSTANTWEAPSWITCH= 1 << 11, // [RH] Switch weapons instantly CF_INSTANTWEAPSWITCH= 1 << 11, // [RH] Switch weapons instantly
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted CF_PREDICTING = 1 << 13, // [RH] Player movement is being predicted
CF_WEAPONREADY = 1 << 14, // [RH] Weapon is in the ready state and can fire its primary attack
CF_TIMEFREEZE = 1 << 15, // Player has an active time freezer
CF_DRAIN = 1 << 16, // Player owns a drain powerup CF_DRAIN = 1 << 16, // Player owns a drain powerup
CF_REGENERATION = 1 << 17, // Player owns a regeneration artifact
CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;) CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;)
CF_REFLECTION = 1 << 19, CF_REFLECTION = 1 << 19,
CF_PROSPERITY = 1 << 20, CF_PROSPERITY = 1 << 20,
CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact
CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths. CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths.
CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
CF_WEAPONBOBBING = 1 << 24, // [HW] Bob weapon while the player is moving
CF_WEAPONREADYALT = 1 << 25, // Weapon can fire its secondary attack
CF_WEAPONSWITCHOK = 1 << 26, // It is okay to switch away from this weapon
CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip
} cheat_t; } cheat_t;
enum
{
WF_WEAPONREADY = 1 << 0, // [RH] Weapon is in the ready state and can fire its primary attack
WF_WEAPONBOBBING = 1 << 1, // [HW] Bob weapon while the player is moving
WF_WEAPONREADYALT = 1 << 2, // Weapon can fire its secondary attack
WF_WEAPONSWITCHOK = 1 << 3, // It is okay to switch away from this weapon
WF_DISABLESWITCH = 1 << 4, // Disable weapon switching completely
WF_WEAPONRELOADOK = 1 << 5, // [XA] Okay to reload this weapon.
WF_WEAPONZOOMOK = 1 << 6, // [XA] Okay to use weapon zoom function.
WF_REFIRESWITCHOK = 1 << 7, // Mirror WF_WEAPONSWITCHOK for A_ReFire
};
#define WPIECE1 1 #define WPIECE1 1
#define WPIECE2 2 #define WPIECE2 2
#define WPIECE3 4 #define WPIECE3 4
@ -216,6 +228,29 @@ typedef enum
#define MAXPLAYERNAME 15 #define MAXPLAYERNAME 15
// [GRB] Custom player classes
enum
{
PCF_NOMENU = 1, // Hide in new game menu
};
class FPlayerClass
{
public:
FPlayerClass ();
FPlayerClass (const FPlayerClass &other);
~FPlayerClass ();
bool CheckSkin (int skin);
const PClass *Type;
DWORD Flags;
TArray<int> Skins;
};
extern TArray<FPlayerClass> PlayerClasses;
// User info (per-player copies of each CVAR_USERINFO cvar)
enum enum
{ {
GENDER_MALE, GENDER_MALE,
@ -223,20 +258,80 @@ enum
GENDER_NEUTER GENDER_NEUTER
}; };
struct userinfo_t struct userinfo_t : TMap<FName,FBaseCVar *>
{ {
char netname[MAXPLAYERNAME+1]; int GetAimDist() const
BYTE team; {
int aimdist; if (dmflags2 & DF2_NOAUTOAIM)
int color; {
int colorset; return 0;
int skin; }
int gender;
bool neverswitch;
fixed_t MoveBob, StillBob;
int PlayerClass;
int GetAimDist() const { return (dmflags2 & DF2_NOAUTOAIM)? 0 : aimdist; } float aim = *static_cast<FFloatCVar *>(*CheckKey(NAME_Autoaim));
if (aim > 35 || aim < 0)
{
return ANGLE_1*35;
}
else
{
return xs_RoundToInt(fabs(aim * ANGLE_1));
}
}
const char *GetName() const
{
return *static_cast<FStringCVar *>(*CheckKey(NAME_Name));
}
int GetTeam() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Team));
}
int GetColorSet() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_ColorSet));
}
uint32 GetColor() const
{
return *static_cast<FColorCVar *>(*CheckKey(NAME_Color));
}
bool GetNeverSwitch() const
{
return *static_cast<FBoolCVar *>(*CheckKey(NAME_NeverSwitchOnPickup));
}
fixed_t GetMoveBob() const
{
return FLOAT2FIXED(*static_cast<FFloatCVar *>(*CheckKey(NAME_MoveBob)));
}
fixed_t GetStillBob() const
{
return FLOAT2FIXED(*static_cast<FFloatCVar *>(*CheckKey(NAME_StillBob)));
}
int GetPlayerClassNum() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_PlayerClass));
}
const PClass *GetPlayerClassType() const
{
return PlayerClasses[GetPlayerClassNum()].Type;
}
int GetSkin() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Skin));
}
int GetGender() const
{
return *static_cast<FIntCVar *>(*CheckKey(NAME_Gender));
}
void Reset();
int TeamChanged(int team);
int SkinChanged(const char *skinname);
int SkinNumChanged(int skinnum);
int GenderChanged(const char *gendername);
int PlayerClassChanged(const char *classname);
int PlayerClassNumChanged(int classnum);
uint32 ColorChanged(const char *colorname);
uint32 ColorChanged(uint32 colorval);
int ColorSetChanged(int setnum);
}; };
FArchive &operator<< (FArchive &arc, userinfo_t &info); FArchive &operator<< (FArchive &arc, userinfo_t &info);
@ -256,6 +351,7 @@ public:
void SetLogNumber (int num); void SetLogNumber (int num);
void SetLogText (const char *text); void SetLogText (const char *text);
void SendPitchLimits() const;
APlayerPawn *mo; APlayerPawn *mo;
BYTE playerstate; BYTE playerstate;
@ -282,6 +378,8 @@ public:
bool centering; bool centering;
BYTE turnticks; BYTE turnticks;
bool attackdown; bool attackdown;
bool usedown; bool usedown;
DWORD oldbuttons; DWORD oldbuttons;
@ -297,17 +395,22 @@ public:
int lastkilltime; // [RH] For multikills int lastkilltime; // [RH] For multikills
BYTE multicount; BYTE multicount;
BYTE spreecount; // [RH] Keep track of killing sprees BYTE spreecount; // [RH] Keep track of killing sprees
BYTE WeaponState;
AWeapon *ReadyWeapon; AWeapon *ReadyWeapon;
AWeapon *PendingWeapon; // WP_NOCHANGE if not changing AWeapon *PendingWeapon; // WP_NOCHANGE if not changing
int cheats; // bit flags int cheats; // bit flags
int timefreezer; // Player has an active time freezer
short refire; // refired shots are less accurate short refire; // refired shots are less accurate
short inconsistant; short inconsistant;
bool waiting;
int killcount, itemcount, secretcount; // for intermission int killcount, itemcount, secretcount; // for intermission
int damagecount, bonuscount;// for screen flashing int damagecount, bonuscount;// for screen flashing
int hazardcount; // for delayed Strife damage int hazardcount; // for delayed Strife damage
int poisoncount; // screen flash for poison damage int poisoncount; // screen flash for poison damage
FName poisontype; // type of poison damage to apply
FName poisonpaintype; // type of Pain state to enter for poison damage
TObjPtr<AActor> poisoner; // NULL for non-player actors TObjPtr<AActor> poisoner; // NULL for non-player actors
TObjPtr<AActor> attacker; // who did damage (NULL for floors) TObjPtr<AActor> attacker; // who did damage (NULL for floors)
int extralight; // so gun flashes light up areas int extralight; // so gun flashes light up areas
@ -327,8 +430,6 @@ public:
int air_finished; // [RH] Time when you start drowning int air_finished; // [RH] Time when you start drowning
WORD accuracy, stamina; // [RH] Strife stats
FName LastDamageType; // [RH] For damage-specific pain and death sounds FName LastDamageType; // [RH] For damage-specific pain and death sounds
//Added by MC: //Added by MC:
@ -342,9 +443,9 @@ public:
TObjPtr<AActor> enemy; // The dead meat. TObjPtr<AActor> enemy; // The dead meat.
TObjPtr<AActor> missile; // A threathing missile that got to be avoided. TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
TObjPtr<AActor> mate; // Friend (used for grouping in templay or coop. TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
TObjPtr<AActor> last_mate; // If bots mate dissapeared (not if died) that mate is TObjPtr<AActor> last_mate; // If bots mate disappeared (not if died) that mate is
// pointed to by this. Allows bot to roam to it if // pointed to by this. Allows bot to roam to it if
// necessary. // necessary.
@ -380,6 +481,9 @@ public:
FString LogText; // [RH] Log for Strife FString LogText; // [RH] Log for Strife
int MinPitch; // Viewpitch limits (negative is up, positive is down)
int MaxPitch;
SBYTE crouching; SBYTE crouching;
SBYTE crouchdir; SBYTE crouchdir;
fixed_t crouchfactor; fixed_t crouchfactor;
@ -406,6 +510,11 @@ public:
crouching = 0; crouching = 0;
crouchviewdelta = 0; crouchviewdelta = 0;
} }
bool CanCrouch() const
{
return morphTics == 0 || mo->PlayerFlags & PPF_CROUCHABLEMORPH;
}
int GetSpawnClass(); int GetSpawnClass();
}; };
@ -415,31 +524,31 @@ extern player_t players[MAXPLAYERS];
FArchive &operator<< (FArchive &arc, player_t *&p); FArchive &operator<< (FArchive &arc, player_t *&p);
void P_CheckPlayerSprites(); void P_CheckPlayerSprite(AActor *mo, int &spritenum, fixed_t &scalex, fixed_t &scaley);
inline void AActor::SetFriendPlayer(player_t *player)
{
if (player == NULL)
{
FriendPlayer = 0;
}
else
{
FriendPlayer = int(player - players) + 1;
}
}
inline bool AActor::IsNoClip2() const
{
if (player != NULL && player->mo == this)
{
return (player->cheats & CF_NOCLIP2) != 0;
}
return false;
}
#define CROUCHSPEED (FRACUNIT/12) #define CROUCHSPEED (FRACUNIT/12)
// [GRB] Custom player classes bool P_IsPlayerTotallyFrozen(const player_t *player);
enum
{
PCF_NOMENU = 1, // Hide in new game menu
};
class FPlayerClass
{
public:
FPlayerClass ();
FPlayerClass (const FPlayerClass &other);
~FPlayerClass ();
bool CheckSkin (int skin);
const PClass *Type;
DWORD Flags;
TArray<int> Skins;
};
extern TArray<FPlayerClass> PlayerClasses;
#endif // __D_PLAYER_H__ #endif // __D_PLAYER_H__

View File

@ -217,10 +217,10 @@ int PackUserCmd (const usercmd_t *ucmd, const usercmd_t *basis, BYTE **stream)
buttons_changed = ucmd->buttons ^ basis->buttons; buttons_changed = ucmd->buttons ^ basis->buttons;
if (buttons_changed != 0) if (buttons_changed != 0)
{ {
BYTE bytes[4] = { ucmd->buttons & 0x7F, BYTE bytes[4] = { BYTE(ucmd->buttons & 0x7F),
(ucmd->buttons >> 7) & 0x7F, BYTE((ucmd->buttons >> 7) & 0x7F),
(ucmd->buttons >> 14) & 0x7F, BYTE((ucmd->buttons >> 14) & 0x7F),
(ucmd->buttons >> 21) & 0xFF }; BYTE((ucmd->buttons >> 21) & 0xFF) };
flags |= UCMDF_BUTTONS; flags |= UCMDF_BUTTONS;

View File

@ -159,6 +159,11 @@ enum EDemoCommand
DEM_CONVCLOSE, // 60 DEM_CONVCLOSE, // 60
DEM_CONVNULL, // 61 DEM_CONVNULL, // 61
DEM_RUNSPECIAL, // 62 Byte: Special number, Byte: Arg count, Ints: Args DEM_RUNSPECIAL, // 62 Byte: Special number, Byte: Arg count, Ints: Args
DEM_SETPITCHLIMIT, // 63 Byte: Up limit, Byte: Down limit (in degrees)
DEM_ADVANCEINTER, // 64 Advance intermission screen state
DEM_RUNNAMEDSCRIPT, // 65 String: Script name, Byte: Arg count + Always flag; each arg is a 4-byte int
DEM_REVERTCAMERA, // 66
DEM_SETSLOTPNUM, // 67 Byte: player number, the rest is the same as DEM_SETSLOT
}; };
// The following are implemented by cht_DoCheat in m_cheat.cpp // The following are implemented by cht_DoCheat in m_cheat.cpp
@ -213,7 +218,8 @@ enum ECheatCommand
CHT_GIMMIEI, CHT_GIMMIEI,
CHT_GIMMIEJ, CHT_GIMMIEJ,
CHT_GIMMIEZ, CHT_GIMMIEZ,
CHT_BUDDHA CHT_BUDDHA,
CHT_NOCLIP2
}; };
void StartChunk (int id, BYTE **stream); void StartChunk (int id, BYTE **stream);

View File

@ -631,7 +631,10 @@ void FDecalLib::ParseGenerator (FScanner &sc)
} }
actor->DecalGenerator = decal; actor->DecalGenerator = decal;
decal->Users.Push (type); if (decal != NULL)
{
decal->Users.Push (type);
}
} }
void FDecalLib::ParseFader (FScanner &sc) void FDecalLib::ParseFader (FScanner &sc)
@ -1121,16 +1124,19 @@ FDecalLib::FTranslation *FDecalLib::FTranslation::LocateTranslation (DWORD start
const FDecalTemplate *FDecalGroup::GetDecal () const const FDecalTemplate *FDecalGroup::GetDecal () const
{ {
const FDecalBase *decal = Choices.PickEntry (); const FDecalBase *decal = Choices.PickEntry ();
const FDecalBase *remember; const FDecalBase *remember = decal;
// Repeatedly GetDecal() until the result is constant, since // Repeatedly GetDecal() until the result is constant, since
// the choice might be another FDecalGroup. // the choice might be another FDecalGroup.
do if (decal != NULL)
{ {
remember = decal; do
decal = decal->GetDecal (); {
} while (decal != remember); remember = decal;
return static_cast<const FDecalTemplate *>(decal); decal = decal->GetDecal ();
} while (decal != NULL && decal != remember);
}
return static_cast<const FDecalTemplate *>(remember);
} }
FDecalAnimator::FDecalAnimator (const char *name) FDecalAnimator::FDecalAnimator (const char *name)

View File

@ -453,8 +453,9 @@ size_t DObject::PropagateMark()
GC::Mark((DObject **)((BYTE *)this + *offsets)); GC::Mark((DObject **)((BYTE *)this + *offsets));
offsets++; offsets++;
} }
return info->Size;
} }
return info->Size; return 0;
} }
size_t DObject::PointerSubstitution (DObject *old, DObject *notOld) size_t DObject::PointerSubstitution (DObject *old, DObject *notOld)

View File

@ -242,8 +242,10 @@ static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count)
// be in a thinker list, then I need to add write barriers for every time a // be in a thinker list, then I need to add write barriers for every time a
// thinker pointer is changed. This seems easier and perfectly reasonable, since // thinker pointer is changed. This seems easier and perfectly reasonable, since
// a live thinker that isn't on a thinker list isn't much of a thinker. // a live thinker that isn't on a thinker list isn't much of a thinker.
assert(!curr->IsKindOf(RUNTIME_CLASS(DThinker)) || (curr->ObjectFlags & OF_Sentinel));
assert(!curr->IsKindOf(RUNTIME_CLASS(DInterpolation))); // However, this can happen during deletion of the thinker list while cleaning up
// from a savegame error so we can't assume that any thinker that gets here is an error.
curr->Destroy(); curr->Destroy();
} }
curr->ObjectFlags |= OF_Cleanup; curr->ObjectFlags |= OF_Cleanup;
@ -305,6 +307,7 @@ static void MarkRoot()
DThinker::MarkRoots(); DThinker::MarkRoots();
FCanvasTextureInfo::Mark(); FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker); Mark(DACSThinker::ActiveThinker);
Mark(level.DefaultSkybox);
// Mark dead bodies. // Mark dead bodies.
for (i = 0; i < BODYQUESIZE; ++i) for (i = 0; i < BODYQUESIZE; ++i)
{ {
@ -531,7 +534,12 @@ void Barrier(DObject *pointing, DObject *pointed)
void DelSoftRootHead() void DelSoftRootHead()
{ {
if (SoftRoots != NULL) delete SoftRoots; if (SoftRoots != NULL)
{
// Don't let the destructor print a warning message
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
delete SoftRoots;
}
SoftRoots = NULL; SoftRoots = NULL;
} }

View File

@ -357,8 +357,8 @@ unsigned int PClass::Extend(unsigned int extension)
} }
// Like FindClass but creates a placeholder if no class // Like FindClass but creates a placeholder if no class
// is found. CreateDerivedClass will automatcally fill in // is found. CreateDerivedClass will automatically fill
// the placeholder when the actual class is defined. // in the placeholder when the actual class is defined.
const PClass *PClass::FindClassTentative (FName name) const PClass *PClass::FindClassTentative (FName name)
{ {
if (name == NAME_None) if (name == NAME_None)

View File

@ -400,11 +400,25 @@ enum EMapThingFlags
STF_ALTSHADOW = 0x0200, STF_ALTSHADOW = 0x0200,
}; };
// A simplified mapthing for player starts
struct FPlayerStart
{
fixed_t x, y, z;
short angle, type;
FPlayerStart() { }
FPlayerStart(const FMapThing *mthing)
: x(mthing->x), y(mthing->y), z(mthing->z),
angle(mthing->angle),
type(mthing->type)
{ }
};
// Player spawn spots for deathmatch. // Player spawn spots for deathmatch.
extern TArray<FMapThing> deathmatchstarts; extern TArray<FPlayerStart> deathmatchstarts;
// Player spawn spots. // Player spawn spots.
extern FMapThing playerstarts[MAXPLAYERS]; extern FPlayerStart playerstarts[MAXPLAYERS];
extern TArray<FPlayerStart> AllPlayerStarts;
#endif // __DOOMDATA__ #endif // __DOOMDATA__

View File

@ -333,7 +333,10 @@ enum
COMPATF_HITSCAN = 1 << 28, // Hitscans use original blockmap anf hit check code. COMPATF_HITSCAN = 1 << 28, // Hitscans use original blockmap anf hit check code.
COMPATF_LIGHT = 1 << 29, // Find neighboring light level like Doom COMPATF_LIGHT = 1 << 29, // Find neighboring light level like Doom
COMPATF_POLYOBJ = 1 << 30, // Draw polyobjects the old fashioned way COMPATF_POLYOBJ = 1 << 30, // Draw polyobjects the old fashioned way
COMPATF_MASKEDMIDTEX = 1 << 31, // Ignore compositing when drawing masked midtextures COMPATF_MASKEDMIDTEX = 1u << 31, // Ignore compositing when drawing masked midtextures
COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW.
COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom.
}; };
// Emulate old bugs for select maps. These are not exposed by a cvar // Emulate old bugs for select maps. These are not exposed by a cvar
@ -345,6 +348,7 @@ enum
BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected. BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected.
BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials
BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior
BCOMPATF_REBUILDNODES = 1 << 5, // Force node rebuild
}; };
// phares 3/20/98: // phares 3/20/98:

View File

@ -230,6 +230,7 @@ struct DehInfo
BYTE ExplosionStyle; BYTE ExplosionStyle;
fixed_t ExplosionAlpha; fixed_t ExplosionAlpha;
int NoAutofreeze; int NoAutofreeze;
int BFGCells;
}; };
extern DehInfo deh; extern DehInfo deh;
EXTERN_CVAR (Int, infighting) EXTERN_CVAR (Int, infighting)
@ -240,6 +241,7 @@ EXTERN_CVAR (Int, dmflags);
EXTERN_CVAR (Int, dmflags2); // [BC] EXTERN_CVAR (Int, dmflags2); // [BC]
EXTERN_CVAR (Int, compatflags); EXTERN_CVAR (Int, compatflags);
extern int i_compatflags, ii_compatflags, ib_compatflags; EXTERN_CVAR (Int, compatflags2);
extern int i_compatflags, i_compatflags2, ii_compatflags, ii_compatflags2, ib_compatflags;
#endif #endif

View File

@ -223,7 +223,7 @@ DMover::EResult DMover::MovePlane (fixed_t speed, fixed_t dest, int crush,
//destheight = (dest < m_Sector->ceilingheight) ? dest : m_Sector->ceilingheight; //destheight = (dest < m_Sector->ceilingheight) ? dest : m_Sector->ceilingheight;
if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b | if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b |
m_Sector->floorplane.a | m_Sector->floorplane.b) == 0 && m_Sector->floorplane.a | m_Sector->floorplane.b) == 0 &&
-dest > m_Sector->ceilingplane.d) (!(i_compatflags2 & COMPATF2_FLOORMOVE) && -dest > m_Sector->ceilingplane.d))
{ {
dest = -m_Sector->ceilingplane.d; dest = -m_Sector->ceilingplane.d;
} }
@ -292,7 +292,7 @@ DMover::EResult DMover::MovePlane (fixed_t speed, fixed_t dest, int crush,
//destheight = (dest > m_Sector->floorheight) ? dest : m_Sector->floorheight; //destheight = (dest > m_Sector->floorheight) ? dest : m_Sector->floorheight;
if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b | if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b |
m_Sector->floorplane.a | m_Sector->floorplane.b) == 0 && m_Sector->floorplane.a | m_Sector->floorplane.b) == 0 &&
dest < -m_Sector->floorplane.d) (!(i_compatflags2 & COMPATF2_FLOORMOVE) && dest < -m_Sector->floorplane.d))
{ {
dest = -m_Sector->floorplane.d; dest = -m_Sector->floorplane.d;
} }

View File

@ -192,19 +192,10 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
statcount--; statcount--;
} }
} }
catch (class CDoomError &err) catch (class CDoomError &)
{ {
bSerialOverride = false; bSerialOverride = false;
DestroyAllThinkers();
// DestroyAllThinkers cannot be called here. It will try to delete the corrupted
// object table left behind by the serializer and crash.
// Trying to continue is not an option here because the garbage collector will
// crash the next time it runs.
// Even making this a fatal error will crash but at least the message can be seen
// before the crash - which is not the case with all other options.
//DestroyAllThinkers();
I_FatalError("%s", err.GetMessage());
throw; throw;
} }
bSerialOverride = false; bSerialOverride = false;
@ -457,7 +448,7 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
NextToThink = node->NextThinker; NextToThink = node->NextThinker;
if (node->ObjectFlags & OF_JustSpawned) if (node->ObjectFlags & OF_JustSpawned)
{ {
node->ObjectFlags &= ~OF_JustSpawned; // Leave OF_JustSpawn set until after Tick() so the ticker can check it.
if (dest != NULL) if (dest != NULL)
{ // Move thinker from this list to the destination list { // Move thinker from this list to the destination list
node->Remove(); node->Remove();
@ -472,7 +463,8 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
if (!(node->ObjectFlags & OF_EuthanizeMe)) if (!(node->ObjectFlags & OF_EuthanizeMe))
{ // Only tick thinkers not scheduled for destruction { // Only tick thinkers not scheduled for destruction
node->Tick (); node->Tick();
node->ObjectFlags &= ~OF_JustSpawned;
GC::CheckGC(); GC::CheckGC();
} }
node = NextToThink; node = NextToThink;

View File

@ -54,6 +54,7 @@
#include "c_cvars.h" #include "c_cvars.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "d_player.h" #include "d_player.h"
#include "m_misc.h"
#include "dobject.h" #include "dobject.h"
// These are special tokens found in the data stream of an archive. // These are special tokens found in the data stream of an archive.
@ -405,7 +406,7 @@ void FCompressedFile::Explode ()
if (r != Z_OK || newlen != expandsize) if (r != Z_OK || newlen != expandsize)
{ {
M_Free (expand); M_Free (expand);
I_Error ("Could not decompress cfile"); I_Error ("Could not decompress buffer: %s", M_ZLibError(r).GetChars());
} }
} }
else else
@ -497,7 +498,18 @@ bool FCompressedMemFile::Reopen ()
m_Mode = EReading; m_Mode = EReading;
m_Buffer = m_ImplodedBuffer; m_Buffer = m_ImplodedBuffer;
m_SourceFromMem = true; m_SourceFromMem = true;
Explode (); try
{
Explode ();
}
catch(...)
{
// If we just leave things as they are, m_Buffer and m_ImplodedBuffer
// both point to the same memory block and both will try to free it.
m_Buffer = NULL;
m_SourceFromMem = false;
throw;
}
m_SourceFromMem = false; m_SourceFromMem = false;
return true; return true;
} }
@ -560,6 +572,20 @@ bool FCompressedMemFile::IsOpen () const
return !!m_Buffer; return !!m_Buffer;
} }
void FCompressedMemFile::GetSizes(unsigned int &compressed, unsigned int &uncompressed) const
{
if (m_ImplodedBuffer != NULL)
{
compressed = BigLong(*(unsigned int *)m_ImplodedBuffer);
uncompressed = BigLong(*(unsigned int *)(m_ImplodedBuffer + 4));
}
else
{
compressed = 0;
uncompressed = m_BufferSize;
}
}
FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id) FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id)
: FCompressedFile (file, EWriting, true, false), m_ChunkID (id) : FCompressedFile (file, EWriting, true, false), m_ChunkID (id)
{ {

View File

@ -124,6 +124,7 @@ public:
bool Reopen (); // Re-opens imploded file for reading only bool Reopen (); // Re-opens imploded file for reading only
void Close (); void Close ();
bool IsOpen () const; bool IsOpen () const;
void GetSizes(unsigned int &one, unsigned int &two) const;
void Serialize (FArchive &arc); void Serialize (FArchive &arc);

View File

@ -36,6 +36,7 @@
#include "files.h" #include "files.h"
#include "i_system.h" #include "i_system.h"
#include "templates.h" #include "templates.h"
#include "m_misc.h"
//========================================================================== //==========================================================================
// //
@ -145,11 +146,16 @@ long FileReader::Read (void *buffer, long len)
char *FileReader::Gets(char *strbuf, int len) char *FileReader::Gets(char *strbuf, int len)
{ {
if (len <= 0) return 0; if (len <= 0 || FilePos >= StartPos + Length) return NULL;
char *p = fgets(strbuf, len, File); char *p = fgets(strbuf, len, File);
if (p != NULL) if (p != NULL)
{ {
FilePos = ftell(File) - StartPos; int old = FilePos;
FilePos = ftell(File);
if (FilePos - StartPos > Length)
{
strbuf[Length - old + StartPos] = 0;
}
} }
return p; return p;
} }
@ -218,7 +224,7 @@ FileReaderZ::FileReaderZ (FileReader &file, bool zip)
if (err != Z_OK) if (err != Z_OK)
{ {
I_Error ("FileReaderZ: inflateInit failed: %d\n", err); I_Error ("FileReaderZ: inflateInit failed: %s\n", M_ZLibError(err).GetChars());
} }
} }

View File

@ -774,7 +774,7 @@ void FParser::SF_PlayerName(void)
if(plnum !=-1) if(plnum !=-1)
{ {
t_return.type = svt_string; t_return.type = svt_string;
t_return.string = players[plnum].userinfo.netname; t_return.string = players[plnum].userinfo.GetName();
} }
else else
{ {
@ -3367,7 +3367,7 @@ void FParser::SF_RadiusAttack()
if (spot && source) if (spot && source)
{ {
P_RadiusAttack(spot, source, damage, damage, NAME_None, true); P_RadiusAttack(spot, source, damage, damage, NAME_None, RADF_HURTSOURCE);
} }
} }
} }
@ -3747,7 +3747,7 @@ void FParser::SF_LineAttack()
angle = (intvalue(t_argv[1]) * (ANG45 / 45)); angle = (intvalue(t_argv[1]) * (ANG45 / 45));
slope = P_AimLineAttack(mo, angle, MISSILERANGE); slope = P_AimLineAttack(mo, angle, MISSILERANGE);
P_LineAttack(mo, angle, MISSILERANGE, slope, damage, NAME_None, NAME_BulletPuff); P_LineAttack(mo, angle, MISSILERANGE, slope, damage, NAME_Hitscan, NAME_BulletPuff);
} }
} }
@ -4256,7 +4256,7 @@ void FParser::SF_SpawnShot2(void)
S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM); S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM);
mo->target = source; mo->target = source;
P_ThrustMobj(mo, mo->angle = source->angle, mo->Speed); P_ThrustMobj(mo, mo->angle = source->angle, mo->Speed);
if (!P_CheckMissileSpawn(mo)) mo = NULL; if (!P_CheckMissileSpawn(mo, source->radius)) mo = NULL;
} }
t_return.value.mobj = mo; t_return.value.mobj = mo;
} }

View File

@ -316,7 +316,7 @@ bool FScriptLoader::ParseInfo(MapData * map)
} }
delete lump; delete[] lump;
return HasScripts; return HasScripts;
} }
@ -342,7 +342,7 @@ void T_LoadScripts(MapData *map)
// the default translator is being used. // the default translator is being used.
// Custom translators will not be patched. // Custom translators will not be patched.
if ((gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Heretic) && level.info->Translator.IsEmpty() && if ((gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Heretic) && level.info->Translator.IsEmpty() &&
level.maptype == MAPTYPE_DOOM && SimpleLineTranslations[272 - 2*HasScripts].special == FS_Execute) level.maptype == MAPTYPE_DOOM && SimpleLineTranslations.Size() > 272 && SimpleLineTranslations[272 - 2*HasScripts].special == FS_Execute)
{ {
FLineTrans t = SimpleLineTranslations[270]; FLineTrans t = SimpleLineTranslations[270];
SimpleLineTranslations[270] = SimpleLineTranslations[272]; SimpleLineTranslations[270] = SimpleLineTranslations[272];

View File

@ -123,8 +123,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
return; return;
S_Sound (self, CHAN_WEAPON, snd, 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);
P_TraceBleed (dmg, target); int newdam = P_DamageMobj (target, self, self, dmg, NAME_None);
P_DamageMobj (target, self, self, dmg, NAME_None); P_TraceBleed (newdam > 0 ? newdam : dmg, target);
an = self->angle >> ANGLETOFINESHIFT; an = self->angle >> ANGLETOFINESHIFT;
fire = self->tracer; fire = self->tracer;
@ -136,7 +136,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
target->y - FixedMul (24*FRACUNIT, finesine[an]), target->y - FixedMul (24*FRACUNIT, finesine[an]),
target->z); target->z);
P_RadiusAttack (fire, self, blastdmg, blastrad, dmgtype, false); P_RadiusAttack (fire, self, blastdmg, blastrad, dmgtype, 0);
} }
target->velz = Scale(thrust, 1000, target->Mass); target->velz = Scale(thrust, 1000, target->Mass);
} }

View File

@ -79,16 +79,22 @@ DEFINE_ACTION_FUNCTION(AActor, A_BrainDie)
// New dmflag: Kill all boss spawned monsters before ending the level. // New dmflag: Kill all boss spawned monsters before ending the level.
if (dmflags2 & DF2_KILLBOSSMONST) if (dmflags2 & DF2_KILLBOSSMONST)
{ {
TThinkerIterator<AActor> it; int count; // Repeat until we have no more boss-spawned monsters.
AActor *mo; do // (e.g. Pain Elementals can spawn more to kill upon death.)
while ((mo = it.Next()))
{ {
if (mo->flags4 & MF4_BOSSSPAWNED) TThinkerIterator<AActor> it;
AActor *mo;
count = 0;
while ((mo = it.Next()))
{ {
P_DamageMobj(mo, self, self, mo->health, NAME_None, if (mo->health > 0 && mo->flags4 & MF4_BOSSSPAWNED)
DMG_NO_ARMOR|DMG_FORCED|DMG_THRUSTLESS|DMG_NO_FACTOR); {
P_DamageMobj(mo, self, self, mo->health, NAME_None,
DMG_NO_ARMOR|DMG_FORCED|DMG_THRUSTLESS|DMG_NO_FACTOR);
count++;
}
} }
} } while (count != 0);
} }
G_ExitLevel (0, false); G_ExitLevel (0, false);
@ -118,12 +124,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit)
// spawn brain missile // spawn brain missile
spit = P_SpawnMissile (self, targ, spawntype); spit = P_SpawnMissile (self, targ, spawntype);
// Boss cubes should move freely to their destination so it's
// probably best to disable all collision detection for them.
if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION;
if (spit != NULL) if (spit != NULL)
{ {
// Boss cubes should move freely to their destination so it's
// probably best to disable all collision detection for them.
if (spit->flags & MF_NOCLIP) spit->flags5 |= MF5_NOINTERACTION;
spit->target = targ; spit->target = targ;
spit->master = self; spit->master = self;
// [RH] Do this correctly for any trajectory. Doom would divide by 0 // [RH] Do this correctly for any trajectory. Doom would divide by 0
@ -258,9 +264,14 @@ static void SpawnFly(AActor *self, const PClass *spawntype, FSoundID sound)
{ {
newmobj->CopyFriendliness (eye, false); newmobj->CopyFriendliness (eye, false);
} }
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true, NULL)) // Make it act as if it was around when the player first made noise
newmobj->SetState (newmobj->SeeState); // (if the player has made noise).
newmobj->LastHeard = newmobj->Sector->SoundTarget;
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true, NULL))
{
newmobj->SetState (newmobj->SeeState);
}
if (!(newmobj->ObjectFlags & OF_EuthanizeMe)) if (!(newmobj->ObjectFlags & OF_EuthanizeMe))
{ {
// telefrag anything in this spot // telefrag anything in this spot

View File

@ -10,8 +10,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_BruisAttack)
{ {
int damage = (pr_bruisattack()%8+1)*10; int damage = (pr_bruisattack()%8+1)*10;
S_Sound (self, CHAN_WEAPON, "baron/melee", 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, "baron/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee); int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self); P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return; return;
} }

View File

@ -22,8 +22,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_HeadAttack)
{ {
int damage = (pr_headattack()%6+1)*10; int damage = (pr_headattack()%6+1)*10;
S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, self->AttackSound, 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee); int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self); P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return; return;
} }

View File

@ -20,7 +20,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SargAttack)
if (self->CheckMeleeRange ()) if (self->CheckMeleeRange ())
{ {
int damage = ((pr_sargattack()%10)+1)*4; int damage = ((pr_sargattack()%10)+1)*4;
P_DamageMobj (self->target, self, self, damage, NAME_Melee); int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self); P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
} }
} }

View File

@ -25,8 +25,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_TroopAttack)
{ {
int damage = (pr_troopattack()%8+1)*3; int damage = (pr_troopattack()%8+1)*3;
S_Sound (self, CHAN_WEAPON, "imp/melee", 1, ATTN_NORM); S_Sound (self, CHAN_WEAPON, "imp/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee); int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self); P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return; return;
} }

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