diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..09e3fb4c46 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index dfedd7f195..7088821970 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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} $ + 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" ) diff --git a/bzip2/CMakeLists.txt b/bzip2/CMakeLists.txt index 5c1255604b..6006f491cb 100644 --- a/bzip2/CMakeLists.txt +++ b/bzip2/CMakeLists.txt @@ -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 diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index c2cbe8d1b2..ee50c4a0af 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -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" ) diff --git a/dumb/include/dumb.h b/dumb/include/dumb.h index 7af4a1d71f..cafcfea3b8 100644 --- a/dumb/include/dumb.h +++ b/dumb/include/dumb.h @@ -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 diff --git a/dumb/include/internal/blip_buf.h b/dumb/include/internal/blip_buf.h new file mode 100644 index 0000000000..7f687c3e9c --- /dev/null +++ b/dumb/include/internal/blip_buf.h @@ -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 diff --git a/dumb/include/internal/it.h b/dumb/include/internal/it.h index e248ade796..922d9bed05 100644 --- a/dumb/include/internal/it.h +++ b/dumb/include/internal/it.h @@ -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 */ diff --git a/dumb/src/core/makeduh.c b/dumb/src/core/makeduh.c index 598b637bce..24f29259b1 100644 --- a/dumb/src/core/makeduh.c +++ b/dumb/src/core/makeduh.c @@ -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; +} diff --git a/dumb/src/core/rawsig.c b/dumb/src/core/rawsig.c index a1edd60d04..2287bb0684 100644 --- a/dumb/src/core/rawsig.c +++ b/dumb/src/core/rawsig.c @@ -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; } diff --git a/dumb/src/helpers/blip_buf.c b/dumb/src/helpers/blip_buf.c new file mode 100644 index 0000000000..03c111198f --- /dev/null +++ b/dumb/src/helpers/blip_buf.c @@ -0,0 +1,354 @@ +/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ + +#include "internal/blip_buf.h" + +#include +#include +#include +#include + +/* 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; +} diff --git a/dumb/src/helpers/resamp2.inc b/dumb/src/helpers/resamp2.inc index 84826ce512..de65dfb340 100644 --- a/dumb/src/helpers/resamp2.inc +++ b/dumb/src/helpers/resamp2.inc @@ -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 diff --git a/dumb/src/helpers/resamp3.inc b/dumb/src/helpers/resamp3.inc index 3d61bfff1a..05c0dfbb7e 100644 --- a/dumb/src/helpers/resamp3.inc +++ b/dumb/src/helpers/resamp3.inc @@ -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 diff --git a/dumb/src/helpers/resample.c b/dumb/src/helpers/resample.c index db42ee7ae4..4424a4934a 100644 --- a/dumb/src/helpers/resample.c +++ b/dumb/src/helpers/resample.c @@ -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) { \ diff --git a/dumb/src/helpers/resample.inc b/dumb/src/helpers/resample.inc index eaef2976c1..364e36ccd6 100644 --- a/dumb/src/helpers/resample.inc +++ b/dumb/src/helpers/resample.inc @@ -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); \ diff --git a/dumb/src/it/itmisc.c b/dumb/src/it/itmisc.c index 1045af8fa3..5a45d3192e 100644 --- a/dumb/src/it/itmisc.c +++ b/dumb/src/it/itmisc.c @@ -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); } diff --git a/dumb/src/it/itread.c b/dumb/src/it/itread.c index b06fbbca55..1f29ad4d9e 100644 --- a/dumb/src/it/itread.c +++ b/dumb/src/it/itread.c @@ -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); } } diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index 6ea048278d..4f3d50dfa8 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -29,15 +29,38 @@ #define END_RAMPING #define RAMP_DOWN -static IT_PLAYING *alloc_playing(DUMB_IT_SIGRENDERER *itsr) +static IT_PLAYING *new_playing(DUMB_IT_SIGRENDERER *itsr) { + IT_PLAYING *r; + if (itsr->free_playing != NULL) { - IT_PLAYING *pl = itsr->free_playing; - itsr->free_playing = pl->next; - return pl; + r = itsr->free_playing; + itsr->free_playing = r->next; + blip_clear(r->resampler.blip_buffer[0]); + blip_clear(r->resampler.blip_buffer[1]); + return r; } - return (IT_PLAYING *)malloc(sizeof(IT_PLAYING)); + r = (IT_PLAYING *)malloc(sizeof(IT_PLAYING)); + if (r) + { + r->resampler.blip_buffer[0] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[0] ) + { + free( r ); + return NULL; + } + r->resampler.blip_buffer[1] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[1] ) + { + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + blip_set_rates(r->resampler.blip_buffer[0], 65536, 1); + blip_set_rates(r->resampler.blip_buffer[1], 65536, 1); + } + return r; } static void free_playing(DUMB_IT_SIGRENDERER *itsr, IT_PLAYING *playing) @@ -46,6 +69,13 @@ static void free_playing(DUMB_IT_SIGRENDERER *itsr, IT_PLAYING *playing) itsr->free_playing = playing; } +static void free_playing_orig(IT_PLAYING * r) +{ + blip_delete( r->resampler.blip_buffer[1] ); + blip_delete( r->resampler.blip_buffer[0] ); + free( r ); +} + static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel) { IT_PLAYING *dst; @@ -135,6 +165,19 @@ static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANN dst->resampler = src->resampler; dst->resampler.pickup_data = dst; + dst->resampler.blip_buffer[0] = blip_dup( dst->resampler.blip_buffer[0] ); + if ( !dst->resampler.blip_buffer[0] ) + { + free( dst ); + return NULL; + } + dst->resampler.blip_buffer[1] = blip_dup( dst->resampler.blip_buffer[1] ); + if ( !dst->resampler.blip_buffer[1] ) + { + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } dst->time_lost = src->time_lost; //dst->output = src->output; @@ -175,6 +218,7 @@ static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) dst->new_note_action = src->new_note_action; dst->arpeggio = src->arpeggio; + dst->arpeggio_shift = src->arpeggio_shift; dst->retrig = src->retrig; dst->xm_retrig = src->xm_retrig; dst->retrig_tick = src->retrig_tick; @@ -192,6 +236,7 @@ static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) dst->last_toneslide_tick = src->last_toneslide_tick; dst->ptm_toneslide = src->ptm_toneslide; dst->ptm_last_toneslide = src->ptm_last_toneslide; + dst->okt_toneslide = src->okt_toneslide; dst->destnote = src->destnote; dst->glissando = src->glissando; @@ -231,6 +276,10 @@ static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) dst->xm_lastX1 = src->xm_lastX1; dst->xm_lastX2 = src->xm_lastX2; + dst->inv_loop_delay = src->inv_loop_delay; + dst->inv_loop_speed = src->inv_loop_speed; + dst->inv_loop_offset = src->inv_loop_offset; + dst->playing = dup_playing(src->playing, dst, src); @@ -760,6 +809,7 @@ static void reset_channel_effects(IT_CHANNEL *channel) channel->panslide = 0; channel->channelvolslide = 0; channel->arpeggio = 0; + channel->arpeggio_shift = 0; channel->retrig = 0; if (channel->xm_retrig) { channel->xm_retrig = 0; @@ -775,6 +825,7 @@ static void reset_channel_effects(IT_CHANNEL *channel) channel->ptm_last_toneslide = 0; channel->ptm_toneslide = 0; channel->toneslide_tick = 0; + channel->okt_toneslide = 0; if (channel->playing) { channel->playing->vibrato_n = 0; channel->playing->tremolo_speed = 0; @@ -854,6 +905,15 @@ static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data) +static void it_pickup_stop_after_reverse(DUMB_RESAMPLER *resampler, void *data) +{ + (void)data; + + resampler->dir = 0; +} + + + static void it_playing_update_resamplers(IT_PLAYING *playing) { if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { @@ -874,8 +934,13 @@ static void it_playing_update_resamplers(IT_PLAYING *playing) playing->resampler.pickup = &it_pickup_pingpong_loop; else playing->resampler.pickup = &it_pickup_loop; - } else { - if (playing->sample->flags & IT_SAMPLE_SUS_LOOP) + } else if (playing->flags & IT_PLAYING_REVERSE) { + playing->resampler.start = 0; + playing->resampler.end = playing->sample->length; + playing->resampler.dir = -1; + playing->resampler.pickup = &it_pickup_stop_after_reverse; + } else { + if (playing->sample->flags & IT_SAMPLE_SUS_LOOP) playing->resampler.start = playing->sample->sus_loop_start; else playing->resampler.start = 0; @@ -981,6 +1046,15 @@ static void update_retrig(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) } +static void update_smooth_effects_playing(IT_PLAYING *playing) +{ + playing->vibrato_time += playing->vibrato_n * + (playing->vibrato_speed << 2); + playing->tremolo_time += playing->tremolo_speed << 2; + playing->panbrello_time += playing->panbrello_speed; + if (playing->panbrello_waveform == 3) + playing->panbrello_random = (rand() % 129) - 64; +} static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) { @@ -991,12 +1065,42 @@ static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) IT_PLAYING *playing = channel->playing; if (playing) { - playing->vibrato_time += playing->vibrato_n * - (playing->vibrato_speed << 2); - playing->tremolo_time += playing->tremolo_speed << 2; - playing->panbrello_time += playing->panbrello_speed; - if (playing->panbrello_waveform == 3) - playing->panbrello_random = (rand() % 129) - 64; + update_smooth_effects_playing(playing); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING *playing = sigrenderer->playing[i]; + + if (playing) { + update_smooth_effects_playing(playing); + } + } +} + + +static const unsigned char pt_tab_invloop[16] = +{ + 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, + 0x0F, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80 +}; + +static void update_invert_loop(IT_CHANNEL *channel, IT_SAMPLE *sample) +{ + channel->inv_loop_delay += pt_tab_invloop[channel->inv_loop_speed]; + if (channel->inv_loop_delay >= 0x80) + { + channel->inv_loop_delay = 0; + + if (sample && ((sample->flags & (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) == (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) && !(sample->flags & (IT_SAMPLE_STEREO | IT_SAMPLE_16BIT))) + { + if (sample->loop_end - sample->loop_start >= 4) + { + channel->inv_loop_offset++; + if (channel->inv_loop_offset >= (sample->loop_end - sample->loop_start)) channel->inv_loop_offset = 0; + + ((char *)sample->data)[sample->loop_start + channel->inv_loop_offset] ^= 0xFF; + } } } } @@ -1005,7 +1109,7 @@ static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) { - int i; + int i, j; if (sigrenderer->globalvolslide) { sigrenderer->globalvolume += sigrenderer->globalvolslide; @@ -1042,10 +1146,11 @@ static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) } if (channel->volslide) { + int clip = (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; channel->volume += channel->volslide; - if (channel->volume > 64) { + if (channel->volume > clip) { if (channel->volslide >= 0) - channel->volume = 64; + channel->volume = clip; else channel->volume = 0; } @@ -1088,81 +1193,135 @@ static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) } if (channel->playing) channel->playing->channel_volume = channel->channelvolume; + for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) { + if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) + sigrenderer->playing[j]->channel_volume = channel->channelvolume; + } } update_tremor(channel); - channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8); - channel->arpeggio &= 0xFFF; + if (channel->arpeggio_shift) channel->arpeggio = (channel->arpeggio << 8) | (channel->arpeggio >> channel->arpeggio_shift); update_retrig(sigrenderer, channel); - if (playing) { - playing->slide += channel->portamento; + if (channel->inv_loop_speed) update_invert_loop(channel, playing ? playing->sample : NULL); - if (channel->ptm_toneslide) { + for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) { + if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) break; + } + + if (playing || j < DUMB_IT_N_NNA_CHANNELS) { + if (playing) playing->slide += channel->portamento; + for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) { + if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) + sigrenderer->playing[j]->slide += channel->portamento; + } + + if (channel->okt_toneslide) { + if (channel->okt_toneslide--) { + if (playing) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + } + for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) { + if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) { + IT_PLAYING *playing = sigrenderer->playing[j]; + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + } + } + } + } else if (channel->ptm_toneslide) { if (--channel->toneslide_tick == 0) { channel->toneslide_tick = channel->ptm_toneslide; - playing->note += channel->toneslide; - if (playing->note >= 120) { - if (channel->toneslide < 0) playing->note = 0; - else playing->note = 119; - } - channel->note = channel->truenote = playing->note; - if (channel->toneslide_retrig) { - it_playing_reset_resamplers(playing, 0); + if (playing) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + channel->note = channel->truenote = playing->note; + if (channel->toneslide_retrig) { + it_playing_reset_resamplers(playing, 0); #ifdef END_RAMPING - playing->declick_stage = 0; - playing->declick_volume = 1.f / 256.f; + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; #endif + } + } + for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) { + if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) { + IT_PLAYING *playing = sigrenderer->playing[j]; + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + if (channel->toneslide_retrig) { + it_playing_reset_resamplers(playing, 0); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } + } } } } - if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) { - if (channel->toneporta && channel->destnote < 120) { - int currpitch = ((playing->note - 60) << 8) + playing->slide; - int destpitch = (channel->destnote - 60) << 8; - if (currpitch > destpitch) { - currpitch -= channel->toneporta; - if (currpitch < destpitch) { - currpitch = destpitch; - channel->destnote = IT_NOTE_OFF; - } - } else if (currpitch < destpitch) { - currpitch += channel->toneporta; + if (playing) { + if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) { + if (channel->toneporta && channel->destnote < 120) { + int currpitch = ((playing->note - 60) << 8) + playing->slide; + int destpitch = (channel->destnote - 60) << 8; if (currpitch > destpitch) { - currpitch = destpitch; - channel->destnote = IT_NOTE_OFF; + currpitch -= channel->toneporta; + if (currpitch < destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } + } else if (currpitch < destpitch) { + currpitch += channel->toneporta; + if (currpitch > destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } } + playing->slide = currpitch - ((playing->note - 60) << 8); } - playing->slide = currpitch - ((playing->note - 60) << 8); - } - } else { - if (channel->toneporta && channel->destnote < 120) { - float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR); + } else { + if (channel->toneporta && channel->destnote < 120) { + float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR); - float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); - /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */ + float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); + /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */ - float deltaslid = deltanote - playing->slide * amiga_multiplier; + float deltaslid = deltanote - playing->slide * amiga_multiplier; - float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote); - if (deltaslid < destdelta) { - playing->slide -= channel->toneporta; - deltaslid = deltanote - playing->slide * amiga_multiplier; - if (deltaslid > destdelta) { - playing->note = channel->destnote; - playing->slide = 0; - channel->destnote = IT_NOTE_OFF; - } - } else { - playing->slide += channel->toneporta; - deltaslid = deltanote - playing->slide * amiga_multiplier; + float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote); if (deltaslid < destdelta) { - playing->note = channel->destnote; - playing->slide = 0; - channel->destnote = IT_NOTE_OFF; + playing->slide -= channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid > destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } + } else { + playing->slide += channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid < destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } } } } @@ -1199,8 +1358,14 @@ static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY * case IT_S: { unsigned char effectvalue = entry->effectvalue; - if (effectvalue == 0) - effectvalue = channel->lastS; + if (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) { + if (effectvalue == 0) + effectvalue = channel->lastDKL; + channel->lastDKL = effectvalue; + } else { + if (effectvalue == 0) + effectvalue = channel->lastS; + } channel->lastS = effectvalue; switch (effectvalue >> 4) { case IT_S_PATTERN_LOOP: @@ -1211,6 +1376,8 @@ static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY * if (!channel->played_patjump) channel->played_patjump = bit_array_create(256); else { + if ( channel->played_patjump_order != 0xFFFE && channel->played_patjump_order != sigrenderer->order ) + bit_array_merge(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); //if (channel->played_patjump_order != sigrenderer->order) bit_array_reset(channel->played_patjump); } @@ -1225,13 +1392,13 @@ static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY * int n; bit_array_destroy(channel->played_patjump); channel->played_patjump = bit_array_create(256); - for (n = 0; n < 256; n++) + for (n = channel->pat_loop_row; n <= sigrenderer->row; n++) bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + n); channel->played_patjump_order = sigrenderer->order; } else if (channel->played_patjump_order == sigrenderer->order) { bit_array_set(channel->played_patjump, sigrenderer->row); bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); - bit_array_reset(channel->played_patjump); + //bit_array_reset(channel->played_patjump); } #endif channel->pat_loop_count = v; @@ -1258,7 +1425,7 @@ static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY * if (channel->played_patjump_order == sigrenderer->order) { bit_array_set(channel->played_patjump, sigrenderer->row); bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); - bit_array_reset(channel->played_patjump); + //bit_array_reset(channel->played_patjump); } #endif sigrenderer->breakrow = channel->pat_loop_row; @@ -1290,8 +1457,8 @@ static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY * - If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60. - Theory: breakrow is not cleared when it's a pattern loop effect! */ - //if (sigrenderer->processrow < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :( - // sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */ + if ((sigrenderer->processrow | 0xC00) < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :( + sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */ } else channel->pat_loop_row = sigrenderer->processrow + 1; #ifdef BIT_ARRAY_BULLSHIT @@ -1419,7 +1586,8 @@ static void it_note_off(IT_PLAYING *playing) static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) { if (channel->playing) { - if (!(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON)) + if (!channel->instrument || channel->instrument > sigdata->n_instruments || + !(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON)) //if (!(entry->mask & IT_ENTRY_INSTRUMENT)) // dunno what that was there for ... channel->volume = 0; @@ -1467,7 +1635,7 @@ static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *chan nna = channel->playing->instrument->new_note_action; #endif - if ((sigdata->flags & IT_USE_INSTRUMENTS) && (channel->playing->enabled_envelopes) && channel->playing->instnum == channel->instrument) { + if (!(channel->playing->flags & IT_PLAYING_DEAD) && (sigdata->flags & IT_USE_INSTRUMENTS) && (channel->playing->enabled_envelopes) && channel->playing->instnum == channel->instrument) { IT_PLAYING * playing = channel->playing; IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1]; if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) { @@ -1523,7 +1691,7 @@ static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *chan for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { IT_PLAYING * playing = sigrenderer->playing[i]; if (playing && playing->channel == channel && playing->instrument->dup_check_type) { - int match = 0; + int match = 1; switch (playing->instrument->dup_check_type) { case DCT_NOTE: @@ -1580,7 +1748,7 @@ static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *chan if (channel->playing) free_playing(sigrenderer, channel->playing); - channel->playing = alloc_playing(sigrenderer); + channel->playing = new_playing(sigrenderer); if (!channel->playing) return; @@ -1588,7 +1756,8 @@ static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *chan if (!flags && sigdata->flags & IT_USE_INSTRUMENTS) { for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { IT_PLAYING * playing = sigrenderer->playing[i]; - if (playing && (playing->enabled_envelopes) && playing->instnum == channel->instrument) { + if (!playing || playing->channel != channel) continue; + if (playing->enabled_envelopes && playing->instnum == channel->instrument) { IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1]; if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) { flags |= 1; @@ -1878,10 +2047,19 @@ static void xm_envelope_calculate_value(IT_ENVELOPE *envelope, IT_PLAYING_ENVELO extern const char xm_convert_vibrato[]; +const char mod_convert_vibrato[] = { + IT_VIBRATO_SINE, + IT_VIBRATO_RAMP_UP, /* this will be inverted by IT_OLD_EFFECTS */ + IT_VIBRATO_XM_SQUARE, + IT_VIBRATO_XM_SQUARE +}; + /* Returns 1 if a callback caused termination of playback. */ static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) { DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_PLAYING *playing; + int i; IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; @@ -1912,7 +2090,18 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than /*if (entry->effectvalue == 255) if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) return 1;*/ - sigrenderer->tick = sigrenderer->speed = entry->effectvalue; + if (sigdata->flags & IT_WAS_AN_STM) + { + int n = entry->effectvalue; + if (n >= 32) + { + sigrenderer->tick = sigrenderer->speed = n; + } + } + else + { + sigrenderer->tick = sigrenderer->speed = entry->effectvalue; + } } else if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { #ifdef BIT_ARRAY_BULLSHIT @@ -1938,10 +2127,17 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than break; case IT_VOLSLIDE_VIBRATO: - if (channel->playing) { - channel->playing->vibrato_speed = channel->lastHspeed; - channel->playing->vibrato_depth = channel->lastHdepth; - channel->playing->vibrato_n++; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = channel->lastHspeed; + playing->vibrato_depth = channel->lastHdepth; + playing->vibrato_n++; + } } /* Fall through and process volume slide. */ case IT_VOLUME_SLIDE: @@ -1954,24 +2150,43 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than v = channel->lastDKL; channel->lastDKL = v; } - if ((v & 0x0F) == 0) { /* Dx0 */ - channel->volslide = v >> 4; - if (channel->volslide == 15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume += 15; - if (channel->volume > 64) channel->volume = 64; + if (!(sigdata->flags & IT_WAS_AN_XM)) { + int clip = (sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; + if ((v & 0x0F) == 0x0F) { + if (!(v & 0xF0)) { + channel->volslide = -15; + channel->volume -= 15; + if (channel->volume > clip) channel->volume = 0; + } else { + channel->volume += v >> 4; + if (channel->volume > clip) channel->volume = clip; + } + } else if ((v & 0xF0) == 0xF0) { + if (!(v & 0x0F)) { + channel->volslide = 15; + channel->volume += 15; + if (channel->volume > clip) channel->volume = clip; + } else { + channel->volume -= v & 15; + if (channel->volume > clip) channel->volume = 0; + } + } else if (!(v & 0x0F)) { + channel->volslide = v >> 4; + } else { + channel->volslide = -(v & 15); } - } else if ((v & 0xF0) == 0) { /* D0x */ - channel->volslide = -v; - if (channel->volslide == -15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume -= 15; + } else { + if ((v & 0x0F) == 0) { /* Dx0 */ + channel->volslide = v >> 4; + } else if ((v & 0xF0) == 0) { /* D0x */ + channel->volslide = -v; + } else if ((v & 0x0F) == 0x0F) { /* DxF */ + channel->volume += v >> 4; + if (channel->volume > 64) channel->volume = 64; + } else if ((v & 0xF0) == 0xF0) { /* DFx */ + channel->volume -= v & 15; if (channel->volume > 64) channel->volume = 0; } - } else if ((v & 0x0F) == 0x0F) { /* DxF */ - channel->volume += v >> 4; - if (channel->volume > 64) channel->volume = 64; - } else if ((v & 0xF0) == 0xF0) { /* DFx */ - channel->volume -= v & 15; - if (channel->volume > 64) channel->volume = 0; } } break; @@ -2009,20 +2224,31 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than else channel->xm_lastX2 = v & 15; } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; } else { if (v == 0) v = channel->lastEF; channel->lastEF = v; } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide -= (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide -= (v & 15) << 2; - else if (sigdata->flags & IT_WAS_A_669) - channel->portamento -= v << 3; - else - channel->portamento -= v << 4; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide -= (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide -= (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento -= v << 3; + else if (i < 0) + channel->portamento -= v << 4; + } } } break; @@ -2040,20 +2266,31 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than else channel->xm_lastX1 = v & 15; } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; } else { if (v == 0) v = channel->lastEF; channel->lastEF = v; } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide += (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide += (v & 15) << 2; - else if (sigdata->flags & IT_WAS_A_669) - channel->portamento += v << 3; - else - channel->portamento += v << 4; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide += (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide += (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento += v << 3; + else if (i < 0) + channel->portamento += v << 4; + } } } break; @@ -2096,16 +2333,23 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than if (depth == 0) depth = channel->lastHdepth; else { - if (sigdata->flags & IT_OLD_EFFECTS) + if (sigdata->flags & IT_OLD_EFFECTS && !(sigdata->flags & IT_WAS_A_MOD)) depth <<= 3; else depth <<= 2; channel->lastHdepth = depth; } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } } } } @@ -2113,13 +2357,20 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than case IT_TREMOR: { unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastI; + if (v == 0) { + if (sigdata->flags & IT_WAS_AN_S3M) + v = channel->lastDKL; + else + v = channel->lastI; + } else if (!(sigdata->flags & IT_OLD_EFFECTS)) { if (v & 0xF0) v -= 0x10; if (v & 0x0F) v -= 0x01; } - channel->lastI = v; + if (sigdata->flags & IT_WAS_AN_S3M) + channel->lastDKL = v; + else + channel->lastI = v; channel->tremor_time |= 128; } update_tremor(channel); @@ -2131,11 +2382,18 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than * and we use lastJ for portamento down instead. */ if (!(sigdata->flags & IT_WAS_AN_XM)) { - if (v == 0) - v = channel->lastJ; - channel->lastJ = v; + if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastJ; + channel->lastJ = v; + } } - channel->arpeggio = v; + channel->arpeggio = ((v & 0xF0) << 4) | (v & 0x0F); + channel->arpeggio_shift = 16; } break; case IT_SET_CHANNEL_VOLUME: @@ -2147,8 +2405,15 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than else channel->channelvolume = 64; #endif - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) + playing->channel_volume = channel->channelvolume; + } break; case IT_CHANNEL_VOLUME_SLIDE: { @@ -2169,8 +2434,15 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than if (channel->channelvolume > 64) channel->channelvolume = 0; } else break; - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) + playing->channel_volume = channel->channelvolume; + } } } break; @@ -2271,11 +2543,16 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than if (sigdata->flags & IT_WAS_AN_XM) { if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F; if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0; + channel->lastQ = v; + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; } else { if (v == 0) v = channel->lastQ; + channel->lastQ = v; } - channel->lastQ = v; if ((v & 0x0F) == 0) v |= 0x01; channel->retrig = v; if (entry->mask & IT_ENTRY_NOTE) { @@ -2300,21 +2577,38 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than break; case IT_TREMOLO: { - unsigned char speed = entry->effectvalue >> 4; - unsigned char depth = entry->effectvalue & 15; - if (speed == 0) - speed = channel->lastRspeed; - channel->lastRspeed = speed; - if (depth == 0) - depth = channel->lastRdepth; - channel->lastRdepth = depth; - if (channel->playing) { - channel->playing->tremolo_speed = speed; - channel->playing->tremolo_depth = depth; + unsigned char speed, depth; + if (sigdata->flags & IT_WAS_AN_S3M) { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + speed = v >> 4; + depth = v & 15; + } else { + speed = entry->effectvalue >> 4; + depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastRspeed; + channel->lastRspeed = speed; + if (depth == 0) + depth = channel->lastRdepth; + channel->lastRdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->tremolo_speed = speed; + playing->tremolo_depth = depth; + } } } break; - case IT_S: + case IT_S: { /* channel->lastS was set in update_pattern_variables(). */ unsigned char effectvalue = channel->lastS; @@ -2339,7 +2633,8 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than case IT_S_SET_VIBRATO_WAVEFORM: { int waveform = effectvalue & 3; - if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; channel->vibrato_waveform = waveform; if (channel->playing) { channel->playing->vibrato_waveform = waveform; @@ -2351,7 +2646,8 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than case IT_S_SET_TREMOLO_WAVEFORM: { int waveform = effectvalue & 3; - if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; channel->tremolo_waveform = waveform; if (channel->playing) { channel->playing->tremolo_waveform = waveform; @@ -2472,7 +2768,13 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than channel->playing->panbrello_depth = 0; break; case IT_S_SET_SURROUND_SOUND: - if ((effectvalue & 15) == 1) { + if ((effectvalue & 15) == 15) { + if (channel->playing && channel->playing->sample && + !(channel->playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP))) { + channel->playing->flags |= IT_PLAYING_REVERSE; + it_playing_reset_resamplers( channel->playing, channel->playing->sample->length - 1 ); + } + } else if ((effectvalue & 15) == 1) { channel->pan = IT_SURROUND; channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; } @@ -2493,7 +2795,10 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than } break; case IT_S_SET_MIDI_MACRO: - channel->SFmacro = effectvalue & 15; + if ((sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == (IT_WAS_AN_XM | IT_WAS_A_MOD)) { + channel->inv_loop_speed = effectvalue & 15; + update_invert_loop(channel, channel->playing ? channel->playing->sample : NULL); + } else channel->SFmacro = effectvalue & 15; break; } } @@ -2526,10 +2831,17 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than depth <<= 1; channel->lastHdepth = depth; } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } } } break; @@ -2706,6 +3018,63 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than channel->toneslide_tick += channel->ptm_toneslide; } break; + + case IT_OKT_NOTE_SLIDE_DOWN: + case IT_OKT_NOTE_SLIDE_DOWN_ROW: + channel->toneslide = -entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_DOWN) ? 255 : 1; + break; + + case IT_OKT_NOTE_SLIDE_UP: + case IT_OKT_NOTE_SLIDE_UP_ROW: + channel->toneslide = entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_UP) ? 255 : 1; + break; + + case IT_OKT_ARPEGGIO_3: + case IT_OKT_ARPEGGIO_4: + case IT_OKT_ARPEGGIO_5: + { + unsigned char low = -(entry->effectvalue >> 4); + unsigned char high = entry->effectvalue & 0x0F; + + switch (entry->effect) + { + case IT_OKT_ARPEGGIO_3: + channel->arpeggio = (low << 16) | high; + channel->arpeggio_shift = 16; + break; + + case IT_OKT_ARPEGGIO_4: + channel->arpeggio = (high << 16) | low; + channel->arpeggio_shift = 24; + break; + + case IT_OKT_ARPEGGIO_5: + channel->arpeggio = (high << 16) | (high << 8); + channel->arpeggio_shift = 16; + break; + } + } + break; + + case IT_OKT_VOLUME_SLIDE_DOWN: + if ( entry->effectvalue <= 16 ) channel->volslide = -entry->effectvalue; + else + { + channel->volume -= entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 0; + } + break; + + case IT_OKT_VOLUME_SLIDE_UP: + if ( entry->effectvalue <= 16 ) channel->volslide = entry->effectvalue; + else + { + channel->volume += entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 64; + } + break; } } @@ -2887,19 +3256,19 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent { DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - IT_PLAYING playing; - - playing.sample = 0; + IT_PLAYING * playing = NULL; if (entry->mask & IT_ENTRY_INSTRUMENT) { int oldsample = channel->sample; int oldvolume = channel->volume; + channel->inv_loop_offset = 0; channel->instrument = entry->instrument; instrument_to_sample(sigdata, channel); if (channel->playing && !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) && !((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) { - playing = *channel->playing; + playing = dup_playing(channel->playing, channel, channel); + if (!playing) return; if (!(sigdata->flags & IT_WAS_A_MOD)) { /* Retrigger vol/pan envelopes if enabled, and cancel fadeout. * Also reset vol/pan to that of _original_ instrument. @@ -2932,12 +3301,11 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent channel->playing = NULL; } } else { - if (!channel->playing) { - channel->playing = alloc_playing(sigrenderer); - if (!channel->playing) return; + if (channel->playing) { + free_playing(sigrenderer, channel->playing); } - *channel->playing = playing; - playing.sample = (IT_SAMPLE *)-1; + channel->playing = playing; + playing = NULL; channel->playing->declick_stage = 0; channel->playing->declick_volume = 1.f / 256.f; #else @@ -2983,7 +3351,11 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent if (channel->playing) { #ifdef RAMP_DOWN int i; - if (playing.sample) *channel->playing = playing; + if (playing) { + free_playing(sigrenderer, channel->playing); + channel->playing = playing; + playing = NULL; + } for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { if (!sigrenderer->playing[i]) { channel->playing->declick_stage = 2; @@ -3001,6 +3373,7 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent channel->playing = NULL; #endif } + if (playing) free_playing(sigrenderer, playing); return; } else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { /* Don't retrigger note; portamento in the volume column. */ @@ -3013,20 +3386,26 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent channel->destnote = IT_NOTE_OFF; if (!channel->playing) { - channel->playing = alloc_playing(sigrenderer); - if (!channel->playing) + channel->playing = new_playing(sigrenderer); + if (!channel->playing) { + if (playing) free_playing(sigrenderer, playing); return; + } // Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone. retrigger_xm_envelopes(channel->playing); } #ifdef RAMP_DOWN - else if (playing.sample != (IT_SAMPLE *)-1) { + else if (playing) { /* volume rampy stuff! move note to NNA */ int i; - IT_PLAYING * ptemp = alloc_playing(sigrenderer); - if (!ptemp) return; - if (playing.sample) *ptemp = playing; - else *ptemp = *channel->playing; + IT_PLAYING * ptemp; + if (playing->sample) ptemp = playing; + else ptemp = channel->playing; + if (!ptemp) { + if (playing) free_playing(sigrenderer, playing); + return; + } + playing = NULL; for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { if (!sigrenderer->playing[i]) { ptemp->declick_stage = 2; @@ -3187,6 +3566,8 @@ static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *ent break; } } + + if (playing) free_playing(sigrenderer, playing); } @@ -3347,7 +3728,7 @@ static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYIN /* Returns 1 when fading should be initiated for a volume envelope. */ static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe, int flags) { - if (!(playing->enabled_envelopes & flags)) + if (!(playing->enabled_envelopes & flags) || !envelope->n_nodes) return 0; ASSERT(envelope->n_nodes > 0); @@ -3373,7 +3754,7 @@ static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLA } pe->tick++; - while (pe->tick >= envelope->node_t[pe->next_node]) { + while (pe->tick > envelope->node_t[pe->next_node]) { pe->next_node++; if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { if (pe->next_node > envelope->sus_loop_end) { @@ -3713,7 +4094,7 @@ static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) if ( channel->arpeggio > 0xFF ) playing->delta = playing->sample->C5_speed * (1.f / 65536.f); } - else*/ playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8);/* + else*/ playing->delta *= (float)pow(DUMB_SEMITONE_BASE, (signed char)(channel->arpeggio >> channel->arpeggio_shift));/* }*/ playing->filter_cutoff = channel->filter_cutoff; @@ -3830,8 +4211,10 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) sigrenderer->processrow = sigrenderer->breakrow; sigrenderer->breakrow = 0; for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0; - } else + } else { sigrenderer->processrow = sigrenderer->breakrow; + sigrenderer->breakrow = 0; // XXX lolwut + } if (sigrenderer->processorder == 0xFFFF) sigrenderer->processorder = sigrenderer->order - 1; @@ -3846,6 +4229,14 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) sigrenderer->processorder = -1; continue; } + if (sigdata->flags & IT_WAS_AN_OKT) { + /* Reset some things */ + sigrenderer->speed = sigdata->speed; + sigrenderer->tempo = sigdata->tempo; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) { + xm_note_off(sigdata, &sigrenderer->channel[n]); + } + } } n = sigdata->order[sigrenderer->processorder]; @@ -3943,7 +4334,9 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) return 1; } - if (!(sigdata->flags & IT_OLD_EFFECTS)) + if (sigdata->flags & IT_WAS_AN_OKT) + update_effects(sigrenderer); + else if (!(sigdata->flags & IT_OLD_EFFECTS)) update_smooth_effects(sigrenderer); } else { { @@ -3962,9 +4355,12 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) update_effects(sigrenderer); } } else { - speed0: - update_effects(sigrenderer); - update_tick_counts(sigrenderer); + if ( !(sigdata->flags & IT_WAS_AN_STM) || !(sigrenderer->tick & 15)) + { + speed0: + update_effects(sigrenderer); + update_tick_counts(sigrenderer); + } } if (sigrenderer->globalvolume == 0) { @@ -3987,7 +4383,12 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) process_all_playing(sigrenderer); { - LONG_LONG t = sigrenderer->sub_time_left + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + LONG_LONG t = ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + if ( sigrenderer->sigdata->flags & IT_WAS_AN_STM ) + { + t /= 16; + } + t += sigrenderer->sub_time_left; sigrenderer->time_left += (int)(t >> 16); sigrenderer->sub_time_left = (int)t & 65535; } @@ -4051,13 +4452,13 @@ static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *play vol = (rand() % 129) - 64; break; case 4: - vol = it_xm_squarewave[playing->vibrato_time]; + vol = it_xm_squarewave[playing->tremolo_time]; break; case 5: - vol = it_xm_ramp[playing->vibrato_time]; + vol = it_xm_ramp[playing->tremolo_time]; break; case 6: - vol = it_xm_ramp[255-playing->vibrato_time]; + vol = it_xm_ramp[255-((sigrenderer->sigdata->flags & IT_WAS_A_MOD)?playing->vibrato_time:playing->tremolo_time)]; break; } vol *= playing->tremolo_depth; @@ -4078,7 +4479,7 @@ static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *play volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f); if (volume && playing->instrument) { - if (playing->enabled_envelopes & IT_ENV_VOLUME) { + if (playing->enabled_envelopes & IT_ENV_VOLUME && playing->env_instrument->volume_envelope.n_nodes) { volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope); volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT); } @@ -4805,7 +5206,7 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha channel->channelvolume = sigdata->channel_volume[i]; channel->instrument = 0; channel->sample = 0; - channel->note = 0; + channel->note = IT_NOTE_OFF; channel->SFmacro = 0; channel->filter_cutoff = 127; channel->filter_resonance = 0; @@ -4820,6 +5221,7 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha channel->toneslide = 0; channel->ptm_toneslide = 0; channel->ptm_last_toneslide = 0; + channel->okt_toneslide = 0; channel->midi_state = 0; channel->lastvolslide = 0; channel->lastDKL = 0; @@ -4849,6 +5251,9 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha channel->xm_lastEB = 0; channel->xm_lastX1 = 0; channel->xm_lastX2 = 0; + channel->inv_loop_delay = 0; + channel->inv_loop_speed = 0; + channel->inv_loop_offset = 0; channel->playing = NULL; #ifdef BIT_ARRAY_BULLSHIT channel->played_patjump = NULL; @@ -5061,7 +5466,7 @@ static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_ } } - while (pos >= sigrenderer->time_left) { + while (pos > 0 && pos >= sigrenderer->time_left) { render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); pos -= sigrenderer->time_left; @@ -5161,7 +5566,7 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { if (sigrenderer->channel[i].playing) - free(sigrenderer->channel[i].playing); + free_playing_orig(sigrenderer->channel[i].playing); #ifdef BIT_ARRAY_BULLSHIT bit_array_destroy(sigrenderer->channel[i].played_patjump); #endif @@ -5169,12 +5574,12 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) if (sigrenderer->playing[i]) - free(sigrenderer->playing[i]); + free_playing_orig(sigrenderer->playing[i]); for (playing = sigrenderer->free_playing; playing != NULL; playing = next) { next = playing->next; - free(playing); + free_playing_orig(playing); } dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover); diff --git a/dumb/src/it/loadmod.c b/dumb/src/it/loadmod.c index 0a2d4019b5..adb4c283af 100644 --- a/dumb/src/it/loadmod.c +++ b/dumb/src/it/loadmod.c @@ -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); diff --git a/dumb/src/it/loadmod2.c b/dumb/src/it/loadmod2.c index d2aae8d0e3..83a416947b 100644 --- a/dumb/src/it/loadmod2.c +++ b/dumb/src/it/loadmod2.c @@ -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; } diff --git a/dumb/src/it/loadokt.c b/dumb/src/it/loadokt.c new file mode 100644 index 0000000000..9670714d81 --- /dev/null +++ b/dumb/src/it/loadokt.c @@ -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; +} diff --git a/dumb/src/it/loadokt2.c b/dumb/src/it/loadokt2.c new file mode 100644 index 0000000000..3d9ae447b0 --- /dev/null +++ b/dumb/src/it/loadokt2.c @@ -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; +} diff --git a/dumb/src/it/readam.c b/dumb/src/it/readam.c index 9910b1d823..6a4994df5f 100644 --- a/dumb/src/it/readam.c +++ b/dumb/src/it/readam.c @@ -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); diff --git a/dumb/src/it/readasy.c b/dumb/src/it/readasy.c index 9ab5a1f32d..5738b78a1b 100644 --- a/dumb/src/it/readasy.c +++ b/dumb/src/it/readasy.c @@ -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 diff --git a/dumb/src/it/readdsmf.c b/dumb/src/it/readdsmf.c index 275f37ffa4..5db09a75cc 100644 --- a/dumb/src/it/readdsmf.c +++ b/dumb/src/it/readdsmf.c @@ -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); diff --git a/dumb/src/it/readmod.c b/dumb/src/it/readmod.c index c2cb4b7bf1..801b1ce766 100644 --- a/dumb/src/it/readmod.c +++ b/dumb/src/it/readmod.c @@ -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; diff --git a/dumb/src/it/readmod2.c b/dumb/src/it/readmod2.c index 34fadf2351..a20ccd24ef 100644 --- a/dumb/src/it/readmod2.c +++ b/dumb/src/it/readmod2.c @@ -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; } diff --git a/dumb/src/it/readokt.c b/dumb/src/it/readokt.c new file mode 100644 index 0000000000..078929c5c3 --- /dev/null +++ b/dumb/src/it/readokt.c @@ -0,0 +1,558 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt.c - Code to read an Oktalyzer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#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); + } +} diff --git a/dumb/src/it/readokt2.c b/dumb/src/it/readokt2.c new file mode 100644 index 0000000000..66dd1f6d4a --- /dev/null +++ b/dumb/src/it/readokt2.c @@ -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; +} diff --git a/dumb/src/it/readoldpsm.c b/dumb/src/it/readoldpsm.c index 3c764af253..5c0441c788 100644 --- a/dumb/src/it/readoldpsm.c +++ b/dumb/src/it/readoldpsm.c @@ -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) { diff --git a/dumb/src/it/reads3m.c b/dumb/src/it/reads3m.c index c9266ac6c5..c55e9f6155 100644 --- a/dumb/src/it/reads3m.c +++ b/dumb/src/it/reads3m.c @@ -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); diff --git a/dumb/src/it/readstm.c b/dumb/src/it/readstm.c index 8b8e8c82d0..42f14e532b 100644 --- a/dumb/src/it/readstm.c +++ b/dumb/src/it/readstm.c @@ -20,6 +20,7 @@ // IT_STEREO... :o #include #include +#include #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); } } diff --git a/dumb/src/it/readxm.c b/dumb/src/it/readxm.c index 7d1b4657b8..7b92cb9548 100644 --- a/dumb/src/it/readxm.c +++ b/dumb/src/it/readxm.c @@ -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; diff --git a/dumb/src/it/xmeffect.c b/dumb/src/it/xmeffect.c index 2187a0b3f0..1aa489222b 100644 --- a/dumb/src/it/xmeffect.c +++ b/dumb/src/it/xmeffect.c @@ -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; diff --git a/dumb/vc6/dumb_static/dumb_static.vcproj b/dumb/vc6/dumb_static/dumb_static.vcproj index 128b3b9d90..3f1373cc31 100644 --- a/dumb/vc6/dumb_static/dumb_static.vcproj +++ b/dumb/vc6/dumb_static/dumb_static.vcproj @@ -877,6 +877,10 @@ RelativePath="..\..\src\helpers\barray.c" > + + @@ -1560,6 +1564,14 @@ RelativePath="..\..\src\it\loadmtm2.c" > + + + + @@ -1778,6 +1790,14 @@ RelativePath="..\..\src\it\readmtm.c" > + + + + @@ -1960,6 +1980,10 @@ + + diff --git a/game-music-emu/CMakeLists.txt b/game-music-emu/CMakeLists.txt index 7905f847ab..5c4a030434 100644 --- a/game-music-emu/CMakeLists.txt +++ b/game-music-emu/CMakeLists.txt @@ -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 diff --git a/game-music-emu/gme/Dual_Resampler.cpp b/game-music-emu/gme/Dual_Resampler.cpp index 8644517cad..fe2085803e 100644 --- a/game-music-emu/gme/Dual_Resampler.cpp +++ b/game-music-emu/gme/Dual_Resampler.cpp @@ -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 ); } diff --git a/game-music-emu/gme/Gbs_Emu.h b/game-music-emu/gme/Gbs_Emu.h index 93fe691e55..6cc6b474bc 100644 --- a/game-music-emu/gme/Gbs_Emu.h +++ b/game-music-emu/gme/Gbs_Emu.h @@ -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 ); } diff --git a/game-music-emu/gme/Gym_Emu.h b/game-music-emu/gme/Gym_Emu.h index 05419ea202..0f7a0e39cf 100644 --- a/game-music-emu/gme/Gym_Emu.h +++ b/game-music-emu/gme/Gym_Emu.h @@ -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 }; diff --git a/game-music-emu/gme/Music_Emu.h b/game-music-emu/gme/Music_Emu.h index 81e1e67cc7..6a2eaa1104 100644 --- a/game-music-emu/gme/Music_Emu.h +++ b/game-music-emu/gme/Music_Emu.h @@ -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 diff --git a/game-music-emu/gme/Nsf_Emu.h b/game-music-emu/gme/Nsf_Emu.h index e06b917276..fcce66a1e2 100644 --- a/game-music-emu/gme/Nsf_Emu.h +++ b/game-music-emu/gme/Nsf_Emu.h @@ -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 ); } diff --git a/game-music-emu/gme/Nsfe_Emu.h b/game-music-emu/gme/Nsfe_Emu.h index 561c3be0fc..00ac40399b 100644 --- a/game-music-emu/gme/Nsfe_Emu.h +++ b/game-music-emu/gme/Nsfe_Emu.h @@ -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() diff --git a/game-music-emu/gme/Spc_Emu.h b/game-music-emu/gme/Spc_Emu.h index 44b54c3095..0c4a7fb270 100644 --- a/game-music-emu/gme/Spc_Emu.h +++ b/game-music-emu/gme/Spc_Emu.h @@ -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() diff --git a/game-music-emu/gme/Vgm_Emu.h b/game-music-emu/gme/Vgm_Emu.h index bcb784d563..7a82fd109e 100644 --- a/game-music-emu/gme/Vgm_Emu.h +++ b/game-music-emu/gme/Vgm_Emu.h @@ -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() diff --git a/gdtoa/CMakeLists.txt b/gdtoa/CMakeLists.txt index 6fc394d8d4..4ad0be6de9 100644 --- a/gdtoa/CMakeLists.txt +++ b/gdtoa/CMakeLists.txt @@ -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 ) diff --git a/jpeg-6b/CMakeLists.txt b/jpeg-6b/CMakeLists.txt index c3a5f458c6..5c8b5972e5 100644 --- a/jpeg-6b/CMakeLists.txt +++ b/jpeg-6b/CMakeLists.txt @@ -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 diff --git a/lzma/CMakeLists.txt b/lzma/CMakeLists.txt index 6a2561c68c..7582712fea 100644 --- a/lzma/CMakeLists.txt +++ b/lzma/CMakeLists.txt @@ -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 diff --git a/specs/fmod_version.txt b/specs/fmod_version.txt index c285c50b98..1016b93883 100644 --- a/specs/fmod_version.txt +++ b/specs/fmod_version.txt @@ -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. diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index f61096f698..3241bf4c84 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -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 fields default to false unless mentioned otherwise. midtex3d = ; // Actors can walk on mid texture. checkswitchrange = ;// Switches can only be activated when vertically reachable. blockprojectiles = ;// Line blocks all projectiles - blockuse = ; // Line blocks all use actions - blocksight = ; // Line blocks monster line of sight - + blockuse = ; // Line blocks all use actions + blocksight = ; // Line blocks monster line of sight + blockhitscan = ; // Line blocks hitscan attacks + locknumber = ; // Line special is locked + arg0str = ; // 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 = ; // X scale for upper texture, Default = 1.0. @@ -135,16 +142,19 @@ Note: All fields default to false unless mentioned otherwise. light = ; // This side's light level. Default is 0. lightabsolute = ; // true = 'light' is an absolute value. Default is // relative to the owning sector's light level. - nofakecontrast = ; // Disables use of fake contrast on this sidedef. - smoothlighting = ; // Use smooth fake contrast. + lightfog = ; // true = This side's relative lighting is used even in + // foggy sectors. Default is to disable relative + // lighting in foggy sectors. + nofakecontrast = ; // Disables use of fake contrast on this sidedef. + smoothlighting = ; // Use smooth fake contrast. clipmidtex = ; // Side's mid textures are clipped to floor and ceiling. wrapmidtex = ; // Side's mid textures are wrapped. - nodecals = ; // Disables decals on the sidedef. + nodecals = ; // Disables decals on the sidedef. } - + sector { - xpanningfloor = ; // X texture offset of floor texture, Default = 0.0. + xpanningfloor = ; // X texture offset of floor texture, Default = 0.0. ypanningfloor = ; // Y texture offset of floor texture, Default = 0.0. xpanningceiling = ; // X texture offset of ceiling texture, Default = 0.0. ypanningceiling = ; // Y texture offset of ceiling texture, Default = 0.0. @@ -163,9 +173,9 @@ Note: All fields default to false unless mentioned otherwise. alphafloor = ; // translucency of floor plane (only has meaning with Sector_SetPortal) Default is 1.0. alphaceiling = ; // translucency of ceiling plane (only has meaning with Sector_SetPortal) Default is 1.0. renderstylefloor = ; // 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 = ; // 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 = ; // Sector's gravity. Default is 1.0. lightcolor = ; // Sector's light color as RRGGBB value, default = 0xffffff. fadecolor = ; // Sector's fog color as RRGGBB value, default = 0x000000. @@ -173,29 +183,38 @@ Note: All fields default to false unless mentioned otherwise. silent = ; // Actors in this sector make no sound, nofallingdamage = ; // Falling damage is disabled in this sector dropactors = ; // Actors drop with instantly moving floors (*) - norespawn = ; // Players can not respawn in this sector - soundsequence = ; // The sound sequence to play when this sector moves. Placing a - // sound sequence thing in the sector will override this property. - hidden = ; // if true this sector will not be drawn on the textured automap. + norespawn = ; // Players can not respawn in this sector + soundsequence = ; // The sound sequence to play when this sector moves. Placing a + // sound sequence thing in the sector will override this property. + hidden = ; // if true this sector will not be drawn on the textured automap. + waterzone = ; // 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# = // Unlike the base spec, # can range from 1-16. class# = // Unlike the base spec, # can range from 1-16. - conversation = // Assigns a conversation dialogue to this thing. - // Parameter is the conversation ID, 0 meaning none. - countsecret = ; // Picking up this actor counts as a secret. + conversation = // Assigns a conversation dialogue to this thing. + // Parameter is the conversation ID, 0 meaning none. + countsecret = ; // Picking up this actor counts as a secret. + arg0str = ; // Alternate string-based version of arg0 + gravity = ; // 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 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 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 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 =============================================================================== diff --git a/specs/usdf.txt b/specs/usdf.txt index fc955f02d5..1367ccfbf9 100644 --- a/specs/usdf.txt +++ b/specs/usdf.txt @@ -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 = ; // 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 = ; // Name that goes in the upper left hand corner panel = ; // 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 = ; // Item that is required for this option. amount = ; // Minimum amount of the item needed. } - displaycost = ; // Weather the cost should be + displaycost = ; // Whether the cost should be // displayed with the option. // If no cost is specified this should // be ignored. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbba7aa609..a2387bab1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 ) diff --git a/src/actionspecials.h b/src/actionspecials.h index 6e77bff7ac..1377def82a 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -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) diff --git a/src/actor.h b/src/actor.h index e7dece16b9..7680081821 100644 --- a/src/actor.h +++ b/src/actor.h @@ -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 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); diff --git a/src/am_map.cpp b/src/am_map.cpp index a6b03446fe..ad34995d0a 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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 MapArrow; static TArray CheatMapArrow; static TArray CheatKey; +static TArray 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 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(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(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<= 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<= 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(); diff --git a/src/b_bot.cpp b/src/b_bot.cpp index f5235e1cc4..26c8f99bd5 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -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; } diff --git a/src/b_game.cpp b/src/b_game.cpp index 7d8e94ffee..a4c0a18428 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -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); diff --git a/src/bbannouncer.cpp b/src/bbannouncer.cpp index b65671eafa..91583af8c3 100644 --- a/src/bbannouncer.cpp +++ b/src/bbannouncer.cpp @@ -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) || diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 0aa8ddc199..0744efeee8 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -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; diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 993b4677fa..cdf615ef49 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -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