Merge branch 'origin/maint'

Conflicts:
	src/sound/fmodsound.cpp
This commit is contained in:
Braden Obrzut 2013-10-23 19:39:22 -04:00
commit 97d7b1e59e
517 changed files with 31100 additions and 11359 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,38 @@
cmake_minimum_required( VERSION 2.4 )
project(ZDoom)
# Generator expression are available some time in CMake 2.8. Due to
# cmake_minimum_required, we can assume a minor version of > 7 implies major >= 2
if(${CMAKE_MAJOR_VERSION} GREATER 2 OR ${CMAKE_MINOR_VERSION} GREATER 7)
option( NO_GENERATOR_EXPRESSIONS "Disable generator expressions (for building pk3s with IDEs)." OFF )
else(${CMAKE_MAJOR_VERSION} GREATER 2 OR ${CMAKE_MINOR_VERSION} GREATER 7)
set( NO_GENERATOR_EXPRESSIONS ON )
endif(${CMAKE_MAJOR_VERSION} GREATER 2 OR ${CMAKE_MINOR_VERSION} GREATER 7)
# Simplify pk3 building, add_pk3(filename srcdirectory)
function( add_pk3 PK3_NAME PK3_DIR )
get_target_property(ZIPDIR_EXE zipdir LOCATION)
# Generate target name. Just use "pk3" for main pk3 target.
string( REPLACE "." "_" PK3_TARGET ${PK3_NAME} )
if( ${PK3_TARGET} STREQUAL "zdoom_pk3" )
set( PK3_TARGET "pk3" )
endif( ${PK3_TARGET} STREQUAL "zdoom_pk3" )
if( NO_GENERATOR_EXPRESSIONS )
add_custom_command( OUTPUT ${ZDOOM_OUTPUT_DIR}/${PK3_NAME}
COMMAND ${ZIPDIR_EXE} -udf ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} ${PK3_DIR}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} $<TARGET_FILE_DIR:zdoom>
DEPENDS zipdir ${PK3_DIR} )
else( NO_GENERATOR_EXPRESSIONS )
add_custom_command( OUTPUT ${ZDOOM_OUTPUT_DIR}/${PK3_NAME}
COMMAND ${ZIPDIR_EXE} -udf ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} ${PK3_DIR}
DEPENDS zipdir ${PK3_DIR} )
endif( NO_GENERATOR_EXPRESSIONS )
add_custom_target( ${PK3_TARGET} ALL
DEPENDS ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} )
endfunction( add_pk3 )
IF( NOT CMAKE_BUILD_TYPE )
SET( CMAKE_BUILD_TYPE Debug CACHE STRING
@ -9,9 +43,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_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." )
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_OPENAL "Disable OpenAL sound support" )

View file

@ -1,8 +1,8 @@
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" )
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_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" )
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" )
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 )
if( NOT ITOA_EXISTS )
@ -36,6 +36,7 @@ add_library( dumb
src/core/rendsig.c
src/core/unload.c
src/helpers/barray.c
src/helpers/blip_buf.c
src/helpers/clickrem.c
src/helpers/memfile.c
src/helpers/resample.c
@ -60,6 +61,8 @@ add_library( dumb
src/it/loadmod2.c
src/it/loadmtm.c
src/it/loadmtm2.c
src/it/loadokt.c
src/it/loadokt2.c
src/it/loadoldpsm.c
src/it/loadoldpsm2.c
src/it/loadpsm.c
@ -83,6 +86,8 @@ add_library( dumb
src/it/readmod.c
src/it/readmod2.c
src/it/readmtm.c
src/it/readokt.c
src/it/readokt2.c
src/it/readoldpsm.c
src/it/readpsm.c
src/it/readptm.c
@ -96,6 +101,6 @@ add_library( dumb
src/it/xmeffect.c )
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 )
endif( CMAKE_COMPILER_IS_GNUCXX )
endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )

View file

@ -44,7 +44,7 @@
#define DUMB_VERSION_STR "0.9.3"
#define DUMB_NAME "DUMB v"DUMB_VERSION_STR
#define DUMB_NAME "DUMB v" DUMB_VERSION_STR
#define DUMB_YEAR 2005
#define DUMB_MONTH 8
@ -56,13 +56,13 @@
#define DUMB_DAY_STR1 "7"
#if DUMB_MONTH < 10
#define DUMB_MONTH_STR2 "0"DUMB_MONTH_STR1
#define DUMB_MONTH_STR2 "0" DUMB_MONTH_STR1
#else
#define DUMB_MONTH_STR2 DUMB_MONTH_STR1
#endif
#if DUMB_DAY < 10
#define DUMB_DAY_STR2 "0"DUMB_DAY_STR1
#define DUMB_DAY_STR2 "0" DUMB_DAY_STR1
#else
#define DUMB_DAY_STR2 DUMB_DAY_STR1
#endif
@ -74,7 +74,7 @@
*/
#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY)
#define DUMB_DATE_STR DUMB_DAY_STR1"."DUMB_MONTH_STR1"."DUMB_YEAR_STR4
#define DUMB_DATE_STR DUMB_DAY_STR1 "." DUMB_MONTH_STR1 "." DUMB_YEAR_STR4
#undef MIN
@ -233,7 +233,6 @@ int32 DUMBEXPORT duh_get_length(DUH *duh);
const char *DUMBEXPORT duh_get_tag(DUH *duh, const char *key);
/* Signal Rendering Functions */
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_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_xm(const char *filename);
DUH *DUMBEXPORT dumb_load_s3m(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_669(const char *filename);
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_riff(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_xm(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_s3m(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_669(DUMBFILE *f);
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_riff(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_xm_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_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_669_quick(const char *filename);
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_riff_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_xm_quick(DUMBFILE *f);
DUH *DUMBEXPORT dumb_read_s3m_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_669_quick(DUMBFILE *f);
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_riff_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);
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);
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 */
@ -662,6 +669,8 @@ typedef struct DUMB_VOLUME_RAMP_INFO DUMB_VOLUME_RAMP_INFO;
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
#include "internal/blip_buf.h"
struct DUMB_RESAMPLER
{
void *src;
@ -679,6 +688,9 @@ struct DUMB_RESAMPLER
signed char x8[3*2];
} x;
int overshot;
int last_clock;
int last_amp[2];
blip_t* blip_buffer[2];
};
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
* 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_NOTES_CAUSE_NOTE_CUT
#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_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. */
#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_AN_OKT 2048
#define IT_WAS_AN_STM 4096
#define IT_ORDER_END 255
#define IT_ORDER_SKIP 254
@ -452,6 +467,7 @@ struct IT_PLAYING_ENVELOPE
#define IT_PLAYING_SUSTAINOFF 2
#define IT_PLAYING_FADING 4
#define IT_PLAYING_DEAD 8
#define IT_PLAYING_REVERSE 16
struct IT_PLAYING
{
@ -586,7 +602,8 @@ struct IT_CHANNEL
unsigned char new_note_action;
int arpeggio;
unsigned int arpeggio;
int arpeggio_shift;
unsigned char retrig;
unsigned char xm_retrig;
int retrig_tick;
@ -601,7 +618,7 @@ struct IT_CHANNEL
int portamento;
int toneporta;
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 toneslide_retrig;
@ -643,6 +660,10 @@ struct IT_CHANNEL
unsigned char xm_lastX1;
unsigned char xm_lastX2;
unsigned char inv_loop_delay;
unsigned char inv_loop_speed;
int inv_loop_offset;
IT_PLAYING *playing;
#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_DELAY 0xD
#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_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);
void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample);
#endif /* INTERNAL_IT_H */

View file

@ -130,3 +130,22 @@ DUH *make_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)
{
int i;
DUH_SIGNAL *signal;
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)
return signal->sigdata;
if (signal && signal->desc->type == type)
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;
}

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 RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
#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_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
@ -137,7 +138,8 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
}
#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_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; }
@ -157,6 +159,9 @@ static int process_pickup(DUMB_RESAMPLER *resampler)
#undef MONO_DEST_VOLUME_ZEROS
#undef MONO_DEST_VOLUME_VARIABLES
#undef MONO_DEST_VOLUME_PARAMETERS
#undef STEREO_DEST_PEEK_ALIAS
#undef MONO_DEST_PEEK_ALIAS
#undef POKE_ALIAS
#undef COPYSRC2
#undef COPYSRC
#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)
{
int dt;
int dt, inv_dt;
int VOLUME_VARIABLES;
long done;
long todo;
LONG_LONG todo64;
int quality;
int blip_samples[256*SRC_CHANNELS];
if (!resampler || resampler->dir == 0) return 0;
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;
dt = (int)(delta * 65536.0 + 0.5);
if (dt == 0 || dt == 0x80000000) return 0;
inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
SET_VOLUME_VARIABLES;
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;
} else if (quality <= DUMB_RQ_ALIASING) {
/* 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 *xstart;
COPYSRC(xbuf, 0, resampler->X, 1);
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
HEAVYASSERT(pos >= resampler->start);
MIX_ALIAS(+=, 1, 0);
subpos += dt;
pos += subpos >> 16;
x -= (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
todo--;
POKE_ALIAS(0);
pos--;
x += SRC_CHANNELS;
}
x = &src[pos*SRC_CHANNELS];
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) {
/* Linear interpolation, backwards */
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;
} else if (quality <= DUMB_RQ_ALIASING) {
/* Aliasing, forwards */
int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
SRCTYPE xbuf[2*SRC_CHANNELS];
SRCTYPE *x = &xbuf[0];
SRCTYPE *xstart;
COPYSRC(xbuf, 0, resampler->X, 1);
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);
MIX_ALIAS(+=, 1, 0);
subpos += dt;
pos += subpos >> 16;
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
todo--;
POKE_ALIAS(0);
pos++;
x += SRC_CHANNELS;
}
x = &src[pos*SRC_CHANNELS];
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) {
/* Linear interpolation, forwards */
SRCTYPE xbuf[3*SRC_CHANNELS];
@ -339,7 +349,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos >= resampler->start);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing, backwards */
MIX_ALIAS(=, 0, 1);
PEEK_ALIAS;
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */
MIX_LINEAR(=, 0, 2, 1);
@ -351,7 +361,7 @@ void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETE
HEAVYASSERT(pos < resampler->end);
if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
/* Aliasing */
MIX_ALIAS(=, 0, 1);
PEEK_ALIAS;
} else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */
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_LINEAR
#undef MIX_ALIAS
#undef PEEK_ALIAS
#undef VOLUMES_ARE_ZERO
#undef SET_VOLUME_VARIABLES
#undef RETURN_VOLUME_VARIABLES

View file

@ -117,9 +117,9 @@ int dumb_resampling_quality = DUMB_RQ_CUBIC;
*/
#define LOOP4(iterator, CONTENT) \
{ \
while (iterator) { \
while ( (iterator)-- ) \
{ \
CONTENT; \
(iterator)--; \
} \
}
#endif
@ -189,7 +189,7 @@ static void init_cubic(void)
#define SRCTYPE sample_t
#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 SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -225,7 +225,7 @@ static void init_cubic(void)
#define SUFFIX _16
#define SRCTYPE short
#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 SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@ -247,7 +247,7 @@ static void init_cubic(void)
#define SUFFIX _8
#define SRCTYPE signed char
#define SRCBITS 8
#define ALIAS(x, vol) (x * vol)
#define ALIAS(x) (x << 8)
#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
/*
#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;
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));
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);
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 MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
*dst++ op ALIAS(x[offset], vol); \
if ( upd ) UPDATE_VOLUME( volume, vol ); \
#define POKE_ALIAS(offset) { \
int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \
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) { \
int xm = x[offset]; \
*dst++ op ALIAS(xm, lvol); \
*dst++ op ALIAS(xm, rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
#define MONO_DEST_MIX_ALIAS(count) { \
int 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, \
*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) { \
*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; \
}
#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
*dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define POKE_ALIAS(offset) { \
int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \
int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \
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) { \
*dst++ op ALIAS(x[(offset)*2], lvol); \
*dst++ op ALIAS(x[(offset)*2+1], rvol); \
if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
#define MONO_DEST_PEEK_ALIAS { \
*dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
MULSC( blip_peek_sample( resampler->blip_buffer[1] ), 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) { \
*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)
{
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
static int it_seek(DUMBFILE *f, int32 offset)
typedef struct tdumbfile_mem_status
{
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;
if (pos < offset)
if (dumbfile_skip(f, offset - pos))
return -1;
s->offset = offset;
return 0;
}
@ -306,6 +359,8 @@ static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
envelope->loop_end = dumbfile_getc(f);
envelope->sus_loop_start = 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++) {
envelope->node_y[n] = dumbfile_getc(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 compression_table[16];
if (dumbfile_getnc(compression_table, 16, f) != 16)
return -1;
return -1;
ptr = (signed char *) sample->data;
delta = 0;
@ -682,15 +737,36 @@ static int32 it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char conv
else
decompress8(f, sample->data, datasize, ((cmwt >= 0x215) && (convert & 4)));
} 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++)
((short *)sample->data)[n] = dumbfile_mgetw(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);
((signed char *)sample->data)[n] = dumbfile_getc(f);
}
if (dumbfile_error(f))
return -1;
@ -934,13 +1010,58 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
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)
{
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata = malloc(sizeof(*sigdata));
if (!sigdata)
{
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->song_message = NULL;
sigdata->order = NULL;
@ -989,12 +1110,16 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
// XXX sample count
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);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->order = malloc(sigdata->n_orders);
if (!sigdata->order) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1002,6 +1127,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
if (!sigdata->instrument) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
}
@ -1010,6 +1137,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
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));
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
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));
if (!component) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1076,6 +1209,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_error(f)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1096,6 +1231,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (!sigdata->midi) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
// 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)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
/* Read embedded MIDI configuration */
@ -1111,6 +1250,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
if (dumbfile_skip(f, 32*9)) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
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) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->midi->SFmacroz[i] = 0;
@ -1174,12 +1317,14 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
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);
if (!buffer) {
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1208,10 +1353,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
continue;
}
if (it_seek(f, component[n].offset)) {
if (it_seek(&memdata, component[n].offset)) {
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1227,6 +1374,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
sigdata->song_message[message_length] = 0;
@ -1243,6 +1392,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
break;
@ -1252,6 +1403,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
break;
@ -1261,6 +1414,8 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1287,10 +1442,12 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
m = component[n].sampfirst;
while (m >= 0) {
if (it_seek(f, component[m].offset)) {
if (it_seek(&memdata, component[m].offset)) {
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
@ -1298,16 +1455,79 @@ static sigdata_t *it_load_sigdata(DUMBFILE *f)
free(buffer);
free(component);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
free(file_buffer);
return NULL;
}
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);
dumbfile_close(f);
free(file_buffer);
_dumb_it_fix_invalid_orders(sigdata);
return sigdata;
@ -1327,9 +1547,11 @@ DUH *DUMBEXPORT dumb_read_it_quick(DUMBFILE *f)
return NULL;
{
const char *tag[1][2];
const char *tag[2][2];
tag[0][0] = "TITLE";
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
* 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;
DUMBFILE *f = dumbfile_open(filename);
@ -34,7 +34,7 @@ DUH *DUMBEXPORT dumb_load_mod_quick(const char *filename, int restrict)
if (!f)
return NULL;
duh = dumb_read_mod_quick(f, restrict);
duh = dumb_read_mod_quick(f, rstrict);
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);
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_orders = 0;
sigdata->restart_position = 0;
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_orders = 0;
sigdata->restart_position = 0;
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;
}
_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;
}
@ -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 )
{
int finetune;
int finetune, key_offset;
/**
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->global_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->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->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;
// 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_orders = 0;
sigdata->restart_position = 0;
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);
}
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;
int sample_number;
BUFFERED_MOD *bm = malloc(sizeof(*bm));
unsigned char *ptr;
if (!bm) return NULL;
bm->buffered = malloc(32768);
@ -430,6 +432,21 @@ static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, int32 *remain)
if (*remain) {
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 {
free(bm->buffered);
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;
int n_channels;
int i;
uint32 fft = 0;
DUMBFILE *rem;
DUMBFILE *rem = NULL;
f = dumbfile_buffer_mod(f, &fft);
if (!f)
@ -550,7 +567,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
}
// moo
if ( restrict && sigdata->n_samples == 15 )
if ( ( rstrict & 1 ) && sigdata->n_samples == 15 )
{
free(sigdata);
dumbfile_close(f);
@ -629,12 +646,48 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
if (sigdata->n_samples == 31)
dumbfile_skip(f, 4);
/* Work out how many patterns there are. */
sigdata->n_patterns = -1;
for (i = 0; i < 128; i++)
if (sigdata->n_patterns < sigdata->order[i])
sigdata->n_patterns = sigdata->order[i];
sigdata->n_patterns++;
if ( ( rstrict & 2 ) )
{
int32 total_sample_size;
int32 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. */
if (sigdata->n_orders < 128) {
@ -646,6 +699,7 @@ static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict)
if (!sigdata->pattern) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
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 */
{
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) {
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
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);
_dumb_it_unload_sigdata(sigdata);
dumbfile_close(f);
if (rem) dumbfile_close(rem);
return NULL;
}
}
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 */
for (i = 0; i < sigdata->n_samples; i++) {
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. */
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. */
if (rem) dumbfile_close(rem);
/* 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;
@ -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;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_mod_load_sigdata(f, restrict);
sigdata = it_mod_load_sigdata(f, rstrict);
if (!sigdata)
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);
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 ((*ptr < 60) && (channel < pchans)) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = *ptr + 36;
entry->note = *ptr + 35;
}
ptr++;
if (*ptr) {

View file

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

View file

@ -20,6 +20,7 @@
// IT_STEREO... :o
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "dumb.h"
#include "internal/it.h"
@ -28,14 +29,16 @@
#define strnicmp strncasecmp
#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 );
sample->filename[12] = 0;
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->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);
}
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;
n = dumbfile_pos( f );
if ( n & 15 ) {
if ( dumbfile_skip( f, 16 - ( n & 15 ) ) )
return -1;
}
sample->data = malloc( sample->length );
if (!sample->data)
return -1;
if ( dumbfile_getnc( sample->data, sample->length, f ) != sample->length )
return -1;
memcpy( sample->data, (unsigned char*)data_block + offset, sample->length );
return 0;
}
@ -141,10 +135,6 @@ static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char
entry->effectvalue = buffer[ pos + 3 ];
if ( entry->instrument && entry->instrument < 32 )
entry->mask |= IT_ENTRY_INSTRUMENT;
if ( note == 0xFC || note == 0xFE ) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = IT_NOTE_CUT;
}
if ( note < 251 ) {
entry->mask |= IT_ENTRY_NOTE;
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_EFFECT;
switch ( entry->effect ) {
case IT_SET_SPEED:
entry->effectvalue >>= 4;
break;
case IT_SET_SPEED:
/* taken care of in the renderer */
break;
case IT_BREAK_TO_ROW:
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;
char tracker_name[ 8 ];
unsigned short sample_offset[ 31 ];
void *data_block;
int n;
long o, p, q;
sigdata = malloc(sizeof(*sigdata));
if (!sigdata) return NULL;
@ -227,8 +223,7 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
return NULL;
}
/* *version = dumbfile_mgetw(f); */
dumbfile_skip( f, 2 );
*version = dumbfile_mgetw(f);
sigdata->song_message = 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_pchannels = 4;
sigdata->tempo = 125;
sigdata->mixing_volume = 48;
sigdata->tempo = 125;
sigdata->mixing_volume = 48;
sigdata->pan_separation = 128;
/** 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;
if ( sigdata->speed < 1 ) sigdata->speed = 1;
sigdata->n_patterns = dumbfile_getc(f);
n = dumbfile_getc(f);
if ( n < 32 ) n = 32;
sigdata->speed = n;
sigdata->n_patterns = dumbfile_getc(f);
sigdata->global_volume = dumbfile_getc(f) << 1;
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;
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 );
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) */
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;
for ( n = 127; n >= 0; --n ) {
@ -332,54 +329,105 @@ static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f /*, int * version*/)
free( buffer );
}
o = LONG_MAX;
p = 0;
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 );
return NULL;
}
}
free( data_block );
_dumb_it_fix_invalid_orders(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)
{
sigdata_t *sigdata;
/*int ver;*/
int ver;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_stm_load_sigdata(f /*, &ver*/);
sigdata = it_stm_load_sigdata(f , &ver);
if (!sigdata)
return NULL;
{
/*char version[16];*/
char version[16];
const char *tag[2][2];
tag[0][0] = "TITLE";
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
tag[1][0] = "FORMAT";
tag[1][1] = "STM";
/*version[0] = 'S';
version[0] = 'S';
version[1] = 'T';
version[2] = 'M';
version[3] = ' ';
version[4] = 'v';
version[5] = hexdigit((ver >> 8) & 15);
version[5] = '0' + ((ver >> 8) & 15);
version[6] = '.';
version[7] = hexdigit((ver >> 4) & 15);
version[8] = hexdigit(ver & 15);
version[9] = 0;
tag[1][1] = (const char *) &version;*/
if ((ver & 255) > 99)
{
version[7] = '0' + ((ver & 255) / 100 );
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);
}
}

View file

@ -111,11 +111,20 @@ typedef struct XM_INSTRUMENT_EXTRA
int vibrato_sweep; /* 0-0xFF */
int vibrato_depth; /* 0-0x0F */
int vibrato_speed; /* 0-0x3F */
int sample_header_size;
}
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
* 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)
{
int i, pos;
int i, pos, val;
if (envelope->n_nodes > 12) {
/* XXX
@ -346,12 +355,13 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data
pos = 0;
for (i = 0; i < envelope->n_nodes; i++) {
envelope->node_t[i] = data[pos++];
if (data[pos] > 64) {
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]);
envelope->n_nodes = 0;
return -1;
val = data[pos++];
if (val > 64) {
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val);
/* 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;
@ -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 int32 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)
{
uint32 size, bytes_read;
unsigned short vol_points[24];
unsigned short pan_points[24];
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.
* So unread bytes must be skipped before reading the first sample
* header.
*/
if ( limit_xm_resize( f, 4 ) < 0 ) return -1;
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);
instrument->name[22] = 0;
trim_whitespace(instrument->name, 22);
instrument->filename[0] = 0;
dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */
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) {
/* 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);
if (i && i != 0x28) { // XXX some crap with 0 here
TRACE("XM error: unexpected sample header size\n");
return -1;
}*/
if (!i || i > 0x28) i = 0x28;*/
dumbfile_skip(f, 4);
i = 0x28;
extra->sample_header_size = i;
/* sample map */
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++)
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;
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);
sample->name[22] = 0;
trim_whitespace(sample->name, 22);
sample->filename[0] = 0;
@ -785,6 +930,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version)
return NULL;
}
sigdata->name[20] = 0;
trim_whitespace(sigdata->name, 20);
if (dumbfile_getc(f) != 0x1A) {
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++) {
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
if ( ! i )
{
TRACE("XM error: instrument %d\n", i+1);
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
else
{
dumbfile_close( lf );
sigdata->n_instruments = i;
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));
if (!sigdata->sample) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
for (j = total_samples; j < total_samples+extra.n_samples; j++)
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 */
for (j = 0; j < extra.n_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) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
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++) {
if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
dumbfile_close( lf );
_dumb_it_unload_sigdata(sigdata);
return NULL;
}
}
total_samples += extra.n_samples;
}
dumbfile_close( lf );
}
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++) {
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);
dumbfile_close(lf);
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
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));
if (!sigdata->sample) {
dumbfile_close( lf );
free(roguebytes);
_dumb_it_unload_sigdata(sigdata);
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++)
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 */
for (j = 0; j < extra.n_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) {
free(roguebytes);
_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;
}
dumbfile_close( lf );
}
sigdata->n_samples = total_samples;

View file

@ -167,6 +167,7 @@ if (log) printf(" - %2d %02X", effect, value);
case XM_SET_GLOBAL_VOLUME:
effect = IT_SET_GLOBAL_VOLUME;
value *= 2;
if (value > 128) value = 128;
break;
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_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_SET_MIDI_MACRO: effect = SBASE+IT_S_SET_MIDI_MACRO; break;
case EBASE + XM_E_FINE_PORTA_UP:
effect = IT_PORTAMENTO_UP;

View file

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

View file

@ -6,7 +6,7 @@ if( NOT CMAKE_BUILD_TYPE MATCHES "Release" )
set( CMAKE_BUILD_TYPE "RelWithDebInfo" )
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" )
if( NOT PROFILE )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" )
@ -15,7 +15,7 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( HAVE_NO_ARRAY_BOUNDS )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-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
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 );
resampler.write( new_count );
long count = resampler.read( sample_buf.begin(), sample_buf_size );
assert( count == (long) sample_buf_size );
mix_samples( blip_buf, out );
blip_buf.remove_samples( pair_count );
}

View file

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

View file

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

View file

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

View file

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

View file

@ -46,7 +46,7 @@ public:
public:
// deprecated
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
{ return load_remaining_( &h, sizeof h, in ); }
void disable_playlist( bool = true ); // use clear_playlist()

View file

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

View file

@ -51,7 +51,7 @@ public:
public:
// deprecated
Music_Emu::load;
using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); }
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" )
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" )
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} )
add_definitions( -DINFNAN_CHECK -DMULTIPLE_THREADS )

View file

@ -1,8 +1,8 @@
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" )
endif( CMAKE_COMPILER_IS_GNUC )
endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
add_library( jpeg
jcomapi.c

View file

@ -1,8 +1,8 @@
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" )
endif( CMAKE_COMPILER_IS_GNUC )
endif( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" )
set( LZMA_FILES
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.

View file

@ -39,7 +39,7 @@ between the TEXTMAP and ENDMAP lumps:
BEHAVIOR = contains compiled ACS code
DIALOGUE = contains compiled Strife conversation scripts.
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.
REJECT = reject table. Recommended use is for special effects only.
@ -111,11 +111,18 @@ Note: All <bool> fields default to false unless mentioned otherwise.
midtex3d = <bool>; // Actors can walk on mid texture.
checkswitchrange = <bool>;// Switches can only be activated when vertically reachable.
blockprojectiles = <bool>;// Line blocks all projectiles
blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight
blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight
blockhitscan = <bool>; // Line blocks hitscan attacks
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
{
scalex_top = <float>; // X scale for upper texture, Default = 1.0.
@ -135,16 +142,19 @@ Note: All <bool> fields default to false unless mentioned otherwise.
light = <integer>; // This side's light level. Default is 0.
lightabsolute = <bool>; // true = 'light' is an absolute value. Default is
// relative to the owning sector's light level.
nofakecontrast = <bool>; // Disables use of fake contrast on this sidedef.
smoothlighting = <bool>; // Use smooth fake contrast.
lightfog = <bool>; // true = This side's relative lighting is used even in
// 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.
wrapmidtex = <bool>; // Side's mid textures are wrapped.
nodecals = <bool>; // Disables decals on the sidedef.
nodecals = <bool>; // Disables decals on the sidedef.
}
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.
xpanningceiling = <float>; // X texture offset of ceiling texture, Default = 0.0.
ypanningceiling = <float>; // Y texture offset of ceiling texture, Default = 0.0.
@ -163,9 +173,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.
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
// 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
// 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.
lightcolor = <integer>; // Sector's light color as RRGGBB value, default = 0xffffff.
fadecolor = <integer>; // Sector's fog color as RRGGBB value, default = 0x000000.
@ -173,29 +183,38 @@ Note: All <bool> fields default to false unless mentioned otherwise.
silent = <bool>; // Actors in this sector make no sound,
nofallingdamage = <bool>; // Falling damage is disabled in this sector
dropactors = <bool>; // Actors drop with instantly moving floors (*)
norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
// sound sequence thing in the sector will override this property.
hidden = <bool>; // if true this sector will not be drawn on the textured automap.
norespawn = <bool>; // Players can not respawn in this sector
soundsequence = <string>; // The sound sequence to play when this sector moves. Placing a
// sound sequence thing in the sector will override this property.
hidden = <bool>; // if true this sector will not be drawn on the textured automap.
waterzone = <bool>; // Sector is under water and swimmable
* Note about dropactors
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
set this flag for each tagged sector.
}
thing
{
skill# = <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.
// Parameter is the conversation ID, 0 meaning none.
countsecret = <bool>; // Picking up this actor counts as a secret.
conversation = <int> // Assigns a conversation dialogue to this thing.
// Parameter is the conversation ID, 0 meaning none.
countsecret = <bool>; // Picking up this actor counts as a secret.
arg0str = <string>; // Alternate string-based version of arg0
gravity = <float>; // Set per-actor gravity. Positive values are multiplied with the class's property,
// negative values are used as their absolute. Default = 1.0.
* 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:
@ -204,7 +223,7 @@ Note: All <bool> fields default to false unless mentioned otherwise.
Unless mentioned differently the arg being used to define the line ID
should be set to 0.
The following line specials are affected:
121: Line_SetIdentification, arg 0
208: TranslucentLine, arg0 (arg0 must be preserved)
1: Polyobj_StartLine, arg3
@ -213,16 +232,16 @@ Note: All <bool> fields default to false unless mentioned otherwise.
215: Teleport_Line, arg0
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)
Some specials also allow setting the extended flags. These must also be
converted to explicitly setting the flags through the defined map fields.
This affects the following specials:
121: Line_SetIdentification, arg1
208: TranslucentLine, arg3
These args are to be converted as follows to flags, bit by bit:
Bit 0 (Value 1): zoneboundary
Bit 1 (Value 2): jumpover
Bit 2 (Value 4): blockfloaters
@ -231,13 +250,13 @@ Note: All <bool> fields default to false unless mentioned otherwise.
Bit 5 (Value 32): midtex3d
Bit 6 (Value 64): checkswitchrange
Bit 7 (Value 128): firstsideonly
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
deleted after conversion.
=======================================
Changelog
=======================================
@ -303,6 +322,19 @@ Removed remarks of 8 being the maximum number of player classes/skill levels the
Added renderstyleceiling and renderstylefloor sector properties
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.
1.21 09.08.2013
Added waterzone sector property.
===============================================================================
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
@ -11,7 +11,7 @@ Graf Zahl
Quasar
et al.
Copyright (c) 2010 Braden Obrzut.
Copyright (c) 2013 Braden Obrzut.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.2
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
=======================================
@ -77,7 +85,7 @@ conversation // Starts a dialog.
actor = <integer>; // mobj for this conversation's actor. If previously
// 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
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.
// 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
{
item = <integer>; // Item that is required for this option.
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.
// If no cost is specified this should
// be ignored.

View file

@ -9,15 +9,20 @@ include( CheckFunctionExists )
include( CheckCXXCompilerFlag )
include( FindPkgConfig )
option( NO_ASM "Disable assembly code" )
if( CMAKE_COMPILER_IS_GNUCXX )
if( NOT APPLE )
option( NO_ASM "Disable assembly code" OFF )
else( NOT APPLE )
# At the moment asm code doesn't work with OS X, so disable by default
option( NO_ASM "Disable assembly code" ON )
endif( NOT APPLE )
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
option( NO_STRIP "Do not strip Release or MinSizeRel builds" )
# At least some versions of Xcode fail if you strip with the linker
# instead of the separate strip utility.
if( APPLE )
set( NO_STRIP ON )
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" )
@ -44,7 +49,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"
"15" "14" "13" "12" "11" "10" "09" "08" "07" "06" "05" "04" "03"
"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" )
foreach( majver ${MAJOR_VERSIONS} )
foreach( minver ${MINOR_VERSIONS} )
@ -71,8 +76,11 @@ if( WIN32 )
set( FMOD_SEARCH_PATHS
"C:/Program Files/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api"
"C:/Program Files (x86)/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api"
# This next one is for me.
"E:/Software/Dev/FMOD/${WIN_TYPE}/api" )
# This next one is for Randy.
"E:/Software/Dev/FMOD/${WIN_TYPE}/api"
# .. and this one for Graf Zahl
"D:/portable/FMOD SoundSystem 4.26/FMOD Programmers API WIN32/api"
)
set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc )
set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib )
set( NASM_NAMES nasmw nasm )
@ -401,7 +409,7 @@ endif( NO_OPENAL )
# Search for FluidSynth
include( ../FindFluidSynth.cmake )
find_package( FluidSynth )
# Search for NASM
@ -503,7 +511,7 @@ endif( NOT NO_ASM )
set( SSE_MATTERS NO )
# 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( -arch:SSE2 CAN_DO_ARCHSSE2 )
if( CAN_DO_MFPMATH )
@ -515,7 +523,7 @@ if( CMAKE_SIZEOF_VOID_P MATCHES "4" )
set( SSE2_ENABLE -arch:SSE2 )
set( SSE_MATTERS YES )
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( WIN32 )
@ -535,7 +543,7 @@ endif( SSE_MATTERS )
# 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 )
set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" )
set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" )
@ -547,24 +555,24 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( NOT PROFILE )
set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" )
endif( NOT PROFILE )
set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${REL_CXX_FLAGS}" )
set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_CXX_FLAGS}" )
set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_CXX_FLAGS}" )
set( CMAKE_CXX_FLAGS_RELEASE "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}" )
set( CMAKE_CXX_FLAGS_MINSIZEREL "${REL_CXX_FLAGS} ${CMAKE_CXX_FLAGS_MINSIZEREL}" )
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.
# 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.
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 )
if( NOT NO_STRIP )
set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s" )
set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -s" )
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.
@ -588,13 +596,6 @@ if( NOT STRNICMP_EXISTS )
add_definitions( -Dstrnicmp=strncasecmp )
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 )
add_definitions( -D__forceinline=inline )
endif( NOT MSVC )
@ -634,10 +635,12 @@ if( BACKPATCH )
add_definitions( -DBACKPATCH )
endif( BACKPATCH )
# Update svnrevision.h
# Update gitinfo.h
get_target_property( UPDATEREVISION_EXE updaterevision LOCATION )
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}
DEPENDS updaterevision )
@ -678,15 +681,15 @@ if( WIN32 )
win32/i_system.cpp
win32/st_start.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. :(
add_custom_command( OUTPUT zdoom-rc.o
COMMAND windres -o zdoom-rc.o -i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zdoom.rc
DEPENDS win32/zdoom.rc )
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 )
endif( CMAKE_COMPILER_IS_GNUCXX )
endif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
else( WIN32 )
set( SYSTEM_SOURCES_DIR sdl )
set( SYSTEM_SOURCES
@ -701,7 +704,7 @@ else( WIN32 )
sdl/sdlvideo.cpp
sdl/st_start.cpp )
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( WIN32 )
@ -717,21 +720,19 @@ else( NO_ASM )
ADD_ASM_FILE( asm_ia32 tmap2 )
ADD_ASM_FILE( asm_ia32 tmap3 )
endif( X64 )
if( WIN32 )
if( NOT X64 )
ADD_ASM_FILE( win32 wrappers )
endif( NOT X64 )
endif( WIN32 )
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
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}
DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y )
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 )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
@ -754,7 +755,37 @@ elseif( FLUIDSYNTH_FOUND )
add_definitions( -DHAVE_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
${HEADER_FILES}
autostart.cpp
${ASM_SOURCES}
${SYSTEM_SOURCES}
@ -803,6 +834,7 @@ add_executable( zdoom WIN32
g_skill.cpp
gameconfigfile.cpp
gi.cpp
gitinfo.cpp
hu_scores.cpp
i_net.cpp
info.cpp
@ -892,6 +924,7 @@ add_executable( zdoom WIN32
tables.cpp
teaminfo.cpp
tempfiles.cpp
v_blend.cpp
v_collection.cpp
v_draw.cpp
v_font.cpp
@ -967,6 +1000,8 @@ add_executable( zdoom WIN32
oplsynth/music_opldumper_mididevice.cpp
oplsynth/music_opl_mididevice.cpp
oplsynth/opl_mus_player.cpp
oplsynth/dosbox/opl.cpp
oplsynth/OPL3.cpp
resourcefiles/ancientzip.cpp
resourcefiles/file_7z.cpp
resourcefiles/file_grp.cpp
@ -1025,6 +1060,7 @@ add_executable( zdoom WIN32
thingdef/thingdef_data.cpp
thingdef/thingdef_exp.cpp
thingdef/thingdef_expression.cpp
thingdef/thingdef_function.cpp
thingdef/thingdef_parse.cpp
thingdef/thingdef_properties.cpp
thingdef/thingdef_states.cpp
@ -1098,7 +1134,7 @@ if( NOT WIN32 )
COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/link-make
COMMAND /bin/sh -c ${CMAKE_CURRENT_BINARY_DIR}/link-make )
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
set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" )
@ -1106,4 +1142,8 @@ if( CMAKE_COMPILER_IS_GNUCXX )
if( SSE_MATTERS )
set_source_files_properties( x86.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mmmx" )
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_LowerByValueTimes8, 36, 3, 3, 3)
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(Ceiling_LowerByValue, 40, 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(Ceiling_MoveToValueTimes8, 69, 4, 4, 4)
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(DamageThing, 73, 1, 2, 2)
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_Terminate, 82, 2, 2, 2)
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(Polyobj_MoveToSpot, 86, 3, 3, 3)
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(Ceiling_LowerAndCrushDist, 97, 3, 5, 5)
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_Right, 101, -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)
// [RH] Begin new specials for ZDoom
DEFINE_SPECIAL(Ceiling_CrushAndRaiseDist, 168, 3, 5, 5)
DEFINE_SPECIAL(Generic_Crusher2, 169, 5, 5, 5)
DEFINE_SPECIAL(Sector_SetCeilingScale2, 170, 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(ACS_ExecuteAlways, 226, 1, 5, 5)
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(Plat_UpByValueStayTx, 230, 3, 3, 3)
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_RaiseByTexture, 240, 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_Secret, 244, 1, 1, 1)
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(Door_CloseWaitOpen, 249, 3, 4, 4)
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_LowerToLowest, 253, 2, 2, 2)
DEFINE_SPECIAL(Ceiling_LowerToFloor, 254, 2, 2, 2)

View file

@ -30,9 +30,7 @@
#include "dthinker.h"
// States are tied to finite states are
// tied to animation frames.
// Needs precompiled tables/data structures.
// States are tied to finite states are tied to animation frames.
#include "info.h"
#include "doomdef.h"
@ -40,6 +38,7 @@
#include "r_data/renderstyle.h"
#include "s_sound.h"
#include "memarena.h"
#include "g_level.h"
struct subsector_t;
//
@ -266,10 +265,10 @@ enum
// --- mobj.flags5 ---
MF5_FASTER = 0x00000001, // moves faster when DF_FAST_MONSTERS or nightmare is on.
MF5_FASTMELEE = 0x00000002, // has a faster melee attack when DF_FAST_MONSTERS or nightmare is on.
MF5_DONTDRAIN = 0x00000001, // cannot be drained health from.
/* = 0x00000002, */
MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances.
/* = 0x00000008, */
MF5_NOFORWARDFALL = 0x00000008, // Does not make any actor fall forward by being damaged by this
MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret
MF5_AVOIDINGDROPOFF = 0x00000020, // Used to move monsters away from dropoffs
MF5_NODAMAGE = 0x00000040, // Actor can be shot and reacts to being shot but takes no damage
@ -330,6 +329,9 @@ enum
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_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.
MF6_NOTONAUTOMAP = 0x40000000, // will not be shown on automap with the 'scanner' powerup.
// --- mobj.renderflags ---
@ -400,7 +402,7 @@ enum EBounceFlags
BOUNCE_Ceilings = 1<<2, // bounces off of ceilings
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_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_UseSeeSound = 1<<7, // compatibility fallback. This will only be set by
@ -412,6 +414,8 @@ enum EBounceFlags
// 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.
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,
@ -581,15 +585,14 @@ public:
bool AdjustReflectionAngle (AActor *thing, angle_t &angle);
// 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 ();
virtual void PostBeginPlay ();
// LevelSpawned: Called after BeginPlay if this actor was spawned by the world
virtual void LevelSpawned ();
// Translates SpawnFlags into in-game flags.
virtual void HandleSpawnFlags ();
virtual void BeginPlay(); // Called immediately after the actor is created
virtual void PostBeginPlay(); // Called immediately before the actor's first tick
virtual void LevelSpawned(); // Called after BeginPlay if this actor was spawned by the world
virtual void HandleSpawnFlags(); // Translates SpawnFlags into in-game flags.
virtual void MarkPrecacheSounds() const; // Marks sounds used by this actor for precaching.
virtual void Activate (AActor *activator);
virtual void Deactivate (AActor *activator);
@ -597,11 +600,11 @@ public:
virtual void Tick ();
// 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.
// 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.
virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype);
@ -656,6 +659,9 @@ public:
// Tosses an item out of the inventory.
virtual AInventory *DropInventory (AInventory *item);
// Removes all items from the inventory.
void ClearInventory();
// Returns true if this view is considered "local" for the player.
bool CheckLocalView (int playernum) const;
@ -696,7 +702,7 @@ public:
virtual bool Massacre ();
// Transforms the actor into a finely-ground paste
bool Grind(bool items);
virtual bool Grind(bool items);
// Is the other actor on my team?
bool IsTeammate (AActor *other);
@ -707,9 +713,20 @@ public:
// Do I hate the other actor?
bool IsHostile (AActor *other);
inline bool IsNoClip2() const;
// What species am I?
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
void Crash();
@ -763,6 +780,10 @@ public:
return bloodcls;
}
inline void SetFriendPlayer(player_t *player);
bool IsVisibleToPlayer() const;
// Calculate amount of missile damage
virtual int GetMissileDamage(int mask, int add);
@ -773,6 +794,8 @@ public:
const char *GetTag(const char *def = NULL) const;
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
// NOTE: The first member variable *must* be x.
@ -814,6 +837,10 @@ public:
DWORD flags4; // [RH] Even more flags!
DWORD flags5; // OMG! We need another one.
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 special2; // Special info
int health;
@ -845,6 +872,8 @@ public:
int special; // special
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
TObjPtr<AActor> goal; // Monster's goal if not chasing anything
int waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes
@ -950,6 +979,7 @@ private:
static FSharedStringArena mStringPropertyData;
friend class FActorIterator;
friend bool P_IsTIDUsed(int tid);
sector_t *LinkToWorldForMapThing ();
@ -978,6 +1008,11 @@ public:
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;
};
@ -1044,6 +1079,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)
{
return AActor::StaticSpawn (type, x, y, z, allowreplacement);

View file

@ -40,6 +40,8 @@
#include "c_bind.h"
#include "farchive.h"
#include "r_renderer.h"
#include "r_sky.h"
#include "sbar.h"
#include "m_cheat.h"
#include "i_system.h"
@ -184,6 +186,9 @@ CVAR (Color, am_ovwallcolor, 0x00ff00, CVAR_ARCHIVE);
CVAR (Color, am_ovspecialwallcolor, 0xffffff, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovotherwallscolor, 0x008844, CVAR_ARCHIVE);
CVAR (Color, am_ovefwallcolor, 0x008844, CVAR_ARCHIVE);
CVAR (Color, am_ovfdwallcolor, 0x008844, CVAR_ARCHIVE);
CVAR (Color, am_ovcdwallcolor, 0x008844, CVAR_ARCHIVE);
CVAR (Color, am_ovunseencolor, 0x00226e, CVAR_ARCHIVE);
CVAR (Color, am_ovtelecolor, 0xffff00, CVAR_ARCHIVE);
CVAR (Color, am_intralevelcolor, 0x0000ff, CVAR_ARCHIVE);
@ -202,6 +207,7 @@ CVAR (Color, am_ovthingcolor_friend, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_monster, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_item, 0xe88800, CVAR_ARCHIVE);
CVAR (Color, am_ovthingcolor_citem, 0xe88800, CVAR_ARCHIVE);
CVAR (Int, am_showthingsprites, 0, CVAR_ARCHIVE);
static int bigstate = 0;
@ -308,6 +314,7 @@ struct islope_t
static TArray<mline_t> MapArrow;
static TArray<mline_t> CheatMapArrow;
static TArray<mline_t> CheatKey;
static TArray<mline_t> EasyKey;
#define R (MAPUNIT)
// [RH] Avoid lots of warnings without compiler-specific #pragmas
@ -409,6 +416,9 @@ static bool stopped = true;
static void AM_calcMinMaxMtoF();
static void DrawMarker (FTexture *tex, fixed_t x, fixed_t y, int yadjust,
INTBOOL flip, fixed_t xscale, fixed_t yscale, int translation, fixed_t alpha, DWORD fillcolor, FRenderStyle renderstyle);
void AM_rotatePoint (fixed_t *x, fixed_t *y);
void AM_rotate (fixed_t *x, fixed_t *y, angle_t an);
void AM_doFollowPlayer ();
@ -536,10 +546,12 @@ void AM_StaticInit()
MapArrow.Clear();
CheatMapArrow.Clear();
CheatKey.Clear();
EasyKey.Clear();
if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow);
if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow);
AM_ParseArrow(CheatKey, "maparrows/key.txt");
AM_ParseArrow(EasyKey, "maparrows/ravenkey.txt");
if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined");
char namebuf[9];
@ -912,7 +924,9 @@ static void AM_initColors (bool overlayed)
ThingColor_Monster.FromCVar (am_ovthingcolor_monster);
ThingColor.FromCVar (am_ovthingcolor);
LockedColor.FromCVar (am_ovotherwallscolor);
EFWallColor = FDWallColor = CDWallColor = LockedColor;
EFWallColor.FromCVar (am_ovefwallcolor);
FDWallColor.FromCVar (am_ovfdwallcolor);
CDWallColor.FromCVar (am_ovcdwallcolor);
TSWallColor.FromCVar (am_ovunseencolor);
NotSeenColor = TSWallColor;
InterTeleportColor.FromCVar (am_ovtelecolor);
@ -1077,7 +1091,7 @@ void AM_Stop ()
{
automapactive = false;
stopped = true;
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
viewactive = true;
}
@ -1155,7 +1169,10 @@ void AM_NewResolution()
CCMD (togglemap)
{
gameaction = ga_togglemap;
if (gameaction == ga_nothing)
{
gameaction = ga_togglemap;
}
}
//=============================================================================
@ -1173,7 +1190,7 @@ void AM_ToggleMap ()
if (dmflags2 & DF2_NO_AUTOMAP)
return;
SB_state = screen->GetPageCount ();
ST_SetNeedRefresh();
if (!automapactive)
{
AM_Start ();
@ -1184,7 +1201,7 @@ void AM_ToggleMap ()
if (am_overlay==1 && viewactive)
{
viewactive = false;
SB_state = screen->GetPageCount ();
ST_SetNeedRefresh();
}
else
{
@ -1605,9 +1622,10 @@ void AM_drawSubsectors()
angle_t rotation;
sector_t tempsec;
int floorlight, ceilinglight;
fixed_t scalex, scaley;
double originx, originy;
FDynamicColormap *colormap;
mpoint_t originpt;
for (int i = 0; i < numsubsectors; ++i)
{
@ -1636,27 +1654,17 @@ void AM_drawSubsectors()
// For lighting and texture determination
sector_t *sec = Renderer->FakeFlat (subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false);
// Find texture origin.
mpoint_t originpt = { -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS,
sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS };
originpt.x = -sec->GetXOffset(sector_t::floor) >> FRACTOMAPBITS;
originpt.y = sec->GetYOffset(sector_t::floor) >> FRACTOMAPBITS;
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
colormap = sec->ColorMap;
FTextureID maptex = sec->GetTexture(sector_t::floor);
scalex = sec->GetXScale(sector_t::floor);
scaley = sec->GetYScale(sector_t::floor);
#ifdef _3DFLOORS
if (sec->e->XFloor.ffloors.Size())
@ -1670,6 +1678,7 @@ void AM_drawSubsectors()
// (Make the comparison in floating point to avoid overflows and improve performance.)
double secx;
double secy;
double seczb, seczt;
double cmpz = FIXED2DBL(viewz);
if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector)
@ -1683,17 +1692,33 @@ void AM_drawSubsectors()
secx = FIXED2DBL(sec->soundorg[0]);
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)
{
F3DFloor *rover = sec->e->XFloor.ffloors[i];
if (!(rover->flags & FF_EXISTS)) continue;
if (rover->flags & FF_FOG) continue;
if (!(rover->flags & FF_RENDERPLANES)) 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);
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;
}
}
@ -1703,6 +1728,24 @@ void AM_drawSubsectors()
colormap = light->extra_colormap;
}
#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
// to see it on the map), tint and desaturate it.
@ -1719,15 +1762,19 @@ void AM_drawSubsectors()
}
// Draw the polygon.
screen->FillSimplePoly(TexMan(maptex),
&points[0], points.Size(),
originx, originy,
scale / (FIXED2FLOAT(sec->GetXScale(sector_t::floor)) * float(1 << MAPBITS)),
scale / (FIXED2FLOAT(sec->GetYScale(sector_t::floor)) * float(1 << MAPBITS)),
rotation,
colormap,
floorlight
);
FTexture *pic = TexMan(maptex);
if (pic != NULL && pic->UseType != FTexture::TEX_Null)
{
screen->FillSimplePoly(TexMan(maptex),
&points[0], points.Size(),
originx, originy,
scale / (FIXED2DBL(scalex) * float(1 << MAPBITS)),
scale / (FIXED2DBL(scaley) * float(1 << MAPBITS)),
rotation,
colormap,
floorlight
);
}
}
}
@ -1907,6 +1954,7 @@ void AM_drawWalls (bool allmap)
{
int i;
static mline_t l;
int lock, color;
for (i = 0; i < numlines; i++)
{
@ -1942,8 +1990,16 @@ void AM_drawWalls (bool allmap)
AM_drawMline(&l, SecretWallColor);
else
AM_drawMline(&l, WallColor);
}
else if ((lines[i].special == Teleport ||
} else if (lines[i].locknumber > 0) { // [Dusk] specials w/ locknumbers
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_ZombieChanger ||
lines[i].special == Teleport_Line) &&
@ -1969,13 +2025,12 @@ void AM_drawWalls (bool allmap)
if (am_colorset == 0 || am_colorset == 3) // Raven games show door colors
{
int P_GetMapColorForLock(int lock);
int lock;
if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated)
lock=lines[i].args[3];
else lock=lines[i].args[4];
int color = P_GetMapColorForLock(lock);
color = P_GetMapColorForLock(lock);
AMColor c;
@ -2147,6 +2202,12 @@ AM_drawLineCharacter
void AM_drawPlayers ()
{
if (am_cheat >= 2 && am_showthingsprites > 0)
{
// Player sprites are drawn with the others
return;
}
mpoint_t pt;
angle_t angle;
int i;
@ -2240,6 +2301,48 @@ 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 ()
{
AMColor color;
@ -2253,70 +2356,116 @@ void AM_drawThings ()
t = sectors[i].thinglist;
while (t)
{
p.x = t->x >> FRACTOMAPBITS;
p.y = t->y >> FRACTOMAPBITS;
angle = t->angle;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
if (am_cheat > 0 || !(t->flags6 & MF6_NOTONAUTOMAP))
{
AM_rotatePoint (&p.x, &p.y);
angle += ANG90 - players[consoleplayer].camera->angle;
}
p.x = t->x >> FRACTOMAPBITS;
p.y = t->y >> FRACTOMAPBITS;
color = ThingColor;
// use separate colors for special thing types
if (t->flags3&MF3_ISMONSTER && !(t->flags&MF_CORPSE))
{
if (t->flags & MF_FRIENDLY || !(t->flags & MF_COUNTKILL)) color = ThingColor_Friend;
else color = ThingColor_Monster;
}
else if (t->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.
if (t->IsKindOf(RUNTIME_CLASS(AKey)))
if (am_showthingsprites > 0 && t->sprite > 0)
{
if (am_showkeys)
{
int P_GetMapColorForKey (AInventory * key);
int c = P_GetMapColorForKey(static_cast<AKey *>(t));
FTexture *texture = NULL;
spriteframe_t *frame;
angle_t rotation = 0;
if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
else color = ThingColor_CountItem;
AM_drawLineCharacter(&CheatKey[0], CheatKey.Size(), 0, 0, color, p.x, p.y);
color.Index = -1;
}
else
// try all modes backwards until a valid texture has been found.
for(int show = am_showthingsprites; show > 0 && texture == NULL; show--)
{
color = ThingColor_Item;
const spritedef_t& sprite = sprites[t->sprite];
const size_t spriteIndex = sprite.spriteframes + (show > 1 ? t->frame : 0);
frame = &SpriteFrames[spriteIndex];
angle_t angle = ANGLE_270 - t->angle;
if (frame->Texture[0] != frame->Texture[1]) angle += (ANGLE_180 / 16);
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
angle += players[consoleplayer].camera->angle - ANGLE_90;
}
rotation = angle >> 28;
const FTextureID textureID = frame->Texture[show > 2 ? rotation : 0];
texture = TexMan(textureID);
}
if (texture == NULL) goto drawTriangle; // fall back to standard display if no sprite can be found.
const fixed_t spriteXScale = FixedMul(t->scaleX, 10 * scale_mtof);
const fixed_t spriteYScale = FixedMul(t->scaleY, 10 * scale_mtof);
DrawMarker (texture, p.x, p.y, 0, !!(frame->Flip & (1 << rotation)),
spriteXScale, spriteYScale, t->Translation, FRACUNIT, 0, LegacyRenderStyles[STYLE_Normal]);
}
else
{
drawTriangle:
angle = t->angle;
if (am_rotate == 1 || (am_rotate == 2 && viewactive))
{
AM_rotatePoint (&p.x, &p.y);
angle += ANG90 - players[consoleplayer].camera->angle;
}
color = ThingColor;
// use separate colors for special thing types
if (t->flags3&MF3_ISMONSTER && !(t->flags&MF_CORPSE))
{
if (t->flags & MF_FRIENDLY || !(t->flags & MF_COUNTKILL)) color = ThingColor_Friend;
else color = ThingColor_Monster;
}
else if (t->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.
if (t->IsKindOf(RUNTIME_CLASS(AKey)))
{
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 c = P_GetMapColorForKey(static_cast<AKey *>(t));
if (c >= 0) color.FromRGB(RPART(c), GPART(c), BPART(c));
else color = ThingColor_CountItem;
AM_drawLineCharacter(&CheatKey[0], CheatKey.Size(), 0, 0, color, p.x, p.y);
color.Index = -1;
}
else
{
color = ThingColor_Item;
}
}
else if (t->flags&MF_COUNTITEM)
color = ThingColor_CountItem;
else
color = ThingColor_Item;
}
if (color.Index != -1)
{
AM_drawLineCharacter
(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
16<<MAPBITS, angle, color, p.x, p.y);
}
if (am_cheat >= 3)
{
static const mline_t box[4] =
{
{ { -MAPUNIT, -MAPUNIT }, { MAPUNIT, -MAPUNIT } },
{ { MAPUNIT, -MAPUNIT }, { MAPUNIT, MAPUNIT } },
{ { MAPUNIT, MAPUNIT }, { -MAPUNIT, MAPUNIT } },
{ { -MAPUNIT, MAPUNIT }, { -MAPUNIT, -MAPUNIT } },
};
AM_drawLineCharacter (box, 4, t->radius >> FRACTOMAPBITS, angle - t->angle, color, p.x, p.y);
}
}
else if (t->flags&MF_COUNTITEM)
color = ThingColor_CountItem;
else
color = ThingColor_Item;
}
if (color.Index != -1)
{
AM_drawLineCharacter
(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
16<<MAPBITS, angle, color, p.x, p.y);
}
if (am_cheat >= 3)
{
static const mline_t box[4] =
{
{ { -MAPUNIT, -MAPUNIT }, { MAPUNIT, -MAPUNIT } },
{ { MAPUNIT, -MAPUNIT }, { MAPUNIT, MAPUNIT } },
{ { MAPUNIT, MAPUNIT }, { -MAPUNIT, MAPUNIT } },
{ { -MAPUNIT, MAPUNIT }, { -MAPUNIT, -MAPUNIT } },
};
AM_drawLineCharacter (box, 4, t->radius >> FRACTOMAPBITS, angle - t->angle, color, p.x, p.y);
}
t = t->snext;
}
@ -2500,6 +2649,8 @@ void AM_Drawer ()
AM_drawWalls(allmap);
AM_drawPlayers();
if (G_SkillProperty(SKILLP_EasyKey))
AM_drawKeys();
if (am_cheat >= 2 || allthings)
AM_drawThings();

View file

@ -51,15 +51,16 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
players[i].mo = NULL;
}
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;
if (bot)
{
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();
::new(&players[i]) player_t;
players[i].userinfo.Reset();
playeringame[i] = false;
}

View file

@ -140,7 +140,7 @@ void FCajunMaster::Main (int buf)
//Check if player should go observer. Or un observe
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;
players[consoleplayer].mo->UnlinkFromWorld ();
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
{
Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.netname);
Printf ("%s returned to the fray\n", players[consoleplayer].userinfo.GetName());
observer = false;
players[consoleplayer].mo->UnlinkFromWorld ();
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)
{
getspawned.Push(players[i].userinfo.netname);
getspawned.Push(players[i].userinfo.GetName());
}
CleanBotstuff (&players[i]);
}
@ -353,7 +353,7 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
if (!deathmatch && playerstarts[bnum].type == 0)
{
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
if (botnum > 0)
{
@ -370,9 +370,9 @@ void FCajunMaster::DoAddBot (int bnum, char *info)
botingame[bnum] = true;
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
Printf ("%s joined the game\n", players[bnum].userinfo.netname);
Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
G_DoReborn (bnum, true);
if (StatusBar != NULL)
@ -474,6 +474,7 @@ static void appendinfo (char *&front, const char *back)
{
size_t newlen = strlen (back) + 2;
newstr = new char[newlen];
newstr[0] = 0;
}
strcat (newstr, "\\");
strcat (newstr, back);

View file

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

View file

@ -829,6 +829,7 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
bool dclick;
int dclickspot;
BYTE dclickmask;
unsigned int nowtime;
if (ev->type != EV_KeyDown && ev->type != EV_KeyUp)
return false;
@ -841,10 +842,11 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
dclick = false;
// 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
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1);
binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] |= dclickmask;
dclick = true;
}
@ -853,11 +855,11 @@ bool C_DoKey (event_t *ev, FKeyBindings *binds, FKeyBindings *doublebinds)
if (ev->type == EV_KeyDown)
{ // Key pressed for a normal press
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
if (doublebinds != NULL) binding = doublebinds->GetBinding(ev->data1);
binding = doublebinds->GetBinding(ev->data1);
DClicked[dclickspot] &= ~dclickmask;
DClickTime[ev->data1] = 0;
dclick = true;

View file

@ -51,6 +51,7 @@
#include "i_system.h"
#include "doomerrors.h"
#include "doomstat.h"
#include "gstrings.h"
#include "s_sound.h"
@ -175,6 +176,15 @@ CCMD (noclip)
Net_WriteByte (CHT_NOCLIP);
}
CCMD (noclip2)
{
if (CheckCheatmode())
return;
Net_WriteByte (DEM_GENERICCHEAT);
Net_WriteByte (CHT_NOCLIP2);
}
CCMD (powerup)
{
if (CheckCheatmode ())
@ -245,7 +255,7 @@ CCMD (chase)
else
{
// 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;
Net_WriteByte (DEM_GENERICCHEAT);
@ -320,7 +330,7 @@ CCMD (hxvisit)
CCMD (changemap)
{
if (who == NULL)
if (who == NULL || !usergame)
{
Printf ("Use the map command when not in a game.\n");
return;
@ -334,22 +344,30 @@ CCMD (changemap)
if (argv.argc() > 1)
{
if (!P_CheckMapData(argv[1]))
try
{
Printf ("No map %s\n", argv[1]);
}
else
{
if (argv.argc() > 2)
if (!P_CheckMapData(argv[1]))
{
Net_WriteByte (DEM_CHANGEMAP2);
Net_WriteByte (atoi(argv[2]));
Printf ("No map %s\n", argv[1]);
}
else
{
Net_WriteByte (DEM_CHANGEMAP);
if (argv.argc() > 2)
{
Net_WriteByte (DEM_CHANGEMAP2);
Net_WriteByte (atoi(argv[2]));
}
else
{
Net_WriteByte (DEM_CHANGEMAP);
}
Net_WriteString (argv[1]);
}
Net_WriteString (argv[1]);
}
catch(CRecoverableError &error)
{
if (error.GetMessage())
Printf("%s", error.GetMessage());
}
}
else
@ -386,7 +404,7 @@ CCMD (take)
CCMD (gameversion)
{
Printf ("%s : " __DATE__ "\n", DOTVERSIONSTR);
Printf ("%s @ %s\nCommit %s", GetVersionString(), GetGitTime(), GetGitHash());
}
CCMD (print)
@ -451,9 +469,9 @@ CCMD (puke)
{
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
{
@ -463,8 +481,8 @@ CCMD (puke)
{ // Script 0 is reserved for Strife support. It is not pukable.
return;
}
int arg[3] = { 0, 0, 0 };
int argn = MIN (argc - 2, 3), i;
int arg[4] = { 0, 0, 0, 0 };
int argn = MIN<int>(argc - 2, countof(arg)), i;
for (i = 0; i < argn; ++i)
{
@ -489,6 +507,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)
{
int argc = argv.argc();
@ -911,9 +967,16 @@ CCMD(thaw)
//-----------------------------------------------------------------------------
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))
{
@ -932,9 +995,16 @@ CCMD(nextmap)
//-----------------------------------------------------------------------------
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))
{
@ -1050,7 +1120,8 @@ CCMD(secret)
{
FString levelname;
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;
for(size_t ii=0; ii<llen; ii++) levelname += '-';
Printf(TEXTCOLOR_YELLOW"%s\n", levelname.GetChars());
@ -1076,4 +1147,4 @@ CCMD(secret)
else inlevel = false;
}
}
}
}

View file

@ -768,7 +768,7 @@ void AddToConsole (int printlevel, const char *text)
// The line start is outside the buffer.
// Make space for the newly inserted stuff.
size_t movesize = work-linestart;
memmove(work + movesize, work, strlen(work));
memmove(work + movesize, work, strlen(work)+1);
work_p += movesize;
linestart = work;
}
@ -1134,7 +1134,7 @@ void C_DrawConsole (bool hw2d)
(viewwindowx || viewwindowy) &&
viewactive)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
}
oldbottom = ConBottom;
@ -1167,9 +1167,9 @@ void C_DrawConsole (bool hw2d)
if (ConBottom >= 12)
{
screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 -
ConFont->StringWidth ("v" DOTVERSIONSTR),
ConFont->StringWidth (GetVersionString()),
ConBottom - ConFont->GetHeight() - 4,
"v" DOTVERSIONSTR, TAG_DONE);
GetVersionString(), TAG_DONE);
if (TickerMax)
{
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)),
player->BlendA, 0, ConBottom, screen->GetWidth(), screen->GetHeight() - ConBottom);
SB_state = screen->GetPageCount ();
BorderNeedRefresh = screen->GetPageCount ();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
}
}
@ -2121,7 +2121,20 @@ static bool C_TabCompleteList ()
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLine+2);
for (i = TabPos; nummatches > 0; ++i, --nummatches)
{
Printf ("%-*s", int(maxwidth), TabCommands[i].TabName.GetChars());
// [Dusk] Print console commands blue, CVars green, aliases red.
const char* colorcode = "";
FConsoleCommand* ccmd;
if (FindCVar (TabCommands[i].TabName, NULL))
colorcode = TEXTCOLOR_GREEN;
else if ((ccmd = FConsoleCommand::FindByName (TabCommands[i].TabName)) != NULL)
{
if (ccmd->IsAlias())
colorcode = TEXTCOLOR_RED;
else
colorcode = TEXTCOLOR_LIGHTBLUE;
}
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
x += maxwidth;
if (x > ConCols - maxwidth)
{

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);
if (Flags & CVAR_USERINFO)
if ((Flags & CVAR_USERINFO) && !nouserinfosend && !(Flags & CVAR_IGNORE))
D_UserInfoChanged (this);
if (m_UseCallback)
Callback ();
@ -266,7 +266,7 @@ static GUID cGUID;
static char truestr[] = "true";
static char falsestr[] = "false";
char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
const char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
{
switch (type)
{
@ -849,9 +849,7 @@ UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const
void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
{
if (DefaultValue)
delete[] DefaultValue;
DefaultValue = ToString (value, type);
ReplaceString(&DefaultValue, ToString(value, type));
if (Flags & CVAR_ISDEFAULT)
{
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)
{
FBaseCVar *cvar = CVars;
while (cvar)
// Accumulate all cvars that match the filter flags.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if (cvar->Flags & filter)
cvars.Push (cvar);
cvar = cvar->m_Next;
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_IGNORE))
cvars.Push(cvar);
}
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)
{
FBaseCVar *cvar = CVars;
BYTE *ptr = *demo_p;
FString dump = C_GetMassCVarString(filter, compact);
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)
{
TArray<FBaseCVar *> cvars;
ptr += sprintf ((char *)ptr, "\\\\%ux", filter);
FilterCompactCVars (cvars, filter);
dump.AppendFormat("\\\\%ux", filter);
FilterCompactCVars(cvars, filter);
while (cvars.Pop (cvar))
{
UCVarValue val = cvar->GetGenericRep (CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s", val.String);
UCVarValue val = cvar->GetGenericRep(CVAR_String);
dump << '\\' << val.String;
}
}
else
{
cvar = CVars;
while (cvar)
for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_NOSAVE))
if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
{
UCVarValue val = cvar->GetGenericRep (CVAR_String);
ptr += sprintf ((char *)ptr, "\\%s\\%s",
cvar->GetName (), val.String);
UCVarValue val = cvar->GetGenericRep(CVAR_String);
dump << '\\' << cvar->GetName() << '\\' << val.String;
}
cvar = cvar->m_Next;
}
}
*demo_p = ptr + 1;
return dump;
}
void C_ReadCVars (BYTE **demo_p)
@ -1390,58 +1392,42 @@ void C_ReadCVars (BYTE **demo_p)
*demo_p += strlen (*((char **)demo_p)) + 1;
}
static struct backup_s
struct FCVarBackup
{
char *name, *string;
} CVarBackups[MAX_DEMOCVARS];
static int numbackedup = 0;
FString Name, String;
};
static TArray<FCVarBackup> CVarBackups;
void C_BackupCVars (void)
{
struct backup_s *backup = CVarBackups;
FBaseCVar *cvar = CVars;
assert(CVarBackups.Size() == 0);
CVarBackups.Clear();
while (cvar)
FCVarBackup backup;
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE))
&& !(cvar->Flags & CVAR_LATCH))
if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) && !(cvar->Flags & CVAR_LATCH))
{
if (backup == &CVarBackups[MAX_DEMOCVARS])
I_Error ("C_BackupDemoCVars: Too many cvars to save (%d)", MAX_DEMOCVARS);
backup->name = copystring (cvar->GetName());
backup->string = copystring (cvar->GetGenericRep (CVAR_String).String);
backup++;
backup.Name = cvar->GetName();
backup.String = cvar->GetGenericRep(CVAR_String).String;
CVarBackups.Push(backup);
}
cvar = cvar->m_Next;
}
numbackedup = int(backup - CVarBackups);
}
void C_RestoreCVars (void)
{
struct backup_s *backup = CVarBackups;
int i;
for (i = numbackedup; i; i--, backup++)
for (unsigned int i = 0; i < CVarBackups.Size(); ++i)
{
cvar_set (backup->name, backup->string);
cvar_set(CVarBackups[i].Name, CVarBackups[i].String);
}
C_ForgetCVars();
}
void C_ForgetCVars (void)
{
struct backup_s *backup = CVarBackups;
int i;
for (i = numbackedup; i; i--, backup++)
{
delete[] backup->name;
delete[] backup->string;
backup->name = backup->string = NULL;
}
numbackedup = 0;
CVarBackups.Clear();
}
FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev)
@ -1489,6 +1475,30 @@ FBaseCVar *FindCVarSub (const char *var_name, int namelen)
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)
{
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;
DWORD filter;
filter = filters[type];
while (cvar)
{
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)
{
UCVarValue val;
@ -1679,14 +1670,16 @@ void FBaseCVar::ListVars (const char *filter, bool plain)
else
{
++count;
Printf ("%c%c%c %s : :%s\n",
Printf ("%c%c%c%c%c %s = %s\n",
flags & CVAR_ARCHIVE ? 'A' : ' ',
flags & CVAR_USERINFO ? 'U' :
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_NOSET ? '-' :
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_MOD ? 'M' : ' ',
flags & CVAR_IGNORE ? 'X' : ' ',
var->GetName(),
var->GetGenericRep (CVAR_String).String);
}

View file

@ -61,6 +61,8 @@ enum
CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section
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_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
@ -68,7 +70,7 @@ union UCVarValue
bool Bool;
int Int;
float Float;
char *String;
const char *String;
const GUID *pGUID;
};
@ -96,9 +98,10 @@ public:
inline const char *GetName () const { return Name; }
inline uint32 GetFlags () const { return Flags; }
inline FBaseCVar *GetNext() const { return m_Next; }
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 ResetToDefault ();
void SetArchiveBit () { Flags |= CVAR_ARCHIVE; }
@ -129,7 +132,7 @@ protected:
static bool ToBool (UCVarValue value, ECVarType type);
static int ToInt (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 UCVarValue FromBool (bool value, ECVarType type);
static UCVarValue FromInt (int value, ECVarType type);
@ -150,18 +153,22 @@ private:
static bool m_UseCallback;
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_BackupCVars (void);
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
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 FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32 filter);
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
// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set.
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 *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()
void UnlatchCVars (void);
// 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
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
// that might possibly have been changed during the course of demo playback.
void C_RestoreCVars (void);
@ -425,5 +431,6 @@ void C_ForgetCVars (void);
#define EXTERN_CVAR(type,name) extern F##type##CVar name;
extern FBaseCVar *CVars;
#endif //__C_CVARS_H__

View file

@ -92,8 +92,8 @@ private:
struct FActionMap
{
unsigned int Key; // value from passing Name to MakeKey()
FButtonStatus *Button;
unsigned int Key; // value from passing Name to MakeKey()
char Name[12];
};
@ -134,38 +134,38 @@ bool ParsingKeyConf;
FActionMap ActionMaps[] =
{
{ 0x0d52d67b, &Button_AM_PanLeft, "am_panleft"},
{ 0x125f5226, &Button_User2, "user2" },
{ 0x1eefa611, &Button_Jump, "jump" },
{ 0x201f1c55, &Button_Right, "right" },
{ 0x20ccc4d5, &Button_Zoom, "zoom" },
{ 0x23a99cd7, &Button_Back, "back" },
{ 0x41df90c2, &Button_AM_ZoomIn, "am_zoomin"},
{ 0x426b69e7, &Button_Reload, "reload" },
{ 0x4463f43a, &Button_LookDown, "lookdown" },
{ 0x51f7a334, &Button_AM_ZoomOut, "am_zoomout"},
{ 0x534c30ee, &Button_User4, "user4" },
{ 0x5622bf42, &Button_Attack, "attack" },
{ 0x577712d0, &Button_User1, "user1" },
{ 0x57c25cb2, &Button_Klook, "klook" },
{ 0x59f3e907, &Button_Forward, "forward" },
{ 0x6167ce99, &Button_MoveDown, "movedown" },
{ 0x676885b8, &Button_AltAttack, "altattack" },
{ 0x6fa41b84, &Button_MoveLeft, "moveleft" },
{ 0x818f08e6, &Button_MoveRight, "moveright" },
{ 0x8197097b, &Button_AM_PanRight, "am_panright"},
{ 0x8d89955e, &Button_AM_PanUp, "am_panup"} ,
{ 0xa2b62d8b, &Button_Mlook, "mlook" },
{ 0xab2c3e71, &Button_Crouch, "crouch" },
{ 0xb000b483, &Button_Left, "left" },
{ 0xb62b1e49, &Button_LookUp, "lookup" },
{ 0xb6f8fe92, &Button_User3, "user3" },
{ 0xb7e6a54b, &Button_Strafe, "strafe" },
{ 0xce301c81, &Button_AM_PanDown, "am_pandown"},
{ 0xd5897c73, &Button_ShowScores, "showscores" },
{ 0xe0ccb317, &Button_Speed, "speed" },
{ 0xe0cfc260, &Button_Use, "use" },
{ 0xfdd701c7, &Button_MoveUp, "moveup" },
{ &Button_AM_PanLeft, 0x0d52d67b, "am_panleft"},
{ &Button_User2, 0x125f5226, "user2" },
{ &Button_Jump, 0x1eefa611, "jump" },
{ &Button_Right, 0x201f1c55, "right" },
{ &Button_Zoom, 0x20ccc4d5, "zoom" },
{ &Button_Back, 0x23a99cd7, "back" },
{ &Button_AM_ZoomIn, 0x41df90c2, "am_zoomin"},
{ &Button_Reload, 0x426b69e7, "reload" },
{ &Button_LookDown, 0x4463f43a, "lookdown" },
{ &Button_AM_ZoomOut, 0x51f7a334, "am_zoomout"},
{ &Button_User4, 0x534c30ee, "user4" },
{ &Button_Attack, 0x5622bf42, "attack" },
{ &Button_User1, 0x577712d0, "user1" },
{ &Button_Klook, 0x57c25cb2, "klook" },
{ &Button_Forward, 0x59f3e907, "forward" },
{ &Button_MoveDown, 0x6167ce99, "movedown" },
{ &Button_AltAttack, 0x676885b8, "altattack" },
{ &Button_MoveLeft, 0x6fa41b84, "moveleft" },
{ &Button_MoveRight, 0x818f08e6, "moveright" },
{ &Button_AM_PanRight, 0x8197097b, "am_panright"},
{ &Button_AM_PanUp, 0x8d89955e, "am_panup"} ,
{ &Button_Mlook, 0xa2b62d8b, "mlook" },
{ &Button_Crouch, 0xab2c3e71, "crouch" },
{ &Button_Left, 0xb000b483, "left" },
{ &Button_LookUp, 0xb62b1e49, "lookup" },
{ &Button_User3, 0xb6f8fe92, "user3" },
{ &Button_Strafe, 0xb7e6a54b, "strafe" },
{ &Button_AM_PanDown, 0xce301c81, "am_pandown"},
{ &Button_ShowScores, 0xd5897c73, "showscores" },
{ &Button_Speed, 0xe0ccb317, "speed" },
{ &Button_Use, 0xe0cfc260, "use" },
{ &Button_MoveUp, 0xfdd701c7, "moveup" },
};
#define NUM_ACTIONS countof(ActionMaps)
@ -627,7 +627,14 @@ void C_DoCommand (const char *cmd, int keynum)
}
else
{
new DStoredCommand (com, beg);
if (len == 4 && strnicmp(beg, "warp", 4) == 0)
{
StoredWarp = beg;
}
else
{
new DStoredCommand (com, beg);
}
}
}
else
@ -955,6 +962,11 @@ bool FConsoleCommand::AddToHash (FConsoleCommand **table)
return true;
}
FConsoleCommand* FConsoleCommand::FindByName (const char* name)
{
return FindNameInHashTable (Commands, name, strlen (name));
}
FConsoleCommand::FConsoleCommand (const char *name, CCmdRun runFunc)
: m_RunFunc (runFunc)
{
@ -1019,32 +1031,7 @@ FConsoleAlias::~FConsoleAlias ()
m_Command[1] = m_Command[0] = FString();
}
FString BuildString (int argc, char **argv)
{
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;
}
}
// Given an argument vector, reconstitute the command line it could have been produced from.
FString BuildString (int argc, FString *argv)
{
if (argc == 1)
@ -1058,8 +1045,23 @@ FString BuildString (int argc, FString *argv)
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] << "\" ";
}
else

View file

@ -61,7 +61,6 @@ void C_SetAlias (const char *name, const char *cmd);
void C_ClearAliases ();
// build a single string out of multiple strings
FString BuildString (int argc, char **argv);
FString BuildString (int argc, FString *argv);
// Class that can parse command lines
@ -94,6 +93,7 @@ public:
void PrintCommand () { Printf ("%s\n", m_Name); }
virtual void Run (FCommandLine &args, APlayerPawn *instigator, int key);
static FConsoleCommand* FindByName (const char* name);
FConsoleCommand *m_Next, **m_Prev;
char *m_Name;

View file

@ -537,6 +537,7 @@ void CreatePath(const char *fn)
}
if (mkdir(copy, 0755) == -1)
{ // failed
free(copy);
return;
}
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.
// 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':
c = 0;
p++;
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;
for (i = 0; i < 2; i++)
{
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;
break;
@ -650,7 +653,7 @@ int strbin (char *str)
// strbin1 -- String-creating version
//
// [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':
c = 0;
p++;
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;
for (i = 0; i < 2; i++)
{
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;
break;
@ -751,7 +756,7 @@ FString strbin1 (const char *start)
//
//==========================================================================
void CleanseString(char *str)
char *CleanseString(char *str)
{
char *escape = strrchr(str, TEXTCOLOR_ESCAPE);
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);
FString strbin1 (const char *start);
void CleanseString (char *str);
char *CleanseString (char *str);
void CreatePath(const char * fn);

View file

@ -50,6 +50,7 @@
#include "g_level.h"
#include "p_lnspec.h"
#include "r_state.h"
#include "w_wad.h"
// MACROS ------------------------------------------------------------------
@ -58,8 +59,15 @@
struct FCompatOption
{
const char *Name;
int CompatFlags;
int BCompatFlags;
DWORD CompatFlags;
int WhichSlot;
};
enum
{
SLOT_COMPAT,
SLOT_COMPAT2,
SLOT_BCOMPAT
};
enum
@ -67,7 +75,12 @@ enum
CP_END,
CP_CLEARFLAGS,
CP_SETFLAGS,
CP_SETSPECIAL
CP_SETSPECIAL,
CP_CLEARSPECIAL,
CP_SETACTIVATION,
CP_SECTORFLOOROFFSET,
CP_SETWALLYSCALE,
CP_SETTHINGZ,
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -77,6 +90,7 @@ enum
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern TArray<FMapThing> MapThingsConverted;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -86,44 +100,59 @@ TMap<FMD5Holder, FCompatValues, FMD5HashTraits> BCompatMap;
static FCompatOption Options[] =
{
{ "setslopeoverflow", 0, BCOMPATF_SETSLOPEOVERFLOW },
{ "resetplayerspeed", 0, BCOMPATF_RESETPLAYERSPEED },
{ "vileghosts", 0, BCOMPATF_VILEGHOSTS },
{ "ignoreteleporttags", 0, BCOMPATF_BADTELEPORTERS },
{ "setslopeoverflow", BCOMPATF_SETSLOPEOVERFLOW, SLOT_BCOMPAT },
{ "resetplayerspeed", BCOMPATF_RESETPLAYERSPEED, SLOT_BCOMPAT },
{ "vileghosts", BCOMPATF_VILEGHOSTS, SLOT_BCOMPAT },
{ "ignoreteleporttags", BCOMPATF_BADTELEPORTERS, SLOT_BCOMPAT },
{ "rebuildnodes", BCOMPATF_REBUILDNODES, SLOT_BCOMPAT },
{ "linkfrozenprops", BCOMPATF_LINKFROZENPROPS, SLOT_BCOMPAT },
// list copied from g_mapinfo.cpp
{ "shorttex", COMPATF_SHORTTEX, 0 },
{ "stairs", COMPATF_STAIRINDEX, 0 },
{ "limitpain", COMPATF_LIMITPAIN, 0 },
{ "nopassover", COMPATF_NO_PASSMOBJ, 0 },
{ "notossdrops", COMPATF_NOTOSSDROPS, 0 },
{ "useblocking", COMPATF_USEBLOCKING, 0 },
{ "nodoorlight", COMPATF_NODOORLIGHT, 0 },
{ "ravenscroll", COMPATF_RAVENSCROLL, 0 },
{ "soundtarget", COMPATF_SOUNDTARGET, 0 },
{ "dehhealth", COMPATF_DEHHEALTH, 0 },
{ "trace", COMPATF_TRACE, 0 },
{ "dropoff", COMPATF_DROPOFF, 0 },
{ "boomscroll", COMPATF_BOOMSCROLL, 0 },
{ "invisibility", COMPATF_INVISIBILITY, 0 },
{ "silentinstantfloors", COMPATF_SILENT_INSTANT_FLOORS, 0 },
{ "sectorsounds", COMPATF_SECTORSOUNDS, 0 },
{ "missileclip", COMPATF_MISSILECLIP, 0 },
{ "crossdropoff", COMPATF_CROSSDROPOFF, 0 },
{ "wallrun", COMPATF_WALLRUN, 0 }, // [GZ] Added for CC MAP29
{ "anybossdeath", COMPATF_ANYBOSSDEATH, 0}, // [GZ] Added for UAC_DEAD
{ "mushroom", COMPATF_MUSHROOM, 0},
{ "mbfmonstermove", COMPATF_MBFMONSTERMOVE, 0 },
{ "corpsegibs", COMPATF_CORPSEGIBS, 0 },
{ "noblockfriends", COMPATF_NOBLOCKFRIENDS, 0 },
{ "spritesort", COMPATF_SPRITESORT, 0 },
{ "hitscan", COMPATF_HITSCAN, 0 },
{ "lightlevel", COMPATF_LIGHT, 0 },
{ "polyobj", COMPATF_POLYOBJ, 0 },
{ "maskedmidtex", COMPATF_MASKEDMIDTEX, 0 },
{ "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT },
{ "stairs", COMPATF_STAIRINDEX, SLOT_COMPAT },
{ "limitpain", COMPATF_LIMITPAIN, SLOT_COMPAT },
{ "nopassover", COMPATF_NO_PASSMOBJ, SLOT_COMPAT },
{ "notossdrops", COMPATF_NOTOSSDROPS, SLOT_COMPAT },
{ "useblocking", COMPATF_USEBLOCKING, SLOT_COMPAT },
{ "nodoorlight", COMPATF_NODOORLIGHT, SLOT_COMPAT },
{ "ravenscroll", COMPATF_RAVENSCROLL, SLOT_COMPAT },
{ "soundtarget", COMPATF_SOUNDTARGET, SLOT_COMPAT },
{ "dehhealth", COMPATF_DEHHEALTH, SLOT_COMPAT },
{ "trace", COMPATF_TRACE, SLOT_COMPAT },
{ "dropoff", COMPATF_DROPOFF, SLOT_COMPAT },
{ "boomscroll", COMPATF_BOOMSCROLL, SLOT_COMPAT },
{ "invisibility", COMPATF_INVISIBILITY, SLOT_COMPAT },
{ "silentinstantfloors", COMPATF_SILENT_INSTANT_FLOORS, SLOT_COMPAT },
{ "sectorsounds", COMPATF_SECTORSOUNDS, SLOT_COMPAT },
{ "missileclip", COMPATF_MISSILECLIP, SLOT_COMPAT },
{ "crossdropoff", COMPATF_CROSSDROPOFF, SLOT_COMPAT },
{ "wallrun", COMPATF_WALLRUN, SLOT_COMPAT }, // [GZ] Added for CC MAP29
{ "anybossdeath", COMPATF_ANYBOSSDEATH, SLOT_COMPAT },// [GZ] Added for UAC_DEAD
{ "mushroom", COMPATF_MUSHROOM, SLOT_COMPAT },
{ "mbfmonstermove", COMPATF_MBFMONSTERMOVE, SLOT_COMPAT },
{ "corpsegibs", COMPATF_CORPSEGIBS, SLOT_COMPAT },
{ "noblockfriends", COMPATF_NOBLOCKFRIENDS, SLOT_COMPAT },
{ "spritesort", COMPATF_SPRITESORT, SLOT_COMPAT },
{ "hitscan", COMPATF_HITSCAN, SLOT_COMPAT },
{ "lightlevel", COMPATF_LIGHT, SLOT_COMPAT },
{ "polyobj", COMPATF_POLYOBJ, SLOT_COMPAT },
{ "maskedmidtex", COMPATF_MASKEDMIDTEX, SLOT_COMPAT },
{ "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 },
{ "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 },
{ 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 int ii_compatparams;
@ -189,15 +218,13 @@ void ParseCompatibility()
md5array.Push(md5);
sc.MustGetString();
} while (!sc.Compare("{"));
flags.CompatFlags = 0;
flags.BCompatFlags = 0;
memset(flags.CompatFlags, 0, sizeof(flags.CompatFlags));
flags.ExtCommandIndex = ~0u;
while (sc.GetString())
{
if ((i = sc.MatchString(&Options[0].Name, sizeof(*Options))) >= 0)
{
flags.CompatFlags |= Options[i].CompatFlags;
flags.BCompatFlags |= Options[i].BCompatFlags;
flags.CompatFlags[Options[i].WhichSlot] |= Options[i].CompatFlags;
}
else if (sc.Compare("clearlineflags"))
{
@ -226,12 +253,59 @@ void ParseCompatibility()
sc.MustGetString();
CompatParams.Push(P_FindLineSpecial(sc.String, NULL, NULL));
for(int i=0;i<5;i++)
for (int i = 0; i < 5; i++)
{
sc.MustGetNumber();
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
{
sc.UnGet();
@ -261,6 +335,7 @@ void CheckCompatibility(MapData *map)
{
FMD5Holder md5;
FCompatValues *flags;
bool onlyparams = true;
// 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.
@ -269,53 +344,74 @@ void CheckCompatibility(MapData *map)
{
ii_compatflags = COMPATF_SHORTTEX|COMPATF_LIGHT;
if (gameinfo.flags & GI_COMPATSTAIRS) ii_compatflags |= COMPATF_STAIRINDEX;
ii_compatflags2 = 0;
ib_compatflags = 0;
ii_compatparams = -1;
}
else if (Wads.GetLumpFile(map->lumpnum) == 1 && (gameinfo.flags & GI_COMPATPOLY1) && Wads.CheckLumpName(map->lumpnum, "MAP36"))
{
ii_compatflags = COMPATF_POLYOBJ;
ii_compatflags2 = 0;
ib_compatflags = 0;
ii_compatparams = -1;
}
else if (Wads.GetLumpFile(map->lumpnum) == 2 && (gameinfo.flags & GI_COMPATPOLY2) && Wads.CheckLumpName(map->lumpnum, "MAP47"))
{
ii_compatflags = COMPATF_POLYOBJ;
ii_compatflags2 = 0;
ib_compatflags = 0;
ii_compatparams = -1;
}
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 = ");
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");
Printf("%02X", md5.Bytes[j]);
}
if (flags != NULL)
{
ii_compatflags = flags->CompatFlags;
ib_compatflags = flags->BCompatFlags;
ii_compatparams = flags->ExtCommandIndex;
Printf(", cflags = %08x, cflags2 = %08x, bflags = %08x\n",
flags->CompatFlags[SLOT_COMPAT], flags->CompatFlags[SLOT_COMPAT2], flags->CompatFlags[SLOT_BCOMPAT]);
}
else
{
ii_compatflags = 0;
ib_compatflags = 0;
ii_compatparams = -1;
Printf("\n");
}
}
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
compatflags.Callback();
compatflags2.Callback();
}
//==========================================================================
@ -368,6 +464,61 @@ void SetCompatibilityParams()
i+=8;
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;
}
}
}
}
@ -390,7 +541,7 @@ CCMD (mapchecksum)
}
for (int i = 1; i < argv.argc(); ++i)
{
map = P_OpenMapData(argv[i]);
map = P_OpenMapData(argv[i], true);
if (map == NULL)
{
Printf("Cannot load %s as a map\n", argv[i]);
@ -416,5 +567,5 @@ CCMD (mapchecksum)
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
{
int CompatFlags;
int BCompatFlags;
int CompatFlags[3];
unsigned int ExtCommandIndex;
};

View file

@ -38,9 +38,12 @@
#include "doomtype.h"
#include "configfile.h"
#include "m_random.h"
#define READBUFFERSIZE 256
static FRandom pr_endtag;
//====================================================================
//
// FConfigFile Constructor
@ -53,6 +56,8 @@ FConfigFile::FConfigFile ()
LastSectionPtr = &Sections;
CurrentEntry = NULL;
PathName = "";
OkayToWrite = true;
FileExisted = true;
}
//====================================================================
@ -70,6 +75,8 @@ FConfigFile::FConfigFile (const char *pathname,
CurrentEntry = NULL;
ChangePathName (pathname);
LoadConfigFile (nosechandler, userdata);
OkayToWrite = true;
FileExisted = true;
}
//====================================================================
@ -85,6 +92,8 @@ FConfigFile::FConfigFile (const FConfigFile &other)
CurrentEntry = NULL;
ChangePathName (other.PathName);
*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");
bool succ;
FileExisted = false;
if (file == NULL)
{
return;
}
succ = ReadConfig (file);
fclose (file);
FileExisted = succ;
if (!succ)
{ // First valid line did not define a section
@ -669,13 +682,68 @@ bool FConfigFile::ReadConfig (void *file)
whiteprobe++;
}
*(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;
}
//====================================================================
//
// 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
@ -695,6 +763,13 @@ char *FConfigFile::ReadLine (char *string, int n, void *file) 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");
FConfigSection *section;
FConfigEntry *entry;
@ -715,7 +790,16 @@ bool FConfigFile::WriteConfigFile () const
fprintf (file, "[%s]\n", section->Name);
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;
}
section = section->Next;
@ -725,6 +809,44 @@ bool FConfigFile::WriteConfigFile () const
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

View file

@ -78,6 +78,10 @@ protected:
virtual char *ReadLine (char *string, int n, void *file) const;
bool ReadConfig (void *file);
static const char *GenerateEndTag(const char *value);
bool OkayToWrite;
bool FileExisted;
private:
struct FConfigEntry
@ -107,6 +111,7 @@ private:
FConfigEntry *FindEntry (FConfigSection *section, const char *key) const;
FConfigSection *NewConfigSection (const char *name);
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);
public:

View file

@ -273,7 +273,9 @@ void CT_Drawer (void)
if (players[consoleplayer].camera != NULL &&
(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]);
}

View file

@ -162,6 +162,41 @@ struct CodePointerAlias
};
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
DehInfo deh =
{
@ -183,6 +218,7 @@ DehInfo deh =
255, // Rocket explosion style, 255=use cvar
FRACUNIT*2/3, // Rocket explosion alpha
false, // .NoAutofreeze
40, // BFG cells per shot
};
// 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
break;
case MBF_PlaySound:
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+0, new FxConstant(SoundMap[value1-1], *pos)); // soundid
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;
case MBF_RandomJump:
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
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];
}
if (vchanged[1])
@ -1182,15 +1230,24 @@ static int PatchThing (int thingy)
PushTouchedActor(const_cast<PClass *>(type));
}
// Make MF3_ISMONSTER match MF_COUNTKILL
if (info->flags & MF_COUNTKILL)
// If MF_COUNTKILL is set, make sure the other standard monster flags are
// set, too. And vice versa.
if (thingy != 1) // don't mess with the player's flags
{
info->flags3 |= MF3_ISMONSTER;
}
else
{
info->flags3 &= ~MF3_ISMONSTER;
if (info->flags & MF_COUNTKILL)
{
info->flags2 |= MF2_PUSHWALL | MF2_MCROSS | MF2_PASSMOBJ;
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)
{
statedef.InstallStates(type->ActorInfo, info);
@ -1296,7 +1353,7 @@ static int PatchFrame (int frameNum)
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)
{
@ -1571,6 +1628,7 @@ static int PatchWeapon (int weapNum)
else if (stricmp (Line1, "Ammo use") == 0 || stricmp (Line1, "Ammo per shot") == 0)
{
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)
{
@ -1724,7 +1782,7 @@ static int PatchMisc (int dummy)
{
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)
{
@ -2279,6 +2337,30 @@ int D_LoadDehLumps()
{
count += D_LoadDehLump(lumpnum);
}
#if 0 // commented out for 'maint' version.
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);
}
}
}
#endif
return count;
}
@ -2482,8 +2564,6 @@ static void UnloadDehSupp ()
BitNames.ShrinkToFit();
StyleNames.Clear();
StyleNames.ShrinkToFit();
WeaponNames.Clear();
WeaponNames.ShrinkToFit();
AmmoNames.Clear();
AmmoNames.ShrinkToFit();
@ -2776,6 +2856,7 @@ static bool LoadDehSupp ()
}
else if (sc.Compare("WeaponNames"))
{
WeaponNames.Clear(); // This won't be cleared by UnloadDEHSupp so we need to do it here explicitly
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
@ -2882,6 +2963,55 @@ void FinishDehPatch ()
StateMap.ShrinkToFit();
TouchedActors.Clear();
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);

View file

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

View file

@ -367,7 +367,7 @@ int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads)
// Under UNIX OSes, the search path is:
// 1. Current directory
// 2. $DOOMWADDIR
// 3. $HOME/.zdoom
// 3. $HOME/.config/zdoom
// 4. The share directory defined at compile time (/usr/local/share/zdoom)
//
// The search path can be altered by editing the IWADSearch.Directories
@ -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"))
{
@ -441,7 +441,8 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
"heretic shadow of the serpent riders/base",
"hexen/base",
"hexen deathkings of the dark citadel/base",
"ultimate doom/base"
"ultimate doom/base",
"DOOM 3 BFG Edition/base/wads"
};
steam_path += "/SteamApps/common/";
for (i = 0; i < countof(steam_dirs); ++i)
@ -515,9 +516,19 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
I_FatalError ("Cannot find a game IWAD (doom.wad, doom2.wad, heretic.wad, etc.).\n"
"Did you install ZDoom properly? You can do either of the following:\n"
"\n"
#if defined(_WIN32)
"1. Place one or more of these wads in the same directory as ZDoom.\n"
"2. Edit your zdoom-username.ini and add the directories of your iwads\n"
"to the list beneath [IWADSearch.Directories]");
#elif defined(__APPLE__)
"1. Place one or more of these wads in ~/Library/Application Support/zdoom/\n"
"2. Edit your ~/Library/Preferences/zdoom.ini and add the directories\n"
"of your iwads to the list beneath [IWADSearch.Directories]");
#else
"1. Place one or more of these wads in ~/.config/zdoom/.\n"
"2. Edit your ~/.config/zdoom/zdoom.ini and add the directories of your\n"
"iwads to the list beneath [IWADSearch.Directories]");
#endif
}
pickwad = 0;

View file

@ -106,6 +106,7 @@
#include "po_man.h"
#include "resourcefiles/resourcefile.h"
#include "r_renderer.h"
#include "p_local.h"
#ifdef USE_POLYMOST
#include "r_polymost.h"
@ -157,7 +158,6 @@ EXTERN_CVAR (Bool, sv_unlimited_pickup)
extern int testingmode;
extern bool setmodeneeded;
extern bool netdemo;
extern int NewWidth, NewHeight, NewBits, DisplayBits;
EXTERN_CVAR (Bool, st_scale)
extern bool gameisdead;
@ -218,6 +218,7 @@ int NoWipe; // [RH] Allow wipe? (Needs to be set each time)
bool singletics = false; // debug flag to cancel adaptiveness
FString startmap;
bool autostart;
FString StoredWarp;
bool advancedemo;
FILE *debugfile;
event_t events[MAXEVENTS];
@ -515,8 +516,8 @@ CVAR (Flag, sv_nocountendmonst, dmflags2, DF2_NOCOUNTENDMONST);
//
//==========================================================================
int i_compatflags; // internal compatflags composed from the compatflags CVAR and MAPINFO settings
int ii_compatflags, ib_compatflags;
int i_compatflags, i_compatflags2; // internal compatflags composed from the compatflags CVAR and MAPINFO settings
int ii_compatflags, ii_compatflags2, ib_compatflags;
EXTERN_CVAR(Int, compatmode)
@ -526,19 +527,30 @@ static int GetCompatibility(int mask)
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)
{
int old = i_compatflags;
i_compatflags = GetCompatibility(self) | ii_compatflags;
if ((old ^i_compatflags) & COMPATF_POLYOBJ)
if ((old ^ i_compatflags) & COMPATF_POLYOBJ)
{
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)
{
int v;
int v, w = 0;
switch (self)
{
@ -551,6 +563,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT|
COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_DEHHEALTH|COMPATF_CROSSDROPOFF|
COMPATF_LIGHT;
w= COMPATF2_FLOORMOVE;
break;
case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set)
@ -558,6 +571,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN|
COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|
COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_LIGHT|COMPATF_MASKEDMIDTEX;
w = COMPATF2_BADANGLES|COMPATF2_FLOORMOVE;
break;
case 3: // Boom compat mode
@ -573,47 +587,50 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL)
COMPATF_MBFMONSTERMOVE|COMPATF_NOBLOCKFRIENDS;
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|
COMPATF_INVISIBILITY|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|COMPATF_WALLRUN|COMPATF_NOTOSSDROPS;
break;
}
compatflags = v;
compatflags2 = w;
}
CVAR (Flag, compat_shortTex, compatflags, COMPATF_SHORTTEX);
CVAR (Flag, compat_stairs, compatflags, COMPATF_STAIRINDEX);
CVAR (Flag, compat_limitpain, compatflags, COMPATF_LIMITPAIN);
CVAR (Flag, compat_silentpickup,compatflags, COMPATF_SILENTPICKUP);
CVAR (Flag, compat_nopassover, compatflags, COMPATF_NO_PASSMOBJ);
CVAR (Flag, compat_soundslots, compatflags, COMPATF_MAGICSILENCE);
CVAR (Flag, compat_wallrun, compatflags, COMPATF_WALLRUN);
CVAR (Flag, compat_notossdrops, compatflags, COMPATF_NOTOSSDROPS);
CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
CVAR (Flag, compat_nodoorlight, compatflags, COMPATF_NODOORLIGHT);
CVAR (Flag, compat_ravenscroll, compatflags, COMPATF_RAVENSCROLL);
CVAR (Flag, compat_soundtarget, compatflags, COMPATF_SOUNDTARGET);
CVAR (Flag, compat_dehhealth, compatflags, COMPATF_DEHHEALTH);
CVAR (Flag, compat_trace, compatflags, COMPATF_TRACE);
CVAR (Flag, compat_dropoff, compatflags, COMPATF_DROPOFF);
CVAR (Flag, compat_boomscroll, compatflags, COMPATF_BOOMSCROLL);
CVAR (Flag, compat_invisibility,compatflags, COMPATF_INVISIBILITY);
CVAR (Flag, compat_silentinstantfloors,compatflags, COMPATF_SILENT_INSTANT_FLOORS);
CVAR (Flag, compat_sectorsounds,compatflags, COMPATF_SECTORSOUNDS);
CVAR (Flag, compat_missileclip, compatflags, COMPATF_MISSILECLIP);
CVAR (Flag, compat_crossdropoff,compatflags, COMPATF_CROSSDROPOFF);
CVAR (Flag, compat_anybossdeath,compatflags, COMPATF_ANYBOSSDEATH);
CVAR (Flag, compat_minotaur, compatflags, COMPATF_MINOTAUR);
CVAR (Flag, compat_mushroom, compatflags, COMPATF_MUSHROOM);
CVAR (Flag, compat_mbfmonstermove,compatflags, COMPATF_MBFMONSTERMOVE);
CVAR (Flag, compat_corpsegibs, compatflags, COMPATF_CORPSEGIBS);
CVAR (Flag, compat_noblockfriends,compatflags,COMPATF_NOBLOCKFRIENDS);
CVAR (Flag, compat_spritesort, compatflags,COMPATF_SPRITESORT);
CVAR (Flag, compat_hitscan, compatflags,COMPATF_HITSCAN);
CVAR (Flag, compat_light, compatflags,COMPATF_LIGHT);
CVAR (Flag, compat_polyobj, compatflags,COMPATF_POLYOBJ);
CVAR (Flag, compat_maskedmidtex,compatflags,COMPATF_MASKEDMIDTEX);
CVAR (Flag, compat_shortTex, compatflags, COMPATF_SHORTTEX);
CVAR (Flag, compat_stairs, compatflags, COMPATF_STAIRINDEX);
CVAR (Flag, compat_limitpain, compatflags, COMPATF_LIMITPAIN);
CVAR (Flag, compat_silentpickup, compatflags, COMPATF_SILENTPICKUP);
CVAR (Flag, compat_nopassover, compatflags, COMPATF_NO_PASSMOBJ);
CVAR (Flag, compat_soundslots, compatflags, COMPATF_MAGICSILENCE);
CVAR (Flag, compat_wallrun, compatflags, COMPATF_WALLRUN);
CVAR (Flag, compat_notossdrops, compatflags, COMPATF_NOTOSSDROPS);
CVAR (Flag, compat_useblocking, compatflags, COMPATF_USEBLOCKING);
CVAR (Flag, compat_nodoorlight, compatflags, COMPATF_NODOORLIGHT);
CVAR (Flag, compat_ravenscroll, compatflags, COMPATF_RAVENSCROLL);
CVAR (Flag, compat_soundtarget, compatflags, COMPATF_SOUNDTARGET);
CVAR (Flag, compat_dehhealth, compatflags, COMPATF_DEHHEALTH);
CVAR (Flag, compat_trace, compatflags, COMPATF_TRACE);
CVAR (Flag, compat_dropoff, compatflags, COMPATF_DROPOFF);
CVAR (Flag, compat_boomscroll, compatflags, COMPATF_BOOMSCROLL);
CVAR (Flag, compat_invisibility, compatflags, COMPATF_INVISIBILITY);
CVAR (Flag, compat_silentinstantfloors, compatflags, COMPATF_SILENT_INSTANT_FLOORS);
CVAR (Flag, compat_sectorsounds, compatflags, COMPATF_SECTORSOUNDS);
CVAR (Flag, compat_missileclip, compatflags, COMPATF_MISSILECLIP);
CVAR (Flag, compat_crossdropoff, compatflags, COMPATF_CROSSDROPOFF);
CVAR (Flag, compat_anybossdeath, compatflags, COMPATF_ANYBOSSDEATH);
CVAR (Flag, compat_minotaur, compatflags, COMPATF_MINOTAUR);
CVAR (Flag, compat_mushroom, compatflags, COMPATF_MUSHROOM);
CVAR (Flag, compat_mbfmonstermove, compatflags, COMPATF_MBFMONSTERMOVE);
CVAR (Flag, compat_corpsegibs, compatflags, COMPATF_CORPSEGIBS);
CVAR (Flag, compat_noblockfriends, compatflags, COMPATF_NOBLOCKFRIENDS);
CVAR (Flag, compat_spritesort, compatflags, COMPATF_SPRITESORT);
CVAR (Flag, compat_hitscan, compatflags, COMPATF_HITSCAN);
CVAR (Flag, compat_light, compatflags, COMPATF_LIGHT);
CVAR (Flag, compat_polyobj, compatflags, COMPATF_POLYOBJ);
CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX);
CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES);
CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE);
//==========================================================================
//
@ -665,6 +682,8 @@ void D_Display ()
// Reload crosshair if transitioned to a different size
ST_LoadCrosshair (true);
AM_NewResolution ();
// Reset the mouse cursor in case the bit depth changed
vid_cursor.Callback();
}
}
@ -679,21 +698,21 @@ void D_Display ()
if (screen->Lock (false))
{
SB_state = screen->GetPageCount ();
BorderNeedRefresh = screen->GetPageCount ();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
// [RH] Allow temporarily disabling wipes
if (NoWipe)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
NoWipe--;
wipe = false;
wipegamestate = gamestate;
}
else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL)
{ // save the current screen if about to wipe
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
switch (wipegamestate)
{
default:
@ -756,13 +775,14 @@ void D_Display ()
}
screen->SetBlendingRect(viewwindowx, viewwindowy,
viewwindowx + viewwidth, viewwindowy + viewheight);
P_CheckPlayerSprites();
P_PredictPlayer(&players[consoleplayer]);
Renderer->RenderView(&players[consoleplayer]);
P_UnPredictPlayer();
if ((hw2d = screen->Begin2D(viewactive)))
{
// Redraw everything every frame when using 2D accel
SB_state = screen->GetPageCount();
BorderNeedRefresh = screen->GetPageCount();
ST_SetNeedRefresh();
V_SetBorderNeedRefresh();
}
Renderer->DrawRemainingPlayerSprites();
screen->DrawBlendingRect();
@ -783,17 +803,22 @@ void D_Display ()
if (hud_althud && viewheight == SCREENHEIGHT && screenblocks > 10)
{
StatusBar->DrawBottomStuff (HUD_AltHud);
if (DrawFSHUD || automapactive) DrawHUD();
StatusBar->DrawTopStuff (HUD_None);
StatusBar->Draw (HUD_AltHud);
StatusBar->DrawTopStuff (HUD_AltHud);
}
else
if (viewheight == SCREENHEIGHT && viewactive && screenblocks > 10)
{
StatusBar->Draw (DrawFSHUD ? HUD_Fullscreen : HUD_None);
StatusBar->DrawTopStuff (DrawFSHUD ? HUD_Fullscreen : HUD_None);
EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None;
StatusBar->DrawBottomStuff (state);
StatusBar->Draw (state);
StatusBar->DrawTopStuff (state);
}
else
{
StatusBar->DrawBottomStuff (HUD_StatusBar);
StatusBar->Draw (HUD_StatusBar);
StatusBar->DrawTopStuff (HUD_StatusBar);
}
@ -912,6 +937,7 @@ void D_Display ()
void D_ErrorCleanup ()
{
savegamerestore = false;
screen->Unlock ();
bglobal.RemoveAllBots (true);
D_QuitNetGame ();
@ -1210,12 +1236,17 @@ void D_DoAdvanceDemo (void)
static int pagecount;
const char *pagename = NULL;
advancedemo = false;
if (gameaction != ga_nothing)
{
return;
}
V_SetBlend (0,0,0,0);
players[consoleplayer].playerstate = PST_LIVE; // not reborn
advancedemo = false;
usergame = false; // no save / end game here
paused = 0;
gameaction = ga_nothing;
// [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.
@ -1248,7 +1279,7 @@ void D_DoAdvanceDemo (void)
Advisory = NULL;
if (!M_DemoNoPlay)
{
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
democount++;
mysnprintf (demoname + 4, countof(demoname) - 4, "%d", democount);
if (Wads.CheckNumForName (demoname) < 0)
@ -1270,7 +1301,7 @@ void D_DoAdvanceDemo (void)
gamestate = GS_DEMOSCREEN;
pagename = gameinfo.titlePage;
pagetic = (int)(gameinfo.titleTime * TICRATE);
S_StartMusic (gameinfo.titleMusic);
S_ChangeMusic (gameinfo.titleMusic, gameinfo.titleOrder, false);
demosequence = 3;
pagecount = 0;
C_HideConsole ();
@ -1328,6 +1359,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
@ -1681,6 +1842,25 @@ static FString ParseGameInfo(TArray<FString> &pwads, const char *fn, const char
sc.MustGetString();
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;
}
@ -1902,7 +2082,7 @@ static void CheckCmdLine()
{
startmap = "&wt@01";
}
autostart = false;
autostart = StoredWarp.IsNotEmpty();
const char *val = Args->CheckValue ("-skill");
if (val)
@ -2010,6 +2190,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
@ -2028,6 +2223,7 @@ void D_DoomMain (void)
D_DoomInit();
PClass::StaticInit ();
atterm(FinalGC);
// [RH] Make sure zdoom.pk3 is always loaded,
// as it contains magic stuff we need.
@ -2086,8 +2282,6 @@ void D_DoomMain (void)
execFiles = Args->GatherFiles ("-exec");
D_MultiExec (execFiles, true);
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
CopyFiles(allwads, pwads);
// Since this function will never leave we must delete this array here manually.
@ -2099,7 +2293,12 @@ void D_DoomMain (void)
allwads.Clear();
allwads.ShrinkToFit();
SetMapxxFlag();
// Now that wads are loaded, define mod-specific cvars.
ParseCVarInfo();
C_ExecCmdLineParams (); // [RH] do all +set commands on the command line
// [RH] Initialize localizable strings.
GStrings.LoadStrings (false);
@ -2127,8 +2326,14 @@ void D_DoomMain (void)
S_Init ();
Printf ("ST_Init: Init startup screen.\n");
if (!restart) StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5);
else StartScreen = new FStartupScreen(0);
if (!restart)
{
StartScreen = FStartupScreen::CreateInstance (TexMan.GuesstimateNumTextures() + 5);
}
else
{
StartScreen = new FStartupScreen(0);
}
ParseCompatibility();
@ -2137,14 +2342,17 @@ void D_DoomMain (void)
// [RH] Load sound environments
S_ParseReverbDef ();
// [RH] Parse any SNDINFO lumps
Printf ("S_InitData: Load sound definitions.\n");
S_InitData ();
// [RH] Parse through all loaded mapinfo lumps
Printf ("G_ParseMapInfo: Load map definitions.\n");
G_ParseMapInfo (iwad_info->MapInfo);
ReadStatistics();
// [RH] Parse any SNDINFO lumps
Printf ("S_InitData: Load sound definitions.\n");
S_InitData ();
// MUSINFO must be parsed after MAPINFO
S_ParseMusInfo();
Printf ("Texman.Init: Init texture manager.\n");
TexMan.Init();
@ -2275,6 +2483,7 @@ void D_DoomMain (void)
delete StartScreen;
StartScreen = NULL;
S_Sound (CHAN_BODY, "misc/startupdone", 1, ATTN_NONE);
if (Args->CheckParm("-norun"))
{
@ -2282,6 +2491,16 @@ void D_DoomMain (void)
}
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");
if (v != NULL)
@ -2297,15 +2516,6 @@ void D_DoomMain (void)
G_TimeDemo (v);
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)
{
@ -2320,6 +2530,11 @@ void D_DoomMain (void)
if (demorecording)
G_BeginRecording (startmap);
G_InitNew (startmap, false);
if (StoredWarp.IsNotEmpty())
{
AddCommandString(StoredWarp.LockBuffer());
StoredWarp = NULL;
}
}
else
{

View file

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

View file

@ -59,9 +59,9 @@
#include "m_argv.h"
#include "p_lnspec.h"
#include "v_video.h"
int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backSide,
int arg0, int arg1, int arg2, int always, bool wantResultCode, bool net);
#include "p_spec.h"
#include "hardware.h"
#include "intermission/intermission.h"
EXTERN_CVAR (Int, disableautosave)
EXTERN_CVAR (Int, autosavecount)
@ -108,6 +108,8 @@ int resendcount[MAXNETNODES];
unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings
unsigned int currrecvtime[MAXPLAYERS];
unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved.
bool hadlate;
int nodeforplayer[MAXPLAYERS];
int playerfornode[MAXNETNODES];
@ -121,6 +123,7 @@ void G_BuildTiccmd (ticcmd_t *cmd);
void D_DoAdvanceDemo (void);
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;
BYTE reboundstore[MAX_MSGLEN];
@ -135,7 +138,18 @@ static int oldentertics;
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
static struct TicSpecial
@ -299,6 +313,8 @@ void Net_ClearBuffers ()
oldentertics = entertic;
gametic = 0;
maketic = 0;
lastglobalrecvtime = 0;
}
//
@ -584,21 +600,24 @@ void PlayerIsGone (int netnode, int netconsole)
if (deathmatch)
{
Printf ("%s left the game with %d frags\n",
players[netconsole].userinfo.netname,
players[netconsole].fragcount);
players[netconsole].userinfo.GetName(),
players[netconsole].fragcount);
}
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
if (players[consoleplayer].camera == players[netconsole].mo)
// [RH] Revert each player to their own view if spying through the player who left
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
players[consoleplayer].camera = players[consoleplayer].mo;
if (StatusBar != NULL)
if (playeringame[ii] && players[ii].camera == players[netconsole].mo)
{
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);
players[netconsole].mo->player = NULL;
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].camera = NULL;
}
@ -626,7 +650,7 @@ void PlayerIsGone (int netnode, int netconsole)
{
Net_Arbitrator = i;
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;
}
}
@ -680,6 +704,8 @@ void GetPackets (void)
}
continue; // extra setup packet
}
lastglobalrecvtime = I_GetTime (false); //Update the last time a packet was recieved
netnode = doomcom.remotenode;
netconsole = playerfornode[netnode] & ~PL_DRONE;
@ -748,6 +774,7 @@ void GetPackets (void)
}
if (netbuffer[0] & NCMD_QUITTERS)
{
numplayers = netbuffer[k++];
for (int i = 0; i < numplayers; ++i)
@ -1340,7 +1367,7 @@ bool DoArbitrate (void *userdata)
data->playersdetected[0] |= 1 << netbuffer[1];
StartScreen->NetMessage ("Found %s (node %d, player %d)",
players[netbuffer[1]].userinfo.netname,
players[netbuffer[1]].userinfo.GetName(),
node, netbuffer[1]+1);
}
}
@ -1799,6 +1826,33 @@ void TryRunTics (void)
if (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
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
if (counts > 0)
{
@ -1947,12 +2008,12 @@ void Net_DoCommand (int type, BYTE **stream, int player)
{
case DEM_SAY:
{
const char *name = players[player].userinfo.netname;
const char *name = players[player].userinfo.GetName();
BYTE who = ReadByte (stream);
s = ReadString (stream);
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
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);
}
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
if (who & 2)
{
@ -2058,10 +2119,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break;
case DEM_CENTERVIEW:
if (players[player].mo != NULL)
{
players[player].mo->pitch = 0;
}
players[player].centering = true;
break;
case DEM_INVUSEALL:
@ -2227,7 +2285,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
paused = player + 1;
S_PauseSound (false, false);
}
BorderNeedRefresh = screen->GetPageCount ();
V_SetBorderNeedRefresh();
}
break;
@ -2313,18 +2371,17 @@ void Net_DoCommand (int type, BYTE **stream, int player)
{
int snum = ReadWord (stream);
int argn = ReadByte (stream);
int arg[3] = { 0, 0, 0 };
for (i = 0; i < argn; ++i)
{
int argval = ReadLong(stream);
if ((unsigned)i < countof(arg))
{
arg[i] = argval;
}
}
P_StartScript (players[player].mo, NULL, snum, level.mapname, false,
arg[0], arg[1], arg[2], type == DEM_RUNSCRIPT2, false, true);
RunScript(stream, players[player].mo, snum, argn, (type == DEM_RUNSCRIPT2) ? ACS_ALWAYS : 0);
}
break;
case DEM_RUNNAMEDSCRIPT:
{
char *sname = ReadString(stream);
int argn = ReadByte(stream);
RunScript(stream, players[player].mo, -FName(sname), argn & 127, (argn & 128) ? ACS_ALWAYS : 0);
}
break;
@ -2351,9 +2408,10 @@ void Net_DoCommand (int type, BYTE **stream, int player)
case DEM_CROUCH:
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;
@ -2374,7 +2432,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = true;
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;
@ -2384,7 +2442,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
players[playernum].settings_controller = false;
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;
@ -2419,17 +2477,27 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break;
case DEM_SETSLOT:
case DEM_SETSLOTPNUM:
{
int pnum;
if (type == DEM_SETSLOTPNUM)
{
pnum = ReadByte(stream);
}
else
{
pnum = player;
}
unsigned int slot = ReadByte(stream);
int count = ReadByte(stream);
if (slot < NUM_WEAPON_SLOTS)
{
players[player].weapons.Slots[slot].Clear();
players[pnum].weapons.Slots[slot].Clear();
}
for(i = 0; i < count; ++i)
{
const PClass *wpn = Net_ReadWeapon(stream);
players[player].weapons.AddSlot(slot, wpn, player == consoleplayer);
players[pnum].weapons.AddSlot(slot, wpn, pnum == consoleplayer);
}
}
break;
@ -2450,6 +2518,19 @@ void Net_DoCommand (int type, BYTE **stream, int player)
}
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:
I_Error ("Unknown net command: %d", type);
break;
@ -2459,6 +2540,23 @@ void Net_DoCommand (int type, BYTE **stream, int player)
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)
{
BYTE t;
@ -2547,6 +2645,11 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 3 + *(*stream + 2) * 4;
break;
case DEM_RUNNAMEDSCRIPT:
skip = strlen((char *)(*stream)) + 2;
skip += ((*(*stream + skip - 1)) & 127) * 4;
break;
case DEM_RUNSPECIAL:
skip = 2 + *(*stream + 1) * 4;
break;
@ -2556,9 +2659,10 @@ void Net_SkipCommand (int type, BYTE **stream)
break;
case DEM_SETSLOT:
case DEM_SETSLOTPNUM:
{
skip = 2;
for(int numweapons = (*stream)[1]; numweapons > 0; numweapons--)
skip = 2 + (type == DEM_SETSLOTPNUM);
for(int numweapons = (*stream)[skip-1]; numweapons > 0; numweapons--)
{
skip += 1 + ((*stream)[skip] >> 7);
}
@ -2570,6 +2674,9 @@ void Net_SkipCommand (int type, BYTE **stream)
skip = 2 + ((*stream)[1] >> 7);
break;
case DEM_SETPITCHLIMIT:
skip = 2;
break;
default:
return;
@ -2586,7 +2693,7 @@ CCMD (pings)
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[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)
{
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;
}
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;
}
@ -2713,7 +2820,7 @@ CCMD (net_listcontrollers)
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" };
static const char *UserInfoStrings[] =
{
"name",
"autoaim",
"color",
"skin",
"team",
"gender",
"neverswitchonpickup",
"movebob",
"stillbob",
"playerclass",
"colorset",
NULL
};
// Replace \ with %/ and % with %%
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;
FPlayerColorSet *colorset = NULL;
int color;
uint32 color;
int team;
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)
{
@ -204,25 +189,28 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v, FPlayerColorSet
}
else
{
color = info->color;
color = info->GetColor();
}
RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f,
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
// and adjust the saturation and value so that the team
// hue is visible in the final color.
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,
h, &ts, &tv);
*s = clamp(ts + *s * 0.15f - 0.075f, 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)
{
@ -261,9 +249,10 @@ int D_PickRandomTeam ()
{
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++;
}
@ -275,7 +264,7 @@ int D_PickRandomTeam ()
{
do
{
team = pr_pickteam() % Teams.Size ();
team = pr_pickteam() % Teams.Size();
} while (Teams[team].m_iPresent != 0);
}
else
@ -316,7 +305,7 @@ static void UpdateTeam (int pnum, int team, bool update)
{
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");
return;
@ -328,19 +317,15 @@ static void UpdateTeam (int pnum, int team, bool update)
{
team = TEAM_NONE;
}
oldteam = info->team;
info->team = team;
oldteam = info->GetTeam();
team = info->TeamChanged(team);
if (teamplay && !TeamLibrary.IsValidTeam (info->team))
{ // Force players onto teams in teamplay mode
info->team = D_PickRandomTeam ();
}
if (update && oldteam != info->team)
if (update && oldteam != team)
{
if (TeamLibrary.IsValidTeam (info->team))
Printf ("%s joined the %s team\n", info->netname, Teams[info->team].GetName ());
if (TeamLibrary.IsValidTeam (team))
Printf ("%s joined the %s team\n", info->GetName(), Teams[team].GetName ());
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
R_BuildPlayerTranslation (pnum);
@ -348,25 +333,28 @@ static void UpdateTeam (int pnum, int team, bool update)
{
StatusBar->AttachToPlayer (&players[pnum]);
}
if (!TeamLibrary.IsValidTeam (info->team))
info->team = TEAM_NONE;
// Double-check
if (!TeamLibrary.IsValidTeam (team))
{
*static_cast<FIntCVar *>((*info)[NAME_Team]) = TEAM_NONE;
}
}
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;
}
else
{
// Count total frags for this player's team
const int team = player->userinfo.team;
int count = 0;
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;
}
@ -378,37 +366,146 @@ int D_GetFragCount (player_t *player)
void D_SetupUserInfo ()
{
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++)
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);
if (teamplay && !TeamLibrary.IsValidTeam (team))
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{
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;
coninfo->colorset = colorset;
coninfo->skin = R_FindSkin (skin, 0);
coninfo->gender = D_GenderToInt (gender);
coninfo->neverswitch = neverswitchonpickup;
coninfo->MoveBob = (fixed_t)(65536.f * movebob);
coninfo->StillBob = (fixed_t)(65536.f * stillbob);
coninfo->PlayerClass = D_PlayerClassToInt (playerclass);
R_BuildPlayerTranslation (consoleplayer);
*static_cast<FIntCVar *>((*this)[NAME_Team]) = team;
return team;
}
int userinfo_t::SkinChanged(const char *skinname)
{
int skinnum = R_FindSkin(skinname, 0);
*static_cast<FIntCVar *>((*this)[NAME_Skin]) = skinnum;
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)
@ -500,7 +597,7 @@ static const char *SetServerVar (char *name, ECVarType type, BYTE **stream, bool
{
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)
{
WriteByte (0, stream);
}
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;
TMap<FName, FBaseCVar *>::ConstPair *pair1 = *(TMap<FName, FBaseCVar *>::ConstPair **)a;
TMap<FName, FBaseCVar *>::ConstPair *pair2 = *(TMap<FName, FBaseCVar *>::ConstPair **)b;
return stricmp(pair1->Key.GetChars(), pair2->Key.GetChars());
}
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 *breakpt;
FString value;
bool compact;
int infotype = -1;
FName keyname;
unsigned int infotype = 0;
if (*ptr++ != '\\')
return;
compact = (*ptr == '\\') ? ptr++, true : false;
if (i < MAXPLAYERS)
// We need the cvar names in sorted order for compact mode
if (compact)
{
for (;;)
{
int j;
TMap<FName, FBaseCVar *>::Iterator it(*info);
TMap<FName, FBaseCVar *>::Pair *pair;
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)
{
// 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));
infotype++;
}
else
{
// Verbose has both the key name and its value.
assert(breakpt != NULL);
// A malicious remote machine could invalidate the above assert.
if (breakpt == NULL)
@ -691,154 +806,187 @@ void D_ReadUserInfoStrings (int i, BYTE **stream, bool update)
{
value = D_UnescapeUserInfo(valstart, strlen(valstart));
}
for (j = 0;
UserInfoStrings[j] && strnicmp (UserInfoStrings[j], ptr, valstart - ptr - 1) != 0;
++j)
{ }
if (UserInfoStrings[j] == NULL)
{
infotype = -1;
}
else
{
infotype = j;
}
keyname = FName(ptr, valstart - ptr - 1, true);
}
switch (infotype)
// A few of these need special handling.
switch (keyname)
{
case INFO_Autoaim: {
double angles;
angles = atof (value);
if (angles > 35.f || angles < 0.f)
{
info->aimdist = ANGLE_1*35;
}
else
{
info->aimdist = abs ((int)(angles * (float)ANGLE_1));
}
}
case NAME_Gender:
info->GenderChanged(value);
break;
case INFO_Name:
{
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);
}
}
case NAME_PlayerClass:
info->PlayerClassChanged(value);
break;
case INFO_Team:
UpdateTeam (i, atoi(value), update);
break;
case INFO_Color:
case INFO_ColorSet:
if (infotype == INFO_Color)
case NAME_Skin:
info->SkinChanged(value);
if (players[pnum].mo != NULL)
{
info->color = V_GetColorFromString (NULL, value);
}
else
{
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)
if (players[pnum].cls != NULL &&
!(players[pnum].mo->flags4 & MF4_NOSKIN) &&
players[pnum].mo->state->sprite ==
GetDefaultByType (players[pnum].cls)->SpawnState->sprite)
{ // Only change the sprite if the player is using a standard one
players[i].mo->sprite = skins[info->skin].sprite;
players[i].mo->scaleX = skins[info->skin].ScaleX;
players[i].mo->scaleY = skins[info->skin].ScaleY;
players[pnum].mo->sprite = skins[info->GetSkin()].sprite;
}
}
// Rebuild translation in case the new skin uses a different range
// than the old one.
R_BuildPlayerTranslation (i);
R_BuildPlayerTranslation(pnum);
break;
case INFO_Gender:
info->gender = D_GenderToInt (value);
case NAME_Team:
UpdateTeam(pnum, atoi(value), update);
break;
case INFO_NeverSwitchOnPickup:
if (value[0] >= '0' && value[0] <= '9')
{
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);
case NAME_Color:
info->ColorChanged(value);
break;
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;
}
if (breakpt)
if (keyname == NAME_Color || keyname == NAME_ColorSet)
{
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;
}
}
}
*stream += strlen (*((char **)stream)) + 1;
if (str != NULL)
{
delete[] str;
}
}
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
{
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;
}
@ -852,27 +1000,63 @@ CCMD (playerinfo)
{
if (playeringame[i])
{
Printf ("%d. %s\n", i, players[i].userinfo.netname);
Printf("%d. %s\n", i, players[i].userinfo.GetName());
}
}
}
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;
Printf ("Name: %s\n", ui->netname);
Printf ("Team: %s (%d)\n", ui->team == TEAM_NONE ? "None" : Teams[ui->team].GetName (), ui->team);
Printf ("Aimdist: %d\n", ui->aimdist);
Printf ("Color: %06x\n", ui->color);
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);
Printf ("NeverSwitch: %d\n", ui->neverswitch);
Printf ("MoveBob: %g\n", ui->MoveBob/65536.f);
Printf ("StillBob: %g\n", ui->StillBob/65536.f);
Printf ("PlayerClass: %s (%d)\n",
ui->PlayerClass == -1 ? "Random" : PlayerClasses[ui->PlayerClass].Type->Meta.GetMetaString (APMETA_DisplayName),
ui->PlayerClass);
if (argv.argc() > 2) PrintMiscActorInfo(players[i].mo);
if (!playeringame[i])
{
Printf(TEXTCOLOR_ORANGE "Player %d is not in the game\n", i);
}
// Print special info
Printf("%20s: %s\n", "Name", ui->GetName());
Printf("%20s: %s (%d)\n", "Team", ui->GetTeam() == TEAM_NONE ? "None" : Teams[ui->GetTeam()].GetName(), ui->GetTeam());
Printf("%20s: %s (%d)\n", "Skin", skins[ui->GetSkin()].name, ui->GetSkin());
Printf("%20s: %s (%d)\n", "Gender", GenderNames[ui->GetGender()], ui->GetGender());
Printf("%20s: %s (%d)\n", "PlayerClass",
ui->GetPlayerClassNum() == -1 ? "Random" : ui->GetPlayerClassType()->Meta.GetMetaString (APMETA_DisplayName),
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);
}
}
}
userinfo_t::~userinfo_t()
{
TMapIterator<FName, FBaseCVar *> it(*this);
TMap<FName, FBaseCVar *>::Pair *pair;
while (it.NextPair(pair))
{
delete pair->Value;
}
this->Clear();
}

View file

@ -90,6 +90,7 @@ public:
virtual void AddInventory (AInventory *item);
virtual void RemoveInventory (AInventory *item);
virtual bool UseInventory (AInventory *item);
virtual void MarkPrecacheSounds () const;
virtual void PlayIdle ();
virtual void PlayRunning ();
@ -107,7 +108,7 @@ public:
void GiveDefaultInventory ();
void PlayAttacking ();
void PlayAttacking2 ();
const char *GetSoundClass ();
const char *GetSoundClass () const;
enum EInvulState
{
@ -118,7 +119,7 @@ public:
};
void BeginPlay ();
void Die (AActor *source, AActor *inflictor);
void Die (AActor *source, AActor *inflictor, int dmgflags);
int crouchsprite;
int MaxHealth;
@ -130,6 +131,8 @@ public:
// [GRB] Player class properties
fixed_t JumpZ;
fixed_t GruntSpeed;
fixed_t FallingScreamMinSpeed, FallingScreamMaxSpeed;
fixed_t ViewHeight;
fixed_t ForwardMove1, ForwardMove2;
fixed_t SideMove1, SideMove2;
@ -137,6 +140,8 @@ public:
int SpawnMask;
FNameNoInit MorphWeapon;
fixed_t AttackZOffset; // attack height, relative to player center
fixed_t UseRange; // [NS] Distance at which player can +use
fixed_t AirCapacity; // Multiplier for air supply underwater.
const PClass *FlechetteType;
// [CW] Fades for when you are being damaged.
@ -159,6 +164,8 @@ class APlayerChunk : public APlayerPawn
enum
{
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 +198,29 @@ typedef enum
CF_INSTANTWEAPSWITCH= 1 << 11, // [RH] Switch weapons instantly
CF_TOTALLYFROZEN = 1 << 12, // [RH] All players can do is press +use
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_REGENERATION = 1 << 17, // Player owns a regeneration artifact
CF_HIGHJUMP = 1 << 18, // more Skulltag flags. Implementation not guaranteed though. ;)
CF_REFLECTION = 1 << 19,
CF_PROSPERITY = 1 << 20,
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_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_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip
} 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 WPIECE2 2
#define WPIECE3 4
@ -216,6 +230,29 @@ typedef enum
#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
{
GENDER_MALE,
@ -223,20 +260,86 @@ enum
GENDER_NEUTER
};
struct userinfo_t
struct userinfo_t : TMap<FName,FBaseCVar *>
{
char netname[MAXPLAYERNAME+1];
BYTE team;
int aimdist;
int color;
int colorset;
int skin;
int gender;
bool neverswitch;
fixed_t MoveBob, StillBob;
int PlayerClass;
~userinfo_t();
int GetAimDist() const { return (dmflags2 & DF2_NOAUTOAIM)? 0 : aimdist; }
int GetAimDist() const
{
if (dmflags2 & DF2_NOAUTOAIM)
{
return 0;
}
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));
}
bool GetNoAutostartMap() const
{
return *static_cast<FBoolCVar *>(*CheckKey(NAME_Wi_NoAutostartMap));
}
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);
@ -249,6 +352,7 @@ class player_t
{
public:
player_t();
player_t &operator= (const player_t &p);
void Serialize (FArchive &arc);
size_t FixPointers (const DObject *obj, DObject *replacement);
@ -256,6 +360,7 @@ public:
void SetLogNumber (int num);
void SetLogText (const char *text);
void SendPitchLimits() const;
APlayerPawn *mo;
BYTE playerstate;
@ -282,6 +387,8 @@ public:
bool centering;
BYTE turnticks;
bool attackdown;
bool usedown;
DWORD oldbuttons;
@ -297,17 +404,22 @@ public:
int lastkilltime; // [RH] For multikills
BYTE multicount;
BYTE spreecount; // [RH] Keep track of killing sprees
BYTE WeaponState;
AWeapon *ReadyWeapon;
AWeapon *PendingWeapon; // WP_NOCHANGE if not changing
int cheats; // bit flags
int timefreezer; // Player has an active time freezer
short refire; // refired shots are less accurate
short inconsistant;
bool waiting;
int killcount, itemcount, secretcount; // for intermission
int damagecount, bonuscount;// for screen flashing
int hazardcount; // for delayed Strife 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> attacker; // who did damage (NULL for floors)
int extralight; // so gun flashes light up areas
@ -327,8 +439,6 @@ public:
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
//Added by MC:
@ -342,9 +452,9 @@ public:
TObjPtr<AActor> enemy; // The dead meat.
TObjPtr<AActor> missile; // A threathing missile that got to be avoided.
TObjPtr<AActor> mate; // Friend (used for grouping in templay or coop.
TObjPtr<AActor> last_mate; // If bots mate dissapeared (not if died) that mate is
TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
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
// necessary.
@ -380,6 +490,9 @@ public:
FString LogText; // [RH] Log for Strife
int MinPitch; // Viewpitch limits (negative is up, positive is down)
int MaxPitch;
SBYTE crouching;
SBYTE crouchdir;
fixed_t crouchfactor;
@ -406,6 +519,11 @@ public:
crouching = 0;
crouchviewdelta = 0;
}
bool CanCrouch() const
{
return morphTics == 0 || mo->PlayerFlags & PPF_CROUCHABLEMORPH;
}
int GetSpawnClass();
};
@ -415,31 +533,31 @@ extern player_t players[MAXPLAYERS];
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)
// [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;
bool P_IsPlayerTotallyFrozen(const player_t *player);
#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;
if (buttons_changed != 0)
{
BYTE bytes[4] = { ucmd->buttons & 0x7F,
(ucmd->buttons >> 7) & 0x7F,
(ucmd->buttons >> 14) & 0x7F,
(ucmd->buttons >> 21) & 0xFF };
BYTE bytes[4] = { BYTE(ucmd->buttons & 0x7F),
BYTE((ucmd->buttons >> 7) & 0x7F),
BYTE((ucmd->buttons >> 14) & 0x7F),
BYTE((ucmd->buttons >> 21) & 0xFF) };
flags |= UCMDF_BUTTONS;

View file

@ -159,6 +159,11 @@ enum EDemoCommand
DEM_CONVCLOSE, // 60
DEM_CONVNULL, // 61
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
@ -213,7 +218,8 @@ enum ECheatCommand
CHT_GIMMIEI,
CHT_GIMMIEJ,
CHT_GIMMIEZ,
CHT_BUDDHA
CHT_BUDDHA,
CHT_NOCLIP2
};
void StartChunk (int id, BYTE **stream);

View file

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

View file

@ -453,8 +453,9 @@ size_t DObject::PropagateMark()
GC::Mark((DObject **)((BYTE *)this + *offsets));
offsets++;
}
return info->Size;
}
return info->Size;
return 0;
}
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
// 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.
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->ObjectFlags |= OF_Cleanup;
@ -305,6 +307,7 @@ static void MarkRoot()
DThinker::MarkRoots();
FCanvasTextureInfo::Mark();
Mark(DACSThinker::ActiveThinker);
Mark(level.DefaultSkybox);
// Mark dead bodies.
for (i = 0; i < BODYQUESIZE; ++i)
{
@ -531,7 +534,12 @@ void Barrier(DObject *pointing, DObject *pointed)
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;
}

View file

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

View file

@ -153,6 +153,7 @@ enum ELineFlags
ML_BLOCKPROJECTILE = 0x01000000,
ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line
ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight
ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks
};
@ -342,6 +343,7 @@ struct FMapThing
int special;
int args[5];
int Conversation;
fixed_t gravity;
void Serialize (FArchive &);
};
@ -400,11 +402,25 @@ enum EMapThingFlags
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.
extern TArray<FMapThing> deathmatchstarts;
extern TArray<FPlayerStart> deathmatchstarts;
// Player spawn spots.
extern FMapThing playerstarts[MAXPLAYERS];
extern FPlayerStart playerstarts[MAXPLAYERS];
extern TArray<FPlayerStart> AllPlayerStarts;
#endif // __DOOMDATA__

View file

@ -333,7 +333,10 @@ enum
COMPATF_HITSCAN = 1 << 28, // Hitscans use original blockmap anf hit check code.
COMPATF_LIGHT = 1 << 29, // Find neighboring light level like Doom
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
@ -345,6 +348,8 @@ enum
BCOMPATF_VILEGHOSTS = 1 << 2, // Monsters' radius and height aren't restored properly when resurrected.
BCOMPATF_BADTELEPORTERS = 1 << 3, // Ignore tags on Teleport specials
BCOMPATF_BADPORTALS = 1 << 4, // Restores the old unstable portal behavior
BCOMPATF_REBUILDNODES = 1 << 5, // Force node rebuild
BCOMPATF_LINKFROZENPROPS = 1 << 6, // Clearing PROP_TOTALLYFROZEN or PROP_FROZEN also clears the other
};
// phares 3/20/98:

View file

@ -59,6 +59,8 @@ extern FString startmap; // [RH] Actual map name now
extern bool autostart;
extern FString StoredWarp; // [RH] +warp at the command line
// Selected by user.
EXTERN_CVAR (Int, gameskill);
extern int NextSkill; // [RH] Skill to use at next level load
@ -230,6 +232,7 @@ struct DehInfo
BYTE ExplosionStyle;
fixed_t ExplosionAlpha;
int NoAutofreeze;
int BFGCells;
};
extern DehInfo deh;
EXTERN_CVAR (Int, infighting)
@ -240,6 +243,7 @@ EXTERN_CVAR (Int, dmflags);
EXTERN_CVAR (Int, dmflags2); // [BC]
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

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;
if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b |
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;
}
@ -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;
if ((m_Sector->ceilingplane.a | m_Sector->ceilingplane.b |
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;
}

View file

@ -192,19 +192,10 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
statcount--;
}
}
catch (class CDoomError &err)
catch (class CDoomError &)
{
bSerialOverride = false;
// 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());
DestroyAllThinkers();
throw;
}
bSerialOverride = false;
@ -366,12 +357,10 @@ void DThinker::DestroyThinkersInList (FThinkerList &list)
{
if (list.Sentinel != NULL)
{
DThinker *node = list.Sentinel->NextThinker;
while (node != list.Sentinel)
for (DThinker *node = list.Sentinel->NextThinker; node != list.Sentinel; node = list.Sentinel->NextThinker)
{
DThinker *next = node->NextThinker;
assert(node != NULL);
node->Destroy();
node = next;
}
list.Sentinel->Destroy();
list.Sentinel = NULL;
@ -389,9 +378,8 @@ void DThinker::DestroyMostThinkersInList (FThinkerList &list, int stat)
// it from the list. G_FinishTravel() will find it later from
// a players[].mo link and destroy it then, after copying various
// information to a new player.
for (DThinker *probe = list.Sentinel->NextThinker, *next; probe != list.Sentinel; probe = next)
for (DThinker *probe = list.Sentinel->NextThinker; probe != list.Sentinel; probe = list.Sentinel->NextThinker)
{
next = probe->NextThinker;
if (!probe->IsKindOf(RUNTIME_CLASS(APlayerPawn)) || // <- should not happen
static_cast<AActor *>(probe)->player == NULL ||
static_cast<AActor *>(probe)->player->mo != probe)
@ -457,7 +445,7 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
NextToThink = node->NextThinker;
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)
{ // Move thinker from this list to the destination list
node->Remove();
@ -472,7 +460,8 @@ int DThinker::TickThinkers (FThinkerList *list, FThinkerList *dest)
if (!(node->ObjectFlags & OF_EuthanizeMe))
{ // Only tick thinkers not scheduled for destruction
node->Tick ();
node->Tick();
node->ObjectFlags &= ~OF_JustSpawned;
GC::CheckGC();
}
node = NextToThink;

View file

@ -54,6 +54,7 @@
#include "c_cvars.h"
#include "c_dispatch.h"
#include "d_player.h"
#include "m_misc.h"
#include "dobject.h"
// 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)
{
M_Free (expand);
I_Error ("Could not decompress cfile");
I_Error ("Could not decompress buffer: %s", M_ZLibError(r).GetChars());
}
}
else
@ -497,7 +498,18 @@ bool FCompressedMemFile::Reopen ()
m_Mode = EReading;
m_Buffer = m_ImplodedBuffer;
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;
return true;
}
@ -560,6 +572,20 @@ bool FCompressedMemFile::IsOpen () const
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)
: FCompressedFile (file, EWriting, true, false), m_ChunkID (id)
{

View file

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

View file

@ -36,6 +36,7 @@
#include "files.h"
#include "i_system.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)
{
if (len <= 0) return 0;
if (len <= 0 || FilePos >= StartPos + Length) return NULL;
char *p = fgets(strbuf, len, File);
if (p != NULL)
{
FilePos = ftell(File) - StartPos;
int old = FilePos;
FilePos = ftell(File);
if (FilePos - StartPos > Length)
{
strbuf[Length - old + StartPos] = 0;
}
}
return p;
}
@ -218,7 +224,7 @@ FileReaderZ::FileReaderZ (FileReader &file, bool zip)
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)
{
t_return.type = svt_string;
t_return.string = players[plnum].userinfo.netname;
t_return.string = players[plnum].userinfo.GetName();
}
else
{
@ -3367,7 +3367,7 @@ void FParser::SF_RadiusAttack()
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));
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);
mo->target = source;
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;
}

View file

@ -316,7 +316,7 @@ bool FScriptLoader::ParseInfo(MapData * map)
}
delete lump;
delete[] lump;
return HasScripts;
}
@ -342,7 +342,7 @@ void T_LoadScripts(MapData *map)
// the default translator is being used.
// Custom translators will not be patched.
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];
SimpleLineTranslations[270] = SimpleLineTranslations[272];

View file

@ -101,15 +101,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileTarget)
//
// A_VileAttack
//
// A_VileAttack flags
#define VAF_DMGTYPEAPPLYTODIRECT 1
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
{
ACTION_PARAM_START(6);
ACTION_PARAM_START(7);
ACTION_PARAM_SOUND(snd,0);
ACTION_PARAM_INT(dmg,1);
ACTION_PARAM_INT(blastdmg,2);
ACTION_PARAM_INT(blastrad,3);
ACTION_PARAM_FIXED(thrust,4);
ACTION_PARAM_NAME(dmgtype,5);
ACTION_PARAM_INT(flags,6);
AActor *fire, *target;
angle_t an;
@ -123,8 +128,16 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
return;
S_Sound (self, CHAN_WEAPON, snd, 1, ATTN_NORM);
P_TraceBleed (dmg, target);
P_DamageMobj (target, self, self, dmg, NAME_None);
int newdam;
if (flags & VAF_DMGTYPEAPPLYTODIRECT)
newdam = P_DamageMobj (target, self, self, dmg, dmgtype);
else
newdam = P_DamageMobj (target, self, self, dmg, NAME_None);
P_TraceBleed (newdam > 0 ? newdam : dmg, target);
an = self->angle >> ANGLETOFINESHIFT;
fire = self->tracer;
@ -136,7 +149,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack)
target->y - FixedMul (24*FRACUNIT, finesine[an]),
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);
}

View file

@ -79,16 +79,22 @@ DEFINE_ACTION_FUNCTION(AActor, A_BrainDie)
// New dmflag: Kill all boss spawned monsters before ending the level.
if (dmflags2 & DF2_KILLBOSSMONST)
{
TThinkerIterator<AActor> it;
AActor *mo;
while ((mo = it.Next()))
int count; // Repeat until we have no more boss-spawned monsters.
do // (e.g. Pain Elementals can spawn more to kill upon death.)
{
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,
DMG_NO_ARMOR|DMG_FORCED|DMG_THRUSTLESS|DMG_NO_FACTOR);
if (mo->health > 0 && mo->flags4 & MF4_BOSSSPAWNED)
{
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);
@ -118,12 +124,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit)
// spawn brain missile
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)
{
// 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->master = self;
// [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);
}
if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true, NULL))
newmobj->SetState (newmobj->SeeState);
// Make it act as if it was around when the player first made noise
// (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))
{
// telefrag anything in this spot

View file

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

View file

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

View file

@ -20,7 +20,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SargAttack)
if (self->CheckMeleeRange ())
{
int damage = ((pr_sargattack()%10)+1)*4;
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
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;
S_Sound (self, CHAN_WEAPON, "imp/melee", 1, ATTN_NORM);
P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (damage, self->target, self);
int newdam = P_DamageMobj (self->target, self, self, damage, NAME_Melee);
P_TraceBleed (newdam > 0 ? newdam : damage, self->target, self);
return;
}

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