diff --git a/CMakeLists.txt b/CMakeLists.txt index dd2c7730fc..3e375a2dff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,7 +123,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS( GME if( MSVC ) # Eliminate unreferenced functions and data # Perform identical COMDAT folding - set( REL_LINKER_FLAGS "/opt:ref /opt:icf /nodefaultlib:msvcrt /TSAWARE" ) + set( REL_LINKER_FLAGS "/opt:ref /opt:icf /nodefaultlib:msvcrt /TSAWARE /LARGEADDRESSAWARE" ) # String pooling # Function-level linking diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index e95c691542..b590aa1655 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -39,7 +39,6 @@ add_library( dumb src/helpers/memfile.c src/helpers/clickrem.c src/helpers/barray.c - src/helpers/tarray.c src/it/xmeffect.c src/it/readxm2.c src/it/readxm.c diff --git a/dumb/include/dumb.h b/dumb/include/dumb.h index c2c0aaa32b..385335da78 100644 --- a/dumb/include/dumb.h +++ b/dumb/include/dumb.h @@ -36,24 +36,24 @@ #endif -#define DUMB_MAJOR_VERSION 0 -#define DUMB_MINOR_VERSION 9 -#define DUMB_REVISION_VERSION 3 +#define DUMB_MAJOR_VERSION 1 +#define DUMB_MINOR_VERSION 0 +#define DUMB_REVISION_VERSION 0 #define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) -#define DUMB_VERSION_STR "0.9.3" +#define DUMB_VERSION_STR "1.0.0" #define DUMB_NAME "DUMB v" DUMB_VERSION_STR -#define DUMB_YEAR 2005 -#define DUMB_MONTH 8 -#define DUMB_DAY 7 +#define DUMB_YEAR 2015 +#define DUMB_MONTH 1 +#define DUMB_DAY 17 -#define DUMB_YEAR_STR2 "05" -#define DUMB_YEAR_STR4 "2005" -#define DUMB_MONTH_STR1 "8" -#define DUMB_DAY_STR1 "7" +#define DUMB_YEAR_STR2 "15" +#define DUMB_YEAR_STR4 "2015" +#define DUMB_MONTH_STR1 "1" +#define DUMB_DAY_STR1 "17" #if DUMB_MONTH < 10 #define DUMB_MONTH_STR2 "0" DUMB_MONTH_STR1 @@ -606,10 +606,6 @@ typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( sample_t *samples ); -typedef int32 (*DUH_SIGRENDERER_GET_POSITION)( - sigrenderer_t *sigrenderer -); - typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer); typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata); @@ -625,7 +621,6 @@ typedef struct DUH_SIGTYPE_DESC DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam; DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples; DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample; - DUH_SIGRENDERER_GET_POSITION sigrenderer_get_position; DUH_END_SIGRENDERER end_sigrenderer; DUH_UNLOAD_SIGDATA unload_sigdata; } diff --git a/dumb/include/internal/it.h b/dumb/include/internal/it.h index 6defa759ae..a9196b3167 100644 --- a/dumb/include/internal/it.h +++ b/dumb/include/internal/it.h @@ -33,7 +33,6 @@ #include #include "barray.h" -#include "tarray.h" /** TO DO: THINK ABOUT THE FOLLOWING: @@ -724,21 +723,6 @@ struct DUMB_IT_SIGRENDERER #ifdef BIT_ARRAY_BULLSHIT /* bit array, which rows are played, only checked by pattern break or loop commands */ void * played; - - /* - Loop indicator for internal processes, may also be useful for external processes - 0 - Not looped - 1 - Looped - -1 - Continued past loop - */ - int looped; - - /* - Kept until looped - */ - LONG_LONG time_played; - - void * row_timekeeper; #endif int32 gvz_time; diff --git a/dumb/include/internal/tarray.h b/dumb/include/internal/tarray.h deleted file mode 100644 index 7eb3af7c60..0000000000 --- a/dumb/include/internal/tarray.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _T_ARRAY_H_ -#define _T_ARRAY_H_ - -#include - -#ifndef LONG_LONG -#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__ -#define LONG_LONG long long -#elif defined _MSC_VER || defined __WATCOMC__ -#define LONG_LONG __int64 -#elif defined __sgi -#define LONG_LONG long long -#else -#error 64-bit integer type unknown -#endif -#endif - -void * timekeeping_array_create(size_t size); -void timekeeping_array_destroy(void * array); -void * timekeeping_array_dup(void * array); - -void timekeeping_array_reset(void * array, size_t loop_start); - -void timekeeping_array_push(void * array, size_t index, LONG_LONG time); -void timekeeping_array_bump(void * array, size_t index); - -unsigned int timekeeping_array_get_count(void * array, size_t index); - -LONG_LONG timekeeping_array_get_item(void * array, size_t index); - -#endif diff --git a/dumb/prj/dumb/dumb.pro b/dumb/prj/dumb/dumb.pro index 629a9294a7..9244ce4bd3 100644 --- a/dumb/prj/dumb/dumb.pro +++ b/dumb/prj/dumb/dumb.pro @@ -37,7 +37,6 @@ SOURCES += \ ../../src/helpers/memfile.c \ ../../src/helpers/clickrem.c \ ../../src/helpers/barray.c \ - ../../src/helpers/tarray.c \ ../../src/it/xmeffect.c \ ../../src/it/readxm2.c \ ../../src/it/readxm.c \ @@ -109,7 +108,6 @@ HEADERS += \ ../../include/internal/it.h \ ../../include/internal/dumb.h \ ../../include/internal/barray.h \ - ../../include/internal/tarray.h \ ../../include/internal/aldumb.h \ ../../include/internal/sinc_resampler.h \ ../../include/internal/stack_alloc.h \ diff --git a/dumb/src/core/rendsig.c b/dumb/src/core/rendsig.c index 053011a113..72da173c5c 100644 --- a/dumb/src/core/rendsig.c +++ b/dumb/src/core/rendsig.c @@ -147,15 +147,7 @@ int DUMBEXPORT duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) int32 DUMBEXPORT duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) { - DUH_SIGRENDERER_GET_POSITION proc; - - if (!sigrenderer) return -1; - - proc = sigrenderer->desc->sigrenderer_get_position; - if (proc) - return (*proc)(sigrenderer->sigrenderer); - else - return sigrenderer->pos; + return sigrenderer ? sigrenderer->pos : -1; } diff --git a/dumb/src/helpers/tarray.c b/dumb/src/helpers/tarray.c deleted file mode 100644 index f3ba422d84..0000000000 --- a/dumb/src/helpers/tarray.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "internal/tarray.h" - -#include - - /* - Structures which contain the play times of each pattern and row combination in the song, - not guaranteed to be valid for the whole song until the loop status is no longer zero. - The initial count and restart count will both be zero on song start, then both will be - incremented until the song loops. Restart count will be reset to zero on loop for all - rows which have a time equal to or greater than the loop start point, so time keeping - functions will know which timestamp the song is currently located at. - - Timestamp lists are guaranteed to be allocated in blocks of 16 timestamps at a time. - */ - - /* - We don't need full timekeeping because the player loop only wants the first play time - of the loop start order/row. We also don't really want full timekeeping because it - involves a lot of memory allocations, which is also slow. - */ - -#undef FULL_TIMEKEEPING - -typedef struct DUMB_IT_ROW_TIME -{ - unsigned int count, restart_count; -#ifndef FULL_TIMEKEEPING - LONG_LONG first_time; -#else - LONG_LONG * times; -#endif -} DUMB_IT_ROW_TIME; - -void * timekeeping_array_create(size_t size) -{ - size_t * _size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * size ); - if ( _size ) { - *_size = size; - } - return _size; -} - -void timekeeping_array_destroy(void * array) -{ -#ifdef FULL_TIMEKEEPING - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - for (i = 0; i < *size; i++) { - if (s[i].times) free(s[i].times); - } -#endif - - free(array); -} - -void * timekeeping_array_dup(void * array) -{ - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - size_t * new_size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * *size ); - if ( new_size ) { - DUMB_IT_ROW_TIME * new_s = (DUMB_IT_ROW_TIME *)(new_size + 1); - - *new_size = *size; - - for (i = 0; i < *size; i++) { - new_s[i].count = s[i].count; - new_s[i].restart_count = s[i].restart_count; - -#ifndef FULL_TIMEKEEPING - new_s[i].first_time = s[i].first_time; -#else - if ( s[i].times ) { - size_t time_count = ( s[i].count + 15 ) & ~15; - new_s[i].times = (LONG_LONG *) malloc( sizeof(LONG_LONG) * time_count ); - if ( new_s[i].times == (void *)0 ) { - timekeeping_array_destroy( new_size ); - return (void *) 0; - } - memcpy( new_s[i].times, s[i].times, sizeof(LONG_LONG) * s[i].count ); - } -#endif - } - } - - return new_size; -} - -void timekeeping_array_reset(void * array, size_t loop_start) -{ - size_t i; - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - DUMB_IT_ROW_TIME * s_loop_start = s + loop_start; - LONG_LONG loop_start_time; - - if ( loop_start >= *size || s_loop_start->count < 1 ) return; - -#ifndef FULL_TIMEKEEPING - loop_start_time = s_loop_start->first_time; -#else - loop_start_time = s_loop_start->times[0]; -#endif - - for ( i = 0; i < *size; i++ ) { -#ifndef FULL_TIMEKEEPING - if ( s[i].count && s[i].first_time >= loop_start_time ) { -#else - if ( s[i].count && s[i].times[0] >= loop_start_time ) { -#endif - s[i].restart_count = 0; - } - } -} - -void timekeeping_array_push(void * array, size_t index, LONG_LONG time) -{ -#ifdef FULL_TIMEKEEPING - size_t i; - size_t time_count; -#endif - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return; - -#ifndef FULL_TIMEKEEPING - if ( !s[index].count++ ) - s[index].first_time = time; -#else - time_count = ( s[index].count + 16 ) & ~15; - - s[index].times = (LONG_LONG *) realloc( s[index].times, sizeof(LONG_LONG) * time_count ); - - s[index].times[s[index].count++] = time; -#endif -} - -void timekeeping_array_bump(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return; - - s[index].restart_count++; -} - -unsigned int timekeeping_array_get_count(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size) return 0; - - return s[index].count; -} - -LONG_LONG timekeeping_array_get_item(void * array, size_t index) -{ - size_t * size = (size_t *) array; - DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); - - if (index >= *size || s[index].restart_count >= s[index].count) return 0; - -#ifndef FULL_TIMEKEEPING - return s[index].first_time; -#else - return s[index].times[s[index].restart_count]; -#endif -} diff --git a/dumb/src/it/itread.c b/dumb/src/it/itread.c index e8661807e2..ca1dde55d2 100644 --- a/dumb/src/it/itread.c +++ b/dumb/src/it/itread.c @@ -290,12 +290,15 @@ static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f) envelope->flags = dumbfile_getc(f); envelope->n_nodes = dumbfile_getc(f); + if(envelope->n_nodes > 25) { + TRACE("IT error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); + envelope->n_nodes = 0; + return -1; + } envelope->loop_start = dumbfile_getc(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); diff --git a/dumb/src/it/itrender.c b/dumb/src/it/itrender.c index 0491e7e59b..0a7feae3c6 100644 --- a/dumb/src/it/itrender.c +++ b/dumb/src/it/itrender.c @@ -352,10 +352,6 @@ static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_chan #ifdef BIT_ARRAY_BULLSHIT dst->played = bit_array_dup(src->played); - - dst->looped = src->looped; - dst->time_played = src->time_played; - dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper); #endif dst->gvz_time = src->gvz_time; @@ -2221,9 +2217,6 @@ Yxy This uses a table 4 times larger (hence 4 times slower) than bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); #endif sigrenderer->speed = 0; -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) return 1; } @@ -4341,8 +4334,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) /* Fix play tracking and timekeeping for orders containing skip commands */ for (n = 0; n < 256; n++) { bit_array_set(sigrenderer->played, sigrenderer->processorder * 256 + n); - timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n, sigrenderer->time_played); - timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n); } #endif } @@ -4367,9 +4358,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) && bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow) #endif ) { -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if (sigrenderer->callbacks->loop) { if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) return 1; @@ -4413,13 +4401,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) } } -#ifdef BIT_ARRAY_BULLSHIT - if (sigrenderer->looped == 0) { - timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); - } - timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); -#endif - if (!(sigdata->flags & IT_WAS_A_669)) reset_effects(sigrenderer); @@ -4471,9 +4452,6 @@ static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) sigrenderer->gvz_time += (int)(t >> 16); sigrenderer->gvz_sub_time = (int)t & 65535; if (sigrenderer->gvz_time >= 65536 * 12) { -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->looped = 1; -#endif if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data)) return 1; } @@ -5288,10 +5266,6 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha #ifdef BIT_ARRAY_BULLSHIT sigrenderer->played = bit_array_create(sigdata->n_orders * 256); - - sigrenderer->looped = 0; - sigrenderer->time_played = 0; - sigrenderer->row_timekeeper = timekeeping_array_create(sigdata->n_orders * 256); #endif { @@ -5310,8 +5284,6 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha /* Fix for played order detection for songs which have skips at the start of the orders list */ for (n = 0; n < 256; n++) { bit_array_set(sigrenderer->played, order * 256 + n); - timekeeping_array_push(sigrenderer->row_timekeeper, order * 256 + n, 0); - timekeeping_array_bump(sigrenderer->row_timekeeper, order * 256 + n); } #endif } @@ -5324,6 +5296,10 @@ static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_cha sigrenderer->time_left = 0; sigrenderer->sub_time_left = 0; +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->played = bit_array_create(sigdata->n_orders * 256); +#endif + sigrenderer->gvz_time = 0; sigrenderer->gvz_sub_time = 0; @@ -5482,10 +5458,6 @@ static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_ while (pos > 0 && pos >= sigrenderer->time_left) { render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)sigrenderer->time_left << 16; -#endif - pos -= sigrenderer->time_left; sigrenderer->time_left = 0; @@ -5498,10 +5470,6 @@ static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_ render(sigrenderer, 0, 1.0f, 0, pos, NULL); sigrenderer->time_left -= pos; -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)pos << 16; -#endif - return sigrenderer; } @@ -5517,7 +5485,6 @@ static int32 it_sigrenderer_get_samples( int32 pos; int dt; int32 todo; - int ret; LONG_LONG t; if (sigrenderer->order < 0) return 0; // problematic @@ -5531,7 +5498,7 @@ static int32 it_sigrenderer_get_samples( if (!samples) volume = 0; for (;;) { - todo = (int32)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); + todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); if (todo >= size) break; @@ -5545,28 +5512,9 @@ static int32 it_sigrenderer_get_samples( sigrenderer->sub_time_left = (int32)t & 65535; sigrenderer->time_left += (int32)(t >> 16); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)todo * dt; -#endif - - ret = process_tick(sigrenderer); - - if (ret) { + if (process_tick(sigrenderer)) { sigrenderer->order = -1; sigrenderer->row = -1; - } - -#ifdef BIT_ARRAY_BULLSHIT - if (sigrenderer->looped == 1) { - sigrenderer->looped = -1; - size = 0; - timekeeping_array_reset(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); - sigrenderer->time_played = timekeeping_array_get_item(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); - break; - } -#endif - - if (ret) { return pos; } } @@ -5579,10 +5527,6 @@ static int32 it_sigrenderer_get_samples( sigrenderer->sub_time_left = (int32)t & 65535; sigrenderer->time_left += (int32)(t >> 16); -#ifdef BIT_ARRAY_BULLSHIT - sigrenderer->time_played += (LONG_LONG)size * dt; -#endif - if (samples) dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta); @@ -5634,8 +5578,6 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) #ifdef BIT_ARRAY_BULLSHIT bit_array_destroy(sigrenderer->played); - - timekeeping_array_destroy(sigrenderer->row_timekeeper); #endif free(vsigrenderer); @@ -5644,17 +5586,6 @@ void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) -#ifdef BIT_ARRAY_BULLSHIT -static int32 it_sigrenderer_get_position(sigrenderer_t *vsigrenderer) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - - return (int32)(sigrenderer->time_played >> 16); -} -#endif - - - DUH_SIGTYPE_DESC _dumb_sigtype_it = { SIGTYPE_IT, NULL, @@ -5662,11 +5593,6 @@ DUH_SIGTYPE_DESC _dumb_sigtype_it = { NULL, &it_sigrenderer_get_samples, &it_sigrenderer_get_current_sample, -#ifdef BIT_ARRAY_BULLSHIT - &it_sigrenderer_get_position, -#else - NULL, -#endif &_dumb_it_end_sigrenderer, &_dumb_it_unload_sigdata }; diff --git a/dumb/src/it/readamf.c b/dumb/src/it/readamf.c index 83f6075e7c..820709e9dd 100644 --- a/dumb/src/it/readamf.c +++ b/dumb/src/it/readamf.c @@ -320,6 +320,8 @@ static DUMB_IT_SIGDATA *it_amf_load_sigdata(DUMBFILE *f, int * version) free( sigdata ); return NULL; } + + sigdata->n_pchannels = nchannels; memset( sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS ); diff --git a/dumb/src/it/readptm.c b/dumb/src/it/readptm.c index 885929e42d..9b34861dbf 100644 --- a/dumb/src/it/readptm.c +++ b/dumb/src/it/readptm.c @@ -439,6 +439,7 @@ static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) } if (dumbfile_seek(f, 352, DFS_SEEK_SET)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } @@ -451,12 +452,14 @@ static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) } if (dumbfile_seek(f, 608, DFS_SEEK_SET)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } for (n = 0; n < sigdata->n_samples; n++) { if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) { + free(component); _dumb_it_unload_sigdata(sigdata); return NULL; } diff --git a/dumb/src/it/readxm.c b/dumb/src/it/readxm.c index a06fd1e992..0c838ade84 100644 --- a/dumb/src/it/readxm.c +++ b/dumb/src/it/readxm.c @@ -1197,6 +1197,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); if (!sigdata->instrument) { + free(roguebytes); _dumb_it_unload_sigdata(sigdata); return NULL; } diff --git a/dumb/vc6/dumb/dumb.vcxproj b/dumb/vc6/dumb/dumb.vcxproj index 6e49557cff..ae8ebdb0b3 100644 --- a/dumb/vc6/dumb/dumb.vcxproj +++ b/dumb/vc6/dumb/dumb.vcxproj @@ -118,7 +118,6 @@ - @@ -210,7 +209,6 @@ - diff --git a/dumb/vc6/dumb/dumb.vcxproj.filters b/dumb/vc6/dumb/dumb.vcxproj.filters index 167393748f..422556dc2b 100644 --- a/dumb/vc6/dumb/dumb.vcxproj.filters +++ b/dumb/vc6/dumb/dumb.vcxproj.filters @@ -279,9 +279,6 @@ src\helpers - - src\helpers - @@ -314,9 +311,6 @@ include\internal - - include\internal - diff --git a/dumb/vc6/dumb_static/dumb_static.vcproj b/dumb/vc6/dumb_static/dumb_static.vcproj index 497fe4a997..6d67362598 100644 --- a/dumb/vc6/dumb_static/dumb_static.vcproj +++ b/dumb/vc6/dumb_static/dumb_static.vcproj @@ -1203,10 +1203,6 @@ /> - - > 3] & (0x80 >> ((i) & 7))) != 0) + +#define SzBitWithVals_Check(p, i) ((p)->Defs && ((p)->Defs[(i) >> 3] & (0x80 >> ((i) & 7))) != 0) typedef struct { - UInt64 *PackSizes; - Byte *PackCRCsDefined; - UInt32 *PackCRCs; - CSzFolder *Folders; - CSzFileItem *Files; UInt32 NumPackStreams; UInt32 NumFolders; - UInt32 NumFiles; + + UInt64 *PackPositions; // NumPackStreams + 1 + CSzBitUi32s FolderCRCs; // NumFolders + + size_t *FoCodersOffsets; // NumFolders + 1 + UInt32 *FoStartPackStreamIndex; // NumFolders + 1 + UInt32 *FoToCoderUnpackSizes; // NumFolders + 1 + Byte *FoToMainUnpackSizeIndex; // NumFolders + UInt64 *CoderUnpackSizes; // for all coders in all folders + + Byte *CodersData; } CSzAr; -void SzAr_Init(CSzAr *p); -void SzAr_Free(CSzAr *p, ISzAlloc *alloc); +UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex); + +SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, + ISzAlloc *allocMain); + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 NumFiles; + + UInt64 *UnpackPositions; // NumFiles + 1 + // Byte *IsEmptyFiles; + Byte *IsDirs; + CSzBitUi32s CRCs; + + CSzBitUi32s Attribs; + // CSzBitUi32s Parents; + CSzBitUi64s MTime; + CSzBitUi64s CTime; + + UInt32 *FolderToFile; // NumFolders + 1 + UInt32 *FileToFolder; // NumFiles + + size_t *FileNameOffsets; /* in 2-byte steps */ + Byte *FileNames; /* UTF-16-LE */ +} CSzArEx; + +#define SzArEx_IsDir(p, i) (SzBitArray_Check((p)->IsDirs, i)) + +#define SzArEx_GetFileSize(p, i) ((p)->UnpackPositions[(i) + 1] - (p)->UnpackPositions[i]) + +void SzArEx_Init(CSzArEx *p); +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); +UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); +int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); + +/* +if dest == NULL, the return value specifies the required size of the buffer, + in 16-bit characters, including the null-terminating character. +if dest != NULL, the return value specifies the number of 16-bit characters that + are written to the dest, including the null-terminating character. */ + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); + +/* +size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex); +UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest); +*/ + /* - SzExtract extracts file from archive + SzArEx_Extract extracts file from archive *outBuffer must be 0 before first call for each new archive. @@ -143,35 +170,6 @@ void SzAr_Free(CSzAr *p, ISzAlloc *alloc); Free *outBuffer and set *outBuffer to 0, if you want to flush cache. */ -typedef struct -{ - CSzAr db; - - UInt64 startPosAfterHeader; - UInt64 dataPos; - - UInt32 *FolderStartPackStreamIndex; - UInt64 *PackStreamStartPositions; - UInt32 *FolderStartFileIndex; - UInt32 *FileIndexToFolderIndexMap; - - size_t *FileNameOffsets; /* in 2-byte steps */ - CBuf FileNames; /* UTF-16-LE */ -} CSzArEx; - -void SzArEx_Init(CSzArEx *p); -void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); -UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder); -int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize); - -/* -if dest == NULL, the return value specifies the required size of the buffer, - in 16-bit characters, including the null-terminating character. -if dest != NULL, the return value specifies the number of 16-bit characters that - are written to the dest, including the null-terminating character. */ - -size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); - SRes SzArEx_Extract( const CSzArEx *db, ILookInStream *inStream, @@ -196,7 +194,8 @@ SZ_ERROR_INPUT_EOF SZ_ERROR_FAIL */ -SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp); +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, + ISzAlloc *allocMain, ISzAlloc *allocTemp); EXTERN_C_END diff --git a/lzma/C/7zArcIn.c b/lzma/C/7zArcIn.c new file mode 100644 index 0000000000..080d8df2ae --- /dev/null +++ b/lzma/C/7zArcIn.c @@ -0,0 +1,1770 @@ +/* 7zArcIn.c -- 7z Input functions +2015-11-18 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include + +#include "7z.h" +#include "7zBuf.h" +#include "7zCrc.h" +#include "CpuArch.h" + +#define MY_ALLOC(T, p, size, alloc) { \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == NULL) return SZ_ERROR_MEM; } + +#define MY_ALLOC_ZE(T, p, size, alloc) { if ((size) == 0) p = NULL; else MY_ALLOC(T, p, size, alloc) } + +#define MY_ALLOC_AND_CPY(to, size, from, alloc) \ + { MY_ALLOC(Byte, to, size, alloc); memcpy(to, from, size); } + +#define MY_ALLOC_ZE_AND_CPY(to, size, from, alloc) \ + { if ((size) == 0) p = NULL; else { MY_ALLOC_AND_CPY(to, size, from, alloc) } } + +#define k7zMajorVersion 0 + +enum EIdEnum +{ + k7zIdEnd, + k7zIdHeader, + k7zIdArchiveProperties, + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + k7zIdSize, + k7zIdCRC, + k7zIdFolder, + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttrib, + k7zIdComment, + k7zIdEncodedHeader, + k7zIdStartPos, + k7zIdDummy + // k7zNtSecure, + // k7zParent, + // k7zIsReal +}; + +const Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + +#define SzBitUi32s_Init(p) { (p)->Defs = NULL; (p)->Vals = NULL; } + +static SRes SzBitUi32s_Alloc(CSzBitUi32s *p, size_t num, ISzAlloc *alloc) +{ + if (num == 0) + { + p->Defs = NULL; + p->Vals = NULL; + } + else + { + MY_ALLOC(Byte, p->Defs, (num + 7) >> 3, alloc); + MY_ALLOC(UInt32, p->Vals, num, alloc); + } + return SZ_OK; +} + +void SzBitUi32s_Free(CSzBitUi32s *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Defs); p->Defs = NULL; + IAlloc_Free(alloc, p->Vals); p->Vals = NULL; +} + +#define SzBitUi64s_Init(p) { (p)->Defs = NULL; (p)->Vals = NULL; } + +void SzBitUi64s_Free(CSzBitUi64s *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Defs); p->Defs = NULL; + IAlloc_Free(alloc, p->Vals); p->Vals = NULL; +} + + +static void SzAr_Init(CSzAr *p) +{ + p->NumPackStreams = 0; + p->NumFolders = 0; + + p->PackPositions = NULL; + SzBitUi32s_Init(&p->FolderCRCs); + + p->FoCodersOffsets = NULL; + p->FoStartPackStreamIndex = NULL; + p->FoToCoderUnpackSizes = NULL; + p->FoToMainUnpackSizeIndex = NULL; + p->CoderUnpackSizes = NULL; + + p->CodersData = NULL; +} + +static void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->PackPositions); + SzBitUi32s_Free(&p->FolderCRCs, alloc); + + IAlloc_Free(alloc, p->FoCodersOffsets); + IAlloc_Free(alloc, p->FoStartPackStreamIndex); + IAlloc_Free(alloc, p->FoToCoderUnpackSizes); + IAlloc_Free(alloc, p->FoToMainUnpackSizeIndex); + IAlloc_Free(alloc, p->CoderUnpackSizes); + + IAlloc_Free(alloc, p->CodersData); + + SzAr_Init(p); +} + + +void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + + p->NumFiles = 0; + p->dataPos = 0; + + p->UnpackPositions = NULL; + p->IsDirs = NULL; + + p->FolderToFile = NULL; + p->FileToFolder = NULL; + + p->FileNameOffsets = NULL; + p->FileNames = NULL; + + SzBitUi32s_Init(&p->CRCs); + SzBitUi32s_Init(&p->Attribs); + // SzBitUi32s_Init(&p->Parents); + SzBitUi64s_Init(&p->MTime); + SzBitUi64s_Init(&p->CTime); +} + +void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->UnpackPositions); + IAlloc_Free(alloc, p->IsDirs); + + IAlloc_Free(alloc, p->FolderToFile); + IAlloc_Free(alloc, p->FileToFolder); + + IAlloc_Free(alloc, p->FileNameOffsets); + IAlloc_Free(alloc, p->FileNames); + + SzBitUi32s_Free(&p->CRCs, alloc); + SzBitUi32s_Free(&p->Attribs, alloc); + // SzBitUi32s_Free(&p->Parents, alloc); + SzBitUi64s_Free(&p->MTime, alloc); + SzBitUi64s_Free(&p->CTime, alloc); + + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + + +static int TestSignatureCandidate(const Byte *testBytes) +{ + unsigned i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +#define SzData_Clear(p) { (p)->Data = NULL; (p)->Size = 0; } + +#define SZ_READ_BYTE_SD(_sd_, dest) if ((_sd_)->Size == 0) return SZ_ERROR_ARCHIVE; (_sd_)->Size--; dest = *(_sd_)->Data++; +#define SZ_READ_BYTE(dest) SZ_READ_BYTE_SD(sd, dest) +#define SZ_READ_BYTE_2(dest) if (sd.Size == 0) return SZ_ERROR_ARCHIVE; sd.Size--; dest = *sd.Data++; + +#define SKIP_DATA(sd, size) { sd->Size -= (size_t)(size); sd->Data += (size_t)(size); } +#define SKIP_DATA2(sd, size) { sd.Size -= (size_t)(size); sd.Data += (size_t)(size); } + +#define SZ_READ_32(dest) if (sd.Size < 4) return SZ_ERROR_ARCHIVE; \ + dest = GetUi32(sd.Data); SKIP_DATA2(sd, 4); + +static MY_NO_INLINE SRes ReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte, mask; + unsigned i; + UInt32 v; + + SZ_READ_BYTE(firstByte); + if ((firstByte & 0x80) == 0) + { + *value = firstByte; + return SZ_OK; + } + SZ_READ_BYTE(v); + if ((firstByte & 0x40) == 0) + { + *value = (((UInt32)firstByte & 0x3F) << 8) | v; + return SZ_OK; + } + SZ_READ_BYTE(mask); + *value = v | ((UInt32)mask << 8); + mask = 0x20; + for (i = 2; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = (unsigned)firstByte & (unsigned)(mask - 1); + *value |= (highPart << (8 * i)); + return SZ_OK; + } + SZ_READ_BYTE(b); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + + +static MY_NO_INLINE SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + Byte firstByte; + UInt64 value64; + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + firstByte = *sd->Data; + if ((firstByte & 0x80) == 0) + { + *value = firstByte; + sd->Data++; + sd->Size--; + return SZ_OK; + } + RINOK(ReadNumber(sd, &value64)); + if (value64 >= (UInt32)0x80000000 - 1) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 4))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +#define ReadID(sd, value) ReadNumber(sd, value) + +static SRes SkipData(CSzData *sd) +{ + UInt64 size; + RINOK(ReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA(sd, size); + return SZ_OK; +} + +static SRes WaitId(CSzData *sd, UInt32 id) +{ + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == id) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SkipData(sd)); + } +} + +static SRes RememberBitVector(CSzData *sd, UInt32 numItems, const Byte **v) +{ + UInt32 numBytes = (numItems + 7) >> 3; + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + *v = sd->Data; + SKIP_DATA(sd, numBytes); + return SZ_OK; +} + +static UInt32 CountDefinedBits(const Byte *bits, UInt32 numItems) +{ + Byte b = 0; + unsigned m = 0; + UInt32 sum = 0; + for (; numItems != 0; numItems--) + { + if (m == 0) + { + b = *bits++; + m = 8; + } + m--; + sum += ((b >> m) & 1); + } + return sum; +} + +static MY_NO_INLINE SRes ReadBitVector(CSzData *sd, UInt32 numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + Byte *v2; + UInt32 numBytes = (numItems + 7) >> 3; + *v = NULL; + SZ_READ_BYTE(allAreDefined); + if (numBytes == 0) + return SZ_OK; + if (allAreDefined == 0) + { + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + MY_ALLOC_AND_CPY(*v, numBytes, sd->Data, alloc); + SKIP_DATA(sd, numBytes); + return SZ_OK; + } + MY_ALLOC(Byte, *v, numBytes, alloc); + v2 = *v; + memset(v2, 0xFF, (size_t)numBytes); + { + unsigned numBits = (unsigned)numItems & 7; + if (numBits != 0) + v2[numBytes - 1] = (Byte)((((UInt32)1 << numBits) - 1) << (8 - numBits)); + } + return SZ_OK; +} + +static MY_NO_INLINE SRes ReadUi32s(CSzData *sd2, UInt32 numItems, CSzBitUi32s *crcs, ISzAlloc *alloc) +{ + UInt32 i; + CSzData sd; + UInt32 *vals; + const Byte *defs; + MY_ALLOC_ZE(UInt32, crcs->Vals, numItems, alloc); + sd = *sd2; + defs = crcs->Defs; + vals = crcs->Vals; + for (i = 0; i < numItems; i++) + if (SzBitArray_Check(defs, i)) + { + SZ_READ_32(vals[i]); + } + else + vals[i] = 0; + *sd2 = sd; + return SZ_OK; +} + +static SRes ReadBitUi32s(CSzData *sd, UInt32 numItems, CSzBitUi32s *crcs, ISzAlloc *alloc) +{ + SzBitUi32s_Free(crcs, alloc); + RINOK(ReadBitVector(sd, numItems, &crcs->Defs, alloc)); + return ReadUi32s(sd, numItems, crcs, alloc); +} + +static SRes SkipBitUi32s(CSzData *sd, UInt32 numItems) +{ + Byte allAreDefined; + UInt32 numDefined = numItems; + SZ_READ_BYTE(allAreDefined); + if (!allAreDefined) + { + size_t numBytes = (numItems + 7) >> 3; + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + numDefined = CountDefinedBits(sd->Data, numItems); + SKIP_DATA(sd, numBytes); + } + if (numDefined > (sd->Size >> 2)) + return SZ_ERROR_ARCHIVE; + SKIP_DATA(sd, (size_t)numDefined * 4); + return SZ_OK; +} + +static SRes ReadPackInfo(CSzAr *p, CSzData *sd, ISzAlloc *alloc) +{ + RINOK(SzReadNumber32(sd, &p->NumPackStreams)); + + RINOK(WaitId(sd, k7zIdSize)); + MY_ALLOC(UInt64, p->PackPositions, (size_t)p->NumPackStreams + 1, alloc); + { + UInt64 sum = 0; + UInt32 i; + UInt32 numPackStreams = p->NumPackStreams; + for (i = 0; i < numPackStreams; i++) + { + UInt64 packSize; + p->PackPositions[i] = sum; + RINOK(ReadNumber(sd, &packSize)); + sum += packSize; + if (sum < packSize) + return SZ_ERROR_ARCHIVE; + } + p->PackPositions[i] = sum; + } + + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + /* CRC of packed streams is unused now */ + RINOK(SkipBitUi32s(sd, p->NumPackStreams)); + continue; + } + RINOK(SkipData(sd)); + } +} + +/* +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} +*/ + +#define k_NumCodersStreams_in_Folder_MAX (SZ_NUM_BONDS_IN_FOLDER_MAX + SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX) + +SRes SzGetNextFolderItem(CSzFolder *f, CSzData *sd) +{ + UInt32 numCoders, i; + UInt32 numInStreams = 0; + const Byte *dataStart = sd->Data; + + f->NumCoders = 0; + f->NumBonds = 0; + f->NumPackStreams = 0; + f->UnpackStream = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders == 0 || numCoders > SZ_NUM_CODERS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = f->Coders + i; + unsigned idSize, j; + UInt64 id; + + SZ_READ_BYTE(mainByte); + if ((mainByte & 0xC0) != 0) + return SZ_ERROR_UNSUPPORTED; + + idSize = (unsigned)(mainByte & 0xF); + if (idSize > sizeof(id)) + return SZ_ERROR_UNSUPPORTED; + if (idSize > sd->Size) + return SZ_ERROR_ARCHIVE; + id = 0; + for (j = 0; j < idSize; j++) + { + id = ((id << 8) | *sd->Data); + sd->Data++; + sd->Size--; + } + if (id > (UInt32)0xFFFFFFFF) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = (UInt32)id; + + coder->NumStreams = 1; + coder->PropsOffset = 0; + coder->PropsSize = 0; + + if ((mainByte & 0x10) != 0) + { + UInt32 numStreams; + + RINOK(SzReadNumber32(sd, &numStreams)); + if (numStreams > k_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + coder->NumStreams = (Byte)numStreams; + + RINOK(SzReadNumber32(sd, &numStreams)); + if (numStreams != 1) + return SZ_ERROR_UNSUPPORTED; + } + + numInStreams += coder->NumStreams; + + if (numInStreams > k_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + + if ((mainByte & 0x20) != 0) + { + UInt32 propsSize = 0; + RINOK(SzReadNumber32(sd, &propsSize)); + if (propsSize > sd->Size) + return SZ_ERROR_ARCHIVE; + if (propsSize >= 0x80) + return SZ_ERROR_UNSUPPORTED; + coder->PropsOffset = sd->Data - dataStart; + coder->PropsSize = (Byte)propsSize; + sd->Data += (size_t)propsSize; + sd->Size -= (size_t)propsSize; + } + } + + /* + if (numInStreams == 1 && numCoders == 1) + { + f->NumPackStreams = 1; + f->PackStreams[0] = 0; + } + else + */ + { + Byte streamUsed[k_NumCodersStreams_in_Folder_MAX]; + UInt32 numBonds, numPackStreams; + + numBonds = numCoders - 1; + if (numInStreams < numBonds) + return SZ_ERROR_ARCHIVE; + if (numBonds > SZ_NUM_BONDS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + f->NumBonds = numBonds; + + numPackStreams = numInStreams - numBonds; + if (numPackStreams > SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + f->NumPackStreams = numPackStreams; + + for (i = 0; i < numInStreams; i++) + streamUsed[i] = False; + + if (numBonds != 0) + { + Byte coderUsed[SZ_NUM_CODERS_IN_FOLDER_MAX]; + + for (i = 0; i < numCoders; i++) + coderUsed[i] = False; + + for (i = 0; i < numBonds; i++) + { + CSzBond *bp = f->Bonds + i; + + RINOK(SzReadNumber32(sd, &bp->InIndex)); + if (bp->InIndex >= numInStreams || streamUsed[bp->InIndex]) + return SZ_ERROR_ARCHIVE; + streamUsed[bp->InIndex] = True; + + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + if (bp->OutIndex >= numCoders || coderUsed[bp->OutIndex]) + return SZ_ERROR_ARCHIVE; + coderUsed[bp->OutIndex] = True; + } + + for (i = 0; i < numCoders; i++) + if (!coderUsed[i]) + { + f->UnpackStream = i; + break; + } + + if (i == numCoders) + return SZ_ERROR_ARCHIVE; + } + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams; i++) + if (!streamUsed[i]) + break; + if (i == numInStreams) + return SZ_ERROR_ARCHIVE; + f->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + f->PackStreams[i] = index; + } + } + + f->NumCoders = numCoders; + + return SZ_OK; +} + + +static MY_NO_INLINE SRes SkipNumbers(CSzData *sd2, UInt32 num) +{ + CSzData sd; + sd = *sd2; + for (; num != 0; num--) + { + Byte firstByte, mask; + unsigned i; + SZ_READ_BYTE_2(firstByte); + if ((firstByte & 0x80) == 0) + continue; + if ((firstByte & 0x40) == 0) + { + if (sd.Size == 0) + return SZ_ERROR_ARCHIVE; + sd.Size--; + sd.Data++; + continue; + } + mask = 0x20; + for (i = 2; i < 8 && (firstByte & mask) != 0; i++) + mask >>= 1; + if (i > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, i); + } + *sd2 = sd; + return SZ_OK; +} + + +#define k_Scan_NumCoders_MAX 64 +#define k_Scan_NumCodersStreams_in_Folder_MAX 64 + + +static SRes ReadUnpackInfo(CSzAr *p, + CSzData *sd2, + UInt32 numFoldersMax, + const CBuf *tempBufs, UInt32 numTempBufs, + ISzAlloc *alloc) +{ + CSzData sd; + + UInt32 fo, numFolders, numCodersOutStreams, packStreamIndex; + const Byte *startBufPtr; + Byte external; + + RINOK(WaitId(sd2, k7zIdFolder)); + + RINOK(SzReadNumber32(sd2, &numFolders)); + if (numFolders > numFoldersMax) + return SZ_ERROR_UNSUPPORTED; + p->NumFolders = numFolders; + + SZ_READ_BYTE_SD(sd2, external); + if (external == 0) + sd = *sd2; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd2, &index)); + if (index >= numTempBufs) + return SZ_ERROR_ARCHIVE; + sd.Data = tempBufs[index].data; + sd.Size = tempBufs[index].size; + } + + MY_ALLOC(size_t, p->FoCodersOffsets, (size_t)numFolders + 1, alloc); + MY_ALLOC(UInt32, p->FoStartPackStreamIndex, (size_t)numFolders + 1, alloc); + MY_ALLOC(UInt32, p->FoToCoderUnpackSizes, (size_t)numFolders + 1, alloc); + MY_ALLOC(Byte, p->FoToMainUnpackSizeIndex, (size_t)numFolders, alloc); + + startBufPtr = sd.Data; + + packStreamIndex = 0; + numCodersOutStreams = 0; + + for (fo = 0; fo < numFolders; fo++) + { + UInt32 numCoders, ci, numInStreams = 0; + + p->FoCodersOffsets[fo] = sd.Data - startBufPtr; + + RINOK(SzReadNumber32(&sd, &numCoders)); + if (numCoders == 0 || numCoders > k_Scan_NumCoders_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (ci = 0; ci < numCoders; ci++) + { + Byte mainByte; + unsigned idSize; + UInt32 coderInStreams; + + SZ_READ_BYTE_2(mainByte); + if ((mainByte & 0xC0) != 0) + return SZ_ERROR_UNSUPPORTED; + idSize = (mainByte & 0xF); + if (idSize > 8) + return SZ_ERROR_UNSUPPORTED; + if (idSize > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, idSize); + + coderInStreams = 1; + + if ((mainByte & 0x10) != 0) + { + UInt32 coderOutStreams; + RINOK(SzReadNumber32(&sd, &coderInStreams)); + RINOK(SzReadNumber32(&sd, &coderOutStreams)); + if (coderInStreams > k_Scan_NumCodersStreams_in_Folder_MAX || coderOutStreams != 1) + return SZ_ERROR_UNSUPPORTED; + } + + numInStreams += coderInStreams; + + if ((mainByte & 0x20) != 0) + { + UInt32 propsSize; + RINOK(SzReadNumber32(&sd, &propsSize)); + if (propsSize > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, propsSize); + } + } + + { + UInt32 indexOfMainStream = 0; + UInt32 numPackStreams = 1; + + if (numCoders != 1 || numInStreams != 1) + { + Byte streamUsed[k_Scan_NumCodersStreams_in_Folder_MAX]; + Byte coderUsed[k_Scan_NumCoders_MAX]; + + UInt32 i; + UInt32 numBonds = numCoders - 1; + if (numInStreams < numBonds) + return SZ_ERROR_ARCHIVE; + + if (numInStreams > k_Scan_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (i = 0; i < numInStreams; i++) + streamUsed[i] = False; + for (i = 0; i < numCoders; i++) + coderUsed[i] = False; + + for (i = 0; i < numBonds; i++) + { + UInt32 index; + + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numCoders || coderUsed[index]) + return SZ_ERROR_ARCHIVE; + coderUsed[index] = True; + } + + numPackStreams = numInStreams - numBonds; + + if (numPackStreams != 1) + for (i = 0; i < numPackStreams; i++) + { + UInt32 index; + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + } + + for (i = 0; i < numCoders; i++) + if (!coderUsed[i]) + { + indexOfMainStream = i; + break; + } + + if (i == numCoders) + return SZ_ERROR_ARCHIVE; + } + + p->FoStartPackStreamIndex[fo] = packStreamIndex; + p->FoToCoderUnpackSizes[fo] = numCodersOutStreams; + p->FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; + numCodersOutStreams += numCoders; + if (numCodersOutStreams < numCoders) + return SZ_ERROR_UNSUPPORTED; + + packStreamIndex += numPackStreams; + if (packStreamIndex < numPackStreams) + return SZ_ERROR_UNSUPPORTED; + + if (packStreamIndex > p->NumPackStreams) + return SZ_ERROR_ARCHIVE; + } + } + + p->FoToCoderUnpackSizes[fo] = numCodersOutStreams; + + { + size_t dataSize = sd.Data - startBufPtr; + p->FoStartPackStreamIndex[fo] = packStreamIndex; + p->FoCodersOffsets[fo] = dataSize; + MY_ALLOC_ZE_AND_CPY(p->CodersData, dataSize, startBufPtr, alloc); + } + + if (external != 0) + { + if (sd.Size != 0) + return SZ_ERROR_ARCHIVE; + sd = *sd2; + } + + RINOK(WaitId(&sd, k7zIdCodersUnpackSize)); + + MY_ALLOC_ZE(UInt64, p->CoderUnpackSizes, (size_t)numCodersOutStreams, alloc); + { + UInt32 i; + for (i = 0; i < numCodersOutStreams; i++) + { + RINOK(ReadNumber(&sd, p->CoderUnpackSizes + i)); + } + } + + for (;;) + { + UInt64 type; + RINOK(ReadID(&sd, &type)); + if (type == k7zIdEnd) + { + *sd2 = sd; + return SZ_OK; + } + if (type == k7zIdCRC) + { + RINOK(ReadBitUi32s(&sd, numFolders, &p->FolderCRCs, alloc)); + continue; + } + RINOK(SkipData(&sd)); + } +} + + +UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex) +{ + return p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex] + p->FoToMainUnpackSizeIndex[folderIndex]]; +} + + +typedef struct +{ + UInt32 NumTotalSubStreams; + UInt32 NumSubDigests; + CSzData sdNumSubStreams; + CSzData sdSizes; + CSzData sdCRCs; +} CSubStreamInfo; + + +static SRes ReadSubStreamsInfo(CSzAr *p, CSzData *sd, CSubStreamInfo *ssi) +{ + UInt64 type = 0; + UInt32 numSubDigests = 0; + UInt32 numFolders = p->NumFolders; + UInt32 numUnpackStreams = numFolders; + UInt32 numUnpackSizesInData = 0; + + for (;;) + { + RINOK(ReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + UInt32 i; + ssi->sdNumSubStreams.Data = sd->Data; + numUnpackStreams = 0; + numSubDigests = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + if (numUnpackStreams > numUnpackStreams + numStreams) + return SZ_ERROR_UNSUPPORTED; + numUnpackStreams += numStreams; + if (numStreams != 0) + numUnpackSizesInData += (numStreams - 1); + if (numStreams != 1 || !SzBitWithVals_Check(&p->FolderCRCs, i)) + numSubDigests += numStreams; + } + ssi->sdNumSubStreams.Size = sd->Data - ssi->sdNumSubStreams.Data; + continue; + } + if (type == k7zIdCRC || type == k7zIdSize || type == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + + if (!ssi->sdNumSubStreams.Data) + { + numSubDigests = numFolders; + if (p->FolderCRCs.Defs) + numSubDigests = numFolders - CountDefinedBits(p->FolderCRCs.Defs, numFolders); + } + + ssi->NumTotalSubStreams = numUnpackStreams; + ssi->NumSubDigests = numSubDigests; + + if (type == k7zIdSize) + { + ssi->sdSizes.Data = sd->Data; + RINOK(SkipNumbers(sd, numUnpackSizesInData)); + ssi->sdSizes.Size = sd->Data - ssi->sdSizes.Data; + RINOK(ReadID(sd, &type)); + } + + for (;;) + { + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + ssi->sdCRCs.Data = sd->Data; + RINOK(SkipBitUi32s(sd, numSubDigests)); + ssi->sdCRCs.Size = sd->Data - ssi->sdCRCs.Data; + } + else + { + RINOK(SkipData(sd)); + } + RINOK(ReadID(sd, &type)); + } +} + +static SRes SzReadStreamsInfo(CSzAr *p, + CSzData *sd, + UInt32 numFoldersMax, const CBuf *tempBufs, UInt32 numTempBufs, + UInt64 *dataOffset, + CSubStreamInfo *ssi, + ISzAlloc *alloc) +{ + UInt64 type; + + SzData_Clear(&ssi->sdSizes); + SzData_Clear(&ssi->sdCRCs); + SzData_Clear(&ssi->sdNumSubStreams); + + *dataOffset = 0; + RINOK(ReadID(sd, &type)); + if (type == k7zIdPackInfo) + { + RINOK(ReadNumber(sd, dataOffset)); + RINOK(ReadPackInfo(p, sd, alloc)); + RINOK(ReadID(sd, &type)); + } + if (type == k7zIdUnpackInfo) + { + RINOK(ReadUnpackInfo(p, sd, numFoldersMax, tempBufs, numTempBufs, alloc)); + RINOK(ReadID(sd, &type)); + } + if (type == k7zIdSubStreamsInfo) + { + RINOK(ReadSubStreamsInfo(p, sd, ssi)); + RINOK(ReadID(sd, &type)); + } + else + { + ssi->NumTotalSubStreams = p->NumFolders; + // ssi->NumSubDigests = 0; + } + + return (type == k7zIdEnd ? SZ_OK : SZ_ERROR_UNSUPPORTED); +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *tempBufs, + UInt32 numFoldersMax, + UInt64 baseOffset, + CSzAr *p, + ISzAlloc *allocTemp) +{ + UInt64 dataStartPos; + UInt32 fo; + CSubStreamInfo ssi; + + RINOK(SzReadStreamsInfo(p, sd, numFoldersMax, NULL, 0, &dataStartPos, &ssi, allocTemp)); + + dataStartPos += baseOffset; + if (p->NumFolders == 0) + return SZ_ERROR_ARCHIVE; + + for (fo = 0; fo < p->NumFolders; fo++) + Buf_Init(tempBufs + fo); + + for (fo = 0; fo < p->NumFolders; fo++) + { + CBuf *tempBuf = tempBufs + fo; + UInt64 unpackSize = SzAr_GetFolderUnpackSize(p, fo); + if ((size_t)unpackSize != unpackSize) + return SZ_ERROR_MEM; + if (!Buf_Create(tempBuf, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + } + + for (fo = 0; fo < p->NumFolders; fo++) + { + const CBuf *tempBuf = tempBufs + fo; + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + RINOK(SzAr_DecodeFolder(p, fo, inStream, dataStartPos, tempBuf->data, tempBuf->size, allocTemp)); + } + + return SZ_OK; +} + +static SRes SzReadFileNames(const Byte *data, size_t size, UInt32 numFiles, size_t *offsets) +{ + size_t pos = 0; + *offsets++ = 0; + if (numFiles == 0) + return (size == 0) ? SZ_OK : SZ_ERROR_ARCHIVE; + if (size < 2) + return SZ_ERROR_ARCHIVE; + if (data[size - 2] != 0 || data[size - 1] != 0) + return SZ_ERROR_ARCHIVE; + do + { + const Byte *p; + if (pos == size) + return SZ_ERROR_ARCHIVE; + for (p = data + pos; + #ifdef _WIN32 + *(const UInt16 *)p != 0 + #else + p[0] != 0 || p[1] != 0 + #endif + ; p += 2); + pos = p - data + 2; + *offsets++ = (pos >> 1); + } + while (--numFiles); + return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; +} + +static MY_NO_INLINE SRes ReadTime(CSzBitUi64s *p, UInt32 num, + CSzData *sd2, + const CBuf *tempBufs, UInt32 numTempBufs, + ISzAlloc *alloc) +{ + CSzData sd; + UInt32 i; + CNtfsFileTime *vals; + Byte *defs; + Byte external; + + RINOK(ReadBitVector(sd2, num, &p->Defs, alloc)); + + SZ_READ_BYTE_SD(sd2, external); + if (external == 0) + sd = *sd2; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd2, &index)); + if (index >= numTempBufs) + return SZ_ERROR_ARCHIVE; + sd.Data = tempBufs[index].data; + sd.Size = tempBufs[index].size; + } + + MY_ALLOC_ZE(CNtfsFileTime, p->Vals, num, alloc); + vals = p->Vals; + defs = p->Defs; + for (i = 0; i < num; i++) + if (SzBitArray_Check(defs, i)) + { + if (sd.Size < 8) + return SZ_ERROR_ARCHIVE; + vals[i].Low = GetUi32(sd.Data); + vals[i].High = GetUi32(sd.Data + 4); + SKIP_DATA2(sd, 8); + } + else + vals[i].High = vals[i].Low = 0; + + if (external == 0) + *sd2 = sd; + + return SZ_OK; +} + + +#define NUM_ADDITIONAL_STREAMS_MAX 8 + + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + ILookInStream *inStream, + CBuf *tempBufs, UInt32 *numTempBufs, + ISzAlloc *allocMain, + ISzAlloc *allocTemp + ) +{ + UInt64 type; + UInt32 numFiles = 0; + UInt32 numEmptyStreams = 0; + CSubStreamInfo ssi; + const Byte *emptyStreams = NULL; + const Byte *emptyFiles = NULL; + + SzData_Clear(&ssi.sdSizes); + SzData_Clear(&ssi.sdCRCs); + SzData_Clear(&ssi.sdNumSubStreams); + + ssi.NumSubDigests = 0; + ssi.NumTotalSubStreams = 0; + + RINOK(ReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdAdditionalStreamsInfo) + { + CSzAr tempAr; + SRes res; + + SzAr_Init(&tempAr); + res = SzReadAndDecodePackedStreams(inStream, sd, tempBufs, NUM_ADDITIONAL_STREAMS_MAX, + p->startPosAfterHeader, &tempAr, allocTemp); + *numTempBufs = tempAr.NumFolders; + SzAr_Free(&tempAr, allocTemp); + + if (res != SZ_OK) + return res; + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(&p->db, sd, (UInt32)1 << 30, tempBufs, *numTempBufs, + &p->dataPos, &ssi, allocMain)); + p->dataPos += p->startPosAfterHeader; + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdEnd) + { + return SZ_OK; + } + + if (type != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->NumFiles = numFiles; + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(ReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + + if (type >= ((UInt32)1 << 8)) + { + SKIP_DATA(sd, size); + } + else switch ((unsigned)type) + { + case k7zIdName: + { + size_t namesSize; + const Byte *namesData; + Byte external; + + SZ_READ_BYTE(external); + if (external == 0) + { + namesSize = (size_t)size - 1; + namesData = sd->Data; + } + else + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= *numTempBufs) + return SZ_ERROR_ARCHIVE; + namesData = (tempBufs)[index].data; + namesSize = (tempBufs)[index].size; + } + + if ((namesSize & 1) != 0) + return SZ_ERROR_ARCHIVE; + MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); + MY_ALLOC_ZE_AND_CPY(p->FileNames, namesSize, namesData, allocMain); + RINOK(SzReadFileNames(p->FileNames, namesSize, numFiles, p->FileNameOffsets)) + if (external == 0) + { + SKIP_DATA(sd, namesSize); + } + break; + } + case k7zIdEmptyStream: + { + RINOK(RememberBitVector(sd, numFiles, &emptyStreams)); + numEmptyStreams = CountDefinedBits(emptyStreams, numFiles); + emptyFiles = NULL; + break; + } + case k7zIdEmptyFile: + { + RINOK(RememberBitVector(sd, numEmptyStreams, &emptyFiles)); + break; + } + case k7zIdWinAttrib: + { + Byte external; + CSzData sdSwitch; + CSzData *sdPtr; + SzBitUi32s_Free(&p->Attribs, allocMain); + RINOK(ReadBitVector(sd, numFiles, &p->Attribs.Defs, allocMain)); + + SZ_READ_BYTE(external); + if (external == 0) + sdPtr = sd; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= *numTempBufs) + return SZ_ERROR_ARCHIVE; + sdSwitch.Data = (tempBufs)[index].data; + sdSwitch.Size = (tempBufs)[index].size; + sdPtr = &sdSwitch; + } + RINOK(ReadUi32s(sdPtr, numFiles, &p->Attribs, allocMain)); + break; + } + /* + case k7zParent: + { + SzBitUi32s_Free(&p->Parents, allocMain); + RINOK(ReadBitVector(sd, numFiles, &p->Parents.Defs, allocMain)); + RINOK(SzReadSwitch(sd)); + RINOK(ReadUi32s(sd, numFiles, &p->Parents, allocMain)); + break; + } + */ + case k7zIdMTime: RINOK(ReadTime(&p->MTime, numFiles, sd, tempBufs, *numTempBufs, allocMain)); break; + case k7zIdCTime: RINOK(ReadTime(&p->CTime, numFiles, sd, tempBufs, *numTempBufs, allocMain)); break; + default: + { + SKIP_DATA(sd, size); + } + } + } + + if (numFiles - numEmptyStreams != ssi.NumTotalSubStreams) + return SZ_ERROR_ARCHIVE; + + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + + { + UInt32 i; + UInt32 emptyFileIndex = 0; + UInt32 folderIndex = 0; + UInt32 remSubStreams = 0; + UInt32 numSubStreams = 0; + UInt64 unpackPos = 0; + const Byte *digestsDefs = NULL; + const Byte *digestsVals = NULL; + UInt32 digestsValsIndex = 0; + UInt32 digestIndex; + Byte allDigestsDefined = 0; + Byte isDirMask = 0; + Byte crcMask = 0; + Byte mask = 0x80; + + MY_ALLOC(UInt32, p->FolderToFile, p->db.NumFolders + 1, allocMain); + MY_ALLOC_ZE(UInt32, p->FileToFolder, p->NumFiles, allocMain); + MY_ALLOC(UInt64, p->UnpackPositions, p->NumFiles + 1, allocMain); + MY_ALLOC_ZE(Byte, p->IsDirs, (p->NumFiles + 7) >> 3, allocMain); + + RINOK(SzBitUi32s_Alloc(&p->CRCs, p->NumFiles, allocMain)); + + if (ssi.sdCRCs.Size != 0) + { + SZ_READ_BYTE_SD(&ssi.sdCRCs, allDigestsDefined); + if (allDigestsDefined) + digestsVals = ssi.sdCRCs.Data; + else + { + size_t numBytes = (ssi.NumSubDigests + 7) >> 3; + digestsDefs = ssi.sdCRCs.Data; + digestsVals = digestsDefs + numBytes; + } + } + + digestIndex = 0; + + for (i = 0; i < numFiles; i++, mask >>= 1) + { + if (mask == 0) + { + UInt32 byteIndex = (i - 1) >> 3; + p->IsDirs[byteIndex] = isDirMask; + p->CRCs.Defs[byteIndex] = crcMask; + isDirMask = 0; + crcMask = 0; + mask = 0x80; + } + + p->UnpackPositions[i] = unpackPos; + p->CRCs.Vals[i] = 0; + + if (emptyStreams && SzBitArray_Check(emptyStreams, i)) + { + if (emptyFiles) + { + if (!SzBitArray_Check(emptyFiles, emptyFileIndex)) + isDirMask |= mask; + emptyFileIndex++; + } + else + isDirMask |= mask; + if (remSubStreams == 0) + { + p->FileToFolder[i] = (UInt32)-1; + continue; + } + } + + if (remSubStreams == 0) + { + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderToFile[folderIndex] = i; + numSubStreams = 1; + if (ssi.sdNumSubStreams.Data) + { + RINOK(SzReadNumber32(&ssi.sdNumSubStreams, &numSubStreams)); + } + remSubStreams = numSubStreams; + if (numSubStreams != 0) + break; + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + unpackPos += folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + } + + folderIndex++; + } + } + + p->FileToFolder[i] = folderIndex; + + if (emptyStreams && SzBitArray_Check(emptyStreams, i)) + continue; + + if (--remSubStreams == 0) + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + UInt64 startFolderUnpackPos = p->UnpackPositions[p->FolderToFile[folderIndex]]; + if (folderUnpackSize < unpackPos - startFolderUnpackPos) + return SZ_ERROR_ARCHIVE; + unpackPos = startFolderUnpackPos + folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + + if (numSubStreams == 1 && SzBitWithVals_Check(&p->db.FolderCRCs, i)) + { + p->CRCs.Vals[i] = p->db.FolderCRCs.Vals[folderIndex]; + crcMask |= mask; + } + else if (allDigestsDefined || (digestsDefs && SzBitArray_Check(digestsDefs, digestIndex))) + { + p->CRCs.Vals[i] = GetUi32(digestsVals + (size_t)digestsValsIndex * 4); + digestsValsIndex++; + crcMask |= mask; + } + + folderIndex++; + } + else + { + UInt64 v; + RINOK(ReadNumber(&ssi.sdSizes, &v)); + unpackPos += v; + if (unpackPos < v) + return SZ_ERROR_ARCHIVE; + if (allDigestsDefined || (digestsDefs && SzBitArray_Check(digestsDefs, digestIndex))) + { + p->CRCs.Vals[i] = GetUi32(digestsVals + (size_t)digestsValsIndex * 4); + digestsValsIndex++; + crcMask |= mask; + } + } + } + + if (mask != 0x80) + { + UInt32 byteIndex = (i - 1) >> 3; + p->IsDirs[byteIndex] = isDirMask; + p->CRCs.Defs[byteIndex] = crcMask; + } + + p->UnpackPositions[i] = unpackPos; + + if (remSubStreams != 0) + return SZ_ERROR_ARCHIVE; + + for (;;) + { + p->FolderToFile[folderIndex] = i; + if (folderIndex >= p->db.NumFolders) + break; + if (!ssi.sdNumSubStreams.Data) + return SZ_ERROR_ARCHIVE; + RINOK(SzReadNumber32(&ssi.sdNumSubStreams, &numSubStreams)); + if (numSubStreams != 0) + return SZ_ERROR_ARCHIVE; + /* + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + unpackPos += folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + } + */ + folderIndex++; + } + + if (ssi.sdNumSubStreams.Data && ssi.sdNumSubStreams.Size != 0) + return SZ_ERROR_ARCHIVE; + } + + return SZ_OK; +} + + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 i; + UInt32 numTempBufs = 0; + SRes res; + CBuf tempBufs[NUM_ADDITIONAL_STREAMS_MAX]; + + for (i = 0; i < NUM_ADDITIONAL_STREAMS_MAX; i++) + Buf_Init(tempBufs + i); + + res = SzReadHeader2(p, sd, inStream, + tempBufs, &numTempBufs, + allocMain, allocTemp); + + for (i = 0; i < NUM_ADDITIONAL_STREAMS_MAX; i++) + Buf_Free(tempBufs + i, allocTemp); + + RINOK(res); + + if (sd->Size != 0) + return SZ_ERROR_FAIL; + + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + Int64 startArcPos; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buf; + SRes res; + + startArcPos = 0; + RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < startArcPos + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buf, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buf.data, nextHeaderSizeT); + + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buf.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buf.data; + sd.Size = buf.size; + + res = ReadID(&sd, &type); + + if (res == SZ_OK && type == k7zIdEncodedHeader) + { + CSzAr tempAr; + CBuf tempBuf; + Buf_Init(&tempBuf); + + SzAr_Init(&tempAr); + res = SzReadAndDecodePackedStreams(inStream, &sd, &tempBuf, 1, p->startPosAfterHeader, &tempAr, allocTemp); + SzAr_Free(&tempAr, allocTemp); + + if (res != SZ_OK) + { + Buf_Free(&tempBuf, allocTemp); + } + else + { + Buf_Free(&buf, allocTemp); + buf.data = tempBuf.data; + buf.size = tempBuf.size; + sd.Data = buf.data; + sd.Size = buf.size; + res = ReadID(&sd, &type); + } + } + + if (res == SZ_OK) + { + if (type == k7zIdHeader) + { + /* + CSzData sd2; + unsigned ttt; + for (ttt = 0; ttt < 40000; ttt++) + { + SzArEx_Free(p, allocMain); + sd2 = sd; + res = SzReadHeader(p, &sd2, inStream, allocMain, allocTemp); + if (res != SZ_OK) + break; + } + */ + res = SzReadHeader(p, &sd, inStream, allocMain, allocTemp); + } + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + + Buf_Free(&buf, allocTemp); + return res; +} + + +SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, + ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} + + +SRes SzArEx_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **tempBuf, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileToFolder[fileIndex]; + SRes res = SZ_OK; + + *offset = 0; + *outSizeProcessed = 0; + + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *tempBuf); + *blockIndex = folderIndex; + *tempBuf = NULL; + *outBufferSize = 0; + return SZ_OK; + } + + if (*tempBuf == NULL || *blockIndex != folderIndex) + { + UInt64 unpackSizeSpec = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + /* + UInt64 unpackSizeSpec = + p->UnpackPositions[p->FolderToFile[folderIndex + 1]] - + p->UnpackPositions[p->FolderToFile[folderIndex]]; + */ + size_t unpackSize = (size_t)unpackSizeSpec; + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *tempBuf); + *tempBuf = NULL; + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *tempBuf = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*tempBuf == NULL) + res = SZ_ERROR_MEM; + } + + if (res == SZ_OK) + { + res = SzAr_DecodeFolder(&p->db, folderIndex, + inStream, p->dataPos, *tempBuf, unpackSize, allocTemp); + } + } + } + + if (res == SZ_OK) + { + UInt64 unpackPos = p->UnpackPositions[fileIndex]; + *offset = (size_t)(unpackPos - p->UnpackPositions[p->FolderToFile[folderIndex]]); + *outSizeProcessed = (size_t)(p->UnpackPositions[fileIndex + 1] - unpackPos); + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + if (SzBitWithVals_Check(&p->CRCs, fileIndex)) + if (CrcCalc(*tempBuf + *offset, *outSizeProcessed) != p->CRCs.Vals[fileIndex]) + res = SZ_ERROR_CRC; + } + + return res; +} + + +size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + size_t offs = p->FileNameOffsets[fileIndex]; + size_t len = p->FileNameOffsets[fileIndex + 1] - offs; + if (dest != 0) + { + size_t i; + const Byte *src = p->FileNames + offs * 2; + for (i = 0; i < len; i++) + dest[i] = GetUi16(src + i * 2); + } + return len; +} + +/* +size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex) +{ + size_t len; + if (!p->FileNameOffsets) + return 1; + len = 0; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + len += p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + if SzBitWithVals_Check(&p->Parents, fileIndex) + parent = p->Parents.Vals[fileIndex]; + if (parent == (UInt32)(Int32)-1) + return len; + fileIndex = parent; + } +} + +UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + Bool needSlash; + if (!p->FileNameOffsets) + { + *(--dest) = 0; + return dest; + } + needSlash = False; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + size_t curLen = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + SzArEx_GetFileNameUtf16(p, fileIndex, dest - curLen); + if (needSlash) + *(dest - 1) = '/'; + needSlash = True; + dest -= curLen; + + if SzBitWithVals_Check(&p->Parents, fileIndex) + parent = p->Parents.Vals[fileIndex]; + if (parent == (UInt32)(Int32)-1) + return dest; + fileIndex = parent; + } +} +*/ diff --git a/lzma/C/7zBuf.c b/lzma/C/7zBuf.c index a35fa2f988..b0ac1104b9 100644 --- a/lzma/C/7zBuf.c +++ b/lzma/C/7zBuf.c @@ -1,7 +1,7 @@ /* 7zBuf.c -- Byte Buffer -2008-03-28 -Igor Pavlov -Public domain */ +2013-01-21 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "7zBuf.h" diff --git a/lzma/C/7zBuf.h b/lzma/C/7zBuf.h index 88ff0c2f2d..e5f9218bed 100644 --- a/lzma/C/7zBuf.h +++ b/lzma/C/7zBuf.h @@ -1,14 +1,12 @@ /* 7zBuf.h -- Byte Buffer -2009-02-07 : Igor Pavlov : Public domain */ +2013-01-18 : Igor Pavlov : Public domain */ #ifndef __7Z_BUF_H #define __7Z_BUF_H -#include "Types.h" +#include "7zTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN typedef struct { @@ -32,8 +30,6 @@ void DynBuf_SeekToBeg(CDynBuf *p); int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc); void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/7zCrc.c b/lzma/C/7zCrc.c index 6993a69884..607db34041 100644 --- a/lzma/C/7zCrc.c +++ b/lzma/C/7zCrc.c @@ -1,29 +1,35 @@ /* 7zCrc.c -- CRC32 init -2010-12-01 : Igor Pavlov : Public domain */ +2015-03-10 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "7zCrc.h" #include "CpuArch.h" #define kCrcPoly 0xEDB88320 -#ifdef MY_CPU_X86_OR_AMD64 +#ifdef MY_CPU_LE #define CRC_NUM_TABLES 8 - UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); -#elif defined(MY_CPU_LE) - #define CRC_NUM_TABLES 4 #else - #define CRC_NUM_TABLES 5 + #define CRC_NUM_TABLES 9 + #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) + UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table); + UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif #ifndef MY_CPU_BE UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); + UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); #endif typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); -static CRC_FUNC g_CrcUpdate; +CRC_FUNC g_CrcUpdateT4; +CRC_FUNC g_CrcUpdateT8; +CRC_FUNC g_CrcUpdate; + UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size) @@ -36,6 +42,17 @@ UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; } +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + const Byte *pEnd = p + size; + for (; p != pEnd; p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + void MY_FAST_CALL CrcGenerateTable() { UInt32 i; @@ -52,22 +69,43 @@ void MY_FAST_CALL CrcGenerateTable() UInt32 r = g_CrcTable[i - 256]; g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); } + + #if CRC_NUM_TABLES < 4 + g_CrcUpdate = CrcUpdateT1; + + #else + #ifdef MY_CPU_LE - g_CrcUpdate = CrcUpdateT4; + g_CrcUpdateT4 = CrcUpdateT4; + g_CrcUpdate = CrcUpdateT4; + + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT8; - #if CRC_NUM_TABLES == 8 - if (!CPU_Is_InOrder()) - g_CrcUpdate = CrcUpdateT8; - #endif + #ifdef MY_CPU_X86_OR_AMD64 + if (!CPU_Is_InOrder()) + g_CrcUpdate = CrcUpdateT8; + #endif + #endif #else { #ifndef MY_CPU_BE - UInt32 k = 1; - if (*(const Byte *)&k == 1) + UInt32 k = 0x01020304; + const Byte *p = (const Byte *)&k; + if (p[0] == 4 && p[1] == 3) + { + g_CrcUpdateT4 = CrcUpdateT4; g_CrcUpdate = CrcUpdateT4; + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT8; + // g_CrcUpdate = CrcUpdateT8; + #endif + } + else if (p[0] != 1 || p[1] != 2) + g_CrcUpdate = CrcUpdateT1; else #endif { @@ -76,8 +114,15 @@ void MY_FAST_CALL CrcGenerateTable() UInt32 x = g_CrcTable[i - 256]; g_CrcTable[i] = CRC_UINT32_SWAP(x); } + g_CrcUpdateT4 = CrcUpdateT1_BeT4; g_CrcUpdate = CrcUpdateT1_BeT4; + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT1_BeT8; + // g_CrcUpdate = CrcUpdateT1_BeT8; + #endif } } #endif + + #endif } diff --git a/lzma/C/7zCrc.h b/lzma/C/7zCrc.h index 4a1ec38c93..3b0459402b 100644 --- a/lzma/C/7zCrc.h +++ b/lzma/C/7zCrc.h @@ -1,10 +1,10 @@ /* 7zCrc.h -- CRC32 calculation -2009-11-21 : Igor Pavlov : Public domain */ +2013-01-18 : Igor Pavlov : Public domain */ #ifndef __7Z_CRC_H #define __7Z_CRC_H -#include "Types.h" +#include "7zTypes.h" EXTERN_C_BEGIN diff --git a/lzma/C/7zCrcOpt.c b/lzma/C/7zCrcOpt.c index 85405ccfc1..58628efe56 100644 --- a/lzma/C/7zCrcOpt.c +++ b/lzma/C/7zCrcOpt.c @@ -1,12 +1,14 @@ /* 7zCrcOpt.c -- CRC32 calculation -2010-12-01 : Igor Pavlov : Public domain */ +2015-03-01 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "CpuArch.h" -#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) - #ifndef MY_CPU_BE +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; @@ -16,10 +18,10 @@ UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const U { v ^= *(const UInt32 *)p; v = - table[0x300 + (v & 0xFF)] ^ - table[0x200 + ((v >> 8) & 0xFF)] ^ - table[0x100 + ((v >> 16) & 0xFF)] ^ - table[0x000 + ((v >> 24))]; + table[0x300 + ((v ) & 0xFF)] + ^ table[0x200 + ((v >> 8) & 0xFF)] + ^ table[0x100 + ((v >> 16) & 0xFF)] + ^ table[0x000 + ((v >> 24))]; } for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); @@ -28,7 +30,28 @@ UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const U UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) { - return CrcUpdateT4(v, data, size, table); + const Byte *p = (const Byte *)data; + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + for (; size >= 8; size -= 8, p += 8) + { + UInt32 d; + v ^= *(const UInt32 *)p; + v = + table[0x700 + ((v ) & 0xFF)] + ^ table[0x600 + ((v >> 8) & 0xFF)] + ^ table[0x500 + ((v >> 16) & 0xFF)] + ^ table[0x400 + ((v >> 24))]; + d = *((const UInt32 *)p + 1); + v ^= + table[0x300 + ((d ) & 0xFF)] + ^ table[0x200 + ((d >> 8) & 0xFF)] + ^ table[0x100 + ((d >> 16) & 0xFF)] + ^ table[0x000 + ((d >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; } #endif @@ -38,27 +61,55 @@ UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const U #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) +#define CRC_UPDATE_BYTE_2_BE(crc, b) (table[(((crc) >> 24) ^ (b))] ^ ((crc) << 8)) + UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table) { const Byte *p = (const Byte *)data; - for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) - v = CRC_UPDATE_BYTE_2(v, *p); - v = CRC_UINT32_SWAP(v); table += 0x100; + v = CRC_UINT32_SWAP(v); + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = - table[0x000 + (v & 0xFF)] ^ - table[0x100 + ((v >> 8) & 0xFF)] ^ - table[0x200 + ((v >> 16) & 0xFF)] ^ - table[0x300 + ((v >> 24))]; + table[0x000 + ((v ) & 0xFF)] + ^ table[0x100 + ((v >> 8) & 0xFF)] + ^ table[0x200 + ((v >> 16) & 0xFF)] + ^ table[0x300 + ((v >> 24))]; } - table -= 0x100; - v = CRC_UINT32_SWAP(v); for (; size > 0; size--, p++) - v = CRC_UPDATE_BYTE_2(v, *p); - return v; + v = CRC_UPDATE_BYTE_2_BE(v, *p); + return CRC_UINT32_SWAP(v); +} + +UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + table += 0x100; + v = CRC_UINT32_SWAP(v); + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + for (; size >= 8; size -= 8, p += 8) + { + UInt32 d; + v ^= *(const UInt32 *)p; + v = + table[0x400 + ((v ) & 0xFF)] + ^ table[0x500 + ((v >> 8) & 0xFF)] + ^ table[0x600 + ((v >> 16) & 0xFF)] + ^ table[0x700 + ((v >> 24))]; + d = *((const UInt32 *)p + 1); + v ^= + table[0x000 + ((d ) & 0xFF)] + ^ table[0x100 + ((d >> 8) & 0xFF)] + ^ table[0x200 + ((d >> 16) & 0xFF)] + ^ table[0x300 + ((d >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + return CRC_UINT32_SWAP(v); } #endif diff --git a/lzma/C/7zDec.c b/lzma/C/7zDec.c index 338680747c..e39b4ff12a 100644 --- a/lzma/C/7zDec.c +++ b/lzma/C/7zDec.c @@ -1,15 +1,19 @@ /* 7zDec.c -- Decoding from 7z folder -2010-11-02 : Igor Pavlov : Public domain */ +2015-11-18 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include /* #define _7ZIP_PPMD_SUPPPORT */ #include "7z.h" +#include "7zCrc.h" #include "Bcj2.h" #include "Bra.h" #include "CpuArch.h" +#include "Delta.h" #include "LzmaDec.h" #include "Lzma2Dec.h" #ifdef _7ZIP_PPMD_SUPPPORT @@ -17,14 +21,17 @@ #endif #define k_Copy 0 +#define k_Delta 3 #define k_LZMA2 0x21 #define k_LZMA 0x30101 -#define k_BCJ 0x03030103 -#define k_PPC 0x03030205 -#define k_ARM 0x03030501 -#define k_ARMT 0x03030701 -#define k_SPARC 0x03030805 -#define k_BCJ2 0x0303011B +#define k_BCJ 0x3030103 +#define k_BCJ2 0x303011B +#define k_PPC 0x3030205 +#define k_IA64 0x3030401 +#define k_ARM 0x3030501 +#define k_ARMT 0x3030701 +#define k_SPARC 0x3030805 + #ifdef _7ZIP_PPMD_SUPPPORT @@ -63,7 +70,7 @@ static Byte ReadByte(void *pp) return 0; } -static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, +static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) { CPpmd7 ppmd; @@ -77,12 +84,12 @@ static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inSt s.res = SZ_OK; s.processed = 0; - if (coder->Props.size != 5) + if (propsSize != 5) return SZ_ERROR_UNSUPPORTED; { - unsigned order = coder->Props.data[0]; - UInt32 memSize = GetUi32(coder->Props.data + 1); + unsigned order = props[0]; + UInt32 memSize = GetUi32(props + 1); if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER || memSize < PPMD7_MIN_MEM_SIZE || @@ -124,25 +131,25 @@ static SRes SzDecodePpmd(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inSt #endif -static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, +static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) { CLzmaDec state; SRes res = SZ_OK; LzmaDec_Construct(&state); - RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain)); + RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain)); state.dic = outBuffer; state.dicBufSize = outSize; LzmaDec_Init(&state); for (;;) { - Byte *inBuf = NULL; + const void *inBuf = NULL; size_t lookahead = (1 << 18); if (lookahead > inSize) lookahead = (size_t)inSize; - res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + res = inStream->Look(inStream, &inBuf, &lookahead); if (res != SZ_OK) break; @@ -154,14 +161,23 @@ static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inSt inSize -= inProcessed; if (res != SZ_OK) break; - if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos)) + + if (status == LZMA_STATUS_FINISHED_WITH_MARK) { - if (state.dicBufSize != outSize || lookahead != 0 || - (status != LZMA_STATUS_FINISHED_WITH_MARK && - status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)) + if (outSize != state.dicPos || inSize != 0) res = SZ_ERROR_DATA; break; } + + if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) + break; + + if (inProcessed == 0 && dicPos == state.dicPos) + { + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); if (res != SZ_OK) break; @@ -172,27 +188,30 @@ static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inSt return res; } -static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream, + +#ifndef _7Z_NO_METHOD_LZMA2 + +static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) { CLzma2Dec state; SRes res = SZ_OK; Lzma2Dec_Construct(&state); - if (coder->Props.size != 1) + if (propsSize != 1) return SZ_ERROR_DATA; - RINOK(Lzma2Dec_AllocateProbs(&state, coder->Props.data[0], allocMain)); + RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain)); state.decoder.dic = outBuffer; state.decoder.dicBufSize = outSize; Lzma2Dec_Init(&state); for (;;) { - Byte *inBuf = NULL; + const void *inBuf = NULL; size_t lookahead = (1 << 18); if (lookahead > inSize) lookahead = (size_t)inSize; - res = inStream->Look((void *)inStream, (const void **)&inBuf, &lookahead); + res = inStream->Look(inStream, &inBuf, &lookahead); if (res != SZ_OK) break; @@ -204,13 +223,20 @@ static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inS inSize -= inProcessed; if (res != SZ_OK) break; - if (state.decoder.dicPos == state.decoder.dicBufSize || (inProcessed == 0 && dicPos == state.decoder.dicPos)) + + if (status == LZMA_STATUS_FINISHED_WITH_MARK) { - if (state.decoder.dicBufSize != outSize || lookahead != 0 || - (status != LZMA_STATUS_FINISHED_WITH_MARK)) + if (outSize != state.decoder.dicPos || inSize != 0) res = SZ_ERROR_DATA; break; } + + if (inProcessed == 0 && dicPos == state.decoder.dicPos) + { + res = SZ_ERROR_DATA; + break; + } + res = inStream->Skip((void *)inStream, inProcessed); if (res != SZ_OK) break; @@ -221,15 +247,18 @@ static SRes SzDecodeLzma2(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inS return res; } +#endif + + static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) { while (inSize > 0) { - void *inBuf; + const void *inBuf; size_t curSize = (1 << 18); if (curSize > inSize) curSize = (size_t)inSize; - RINOK(inStream->Look((void *)inStream, (const void **)&inBuf, &curSize)); + RINOK(inStream->Look(inStream, &inBuf, &curSize)); if (curSize == 0) return SZ_ERROR_INPUT_EOF; memcpy(outBuffer, inBuf, curSize); @@ -242,11 +271,13 @@ static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer static Bool IS_MAIN_METHOD(UInt32 m) { - switch(m) + switch (m) { case k_Copy: case k_LZMA: + #ifndef _7Z_NO_METHOD_LZMA2 case k_LZMA2: + #endif #ifdef _7ZIP_PPMD_SUPPPORT case k_PPMD: #endif @@ -258,13 +289,12 @@ static Bool IS_MAIN_METHOD(UInt32 m) static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c) { return - c->NumInStreams == 1 && - c->NumOutStreams == 1 && - c->MethodID <= (UInt32)0xFFFFFFFF && - IS_MAIN_METHOD((UInt32)c->MethodID); + c->NumStreams == 1 + /* && c->MethodID <= (UInt32)0xFFFFFFFF */ + && IS_MAIN_METHOD((UInt32)c->MethodID); } -#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumInStreams == 4 && (c)->NumOutStreams == 1) +#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4) static SRes CheckSupportedFolder(const CSzFolder *f) { @@ -274,65 +304,73 @@ static SRes CheckSupportedFolder(const CSzFolder *f) return SZ_ERROR_UNSUPPORTED; if (f->NumCoders == 1) { - if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0) + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBonds != 0) return SZ_ERROR_UNSUPPORTED; return SZ_OK; } + + + #ifndef _7Z_NO_METHODS_FILTERS + if (f->NumCoders == 2) { - CSzCoderInfo *c = &f->Coders[1]; - if (c->MethodID > (UInt32)0xFFFFFFFF || - c->NumInStreams != 1 || - c->NumOutStreams != 1 || - f->NumPackStreams != 1 || - f->PackStreams[0] != 0 || - f->NumBindPairs != 1 || - f->BindPairs[0].InIndex != 1 || - f->BindPairs[0].OutIndex != 0) + const CSzCoderInfo *c = &f->Coders[1]; + if ( + /* c->MethodID > (UInt32)0xFFFFFFFF || */ + c->NumStreams != 1 + || f->NumPackStreams != 1 + || f->PackStreams[0] != 0 + || f->NumBonds != 1 + || f->Bonds[0].InIndex != 1 + || f->Bonds[0].OutIndex != 0) return SZ_ERROR_UNSUPPORTED; switch ((UInt32)c->MethodID) { + case k_Delta: case k_BCJ: + case k_PPC: + case k_IA64: + case k_SPARC: case k_ARM: + case k_ARMT: break; default: return SZ_ERROR_UNSUPPORTED; } return SZ_OK; } + + #endif + + if (f->NumCoders == 4) { - if (!IS_SUPPORTED_CODER(&f->Coders[1]) || - !IS_SUPPORTED_CODER(&f->Coders[2]) || - !IS_BCJ2(&f->Coders[3])) + if (!IS_SUPPORTED_CODER(&f->Coders[1]) + || !IS_SUPPORTED_CODER(&f->Coders[2]) + || !IS_BCJ2(&f->Coders[3])) return SZ_ERROR_UNSUPPORTED; - if (f->NumPackStreams != 4 || - f->PackStreams[0] != 2 || - f->PackStreams[1] != 6 || - f->PackStreams[2] != 1 || - f->PackStreams[3] != 0 || - f->NumBindPairs != 3 || - f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 || - f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 || - f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2) + if (f->NumPackStreams != 4 + || f->PackStreams[0] != 2 + || f->PackStreams[1] != 6 + || f->PackStreams[2] != 1 + || f->PackStreams[3] != 0 + || f->NumBonds != 3 + || f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0 + || f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1 + || f->Bonds[2].InIndex != 3 || f->Bonds[2].OutIndex != 2) return SZ_ERROR_UNSUPPORTED; return SZ_OK; } + return SZ_ERROR_UNSUPPORTED; } -static UInt64 GetSum(const UInt64 *values, UInt32 index) -{ - UInt64 sum = 0; - UInt32 i; - for (i = 0; i < index; i++) - sum += values[i]; - return sum; -} - #define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; -static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, +static SRes SzFolder_Decode2(const CSzFolder *folder, + const Byte *propsData, + const UInt64 *unpackSizes, + const UInt64 *packPositions, ILookInStream *inStream, UInt64 startPos, Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, Byte *tempBuf[]) @@ -346,7 +384,7 @@ static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, for (ci = 0; ci < folder->NumCoders; ci++) { - CSzCoderInfo *coder = &folder->Coders[ci]; + const CSzCoderInfo *coder = &folder->Coders[ci]; if (IS_MAIN_METHOD((UInt32)coder->MethodID)) { @@ -358,7 +396,7 @@ static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, if (folder->NumCoders == 4) { UInt32 indices[] = { 3, 2, 0 }; - UInt64 unpackSize = folder->UnpackSizes[ci]; + UInt64 unpackSize = unpackSizes[ci]; si = indices[ci]; if (ci < 2) { @@ -367,7 +405,7 @@ static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, if (outSizeCur != unpackSize) return SZ_ERROR_MEM; temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); - if (temp == 0 && outSizeCur != 0) + if (!temp && outSizeCur != 0) return SZ_ERROR_MEM; outBufCur = tempBuf[1 - ci] = temp; tempSizes[1 - ci] = outSizeCur; @@ -382,8 +420,8 @@ static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, else return SZ_ERROR_UNSUPPORTED; } - offset = GetSum(packSizes, si); - inSize = packSizes[si]; + offset = packPositions[si]; + inSize = packPositions[si + 1] - offset; RINOK(LookInStream_SeekTo(inStream, startPos + offset)); if (coder->MethodID == k_Copy) @@ -394,77 +432,160 @@ static SRes SzFolder_Decode2(const CSzFolder *folder, const UInt64 *packSizes, } else if (coder->MethodID == k_LZMA) { - RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); } + #ifndef _7Z_NO_METHOD_LZMA2 else if (coder->MethodID == k_LZMA2) { - RINOK(SzDecodeLzma2(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); + RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); } - else + #endif + #ifdef _7ZIP_PPMD_SUPPPORT + else if (coder->MethodID == k_PPMD) { - #ifdef _7ZIP_PPMD_SUPPPORT - RINOK(SzDecodePpmd(coder, inSize, inStream, outBufCur, outSizeCur, allocMain)); - #else - return SZ_ERROR_UNSUPPORTED; - #endif + RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); } + #endif + else + return SZ_ERROR_UNSUPPORTED; } else if (coder->MethodID == k_BCJ2) { - UInt64 offset = GetSum(packSizes, 1); - UInt64 s3Size = packSizes[1]; - SRes res; + UInt64 offset = packPositions[1]; + UInt64 s3Size = packPositions[2] - offset; + if (ci != 3) return SZ_ERROR_UNSUPPORTED; - RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + tempSizes[2] = (SizeT)s3Size; if (tempSizes[2] != s3Size) return SZ_ERROR_MEM; tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); - if (tempBuf[2] == 0 && tempSizes[2] != 0) + if (!tempBuf[2] && tempSizes[2] != 0) return SZ_ERROR_MEM; - res = SzDecodeCopy(s3Size, inStream, tempBuf[2]); - RINOK(res) + + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2])); + + if ((tempSizes[0] & 3) != 0 || + (tempSizes[1] & 3) != 0 || + tempSize3 + tempSizes[0] + tempSizes[1] != outSize) + return SZ_ERROR_DATA; - res = Bcj2_Decode( - tempBuf3, tempSize3, - tempBuf[0], tempSizes[0], - tempBuf[1], tempSizes[1], - tempBuf[2], tempSizes[2], - outBuffer, outSize); - RINOK(res) - } - else - { - if (ci != 1) - return SZ_ERROR_UNSUPPORTED; - switch(coder->MethodID) { - case k_BCJ: + CBcj2Dec p; + + p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3; + p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0]; + p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1]; + p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2]; + + p.dest = outBuffer; + p.destLim = outBuffer + outSize; + + Bcj2Dec_Init(&p); + RINOK(Bcj2Dec_Decode(&p)); + { - UInt32 state; - x86_Convert_Init(state); - x86_Convert(outBuffer, outSize, 0, &state, 0); - break; + unsigned i; + for (i = 0; i < 4; i++) + if (p.bufs[i] != p.lims[i]) + return SZ_ERROR_DATA; + + if (!Bcj2Dec_IsFinished(&p)) + return SZ_ERROR_DATA; + + if (p.dest != p.destLim + || p.state != BCJ2_STREAM_MAIN) + return SZ_ERROR_DATA; } - CASE_BRA_CONV(ARM) - default: - return SZ_ERROR_UNSUPPORTED; } } + #ifndef _7Z_NO_METHODS_FILTERS + else if (ci == 1) + { + if (coder->MethodID == k_Delta) + { + if (coder->PropsSize != 1) + return SZ_ERROR_UNSUPPORTED; + { + Byte state[DELTA_STATE_SIZE]; + Delta_Init(state); + Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize); + } + } + else + { + if (coder->PropsSize != 0) + return SZ_ERROR_UNSUPPORTED; + switch (coder->MethodID) + { + case k_BCJ: + { + UInt32 state; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + break; + } + CASE_BRA_CONV(PPC) + CASE_BRA_CONV(IA64) + CASE_BRA_CONV(SPARC) + CASE_BRA_CONV(ARM) + CASE_BRA_CONV(ARMT) + default: + return SZ_ERROR_UNSUPPORTED; + } + } + } + #endif + else + return SZ_ERROR_UNSUPPORTED; } + return SZ_OK; } -SRes SzFolder_Decode(const CSzFolder *folder, const UInt64 *packSizes, + +SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, ILookInStream *inStream, UInt64 startPos, - Byte *outBuffer, size_t outSize, ISzAlloc *allocMain) + Byte *outBuffer, size_t outSize, + ISzAlloc *allocMain) { - Byte *tempBuf[3] = { 0, 0, 0}; - int i; - SRes res = SzFolder_Decode2(folder, packSizes, inStream, startPos, - outBuffer, (SizeT)outSize, allocMain, tempBuf); - for (i = 0; i < 3; i++) - IAlloc_Free(allocMain, tempBuf[i]); - return res; + SRes res; + CSzFolder folder; + CSzData sd; + + const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex]; + sd.Data = data; + sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex]; + + res = SzGetNextFolderItem(&folder, &sd); + + if (res != SZ_OK) + return res; + + if (sd.Size != 0 + || folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex] + || outSize != SzAr_GetFolderUnpackSize(p, folderIndex)) + return SZ_ERROR_FAIL; + { + unsigned i; + Byte *tempBuf[3] = { 0, 0, 0}; + + res = SzFolder_Decode2(&folder, data, + &p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]], + p->PackPositions + p->FoStartPackStreamIndex[folderIndex], + inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + + if (res == SZ_OK) + if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex)) + if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex]) + res = SZ_ERROR_CRC; + + return res; + } } diff --git a/lzma/C/7zIn.c b/lzma/C/7zIn.c deleted file mode 100644 index f1a44928e2..0000000000 --- a/lzma/C/7zIn.c +++ /dev/null @@ -1,1402 +0,0 @@ -/* 7zIn.c -- 7z Input functions -2010-10-29 : Igor Pavlov : Public domain */ - -#include - -#include "7z.h" -#include "7zCrc.h" -#include "CpuArch.h" - -Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; - -#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; } - -#define NUM_FOLDER_CODERS_MAX 32 -#define NUM_CODER_STREAMS_MAX 32 - -void SzCoderInfo_Init(CSzCoderInfo *p) -{ - Buf_Init(&p->Props); -} - -void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc) -{ - Buf_Free(&p->Props, alloc); - SzCoderInfo_Init(p); -} - -void SzFolder_Init(CSzFolder *p) -{ - p->Coders = 0; - p->BindPairs = 0; - p->PackStreams = 0; - p->UnpackSizes = 0; - p->NumCoders = 0; - p->NumBindPairs = 0; - p->NumPackStreams = 0; - p->UnpackCRCDefined = 0; - p->UnpackCRC = 0; - p->NumUnpackStreams = 0; -} - -void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Coders) - for (i = 0; i < p->NumCoders; i++) - SzCoderInfo_Free(&p->Coders[i], alloc); - IAlloc_Free(alloc, p->Coders); - IAlloc_Free(alloc, p->BindPairs); - IAlloc_Free(alloc, p->PackStreams); - IAlloc_Free(alloc, p->UnpackSizes); - SzFolder_Init(p); -} - -UInt32 SzFolder_GetNumOutStreams(CSzFolder *p) -{ - UInt32 result = 0; - UInt32 i; - for (i = 0; i < p->NumCoders; i++) - result += p->Coders[i].NumOutStreams; - return result; -} - -int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].InIndex == inStreamIndex) - return i; - return -1; -} - - -int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex) -{ - UInt32 i; - for (i = 0; i < p->NumBindPairs; i++) - if (p->BindPairs[i].OutIndex == outStreamIndex) - return i; - return -1; -} - -UInt64 SzFolder_GetUnpackSize(CSzFolder *p) -{ - int i = (int)SzFolder_GetNumOutStreams(p); - if (i == 0) - return 0; - for (i--; i >= 0; i--) - if (SzFolder_FindBindPairForOutStream(p, i) < 0) - return p->UnpackSizes[i]; - /* throw 1; */ - return 0; -} - -void SzFile_Init(CSzFileItem *p) -{ - p->HasStream = 1; - p->IsDir = 0; - p->IsAnti = 0; - p->CrcDefined = 0; - p->MTimeDefined = 0; -} - -void SzAr_Init(CSzAr *p) -{ - p->PackSizes = 0; - p->PackCRCsDefined = 0; - p->PackCRCs = 0; - p->Folders = 0; - p->Files = 0; - p->NumPackStreams = 0; - p->NumFolders = 0; - p->NumFiles = 0; -} - -void SzAr_Free(CSzAr *p, ISzAlloc *alloc) -{ - UInt32 i; - if (p->Folders) - for (i = 0; i < p->NumFolders; i++) - SzFolder_Free(&p->Folders[i], alloc); - - IAlloc_Free(alloc, p->PackSizes); - IAlloc_Free(alloc, p->PackCRCsDefined); - IAlloc_Free(alloc, p->PackCRCs); - IAlloc_Free(alloc, p->Folders); - IAlloc_Free(alloc, p->Files); - SzAr_Init(p); -} - - -void SzArEx_Init(CSzArEx *p) -{ - SzAr_Init(&p->db); - p->FolderStartPackStreamIndex = 0; - p->PackStreamStartPositions = 0; - p->FolderStartFileIndex = 0; - p->FileIndexToFolderIndexMap = 0; - p->FileNameOffsets = 0; - Buf_Init(&p->FileNames); -} - -void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) -{ - IAlloc_Free(alloc, p->FolderStartPackStreamIndex); - IAlloc_Free(alloc, p->PackStreamStartPositions); - IAlloc_Free(alloc, p->FolderStartFileIndex); - IAlloc_Free(alloc, p->FileIndexToFolderIndexMap); - - IAlloc_Free(alloc, p->FileNameOffsets); - Buf_Free(&p->FileNames, alloc); - - SzAr_Free(&p->db, alloc); - SzArEx_Init(p); -} - -/* -UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const -{ - return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; -} - -UInt64 GetFilePackSize(int fileIndex) const -{ - int folderIndex = FileIndexToFolderIndexMap[fileIndex]; - if (folderIndex >= 0) - { - const CSzFolder &folderInfo = Folders[folderIndex]; - if (FolderStartFileIndex[folderIndex] == fileIndex) - return GetFolderFullPackSize(folderIndex); - } - return 0; -} -*/ - -#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \ - if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; } - -static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc) -{ - UInt32 startPos = 0; - UInt64 startPosSize = 0; - UInt32 i; - UInt32 folderIndex = 0; - UInt32 indexInFolder = 0; - MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc); - for (i = 0; i < p->db.NumFolders; i++) - { - p->FolderStartPackStreamIndex[i] = startPos; - startPos += p->db.Folders[i].NumPackStreams; - } - - MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc); - - for (i = 0; i < p->db.NumPackStreams; i++) - { - p->PackStreamStartPositions[i] = startPosSize; - startPosSize += p->db.PackSizes[i]; - } - - MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc); - MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc); - - for (i = 0; i < p->db.NumFiles; i++) - { - CSzFileItem *file = p->db.Files + i; - int emptyStream = !file->HasStream; - if (emptyStream && indexInFolder == 0) - { - p->FileIndexToFolderIndexMap[i] = (UInt32)-1; - continue; - } - if (indexInFolder == 0) - { - /* - v3.13 incorrectly worked with empty folders - v4.07: Loop for skipping empty folders - */ - for (;;) - { - if (folderIndex >= p->db.NumFolders) - return SZ_ERROR_ARCHIVE; - p->FolderStartFileIndex[folderIndex] = i; - if (p->db.Folders[folderIndex].NumUnpackStreams != 0) - break; - folderIndex++; - } - } - p->FileIndexToFolderIndexMap[i] = folderIndex; - if (emptyStream) - continue; - indexInFolder++; - if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams) - { - folderIndex++; - indexInFolder = 0; - } - } - return SZ_OK; -} - - -UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder) -{ - return p->dataPos + - p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder]; -} - -int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize) -{ - UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex]; - CSzFolder *folder = p->db.Folders + folderIndex; - UInt64 size = 0; - UInt32 i; - for (i = 0; i < folder->NumPackStreams; i++) - { - UInt64 t = size + p->db.PackSizes[packStreamIndex + i]; - if (t < size) /* check it */ - return SZ_ERROR_FAIL; - size = t; - } - *resSize = size; - return SZ_OK; -} - - -/* -SRes SzReadTime(const CObjectVector &dataVector, - CObjectVector &files, UInt64 type) -{ - CBoolVector boolVector; - RINOK(ReadBoolVector2(files.Size(), boolVector)) - - CStreamSwitch streamSwitch; - RINOK(streamSwitch.Set(this, &dataVector)); - - for (int i = 0; i < files.Size(); i++) - { - CSzFileItem &file = files[i]; - CArchiveFileTime fileTime; - bool defined = boolVector[i]; - if (defined) - { - UInt32 low, high; - RINOK(SzReadUInt32(low)); - RINOK(SzReadUInt32(high)); - fileTime.dwLowDateTime = low; - fileTime.dwHighDateTime = high; - } - switch(type) - { - case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break; - case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break; - case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break; - } - } - return SZ_OK; -} -*/ - -static int TestSignatureCandidate(Byte *testBytes) -{ - size_t i; - for (i = 0; i < k7zSignatureSize; i++) - if (testBytes[i] != k7zSignature[i]) - return 0; - return 1; -} - -typedef struct _CSzState -{ - Byte *Data; - size_t Size; -}CSzData; - -static SRes SzReadByte(CSzData *sd, Byte *b) -{ - if (sd->Size == 0) - return SZ_ERROR_ARCHIVE; - sd->Size--; - *b = *sd->Data++; - return SZ_OK; -} - -static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size) -{ - size_t i; - for (i = 0; i < size; i++) - { - RINOK(SzReadByte(sd, data + i)); - } - return SZ_OK; -} - -static SRes SzReadUInt32(CSzData *sd, UInt32 *value) -{ - int i; - *value = 0; - for (i = 0; i < 4; i++) - { - Byte b; - RINOK(SzReadByte(sd, &b)); - *value |= ((UInt32)(b) << (8 * i)); - } - return SZ_OK; -} - -static SRes SzReadNumber(CSzData *sd, UInt64 *value) -{ - Byte firstByte; - Byte mask = 0x80; - int i; - RINOK(SzReadByte(sd, &firstByte)); - *value = 0; - for (i = 0; i < 8; i++) - { - Byte b; - if ((firstByte & mask) == 0) - { - UInt64 highPart = firstByte & (mask - 1); - *value += (highPart << (8 * i)); - return SZ_OK; - } - RINOK(SzReadByte(sd, &b)); - *value |= ((UInt64)b << (8 * i)); - mask >>= 1; - } - return SZ_OK; -} - -static SRes SzReadNumber32(CSzData *sd, UInt32 *value) -{ - UInt64 value64; - RINOK(SzReadNumber(sd, &value64)); - if (value64 >= 0x80000000) - return SZ_ERROR_UNSUPPORTED; - if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2))) - return SZ_ERROR_UNSUPPORTED; - *value = (UInt32)value64; - return SZ_OK; -} - -static SRes SzReadID(CSzData *sd, UInt64 *value) -{ - return SzReadNumber(sd, value); -} - -static SRes SzSkeepDataSize(CSzData *sd, UInt64 size) -{ - if (size > sd->Size) - return SZ_ERROR_ARCHIVE; - sd->Size -= (size_t)size; - sd->Data += (size_t)size; - return SZ_OK; -} - -static SRes SzSkeepData(CSzData *sd) -{ - UInt64 size; - RINOK(SzReadNumber(sd, &size)); - return SzSkeepDataSize(sd, size); -} - -static SRes SzReadArchiveProperties(CSzData *sd) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - SzSkeepData(sd); - } - return SZ_OK; -} - -static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == attribute) - return SZ_OK; - if (type == k7zIdEnd) - return SZ_ERROR_ARCHIVE; - RINOK(SzSkeepData(sd)); - } -} - -static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) -{ - Byte b = 0; - Byte mask = 0; - size_t i; - MY_ALLOC(Byte, *v, numItems, alloc); - for (i = 0; i < numItems; i++) - { - if (mask == 0) - { - RINOK(SzReadByte(sd, &b)); - mask = 0x80; - } - (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0); - mask >>= 1; - } - return SZ_OK; -} - -static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc) -{ - Byte allAreDefined; - size_t i; - RINOK(SzReadByte(sd, &allAreDefined)); - if (allAreDefined == 0) - return SzReadBoolVector(sd, numItems, v, alloc); - MY_ALLOC(Byte, *v, numItems, alloc); - for (i = 0; i < numItems; i++) - (*v)[i] = 1; - return SZ_OK; -} - -static SRes SzReadHashDigests( - CSzData *sd, - size_t numItems, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *alloc) -{ - size_t i; - RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc)); - MY_ALLOC(UInt32, *digests, numItems, alloc); - for (i = 0; i < numItems; i++) - if ((*digestsDefined)[i]) - { - RINOK(SzReadUInt32(sd, (*digests) + i)); - } - return SZ_OK; -} - -static SRes SzReadPackInfo( - CSzData *sd, - UInt64 *dataOffset, - UInt32 *numPackStreams, - UInt64 **packSizes, - Byte **packCRCsDefined, - UInt32 **packCRCs, - ISzAlloc *alloc) -{ - UInt32 i; - RINOK(SzReadNumber(sd, dataOffset)); - RINOK(SzReadNumber32(sd, numPackStreams)); - - RINOK(SzWaitAttribute(sd, k7zIdSize)); - - MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc); - - for (i = 0; i < *numPackStreams; i++) - { - RINOK(SzReadNumber(sd, (*packSizes) + i)); - } - - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - if (type == k7zIdCRC) - { - RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc)); - continue; - } - RINOK(SzSkeepData(sd)); - } - if (*packCRCsDefined == 0) - { - MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc); - MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc); - for (i = 0; i < *numPackStreams; i++) - { - (*packCRCsDefined)[i] = 0; - (*packCRCs)[i] = 0; - } - } - return SZ_OK; -} - -static SRes SzReadSwitch(CSzData *sd) -{ - Byte external; - RINOK(SzReadByte(sd, &external)); - return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; -} - -static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc) -{ - UInt32 numCoders, numBindPairs, numPackStreams, i; - UInt32 numInStreams = 0, numOutStreams = 0; - - RINOK(SzReadNumber32(sd, &numCoders)); - if (numCoders > NUM_FOLDER_CODERS_MAX) - return SZ_ERROR_UNSUPPORTED; - folder->NumCoders = numCoders; - - MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc); - - for (i = 0; i < numCoders; i++) - SzCoderInfo_Init(folder->Coders + i); - - for (i = 0; i < numCoders; i++) - { - Byte mainByte; - CSzCoderInfo *coder = folder->Coders + i; - { - unsigned idSize, j; - Byte longID[15]; - RINOK(SzReadByte(sd, &mainByte)); - idSize = (unsigned)(mainByte & 0xF); - RINOK(SzReadBytes(sd, longID, idSize)); - if (idSize > sizeof(coder->MethodID)) - return SZ_ERROR_UNSUPPORTED; - coder->MethodID = 0; - for (j = 0; j < idSize; j++) - coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j); - - if ((mainByte & 0x10) != 0) - { - RINOK(SzReadNumber32(sd, &coder->NumInStreams)); - RINOK(SzReadNumber32(sd, &coder->NumOutStreams)); - if (coder->NumInStreams > NUM_CODER_STREAMS_MAX || - coder->NumOutStreams > NUM_CODER_STREAMS_MAX) - return SZ_ERROR_UNSUPPORTED; - } - else - { - coder->NumInStreams = 1; - coder->NumOutStreams = 1; - } - if ((mainByte & 0x20) != 0) - { - UInt64 propertiesSize = 0; - RINOK(SzReadNumber(sd, &propertiesSize)); - if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc)) - return SZ_ERROR_MEM; - RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize)); - } - } - while ((mainByte & 0x80) != 0) - { - RINOK(SzReadByte(sd, &mainByte)); - RINOK(SzSkeepDataSize(sd, (mainByte & 0xF))); - if ((mainByte & 0x10) != 0) - { - UInt32 n; - RINOK(SzReadNumber32(sd, &n)); - RINOK(SzReadNumber32(sd, &n)); - } - if ((mainByte & 0x20) != 0) - { - UInt64 propertiesSize = 0; - RINOK(SzReadNumber(sd, &propertiesSize)); - RINOK(SzSkeepDataSize(sd, propertiesSize)); - } - } - numInStreams += coder->NumInStreams; - numOutStreams += coder->NumOutStreams; - } - - if (numOutStreams == 0) - return SZ_ERROR_UNSUPPORTED; - - folder->NumBindPairs = numBindPairs = numOutStreams - 1; - MY_ALLOC(CSzBindPair, folder->BindPairs, (size_t)numBindPairs, alloc); - - for (i = 0; i < numBindPairs; i++) - { - CSzBindPair *bp = folder->BindPairs + i; - RINOK(SzReadNumber32(sd, &bp->InIndex)); - RINOK(SzReadNumber32(sd, &bp->OutIndex)); - } - - if (numInStreams < numBindPairs) - return SZ_ERROR_UNSUPPORTED; - - folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs; - MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc); - - if (numPackStreams == 1) - { - for (i = 0; i < numInStreams ; i++) - if (SzFolder_FindBindPairForInStream(folder, i) < 0) - break; - if (i == numInStreams) - return SZ_ERROR_UNSUPPORTED; - folder->PackStreams[0] = i; - } - else - for (i = 0; i < numPackStreams; i++) - { - RINOK(SzReadNumber32(sd, folder->PackStreams + i)); - } - return SZ_OK; -} - -static SRes SzReadUnpackInfo( - CSzData *sd, - UInt32 *numFolders, - CSzFolder **folders, /* for alloc */ - ISzAlloc *alloc, - ISzAlloc *allocTemp) -{ - UInt32 i; - RINOK(SzWaitAttribute(sd, k7zIdFolder)); - RINOK(SzReadNumber32(sd, numFolders)); - { - RINOK(SzReadSwitch(sd)); - - MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc); - - for (i = 0; i < *numFolders; i++) - SzFolder_Init((*folders) + i); - - for (i = 0; i < *numFolders; i++) - { - RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc)); - } - } - - RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize)); - - for (i = 0; i < *numFolders; i++) - { - UInt32 j; - CSzFolder *folder = (*folders) + i; - UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder); - - MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc); - - for (j = 0; j < numOutStreams; j++) - { - RINOK(SzReadNumber(sd, folder->UnpackSizes + j)); - } - } - - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - return SZ_OK; - if (type == k7zIdCRC) - { - SRes res; - Byte *crcsDefined = 0; - UInt32 *crcs = 0; - res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp); - if (res == SZ_OK) - { - for (i = 0; i < *numFolders; i++) - { - CSzFolder *folder = (*folders) + i; - folder->UnpackCRCDefined = crcsDefined[i]; - folder->UnpackCRC = crcs[i]; - } - } - IAlloc_Free(allocTemp, crcs); - IAlloc_Free(allocTemp, crcsDefined); - RINOK(res); - continue; - } - RINOK(SzSkeepData(sd)); - } -} - -static SRes SzReadSubStreamsInfo( - CSzData *sd, - UInt32 numFolders, - CSzFolder *folders, - UInt32 *numUnpackStreams, - UInt64 **unpackSizes, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *allocTemp) -{ - UInt64 type = 0; - UInt32 i; - UInt32 si = 0; - UInt32 numDigests = 0; - - for (i = 0; i < numFolders; i++) - folders[i].NumUnpackStreams = 1; - *numUnpackStreams = numFolders; - - for (;;) - { - RINOK(SzReadID(sd, &type)); - if (type == k7zIdNumUnpackStream) - { - *numUnpackStreams = 0; - for (i = 0; i < numFolders; i++) - { - UInt32 numStreams; - RINOK(SzReadNumber32(sd, &numStreams)); - folders[i].NumUnpackStreams = numStreams; - *numUnpackStreams += numStreams; - } - continue; - } - if (type == k7zIdCRC || type == k7zIdSize) - break; - if (type == k7zIdEnd) - break; - RINOK(SzSkeepData(sd)); - } - - if (*numUnpackStreams == 0) - { - *unpackSizes = 0; - *digestsDefined = 0; - *digests = 0; - } - else - { - *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64)); - RINOM(*unpackSizes); - *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte)); - RINOM(*digestsDefined); - *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32)); - RINOM(*digests); - } - - for (i = 0; i < numFolders; i++) - { - /* - v3.13 incorrectly worked with empty folders - v4.07: we check that folder is empty - */ - UInt64 sum = 0; - UInt32 j; - UInt32 numSubstreams = folders[i].NumUnpackStreams; - if (numSubstreams == 0) - continue; - if (type == k7zIdSize) - for (j = 1; j < numSubstreams; j++) - { - UInt64 size; - RINOK(SzReadNumber(sd, &size)); - (*unpackSizes)[si++] = size; - sum += size; - } - (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum; - } - if (type == k7zIdSize) - { - RINOK(SzReadID(sd, &type)); - } - - for (i = 0; i < *numUnpackStreams; i++) - { - (*digestsDefined)[i] = 0; - (*digests)[i] = 0; - } - - - for (i = 0; i < numFolders; i++) - { - UInt32 numSubstreams = folders[i].NumUnpackStreams; - if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) - numDigests += numSubstreams; - } - - - si = 0; - for (;;) - { - if (type == k7zIdCRC) - { - int digestIndex = 0; - Byte *digestsDefined2 = 0; - UInt32 *digests2 = 0; - SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp); - if (res == SZ_OK) - { - for (i = 0; i < numFolders; i++) - { - CSzFolder *folder = folders + i; - UInt32 numSubstreams = folder->NumUnpackStreams; - if (numSubstreams == 1 && folder->UnpackCRCDefined) - { - (*digestsDefined)[si] = 1; - (*digests)[si] = folder->UnpackCRC; - si++; - } - else - { - UInt32 j; - for (j = 0; j < numSubstreams; j++, digestIndex++) - { - (*digestsDefined)[si] = digestsDefined2[digestIndex]; - (*digests)[si] = digests2[digestIndex]; - si++; - } - } - } - } - IAlloc_Free(allocTemp, digestsDefined2); - IAlloc_Free(allocTemp, digests2); - RINOK(res); - } - else if (type == k7zIdEnd) - return SZ_OK; - else - { - RINOK(SzSkeepData(sd)); - } - RINOK(SzReadID(sd, &type)); - } -} - - -static SRes SzReadStreamsInfo( - CSzData *sd, - UInt64 *dataOffset, - CSzAr *p, - UInt32 *numUnpackStreams, - UInt64 **unpackSizes, /* allocTemp */ - Byte **digestsDefined, /* allocTemp */ - UInt32 **digests, /* allocTemp */ - ISzAlloc *alloc, - ISzAlloc *allocTemp) -{ - for (;;) - { - UInt64 type; - RINOK(SzReadID(sd, &type)); - if ((UInt64)(int)type != type) - return SZ_ERROR_UNSUPPORTED; - switch((int)type) - { - case k7zIdEnd: - return SZ_OK; - case k7zIdPackInfo: - { - RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams, - &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc)); - break; - } - case k7zIdUnpackInfo: - { - RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp)); - break; - } - case k7zIdSubStreamsInfo: - { - RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders, - numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp)); - break; - } - default: - return SZ_ERROR_UNSUPPORTED; - } - } -} - -size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) -{ - size_t len = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; - if (dest != 0) - { - size_t i; - const Byte *src = p->FileNames.data + (p->FileNameOffsets[fileIndex] * 2); - for (i = 0; i < len; i++) - dest[i] = GetUi16(src + i * 2); - } - return len; -} - -static SRes SzReadFileNames(const Byte *p, size_t size, UInt32 numFiles, size_t *sizes) -{ - UInt32 i; - size_t pos = 0; - for (i = 0; i < numFiles; i++) - { - sizes[i] = pos; - for (;;) - { - if (pos >= size) - return SZ_ERROR_ARCHIVE; - if (p[pos * 2] == 0 && p[pos * 2 + 1] == 0) - break; - pos++; - } - pos++; - } - sizes[i] = pos; - return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; -} - -static SRes SzReadHeader2( - CSzArEx *p, /* allocMain */ - CSzData *sd, - UInt64 **unpackSizes, /* allocTemp */ - Byte **digestsDefined, /* allocTemp */ - UInt32 **digests, /* allocTemp */ - Byte **emptyStreamVector, /* allocTemp */ - Byte **emptyFileVector, /* allocTemp */ - Byte **lwtVector, /* allocTemp */ - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt64 type; - UInt32 numUnpackStreams = 0; - UInt32 numFiles = 0; - CSzFileItem *files = 0; - UInt32 numEmptyStreams = 0; - UInt32 i; - - RINOK(SzReadID(sd, &type)); - - if (type == k7zIdArchiveProperties) - { - RINOK(SzReadArchiveProperties(sd)); - RINOK(SzReadID(sd, &type)); - } - - - if (type == k7zIdMainStreamsInfo) - { - RINOK(SzReadStreamsInfo(sd, - &p->dataPos, - &p->db, - &numUnpackStreams, - unpackSizes, - digestsDefined, - digests, allocMain, allocTemp)); - p->dataPos += p->startPosAfterHeader; - RINOK(SzReadID(sd, &type)); - } - - if (type == k7zIdEnd) - return SZ_OK; - if (type != k7zIdFilesInfo) - return SZ_ERROR_ARCHIVE; - - RINOK(SzReadNumber32(sd, &numFiles)); - p->db.NumFiles = numFiles; - - MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain); - - p->db.Files = files; - for (i = 0; i < numFiles; i++) - SzFile_Init(files + i); - - for (;;) - { - UInt64 type; - UInt64 size; - RINOK(SzReadID(sd, &type)); - if (type == k7zIdEnd) - break; - RINOK(SzReadNumber(sd, &size)); - if (size > sd->Size) - return SZ_ERROR_ARCHIVE; - if ((UInt64)(int)type != type) - { - RINOK(SzSkeepDataSize(sd, size)); - } - else - switch((int)type) - { - case k7zIdName: - { - size_t namesSize; - RINOK(SzReadSwitch(sd)); - namesSize = (size_t)size - 1; - if ((namesSize & 1) != 0) - return SZ_ERROR_ARCHIVE; - if (!Buf_Create(&p->FileNames, namesSize, allocMain)) - return SZ_ERROR_MEM; - MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); - memcpy(p->FileNames.data, sd->Data, namesSize); - RINOK(SzReadFileNames(sd->Data, namesSize >> 1, numFiles, p->FileNameOffsets)) - RINOK(SzSkeepDataSize(sd, namesSize)); - break; - } - case k7zIdEmptyStream: - { - RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp)); - numEmptyStreams = 0; - for (i = 0; i < numFiles; i++) - if ((*emptyStreamVector)[i]) - numEmptyStreams++; - break; - } - case k7zIdEmptyFile: - { - RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp)); - break; - } - case k7zIdWinAttributes: - { - RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); - RINOK(SzReadSwitch(sd)); - for (i = 0; i < numFiles; i++) - { - CSzFileItem *f = &files[i]; - Byte defined = (*lwtVector)[i]; - f->AttribDefined = defined; - f->Attrib = 0; - if (defined) - { - RINOK(SzReadUInt32(sd, &f->Attrib)); - } - } - IAlloc_Free(allocTemp, *lwtVector); - *lwtVector = NULL; - break; - } - case k7zIdMTime: - { - RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp)); - RINOK(SzReadSwitch(sd)); - for (i = 0; i < numFiles; i++) - { - CSzFileItem *f = &files[i]; - Byte defined = (*lwtVector)[i]; - f->MTimeDefined = defined; - f->MTime.Low = f->MTime.High = 0; - if (defined) - { - RINOK(SzReadUInt32(sd, &f->MTime.Low)); - RINOK(SzReadUInt32(sd, &f->MTime.High)); - } - } - IAlloc_Free(allocTemp, *lwtVector); - *lwtVector = NULL; - break; - } - default: - { - RINOK(SzSkeepDataSize(sd, size)); - } - } - } - - { - UInt32 emptyFileIndex = 0; - UInt32 sizeIndex = 0; - for (i = 0; i < numFiles; i++) - { - CSzFileItem *file = files + i; - file->IsAnti = 0; - if (*emptyStreamVector == 0) - file->HasStream = 1; - else - file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1); - if (file->HasStream) - { - file->IsDir = 0; - file->Size = (*unpackSizes)[sizeIndex]; - file->Crc = (*digests)[sizeIndex]; - file->CrcDefined = (Byte)(*digestsDefined)[sizeIndex]; - sizeIndex++; - } - else - { - if (*emptyFileVector == 0) - file->IsDir = 1; - else - file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1); - emptyFileIndex++; - file->Size = 0; - file->Crc = 0; - file->CrcDefined = 0; - } - } - } - return SzArEx_Fill(p, allocMain); -} - -static SRes SzReadHeader( - CSzArEx *p, - CSzData *sd, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt64 *unpackSizes = 0; - Byte *digestsDefined = 0; - UInt32 *digests = 0; - Byte *emptyStreamVector = 0; - Byte *emptyFileVector = 0; - Byte *lwtVector = 0; - SRes res = SzReadHeader2(p, sd, - &unpackSizes, &digestsDefined, &digests, - &emptyStreamVector, &emptyFileVector, &lwtVector, - allocMain, allocTemp); - IAlloc_Free(allocTemp, unpackSizes); - IAlloc_Free(allocTemp, digestsDefined); - IAlloc_Free(allocTemp, digests); - IAlloc_Free(allocTemp, emptyStreamVector); - IAlloc_Free(allocTemp, emptyFileVector); - IAlloc_Free(allocTemp, lwtVector); - return res; -} - -static SRes SzReadAndDecodePackedStreams2( - ILookInStream *inStream, - CSzData *sd, - CBuf *outBuffer, - UInt64 baseOffset, - CSzAr *p, - UInt64 **unpackSizes, - Byte **digestsDefined, - UInt32 **digests, - ISzAlloc *allocTemp) -{ - - UInt32 numUnpackStreams = 0; - UInt64 dataStartPos; - CSzFolder *folder; - UInt64 unpackSize; - SRes res; - - RINOK(SzReadStreamsInfo(sd, &dataStartPos, p, - &numUnpackStreams, unpackSizes, digestsDefined, digests, - allocTemp, allocTemp)); - - dataStartPos += baseOffset; - if (p->NumFolders != 1) - return SZ_ERROR_ARCHIVE; - - folder = p->Folders; - unpackSize = SzFolder_GetUnpackSize(folder); - - RINOK(LookInStream_SeekTo(inStream, dataStartPos)); - - if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp)) - return SZ_ERROR_MEM; - - res = SzFolder_Decode(folder, p->PackSizes, - inStream, dataStartPos, - outBuffer->data, (size_t)unpackSize, allocTemp); - RINOK(res); - if (folder->UnpackCRCDefined) - if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC) - return SZ_ERROR_CRC; - return SZ_OK; -} - -static SRes SzReadAndDecodePackedStreams( - ILookInStream *inStream, - CSzData *sd, - CBuf *outBuffer, - UInt64 baseOffset, - ISzAlloc *allocTemp) -{ - CSzAr p; - UInt64 *unpackSizes = 0; - Byte *digestsDefined = 0; - UInt32 *digests = 0; - SRes res; - SzAr_Init(&p); - res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset, - &p, &unpackSizes, &digestsDefined, &digests, - allocTemp); - SzAr_Free(&p, allocTemp); - IAlloc_Free(allocTemp, unpackSizes); - IAlloc_Free(allocTemp, digestsDefined); - IAlloc_Free(allocTemp, digests); - return res; -} - -static SRes SzArEx_Open2( - CSzArEx *p, - ILookInStream *inStream, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - Byte header[k7zStartHeaderSize]; - Int64 startArcPos; - UInt64 nextHeaderOffset, nextHeaderSize; - size_t nextHeaderSizeT; - UInt32 nextHeaderCRC; - CBuf buffer; - SRes res; - - startArcPos = 0; - RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); - - RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); - - if (!TestSignatureCandidate(header)) - return SZ_ERROR_NO_ARCHIVE; - if (header[6] != k7zMajorVersion) - return SZ_ERROR_UNSUPPORTED; - - nextHeaderOffset = GetUi64(header + 12); - nextHeaderSize = GetUi64(header + 20); - nextHeaderCRC = GetUi32(header + 28); - - p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; - - if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) - return SZ_ERROR_CRC; - - nextHeaderSizeT = (size_t)nextHeaderSize; - if (nextHeaderSizeT != nextHeaderSize) - return SZ_ERROR_MEM; - if (nextHeaderSizeT == 0) - return SZ_OK; - if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || - nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) - return SZ_ERROR_NO_ARCHIVE; - - { - Int64 pos = 0; - RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); - if ((UInt64)pos < startArcPos + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || - (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) - return SZ_ERROR_INPUT_EOF; - } - - RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); - - if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp)) - return SZ_ERROR_MEM; - - res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT); - if (res == SZ_OK) - { - res = SZ_ERROR_ARCHIVE; - if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC) - { - CSzData sd; - UInt64 type; - sd.Data = buffer.data; - sd.Size = buffer.size; - res = SzReadID(&sd, &type); - if (res == SZ_OK) - { - if (type == k7zIdEncodedHeader) - { - CBuf outBuffer; - Buf_Init(&outBuffer); - res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp); - if (res != SZ_OK) - Buf_Free(&outBuffer, allocTemp); - else - { - Buf_Free(&buffer, allocTemp); - buffer.data = outBuffer.data; - buffer.size = outBuffer.size; - sd.Data = buffer.data; - sd.Size = buffer.size; - res = SzReadID(&sd, &type); - } - } - } - if (res == SZ_OK) - { - if (type == k7zIdHeader) - res = SzReadHeader(p, &sd, allocMain, allocTemp); - else - res = SZ_ERROR_UNSUPPORTED; - } - } - } - Buf_Free(&buffer, allocTemp); - return res; -} - -SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp) -{ - SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); - if (res != SZ_OK) - SzArEx_Free(p, allocMain); - return res; -} - -SRes SzArEx_Extract( - const CSzArEx *p, - ILookInStream *inStream, - UInt32 fileIndex, - UInt32 *blockIndex, - Byte **outBuffer, - size_t *outBufferSize, - size_t *offset, - size_t *outSizeProcessed, - ISzAlloc *allocMain, - ISzAlloc *allocTemp) -{ - UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex]; - SRes res = SZ_OK; - *offset = 0; - *outSizeProcessed = 0; - if (folderIndex == (UInt32)-1) - { - IAlloc_Free(allocMain, *outBuffer); - *blockIndex = folderIndex; - *outBuffer = 0; - *outBufferSize = 0; - return SZ_OK; - } - - if (*outBuffer == 0 || *blockIndex != folderIndex) - { - CSzFolder *folder = p->db.Folders + folderIndex; - UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder); - size_t unpackSize = (size_t)unpackSizeSpec; - UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0); - - if (unpackSize != unpackSizeSpec) - return SZ_ERROR_MEM; - *blockIndex = folderIndex; - IAlloc_Free(allocMain, *outBuffer); - *outBuffer = 0; - - RINOK(LookInStream_SeekTo(inStream, startOffset)); - - if (res == SZ_OK) - { - *outBufferSize = unpackSize; - if (unpackSize != 0) - { - *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize); - if (*outBuffer == 0) - res = SZ_ERROR_MEM; - } - if (res == SZ_OK) - { - res = SzFolder_Decode(folder, - p->db.PackSizes + p->FolderStartPackStreamIndex[folderIndex], - inStream, startOffset, - *outBuffer, unpackSize, allocTemp); - if (res == SZ_OK) - { - if (folder->UnpackCRCDefined) - { - if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC) - res = SZ_ERROR_CRC; - } - } - } - } - } - if (res == SZ_OK) - { - UInt32 i; - CSzFileItem *fileItem = p->db.Files + fileIndex; - *offset = 0; - for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++) - *offset += (UInt32)p->db.Files[i].Size; - *outSizeProcessed = (size_t)fileItem->Size; - if (*offset + *outSizeProcessed > *outBufferSize) - return SZ_ERROR_FAIL; - if (fileItem->CrcDefined && CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->Crc) - res = SZ_ERROR_CRC; - } - return res; -} diff --git a/lzma/C/7zStream.c b/lzma/C/7zStream.c index f0959fb078..5a92d532cc 100644 --- a/lzma/C/7zStream.c +++ b/lzma/C/7zStream.c @@ -1,9 +1,11 @@ /* 7zStream.c -- 7z Stream functions -2010-03-11 : Igor Pavlov : Public domain */ +2013-11-12 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include -#include "Types.h" +#include "7zTypes.h" SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType) { diff --git a/lzma/C/Types.h b/lzma/C/7zTypes.h similarity index 93% rename from lzma/C/Types.h rename to lzma/C/7zTypes.h index f193ce2f53..f39d103262 100644 --- a/lzma/C/Types.h +++ b/lzma/C/7zTypes.h @@ -1,15 +1,15 @@ -/* Types.h -- Basic types -2010-10-09 : Igor Pavlov : Public domain */ +/* 7zTypes.h -- Basic types +2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H -#include - #ifdef _WIN32 #include #endif +#include + #ifndef EXTERN_C_BEGIN #ifdef __cplusplus #define EXTERN_C_BEGIN extern "C" { @@ -44,6 +44,7 @@ typedef int SRes; #ifdef _WIN32 typedef DWORD WRes; +/* typedef unsigned WRes; */ #else typedef int WRes; #endif @@ -116,6 +117,7 @@ typedef int Bool; #else +#define MY_NO_INLINE #define MY_CDECL #define MY_FAST_CALL diff --git a/lzma/C/7zVersion.h b/lzma/C/7zVersion.h index 43f0fef6ff..52ad3c1b16 100644 --- a/lzma/C/7zVersion.h +++ b/lzma/C/7zVersion.h @@ -1,8 +1,19 @@ -#define MY_VER_MAJOR 9 -#define MY_VER_MINOR 22 -#define MY_VER_BUILD 00 -#define MY_VERSION "9.22 beta" -#define MY_7ZIP_VERSION "9.22 beta" -#define MY_DATE "2011-04-18" -#define MY_COPYRIGHT ": Igor Pavlov : Public domain" -#define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE +#define MY_VER_MAJOR 15 +#define MY_VER_MINOR 14 +#define MY_VER_BUILD 0 +#define MY_VERSION_NUMBERS "15.14" +#define MY_VERSION "15.14" +#define MY_DATE "2015-12-31" +#undef MY_COPYRIGHT +#undef MY_VERSION_COPYRIGHT_DATE +#define MY_AUTHOR_NAME "Igor Pavlov" +#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain" +#define MY_COPYRIGHT_CR "Copyright (c) 1999-2015 Igor Pavlov" + +#ifdef USE_COPYRIGHT_CR + #define MY_COPYRIGHT MY_COPYRIGHT_CR +#else + #define MY_COPYRIGHT MY_COPYRIGHT_PD +#endif + +#define MY_VERSION_COPYRIGHT_DATE MY_VERSION " : " MY_COPYRIGHT " : " MY_DATE diff --git a/lzma/C/Bcj2.c b/lzma/C/Bcj2.c index 474bdd45d3..707362a615 100644 --- a/lzma/C/Bcj2.c +++ b/lzma/C/Bcj2.c @@ -1,132 +1,256 @@ -/* Bcj2.c -- Converter for x86 code (BCJ2) -2008-10-04 : Igor Pavlov : Public domain */ +/* Bcj2.c -- BCJ2 Decoder (Converter for x86 code) +2015-08-01 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "Bcj2.h" +#include "CpuArch.h" -#ifdef _LZMA_PROB32 -#define CProb UInt32 -#else #define CProb UInt16 -#endif -#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) -#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) - -#define kNumTopBits 24 -#define kTopValue ((UInt32)1 << kNumTopBits) - -#define kNumBitModelTotalBits 11 -#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kTopValue ((UInt32)1 << 24) +#define kNumModelBits 11 +#define kBitModelTotal (1 << kNumModelBits) #define kNumMoveBits 5 -#define RC_READ_BYTE (*buffer++) -#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } -#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \ - { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }} +#define _IF_BIT_0 ttt = *prob; bound = (p->range >> kNumModelBits) * ttt; if (p->code < bound) +#define _UPDATE_0 p->range = bound; *prob = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define _UPDATE_1 p->range -= bound; p->code -= bound; *prob = (CProb)(ttt - (ttt >> kNumMoveBits)); -#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; } - -#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; -#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; - -int Bcj2_Decode( - const Byte *buf0, SizeT size0, - const Byte *buf1, SizeT size1, - const Byte *buf2, SizeT size2, - const Byte *buf3, SizeT size3, - Byte *outBuf, SizeT outSize) +void Bcj2Dec_Init(CBcj2Dec *p) { - CProb p[256 + 2]; - SizeT inPos = 0, outPos = 0; + unsigned i; - const Byte *buffer, *bufferLim; - UInt32 range, code; - Byte prevByte = 0; + p->state = BCJ2_DEC_STATE_OK; + p->ip = 0; + p->temp[3] = 0; + p->range = 0; + p->code = 0; + for (i = 0; i < sizeof(p->probs) / sizeof(p->probs[0]); i++) + p->probs[i] = kBitModelTotal >> 1; +} - unsigned int i; - for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) - p[i] = kBitModelTotal >> 1; +SRes Bcj2Dec_Decode(CBcj2Dec *p) +{ + if (p->range <= 5) + { + p->state = BCJ2_DEC_STATE_OK; + for (; p->range != 5; p->range++) + { + if (p->range == 1 && p->code != 0) + return SZ_ERROR_DATA; + + if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC]) + { + p->state = BCJ2_STREAM_RC; + return SZ_OK; + } - buffer = buf3; - bufferLim = buffer + size3; - RC_INIT2 + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; + } + + if (p->code == 0xFFFFFFFF) + return SZ_ERROR_DATA; + + p->range = 0xFFFFFFFF; + } + else if (p->state >= BCJ2_DEC_STATE_ORIG_0) + { + while (p->state <= BCJ2_DEC_STATE_ORIG_3) + { + Byte *dest = p->dest; + if (dest == p->destLim) + return SZ_OK; + *dest = p->temp[p->state++ - BCJ2_DEC_STATE_ORIG_0]; + p->dest = dest + 1; + } + } - if (outSize == 0) - return SZ_OK; + /* + if (BCJ2_IS_32BIT_STREAM(p->state)) + { + const Byte *cur = p->bufs[p->state]; + if (cur == p->lims[p->state]) + return SZ_OK; + p->bufs[p->state] = cur + 4; + + { + UInt32 val; + Byte *dest; + SizeT rem; + + p->ip += 4; + val = GetBe32(cur) - p->ip; + dest = p->dest; + rem = p->destLim - dest; + if (rem < 4) + { + SizeT i; + SetUi32(p->temp, val); + for (i = 0; i < rem; i++) + dest[i] = p->temp[i]; + p->dest = dest + rem; + p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem; + return SZ_OK; + } + SetUi32(dest, val); + p->temp[3] = (Byte)(val >> 24); + p->dest = dest + 4; + p->state = BCJ2_DEC_STATE_OK; + } + } + */ for (;;) { - Byte b; - CProb *prob; - UInt32 bound; - UInt32 ttt; - - SizeT limit = size0 - inPos; - if (outSize - outPos < limit) - limit = outSize - outPos; - while (limit != 0) - { - Byte b = buf0[inPos]; - outBuf[outPos++] = b; - if (IsJ(prevByte, b)) - break; - inPos++; - prevByte = b; - limit--; - } - - if (limit == 0 || outPos == outSize) - break; - - b = buf0[inPos++]; - - if (b == 0xE8) - prob = p + prevByte; - else if (b == 0xE9) - prob = p + 256; - else - prob = p + 257; - - IF_BIT_0(prob) - { - UPDATE_0(prob) - prevByte = b; - } + if (BCJ2_IS_32BIT_STREAM(p->state)) + p->state = BCJ2_DEC_STATE_OK; else { - UInt32 dest; - const Byte *v; - UPDATE_1(prob) - if (b == 0xE8) + if (p->range < kTopValue) { - v = buf1; - if (size1 < 4) - return SZ_ERROR_DATA; - buf1 += 4; - size1 -= 4; + if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC]) + { + p->state = BCJ2_STREAM_RC; + return SZ_OK; + } + p->range <<= 8; + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; } - else + { - v = buf2; - if (size2 < 4) - return SZ_ERROR_DATA; - buf2 += 4; - size2 -= 4; + const Byte *src = p->bufs[BCJ2_STREAM_MAIN]; + const Byte *srcLim; + Byte *dest; + SizeT num = p->lims[BCJ2_STREAM_MAIN] - src; + + if (num == 0) + { + p->state = BCJ2_STREAM_MAIN; + return SZ_OK; + } + + dest = p->dest; + if (num > (SizeT)(p->destLim - dest)) + { + num = p->destLim - dest; + if (num == 0) + { + p->state = BCJ2_DEC_STATE_ORIG; + return SZ_OK; + } + } + + srcLim = src + num; + + if (p->temp[3] == 0x0F && (src[0] & 0xF0) == 0x80) + *dest = src[0]; + else for (;;) + { + Byte b = *src; + *dest = b; + if (b != 0x0F) + { + if ((b & 0xFE) == 0xE8) + break; + dest++; + if (++src != srcLim) + continue; + break; + } + dest++; + if (++src == srcLim) + break; + if ((*src & 0xF0) != 0x80) + continue; + *dest = *src; + break; + } + + num = src - p->bufs[BCJ2_STREAM_MAIN]; + + if (src == srcLim) + { + p->temp[3] = src[-1]; + p->bufs[BCJ2_STREAM_MAIN] = src; + p->ip += (UInt32)num; + p->dest += num; + p->state = + p->bufs[BCJ2_STREAM_MAIN] == + p->lims[BCJ2_STREAM_MAIN] ? + (unsigned)BCJ2_STREAM_MAIN : + (unsigned)BCJ2_DEC_STATE_ORIG; + return SZ_OK; + } + + { + UInt32 bound, ttt; + CProb *prob; + Byte b = src[0]; + Byte prev = (Byte)(num == 0 ? p->temp[3] : src[-1]); + + p->temp[3] = b; + p->bufs[BCJ2_STREAM_MAIN] = src + 1; + num++; + p->ip += (UInt32)num; + p->dest += num; + + prob = p->probs + (unsigned)(b == 0xE8 ? 2 + (unsigned)prev : (b == 0xE9 ? 1 : 0)); + + _IF_BIT_0 + { + _UPDATE_0 + continue; + } + _UPDATE_1 + + } } - dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) | - ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4); - outBuf[outPos++] = (Byte)dest; - if (outPos == outSize) + } + + { + UInt32 val; + unsigned cj = (p->temp[3] == 0xE8) ? BCJ2_STREAM_CALL : BCJ2_STREAM_JUMP; + const Byte *cur = p->bufs[cj]; + Byte *dest; + SizeT rem; + + if (cur == p->lims[cj]) + { + p->state = cj; break; - outBuf[outPos++] = (Byte)(dest >> 8); - if (outPos == outSize) + } + + val = GetBe32(cur); + p->bufs[cj] = cur + 4; + + p->ip += 4; + val -= p->ip; + dest = p->dest; + rem = p->destLim - dest; + + if (rem < 4) + { + SizeT i; + SetUi32(p->temp, val); + for (i = 0; i < rem; i++) + dest[i] = p->temp[i]; + p->dest = dest + rem; + p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem; break; - outBuf[outPos++] = (Byte)(dest >> 16); - if (outPos == outSize) - break; - outBuf[outPos++] = prevByte = (Byte)(dest >> 24); + } + + SetUi32(dest, val); + p->temp[3] = (Byte)(val >> 24); + p->dest = dest + 4; } } - return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA; + + if (p->range < kTopValue && p->bufs[BCJ2_STREAM_RC] != p->lims[BCJ2_STREAM_RC]) + { + p->range <<= 8; + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; + } + + return SZ_OK; } diff --git a/lzma/C/Bcj2.h b/lzma/C/Bcj2.h index d9d857bc30..68893d2d12 100644 --- a/lzma/C/Bcj2.h +++ b/lzma/C/Bcj2.h @@ -1,38 +1,146 @@ -/* Bcj2.h -- Converter for x86 code (BCJ2) -2009-02-07 : Igor Pavlov : Public domain */ +/* Bcj2.h -- BCJ2 Converter for x86 code +2014-11-10 : Igor Pavlov : Public domain */ #ifndef __BCJ2_H #define __BCJ2_H -#include "Types.h" +#include "7zTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN + +#define BCJ2_NUM_STREAMS 4 + +enum +{ + BCJ2_STREAM_MAIN, + BCJ2_STREAM_CALL, + BCJ2_STREAM_JUMP, + BCJ2_STREAM_RC +}; + +enum +{ + BCJ2_DEC_STATE_ORIG_0 = BCJ2_NUM_STREAMS, + BCJ2_DEC_STATE_ORIG_1, + BCJ2_DEC_STATE_ORIG_2, + BCJ2_DEC_STATE_ORIG_3, + + BCJ2_DEC_STATE_ORIG, + BCJ2_DEC_STATE_OK +}; + +enum +{ + BCJ2_ENC_STATE_ORIG = BCJ2_NUM_STREAMS, + BCJ2_ENC_STATE_OK +}; + + +#define BCJ2_IS_32BIT_STREAM(s) ((s) == BCJ2_STREAM_CALL || (s) == BCJ2_STREAM_JUMP) /* -Conditions: - outSize <= FullOutputSize, - where FullOutputSize is full size of output stream of x86_2 filter. - -If buf0 overlaps outBuf, there are two required conditions: - 1) (buf0 >= outBuf) - 2) (buf0 + size0 >= outBuf + FullOutputSize). - -Returns: - SZ_OK - SZ_ERROR_DATA - Data error +CBcj2Dec / CBcj2Enc +bufs sizes: + BUF_SIZE(n) = lims[n] - bufs[n] +bufs sizes for BCJ2_STREAM_CALL and BCJ2_STREAM_JUMP must be mutliply of 4: + (BUF_SIZE(BCJ2_STREAM_CALL) & 3) == 0 + (BUF_SIZE(BCJ2_STREAM_JUMP) & 3) == 0 */ -int Bcj2_Decode( - const Byte *buf0, SizeT size0, - const Byte *buf1, SizeT size1, - const Byte *buf2, SizeT size2, - const Byte *buf3, SizeT size3, - Byte *outBuf, SizeT outSize); +/* +CBcj2Dec: +dest is allowed to overlap with bufs[BCJ2_STREAM_MAIN], with the following conditions: + bufs[BCJ2_STREAM_MAIN] >= dest && + bufs[BCJ2_STREAM_MAIN] - dest >= tempReserv + + BUF_SIZE(BCJ2_STREAM_CALL) + + BUF_SIZE(BCJ2_STREAM_JUMP) + tempReserv = 0 : for first call of Bcj2Dec_Decode + tempReserv = 4 : for any other calls of Bcj2Dec_Decode + overlap with offset = 1 is not allowed +*/ -#ifdef __cplusplus -} -#endif +typedef struct +{ + const Byte *bufs[BCJ2_NUM_STREAMS]; + const Byte *lims[BCJ2_NUM_STREAMS]; + Byte *dest; + const Byte *destLim; + + unsigned state; /* BCJ2_STREAM_MAIN has more priority than BCJ2_STATE_ORIG */ + + UInt32 ip; + Byte temp[4]; + UInt32 range; + UInt32 code; + UInt16 probs[2 + 256]; +} CBcj2Dec; + +void Bcj2Dec_Init(CBcj2Dec *p); + +/* Returns: SZ_OK or SZ_ERROR_DATA */ +SRes Bcj2Dec_Decode(CBcj2Dec *p); + +#define Bcj2Dec_IsFinished(_p_) ((_p_)->code == 0) + + + +typedef enum +{ + BCJ2_ENC_FINISH_MODE_CONTINUE, + BCJ2_ENC_FINISH_MODE_END_BLOCK, + BCJ2_ENC_FINISH_MODE_END_STREAM +} EBcj2Enc_FinishMode; + +typedef struct +{ + Byte *bufs[BCJ2_NUM_STREAMS]; + const Byte *lims[BCJ2_NUM_STREAMS]; + const Byte *src; + const Byte *srcLim; + + unsigned state; + EBcj2Enc_FinishMode finishMode; + + Byte prevByte; + + Byte cache; + UInt32 range; + UInt64 low; + UInt64 cacheSize; + + UInt32 ip; + + /* 32-bit ralative offset in JUMP/CALL commands is + - (mod 4 GB) in 32-bit mode + - signed Int32 in 64-bit mode + We use (mod 4 GB) check for fileSize. + Use fileSize up to 2 GB, if you want to support 32-bit and 64-bit code conversion. */ + UInt32 fileIp; + UInt32 fileSize; /* (fileSize <= ((UInt32)1 << 31)), 0 means no_limit */ + UInt32 relatLimit; /* (relatLimit <= ((UInt32)1 << 31)), 0 means desable_conversion */ + + UInt32 tempTarget; + unsigned tempPos; + Byte temp[4 * 2]; + + unsigned flushPos; + + UInt16 probs[2 + 256]; +} CBcj2Enc; + +void Bcj2Enc_Init(CBcj2Enc *p); +void Bcj2Enc_Encode(CBcj2Enc *p); + +#define Bcj2Enc_Get_InputData_Size(p) ((SizeT)((p)->srcLim - (p)->src) + (p)->tempPos) +#define Bcj2Enc_IsFinished(p) ((p)->flushPos == 5) + + +#define BCJ2_RELAT_LIMIT_NUM_BITS 26 +#define BCJ2_RELAT_LIMIT ((UInt32)1 << BCJ2_RELAT_LIMIT_NUM_BITS) + +/* limit for CBcj2Enc::fileSize variable */ +#define BCJ2_FileSize_MAX ((UInt32)1 << 31) + +EXTERN_C_END #endif diff --git a/lzma/C/Bra.c b/lzma/C/Bra.c index 2a0f147b2e..976810c96e 100644 --- a/lzma/C/Bra.c +++ b/lzma/C/Bra.c @@ -1,6 +1,8 @@ /* Bra.c -- Converters for RISC code 2010-04-16 : Igor Pavlov : Public domain */ +#include "Precomp.h" + #include "Bra.h" SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) diff --git a/lzma/C/Bra.h b/lzma/C/Bra.h index 9c91e332d4..aba8dce14f 100644 --- a/lzma/C/Bra.h +++ b/lzma/C/Bra.h @@ -1,14 +1,12 @@ /* Bra.h -- Branch converters for executables -2009-02-07 : Igor Pavlov : Public domain */ +2013-01-18 : Igor Pavlov : Public domain */ #ifndef __BRA_H #define __BRA_H -#include "Types.h" +#include "7zTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN /* These functions convert relative addresses to absolute addresses @@ -61,8 +59,6 @@ SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/Bra86.c b/lzma/C/Bra86.c index 93566cb212..8dd3ed48d9 100644 --- a/lzma/C/Bra86.c +++ b/lzma/C/Bra86.c @@ -1,85 +1,82 @@ /* Bra86.c -- Converter for x86 code (BCJ) -2008-10-04 : Igor Pavlov : Public domain */ +2013-11-12 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "Bra.h" -#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) - -const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; -const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; +#define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) { - SizeT bufferPos = 0, prevPosT; - UInt32 prevMask = *state & 0x7; + SizeT pos = 0; + UInt32 mask = *state & 7; if (size < 5) return 0; + size -= 4; ip += 5; - prevPosT = (SizeT)0 - 1; for (;;) { - Byte *p = data + bufferPos; - Byte *limit = data + size - 4; + Byte *p = data + pos; + const Byte *limit = data + size; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; - bufferPos = (SizeT)(p - data); - if (p >= limit) - break; - prevPosT = bufferPos - prevPosT; - if (prevPosT > 3) - prevMask = 0; - else + { - prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; - if (prevMask != 0) + SizeT d = (SizeT)(p - data - pos); + pos = (SizeT)(p - data); + if (p >= limit) { - Byte b = p[4 - kMaskToBitNumber[prevMask]]; - if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) + *state = (d > 2 ? 0 : mask >> (unsigned)d); + return pos; + } + if (d > 2) + mask = 0; + else + { + mask >>= (unsigned)d; + if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1]))) { - prevPosT = bufferPos; - prevMask = ((prevMask << 1) & 0x7) | 1; - bufferPos++; + mask = (mask >> 1) | 4; + pos++; continue; } } } - prevPosT = bufferPos; if (Test86MSByte(p[4])) { - UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); - UInt32 dest; - for (;;) + UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 cur = ip + (UInt32)pos; + pos += 5; + if (encoding) + v += cur; + else + v -= cur; + if (mask != 0) { - Byte b; - int index; - if (encoding) - dest = (ip + (UInt32)bufferPos) + src; - else - dest = src - (ip + (UInt32)bufferPos); - if (prevMask == 0) - break; - index = kMaskToBitNumber[prevMask] * 8; - b = (Byte)(dest >> (24 - index)); - if (!Test86MSByte(b)) - break; - src = dest ^ ((1 << (32 - index)) - 1); + unsigned sh = (mask & 6) << 2; + if (Test86MSByte((Byte)(v >> sh))) + { + v ^= (((UInt32)0x100 << sh) - 1); + if (encoding) + v += cur; + else + v -= cur; + } + mask = 0; } - p[4] = (Byte)(~(((dest >> 24) & 1) - 1)); - p[3] = (Byte)(dest >> 16); - p[2] = (Byte)(dest >> 8); - p[1] = (Byte)dest; - bufferPos += 5; + p[1] = (Byte)v; + p[2] = (Byte)(v >> 8); + p[3] = (Byte)(v >> 16); + p[4] = (Byte)(0 - ((v >> 24) & 1)); } else { - prevMask = ((prevMask << 1) & 0x7) | 1; - bufferPos++; + mask = (mask >> 1) | 4; + pos++; } } - prevPosT = bufferPos - prevPosT; - *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7)); - return bufferPos; } diff --git a/lzma/C/BraIA64.c b/lzma/C/BraIA64.c new file mode 100644 index 0000000000..813830c79d --- /dev/null +++ b/lzma/C/BraIA64.c @@ -0,0 +1,69 @@ +/* BraIA64.c -- Converter for IA-64 code +2013-11-12 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Bra.h" + +static const Byte kBranchTable[32] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 +}; + +SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 16) + return 0; + size -= 16; + for (i = 0; i <= size; i += 16) + { + UInt32 instrTemplate = data[i] & 0x1F; + UInt32 mask = kBranchTable[instrTemplate]; + UInt32 bitPos = 5; + int slot; + for (slot = 0; slot < 3; slot++, bitPos += 41) + { + UInt32 bytePos, bitRes; + UInt64 instruction, instNorm; + int j; + if (((mask >> slot) & 1) == 0) + continue; + bytePos = (bitPos >> 3); + bitRes = bitPos & 0x7; + instruction = 0; + for (j = 0; j < 6; j++) + instruction += (UInt64)data[i + j + bytePos] << (8 * j); + + instNorm = instruction >> bitRes; + if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0) + { + UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); + UInt32 dest; + src |= ((UInt32)(instNorm >> 36) & 1) << 20; + + src <<= 4; + + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + + dest >>= 4; + + instNorm &= ~((UInt64)(0x8FFFFF) << 13); + instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); + instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); + + instruction &= (1 << bitRes) - 1; + instruction |= (instNorm << bitRes); + for (j = 0; j < 6; j++) + data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); + } + } + } + return i; +} diff --git a/lzma/C/Compiler.h b/lzma/C/Compiler.h new file mode 100644 index 0000000000..de8fab3749 --- /dev/null +++ b/lzma/C/Compiler.h @@ -0,0 +1,32 @@ +/* Compiler.h +2015-08-02 : Igor Pavlov : Public domain */ + +#ifndef __7Z_COMPILER_H +#define __7Z_COMPILER_H + +#ifdef _MSC_VER + + #ifdef UNDER_CE + #define RPC_NO_WINDOWS_H + /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */ + #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union + #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int + #endif + + #if _MSC_VER >= 1300 + #pragma warning(disable : 4996) // This function or variable may be unsafe + #else + #pragma warning(disable : 4511) // copy constructor could not be generated + #pragma warning(disable : 4512) // assignment operator could not be generated + #pragma warning(disable : 4514) // unreferenced inline function has been removed + #pragma warning(disable : 4702) // unreachable code + #pragma warning(disable : 4710) // not inlined + #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information + #endif + +#endif + +#define UNUSED_VAR(x) (void)x; +/* #define UNUSED_VAR(x) x=x; */ + +#endif diff --git a/lzma/C/CpuArch.c b/lzma/C/CpuArch.c index d6ab3f7f83..bcb84cb2bc 100644 --- a/lzma/C/CpuArch.c +++ b/lzma/C/CpuArch.c @@ -1,5 +1,7 @@ /* CpuArch.c -- CPU specific code -2010-10-26: Igor Pavlov : Public domain */ +2015-03-25: Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "CpuArch.h" @@ -9,6 +11,10 @@ #define USE_ASM #endif +#if !defined(USE_ASM) && _MSC_VER >= 1500 +#include +#endif + #if defined(USE_ASM) && !defined(MY_CPU_AMD64) static UInt32 CheckFlag(UInt32 flag) { @@ -48,7 +54,7 @@ static UInt32 CheckFlag(UInt32 flag) #define CHECK_CPUID_IS_SUPPORTED #endif -static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) +void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) { #ifdef USE_ASM @@ -70,28 +76,20 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) *c = c2; *d = d2; - #elif defined __PIC__ && defined __i386__ - - /* GCC or Clang WITH position-independent code generation, i386 only */ - - __asm__ __volatile__ ( - "xchgl %%ebx, %1\n" - "cpuid\n" - "xchgl %%ebx, %1\n" - : "=a" (*a) , - "=r" (*b) , - "=c" (*c) , - "=d" (*d) - : "0" (function)) ; - #else - /* GCC or Clang WITHOUT position-independent code generation, or x86_64 */ - __asm__ __volatile__ ( + #if defined(MY_CPU_X86) && defined(__PIC__) + "mov %%ebx, %%edi;" + "cpuid;" + "xchgl %%ebx, %%edi;" + : "=a" (*a) , + "=D" (*b) , + #else "cpuid" : "=a" (*a) , "=b" (*b) , + #endif "=c" (*c) , "=d" (*d) : "0" (function)) ; @@ -118,7 +116,7 @@ Bool x86cpuid_CheckAndRead(Cx86cpuid *p) return True; } -static UInt32 kVendors[][3] = +static const UInt32 kVendors[][3] = { { 0x756E6547, 0x49656E69, 0x6C65746E}, { 0x68747541, 0x69746E65, 0x444D4163}, @@ -146,12 +144,22 @@ Bool CPU_Is_InOrder() UInt32 family, model; if (!x86cpuid_CheckAndRead(&p)) return True; - family = x86cpuid_GetFamily(&p); - model = x86cpuid_GetModel(&p); + + family = x86cpuid_GetFamily(p.ver); + model = x86cpuid_GetModel(p.ver); + firm = x86cpuid_GetFirm(&p); + switch (firm) { - case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && model == 0x100C)); + case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( + /* In-Order Atom CPU */ + model == 0x1C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ + || model == 0x26 /* 45 nm, Z6xx */ + || model == 0x27 /* 32 nm, Z2460 */ + || model == 0x35 /* 32 nm, Z2760 */ + || model == 0x36 /* 32 nm, N2xxx, D2xxx */ + ))); case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); } @@ -159,6 +167,7 @@ Bool CPU_Is_InOrder() } #if !defined(MY_CPU_AMD64) && defined(_WIN32) +#include static Bool CPU_Sys_Is_SSE_Supported() { OSVERSIONINFO vi; diff --git a/lzma/C/CpuArch.h b/lzma/C/CpuArch.h index 1cd7c29c5d..b31c2546c0 100644 --- a/lzma/C/CpuArch.h +++ b/lzma/C/CpuArch.h @@ -1,27 +1,34 @@ /* CpuArch.h -- CPU specific code -2010-12-01: Igor Pavlov : Public domain */ +2015-12-01: Igor Pavlov : Public domain */ #ifndef __CPU_ARCH_H #define __CPU_ARCH_H -#include "Types.h" +#include "7zTypes.h" EXTERN_C_BEGIN /* MY_CPU_LE means that CPU is LITTLE ENDIAN. -If MY_CPU_LE is not defined, we don't know about that property of platform (it can be LITTLE ENDIAN). +MY_CPU_BE means that CPU is BIG ENDIAN. +If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform. MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. -If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of platform. */ -#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) -#define MY_CPU_AMD64 +#if defined(_M_X64) \ + || defined(_M_AMD64) \ + || defined(__x86_64__) \ + || defined(__AMD64__) \ + || defined(__amd64__) + #define MY_CPU_AMD64 #endif -#if defined(MY_CPU_AMD64) || defined(_M_IA64) -#define MY_CPU_64BIT +#if defined(MY_CPU_AMD64) \ + || defined(_M_IA64) \ + || defined(__AARCH64EL__) \ + || defined(__AARCH64EB__) + #define MY_CPU_64BIT #endif #if defined(_M_IX86) || defined(__i386__) @@ -32,8 +39,13 @@ If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of pla #define MY_CPU_X86_OR_AMD64 #endif -#if defined(MY_CPU_X86) || defined(_M_ARM) -#define MY_CPU_32BIT +#if defined(MY_CPU_X86) \ + || defined(_M_ARM) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) + #define MY_CPU_32BIT #endif #if defined(_WIN32) && defined(_M_ARM) @@ -44,34 +56,63 @@ If MY_CPU_LE_UNALIGN is not defined, we don't know about these properties of pla #define MY_CPU_IA64_LE #endif -#if defined(MY_CPU_X86_OR_AMD64) -#define MY_CPU_LE_UNALIGN +#if defined(MY_CPU_X86_OR_AMD64) \ + || defined(MY_CPU_ARM_LE) \ + || defined(MY_CPU_IA64_LE) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__AARCH64EL__) \ + || defined(__MIPSEL__) \ + || defined(__MIPSEL) \ + || defined(_MIPSEL) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + #define MY_CPU_LE #endif -#if defined(MY_CPU_X86_OR_AMD64) || defined(MY_CPU_ARM_LE) || defined(MY_CPU_IA64_LE) || defined(__ARMEL__) || defined(__MIPSEL__) || defined(__LITTLE_ENDIAN__) -#define MY_CPU_LE -#endif - -#if defined(__BIG_ENDIAN__) || defined(__m68k__) || defined(__ARMEB__) || defined(__MIPSEB__) -#define MY_CPU_BE +#if defined(__BIG_ENDIAN__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) \ + || defined(__AARCH64EB__) \ + || defined(__MIPSEB__) \ + || defined(__MIPSEB) \ + || defined(_MIPSEB) \ + || defined(__m68k__) \ + || defined(__s390__) \ + || defined(__s390x__) \ + || defined(__zarch__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + #define MY_CPU_BE #endif #if defined(MY_CPU_LE) && defined(MY_CPU_BE) Stop_Compiling_Bad_Endian #endif + +#ifdef MY_CPU_LE + #if defined(MY_CPU_X86_OR_AMD64) \ + /* || defined(__AARCH64EL__) */ + #define MY_CPU_LE_UNALIGN + #endif +#endif + + #ifdef MY_CPU_LE_UNALIGN -#define GetUi16(p) (*(const UInt16 *)(p)) -#define GetUi32(p) (*(const UInt32 *)(p)) -#define GetUi64(p) (*(const UInt64 *)(p)) -#define SetUi16(p, d) *(UInt16 *)(p) = (d); -#define SetUi32(p, d) *(UInt32 *)(p) = (d); -#define SetUi64(p, d) *(UInt64 *)(p) = (d); +#define GetUi16(p) (*(const UInt16 *)(const void *)(p)) +#define GetUi32(p) (*(const UInt32 *)(const void *)(p)) +#define GetUi64(p) (*(const UInt64 *)(const void *)(p)) + +#define SetUi16(p, v) { *(UInt16 *)(p) = (v); } +#define SetUi32(p, v) { *(UInt32 *)(p) = (v); } +#define SetUi64(p, v) { *(UInt64 *)(p) = (v); } #else -#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8)) +#define GetUi16(p) ( (UInt16) ( \ + ((const Byte *)(p))[0] | \ + ((UInt16)((const Byte *)(p))[1] << 8) )) #define GetUi32(p) ( \ ((const Byte *)(p))[0] | \ @@ -81,29 +122,43 @@ Stop_Compiling_Bad_Endian #define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) -#define SetUi16(p, d) { UInt32 _x_ = (d); \ - ((Byte *)(p))[0] = (Byte)_x_; \ - ((Byte *)(p))[1] = (Byte)(_x_ >> 8); } +#define SetUi16(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); } -#define SetUi32(p, d) { UInt32 _x_ = (d); \ - ((Byte *)(p))[0] = (Byte)_x_; \ - ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \ - ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \ - ((Byte *)(p))[3] = (Byte)(_x_ >> 24); } +#define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); \ + _ppp_[2] = (Byte)(_vvv_ >> 16); \ + _ppp_[3] = (Byte)(_vvv_ >> 24); } -#define SetUi64(p, d) { UInt64 _x64_ = (d); \ - SetUi32(p, (UInt32)_x64_); \ - SetUi32(((Byte *)(p)) + 4, (UInt32)(_x64_ >> 32)); } +#define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ + SetUi32(_ppp2_ , (UInt32)_vvv2_); \ + SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); } #endif -#if defined(MY_CPU_LE_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300) + +#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300) + +/* Note: we use bswap instruction, that is unsupported in 386 cpu */ + +#include #pragma intrinsic(_byteswap_ulong) #pragma intrinsic(_byteswap_uint64) #define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) #define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v) + +#elif defined(MY_CPU_LE_UNALIGN) && defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + +#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p)) + +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v) + #else #define GetBe32(p) ( \ @@ -114,9 +169,19 @@ Stop_Compiling_Bad_Endian #define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) +#define SetBe32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)(_vvv_ >> 24); \ + _ppp_[1] = (Byte)(_vvv_ >> 16); \ + _ppp_[2] = (Byte)(_vvv_ >> 8); \ + _ppp_[3] = (Byte)_vvv_; } + #endif -#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1]) + +#define GetBe16(p) ( (UInt16) ( \ + ((UInt16)((const Byte *)(p))[0] << 8) | \ + ((const Byte *)(p))[1] )) + #ifdef MY_CPU_X86_OR_AMD64 @@ -138,12 +203,14 @@ enum CPU_FIRM_VIA }; +void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d); + Bool x86cpuid_CheckAndRead(Cx86cpuid *p); int x86cpuid_GetFirm(const Cx86cpuid *p); -#define x86cpuid_GetFamily(p) (((p)->ver >> 8) & 0xFF00F) -#define x86cpuid_GetModel(p) (((p)->ver >> 4) & 0xF00F) -#define x86cpuid_GetStepping(p) ((p)->ver & 0xF) +#define x86cpuid_GetFamily(ver) (((ver >> 16) & 0xFF0) | ((ver >> 8) & 0xF)) +#define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF)) +#define x86cpuid_GetStepping(ver) (ver & 0xF) Bool CPU_Is_InOrder(); Bool CPU_Is_Aes_Supported(); diff --git a/lzma/C/Delta.c b/lzma/C/Delta.c new file mode 100644 index 0000000000..6cbbe46012 --- /dev/null +++ b/lzma/C/Delta.c @@ -0,0 +1,64 @@ +/* Delta.c -- Delta converter +2009-05-26 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include "Delta.h" + +void Delta_Init(Byte *state) +{ + unsigned i; + for (i = 0; i < DELTA_STATE_SIZE; i++) + state[i] = 0; +} + +static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) +{ + unsigned i; + for (i = 0; i < size; i++) + dest[i] = src[i]; +} + +void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size) +{ + Byte buf[DELTA_STATE_SIZE]; + unsigned j = 0; + MyMemCpy(buf, state, delta); + { + SizeT i; + for (i = 0; i < size;) + { + for (j = 0; j < delta && i < size; i++, j++) + { + Byte b = data[i]; + data[i] = (Byte)(b - buf[j]); + buf[j] = b; + } + } + } + if (j == delta) + j = 0; + MyMemCpy(state, buf + j, delta - j); + MyMemCpy(state + delta - j, buf, j); +} + +void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) +{ + Byte buf[DELTA_STATE_SIZE]; + unsigned j = 0; + MyMemCpy(buf, state, delta); + { + SizeT i; + for (i = 0; i < size;) + { + for (j = 0; j < delta && i < size; i++, j++) + { + buf[j] = data[i] = (Byte)(buf[j] + data[i]); + } + } + } + if (j == delta) + j = 0; + MyMemCpy(state, buf + j, delta - j); + MyMemCpy(state + delta - j, buf, j); +} diff --git a/lzma/C/Delta.h b/lzma/C/Delta.h new file mode 100644 index 0000000000..e59d5a252b --- /dev/null +++ b/lzma/C/Delta.h @@ -0,0 +1,19 @@ +/* Delta.h -- Delta converter +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __DELTA_H +#define __DELTA_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +#define DELTA_STATE_SIZE 256 + +void Delta_Init(Byte *state); +void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size); +void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size); + +EXTERN_C_END + +#endif diff --git a/lzma/C/LzFind.c b/lzma/C/LzFind.c index f6c9e66d57..c335d363ce 100644 --- a/lzma/C/LzFind.c +++ b/lzma/C/LzFind.c @@ -1,5 +1,7 @@ /* LzFind.c -- Match finder for LZ algorithms -2009-04-22 : Igor Pavlov : Public domain */ +2015-10-15 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include @@ -9,8 +11,8 @@ #define kEmptyHashValue 0 #define kMaxValForNormalize ((UInt32)0xFFFFFFFF) #define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ -#define kNormalizeMask (~(kNormalizeStepMin - 1)) -#define kMaxHistorySize ((UInt32)3 << 30) +#define kNormalizeMask (~(UInt32)(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)7 << 29) #define kStartMaxLen 3 @@ -19,7 +21,7 @@ static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) if (!p->directInput) { alloc->Free(alloc, p->bufferBase); - p->bufferBase = 0; + p->bufferBase = NULL; } } @@ -33,17 +35,16 @@ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *a p->blockSize = blockSize; return 1; } - if (p->bufferBase == 0 || p->blockSize != blockSize) + if (!p->bufferBase || p->blockSize != blockSize) { LzInWindow_Free(p, alloc); p->blockSize = blockSize; p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); } - return (p->bufferBase != 0); + return (p->bufferBase != NULL); } Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } -Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } @@ -58,9 +59,12 @@ static void MatchFinder_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; + + /* We use (p->streamPos - p->pos) value. (p->streamPos < p->pos) is allowed. */ + if (p->directInput) { - UInt32 curSize = 0xFFFFFFFF - p->streamPos; + UInt32 curSize = 0xFFFFFFFF - (p->streamPos - p->pos); if (curSize > p->directInputRem) curSize = (UInt32)p->directInputRem; p->directInputRem -= curSize; @@ -69,12 +73,14 @@ static void MatchFinder_ReadBlock(CMatchFinder *p) p->streamEndWasReached = 1; return; } + for (;;) { Byte *dest = p->buffer + (p->streamPos - p->pos); size_t size = (p->bufferBase + p->blockSize - dest); if (size == 0) return; + p->result = p->stream->Read(p->stream, dest, &size); if (p->result != SZ_OK) return; @@ -92,8 +98,8 @@ static void MatchFinder_ReadBlock(CMatchFinder *p) void MatchFinder_MoveBlock(CMatchFinder *p) { memmove(p->bufferBase, - p->buffer - p->keepSizeBefore, - (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); + p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos) + p->keepSizeBefore); p->buffer = p->bufferBase + p->keepSizeBefore; } @@ -133,15 +139,15 @@ static void MatchFinder_SetDefaultSettings(CMatchFinder *p) void MatchFinder_Construct(CMatchFinder *p) { UInt32 i; - p->bufferBase = 0; + p->bufferBase = NULL; p->directInput = 0; - p->hash = 0; + p->hash = NULL; MatchFinder_SetDefaultSettings(p); for (i = 0; i < 256; i++) { UInt32 r = i; - int j; + unsigned j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); p->crc[i] = r; @@ -151,7 +157,7 @@ void MatchFinder_Construct(CMatchFinder *p) static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hash); - p->hash = 0; + p->hash = NULL; } void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) @@ -160,11 +166,11 @@ void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) LzInWindow_Free(p, alloc); } -static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) +static CLzRef* AllocRefs(size_t num, ISzAlloc *alloc) { size_t sizeInBytes = (size_t)num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) - return 0; + return NULL; return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); } @@ -173,19 +179,24 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ISzAlloc *alloc) { UInt32 sizeReserv; + if (historySize > kMaxHistorySize) { MatchFinder_Free(p, alloc); return 0; } + sizeReserv = historySize >> 1; - if (historySize > ((UInt32)2 << 30)) - sizeReserv = historySize >> 2; + if (historySize >= ((UInt32)3 << 30)) sizeReserv = historySize >> 3; + else if (historySize >= ((UInt32)2 << 30)) sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); p->keepSizeBefore = historySize + keepAddBufferBefore + 1; p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ + if (LzInWindow_Create(p, sizeReserv, alloc)) { UInt32 newCyclicBufferSize = historySize + 1; @@ -210,6 +221,7 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, hs = (1 << 24) - 1; else hs >>= 1; + /* if (bigHash) mode, GetHeads4b() in LzFindMt.c needs (hs >= ((1 << 24) - 1))) */ } } p->hashMask = hs; @@ -221,24 +233,32 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, } { - UInt32 prevSize = p->hashSizeSum + p->numSons; - UInt32 newSize; + size_t newSize; + size_t numSons; p->historySize = historySize; p->hashSizeSum = hs; p->cyclicBufferSize = newCyclicBufferSize; - p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); - newSize = p->hashSizeSum + p->numSons; - if (p->hash != 0 && prevSize == newSize) + + numSons = newCyclicBufferSize; + if (p->btMode) + numSons <<= 1; + newSize = hs + numSons; + + if (p->hash && p->numRefs == newSize) return 1; + MatchFinder_FreeThisClassMemory(p, alloc); + p->numRefs = newSize; p->hash = AllocRefs(newSize, alloc); - if (p->hash != 0) + + if (p->hash) { p->son = p->hash + p->hashSizeSum; return 1; } } } + MatchFinder_Free(p, alloc); return 0; } @@ -247,9 +267,11 @@ static void MatchFinder_SetLimits(CMatchFinder *p) { UInt32 limit = kMaxValForNormalize - p->pos; UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) limit = limit2; limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) { if (limit2 > 0) @@ -257,8 +279,10 @@ static void MatchFinder_SetLimits(CMatchFinder *p) } else limit2 -= p->keepSizeAfter; + if (limit2 < limit) limit = limit2; + { UInt32 lenLimit = p->streamPos - p->pos; if (lenLimit > p->matchMaxLen) @@ -268,28 +292,39 @@ static void MatchFinder_SetLimits(CMatchFinder *p) p->posLimit = p->pos + limit; } -void MatchFinder_Init(CMatchFinder *p) +void MatchFinder_Init_2(CMatchFinder *p, int readData) { UInt32 i; - for (i = 0; i < p->hashSizeSum; i++) - p->hash[i] = kEmptyHashValue; + UInt32 *hash = p->hash; + UInt32 num = p->hashSizeSum; + for (i = 0; i < num; i++) + hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; p->buffer = p->bufferBase; p->pos = p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = 0; - MatchFinder_ReadBlock(p); + + if (readData) + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); } +void MatchFinder_Init(CMatchFinder *p) +{ + MatchFinder_Init_2(p, True); +} + static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) { return (p->pos - p->historySize - 1) & kNormalizeMask; } -void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems) { - UInt32 i; + size_t i; for (i = 0; i < numItems; i++) { UInt32 value = items[i]; @@ -304,7 +339,7 @@ void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) static void MatchFinder_Normalize(CMatchFinder *p) { UInt32 subValue = MatchFinder_GetSubValue(p); - MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_Normalize3(subValue, p->hash, p->numRefs); MatchFinder_ReduceOffsets(p, subValue); } @@ -465,7 +500,7 @@ static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } #define GET_MATCHES_HEADER2(minLen, ret_op) \ - UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ + UInt32 lenLimit; UInt32 hv; const Byte *cur; UInt32 curMatch; \ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ cur = p->buffer; @@ -481,13 +516,20 @@ static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } #define SKIP_FOOTER \ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; +#define UPDATE_maxLen { \ + ptrdiff_t diff = (ptrdiff_t)0 - d2; \ + const Byte *c = cur + maxLen; \ + const Byte *lim = cur + lenLimit; \ + for (; c != lim; c++) if (*(c + diff) != *c) break; \ + maxLen = (UInt32)(c - cur); } + static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(2) HASH2_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 1) } @@ -497,35 +539,38 @@ UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 2) } static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - UInt32 hash2Value, delta2, maxLen, offset; + UInt32 h2, d2, maxLen, offset, pos; + UInt32 *hash; GET_MATCHES_HEADER(3) HASH3_CALC; - delta2 = p->pos - p->hash[hash2Value]; - curMatch = p->hash[kFix3HashSize + hashValue]; - - p->hash[hash2Value] = - p->hash[kFix3HashSize + hashValue] = p->pos; + hash = p->hash; + pos = p->pos; + d2 = pos - hash[h2]; + + curMatch = hash[kFix3HashSize + hv]; + + hash[h2] = pos; + hash[kFix3HashSize + hv] = pos; maxLen = 2; offset = 0; - if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) { - for (; maxLen != lenLimit; maxLen++) - if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) - break; + UPDATE_maxLen distances[0] = maxLen; - distances[1] = delta2 - 1; + distances[1] = d2 - 1; offset = 2; if (maxLen == lenLimit) { @@ -533,44 +578,51 @@ static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) MOVE_POS_RET; } } + GET_MATCHES_FOOTER(offset, maxLen) } static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + UInt32 h2, h3, d2, d3, maxLen, offset, pos; + UInt32 *hash; GET_MATCHES_HEADER(4) HASH4_CALC; - delta2 = p->pos - p->hash[ hash2Value]; - delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; - curMatch = p->hash[kFix4HashSize + hashValue]; - - p->hash[ hash2Value] = - p->hash[kFix3HashSize + hash3Value] = - p->hash[kFix4HashSize + hashValue] = p->pos; + hash = p->hash; + pos = p->pos; - maxLen = 1; + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + + curMatch = hash[kFix4HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + hv] = pos; + + maxLen = 0; offset = 0; - if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) { distances[0] = maxLen = 2; - distances[1] = delta2 - 1; + distances[1] = d2 - 1; offset = 2; } - if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + + if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) { maxLen = 3; - distances[offset + 1] = delta3 - 1; + distances[offset + 1] = d3 - 1; offset += 2; - delta2 = delta3; + d2 = d3; } + if (offset != 0) { - for (; maxLen != lenLimit; maxLen++) - if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) - break; + UPDATE_maxLen distances[offset - 2] = maxLen; if (maxLen == lenLimit) { @@ -578,46 +630,131 @@ static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) MOVE_POS_RET; } } + if (maxLen < 3) maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) } +/* +static UInt32 Bt5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos; + UInt32 *hash; + GET_MATCHES_HEADER(5) + + HASH5_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + d4 = pos - hash[kFix4HashSize + h4]; + + curMatch = hash[kFix5HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + h4] = pos; + hash[kFix5HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + if (*(cur - d2 + 2) == cur[2]) + distances[0] = maxLen = 3; + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[2] = maxLen = 3; + distances[3] = d3 - 1; + offset = 4; + d2 = d3; + } + } + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[0] = maxLen = 3; + distances[1] = d3 - 1; + offset = 2; + d2 = d3; + } + + if (d2 != d4 && d4 < p->cyclicBufferSize + && *(cur - d4) == *cur + && *(cur - d4 + 3) == *(cur + 3)) + { + maxLen = 4; + distances[offset + 1] = d4 - 1; + offset += 2; + d2 = d4; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + + if (maxLen < 4) + maxLen = 4; + + GET_MATCHES_FOOTER(offset, maxLen) +} +*/ + static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { - UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; + UInt32 h2, h3, d2, d3, maxLen, offset, pos; + UInt32 *hash; GET_MATCHES_HEADER(4) HASH4_CALC; - delta2 = p->pos - p->hash[ hash2Value]; - delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; - curMatch = p->hash[kFix4HashSize + hashValue]; + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + + curMatch = hash[kFix4HashSize + hv]; - p->hash[ hash2Value] = - p->hash[kFix3HashSize + hash3Value] = - p->hash[kFix4HashSize + hashValue] = p->pos; + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + hv] = pos; - maxLen = 1; + maxLen = 0; offset = 0; - if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) { distances[0] = maxLen = 2; - distances[1] = delta2 - 1; + distances[1] = d2 - 1; offset = 2; } - if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + + if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) { maxLen = 3; - distances[offset + 1] = delta3 - 1; + distances[offset + 1] = d3 - 1; offset += 2; - delta2 = delta3; + d2 = d3; } + if (offset != 0) { - for (; maxLen != lenLimit; maxLen++) - if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) - break; + UPDATE_maxLen distances[offset - 2] = maxLen; if (maxLen == lenLimit) { @@ -625,22 +762,103 @@ static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) MOVE_POS_RET; } } + if (maxLen < 3) maxLen = 3; + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), - distances + offset, maxLen) - (distances)); + distances + offset, maxLen) - (distances)); MOVE_POS_RET } +/* +static UInt32 Hc5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ + UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos + UInt32 *hash; + GET_MATCHES_HEADER(5) + + HASH5_CALC; + + hash = p->hash; + pos = p->pos; + + d2 = pos - hash[ h2]; + d3 = pos - hash[kFix3HashSize + h3]; + d4 = pos - hash[kFix4HashSize + h4]; + + curMatch = hash[kFix5HashSize + hv]; + + hash[ h2] = pos; + hash[kFix3HashSize + h3] = pos; + hash[kFix4HashSize + h4] = pos; + hash[kFix5HashSize + hv] = pos; + + maxLen = 0; + offset = 0; + + if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = d2 - 1; + offset = 2; + if (*(cur - d2 + 2) == cur[2]) + distances[0] = maxLen = 3; + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[2] = maxLen = 3; + distances[3] = d3 - 1; + offset = 4; + d2 = d3; + } + } + else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur) + { + distances[0] = maxLen = 3; + distances[1] = d3 - 1; + offset = 2; + d2 = d3; + } + + if (d2 != d4 && d4 < p->cyclicBufferSize + && *(cur - d4) == *cur + && *(cur - d4 + 3) == *(cur + 3)) + { + maxLen = 4; + distances[offset + 1] = d4 - 1; + offset += 2; + d2 = d4; + } + + if (offset != 0) + { + UPDATE_maxLen + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + + if (maxLen < 4) + maxLen = 4; + + offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), + distances + offset, maxLen) - (distances)); + MOVE_POS_RET +} +*/ + UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) { UInt32 offset; GET_MATCHES_HEADER(3) HASH_ZIP_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), - distances, 2) - (distances)); + distances, 2) - (distances)); MOVE_POS_RET } @@ -650,8 +868,8 @@ static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { SKIP_HEADER(2) HASH2_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -663,8 +881,8 @@ void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { SKIP_HEADER(3) HASH_ZIP_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -674,12 +892,14 @@ static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { - UInt32 hash2Value; + UInt32 h2; + UInt32 *hash; SKIP_HEADER(3) HASH3_CALC; - curMatch = p->hash[kFix3HashSize + hashValue]; - p->hash[hash2Value] = - p->hash[kFix3HashSize + hashValue] = p->pos; + hash = p->hash; + curMatch = hash[kFix3HashSize + hv]; + hash[h2] = + hash[kFix3HashSize + hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -689,43 +909,90 @@ static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { - UInt32 hash2Value, hash3Value; + UInt32 h2, h3; + UInt32 *hash; SKIP_HEADER(4) HASH4_CALC; - curMatch = p->hash[kFix4HashSize + hashValue]; - p->hash[ hash2Value] = - p->hash[kFix3HashSize + hash3Value] = p->pos; - p->hash[kFix4HashSize + hashValue] = p->pos; + hash = p->hash; + curMatch = hash[kFix4HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + hv] = p->pos; SKIP_FOOTER } while (--num != 0); } +/* +static void Bt5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3, h4; + UInt32 *hash; + SKIP_HEADER(5) + HASH5_CALC; + hash = p->hash; + curMatch = hash[kFix5HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + h4] = + hash[kFix5HashSize + hv] = p->pos; + SKIP_FOOTER + } + while (--num != 0); +} +*/ + static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { - UInt32 hash2Value, hash3Value; + UInt32 h2, h3; + UInt32 *hash; SKIP_HEADER(4) HASH4_CALC; - curMatch = p->hash[kFix4HashSize + hashValue]; - p->hash[ hash2Value] = - p->hash[kFix3HashSize + hash3Value] = - p->hash[kFix4HashSize + hashValue] = p->pos; + hash = p->hash; + curMatch = hash[kFix4HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + hv] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } +/* +static void Hc5_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ + do + { + UInt32 h2, h3, h4; + UInt32 *hash; + SKIP_HEADER(5) + HASH5_CALC; + hash = p->hash; + curMatch = p->hash[kFix5HashSize + hv]; + hash[ h2] = + hash[kFix3HashSize + h3] = + hash[kFix4HashSize + h4] = + hash[kFix5HashSize + hv] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } + while (--num != 0); +} +*/ + void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) { do { SKIP_HEADER(3) HASH_ZIP_CALC; - curMatch = p->hash[hashValue]; - p->hash[hashValue] = p->pos; + curMatch = p->hash[hv]; + p->hash[hv] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } @@ -735,13 +1002,22 @@ void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinder_Init; - vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; if (!p->btMode) { - vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; - vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + /* if (p->numHashBytes <= 4) */ + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + /* + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc5_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc5_MatchFinder_Skip; + } + */ } else if (p->numHashBytes == 2) { @@ -753,9 +1029,16 @@ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } - else + else /* if (p->numHashBytes == 4) */ { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } + /* + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt5_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt5_MatchFinder_Skip; + } + */ } diff --git a/lzma/C/LzFind.h b/lzma/C/LzFind.h index 7ebdfa4464..2ff6673771 100644 --- a/lzma/C/LzFind.h +++ b/lzma/C/LzFind.h @@ -1,14 +1,12 @@ /* LzFind.h -- Match finder for LZ algorithms -2009-04-22 : Igor Pavlov : Public domain */ +2015-10-15 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H -#include "Types.h" +#include "7zTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN typedef UInt32 CLzRef; @@ -23,6 +21,11 @@ typedef struct _CMatchFinder UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + Byte streamEndWasReached; + Byte btMode; + Byte bigHash; + Byte directInput; + UInt32 matchMaxLen; CLzRef *hash; CLzRef *son; @@ -31,30 +34,30 @@ typedef struct _CMatchFinder Byte *bufferBase; ISeqInStream *stream; - int streamEndWasReached; - + UInt32 blockSize; UInt32 keepSizeBefore; UInt32 keepSizeAfter; UInt32 numHashBytes; - int directInput; size_t directInputRem; - int btMode; - int bigHash; UInt32 historySize; UInt32 fixedHashSize; UInt32 hashSizeSum; - UInt32 numSons; SRes result; UInt32 crc[256]; + size_t numRefs; } CMatchFinder; #define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) -#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) #define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) +#define Inline_MatchFinder_IsFinishedOK(p) \ + ((p)->streamEndWasReached \ + && (p)->streamPos == (p)->pos \ + && (!(p)->directInput || (p)->directInputRem == 0)) + int MatchFinder_NeedMove(CMatchFinder *p); Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); void MatchFinder_MoveBlock(CMatchFinder *p); @@ -70,7 +73,7 @@ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); -void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems); void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, @@ -84,7 +87,6 @@ Conditions: */ typedef void (*Mf_Init_Func)(void *object); -typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); @@ -93,7 +95,6 @@ typedef void (*Mf_Skip_Func)(void *object, UInt32); typedef struct _IMatchFinder { Mf_Init_Func Init; - Mf_GetIndexByte_Func GetIndexByte; Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; Mf_GetMatches_Func GetMatches; @@ -102,14 +103,15 @@ typedef struct _IMatchFinder void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); +void MatchFinder_Init_2(CMatchFinder *p, int readData); void MatchFinder_Init(CMatchFinder *p); + UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); + void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/LzFindMt.c b/lzma/C/LzFindMt.c index db95590c34..cb61e0953a 100644 --- a/lzma/C/LzFindMt.c +++ b/lzma/C/LzFindMt.c @@ -1,11 +1,13 @@ /* LzFindMt.c -- multithreaded Match finder for LZ algorithms -2009-09-20 : Igor Pavlov : Public domain */ +2015-10-15 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "LzHash.h" #include "LzFindMt.h" -void MtSync_Construct(CMtSync *p) +static void MtSync_Construct(CMtSync *p) { p->wasCreated = False; p->csWasInitialized = False; @@ -18,7 +20,7 @@ void MtSync_Construct(CMtSync *p) Semaphore_Construct(&p->filledSemaphore); } -void MtSync_GetNextBlock(CMtSync *p) +static void MtSync_GetNextBlock(CMtSync *p) { if (p->needStart) { @@ -46,7 +48,7 @@ void MtSync_GetNextBlock(CMtSync *p) /* MtSync_StopWriting must be called if Writing was started */ -void MtSync_StopWriting(CMtSync *p) +static void MtSync_StopWriting(CMtSync *p) { UInt32 myNumBlocks = p->numProcessedBlocks; if (!Thread_WasCreated(&p->thread) || p->needStart) @@ -69,7 +71,7 @@ void MtSync_StopWriting(CMtSync *p) p->needStart = True; } -void MtSync_Destruct(CMtSync *p) +static void MtSync_Destruct(CMtSync *p) { if (Thread_WasCreated(&p->thread)) { @@ -97,7 +99,7 @@ void MtSync_Destruct(CMtSync *p) #define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; } -static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks) +static SRes MtSync_Create2(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { if (p->wasCreated) return SZ_OK; @@ -119,7 +121,7 @@ static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void return SZ_OK; } -static SRes MtSync_Create(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks) +static SRes MtSync_Create(CMtSync *p, THREAD_FUNC_TYPE startAddress, void *obj, UInt32 numBlocks) { SRes res = MtSync_Create2(p, startAddress, obj, numBlocks); if (res != SZ_OK) @@ -132,20 +134,20 @@ void MtSync_Init(CMtSync *p) { p->needStart = True; } #define kMtMaxValForNormalize 0xFFFFFFFF #define DEF_GetHeads2(name, v, action) \ -static void GetHeads ## name(const Byte *p, UInt32 pos, \ -UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \ -{ action; for (; numHeads != 0; numHeads--) { \ -const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++; } } + static void GetHeads ## name(const Byte *p, UInt32 pos, \ + UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \ + { action; for (; numHeads != 0; numHeads--) { \ + const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++; } } #define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;) -DEF_GetHeads2(2, (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; ) +DEF_GetHeads2(2, (p[0] | ((UInt32)p[1] << 8)), UNUSED_VAR(hashMask); UNUSED_VAR(crc); ) DEF_GetHeads(3, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask) DEF_GetHeads(4, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask) DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask) /* DEF_GetHeads(5, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask) */ -void HashThreadFunc(CMatchFinderMt *mt) +static void HashThreadFunc(CMatchFinderMt *mt) { CMtSync *p = &mt->hashSync; for (;;) @@ -171,12 +173,12 @@ void HashThreadFunc(CMatchFinderMt *mt) CriticalSection_Enter(&mt->btSync.cs); CriticalSection_Enter(&mt->hashSync.cs); { - const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf); - const Byte *afterPtr; + const Byte *beforePtr = Inline_MatchFinder_GetPointerToCurrentPos(mf); + ptrdiff_t offset; MatchFinder_MoveBlock(mf); - afterPtr = MatchFinder_GetPointerToCurrentPos(mf); - mt->pointerToCurPos -= beforePtr - afterPtr; - mt->buffer -= beforePtr - afterPtr; + offset = beforePtr - Inline_MatchFinder_GetPointerToCurrentPos(mf); + mt->pointerToCurPos -= offset; + mt->buffer -= offset; } CriticalSection_Leave(&mt->btSync.cs); CriticalSection_Leave(&mt->hashSync.cs); @@ -190,7 +192,7 @@ void HashThreadFunc(CMatchFinderMt *mt) { UInt32 subValue = (mf->pos - mf->historySize - 1); MatchFinder_ReduceOffsets(mf, subValue); - MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1); + MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, (size_t)mf->hashMask + 1); } { UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize; @@ -215,7 +217,7 @@ void HashThreadFunc(CMatchFinderMt *mt) } } -void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) +static void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) { MtSync_GetNextBlock(&p->hashSync); p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize; @@ -231,7 +233,7 @@ void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) #define NO_INLINE MY_FAST_CALL -Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, +static Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes) { @@ -308,12 +310,14 @@ Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CL #endif -void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) +static void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) { UInt32 numProcessed = 0; UInt32 curPos = 2; UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2); + distances[1] = p->hashNumAvail; + while (curPos < limit) { if (p->hashBufPos == p->hashBufPosLimit) @@ -322,9 +326,11 @@ void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) distances[1] = numProcessed + p->hashNumAvail; if (p->hashNumAvail >= p->numHashBytes) continue; + distances[0] = curPos + p->hashNumAvail; + distances += curPos; for (; p->hashNumAvail != 0; p->hashNumAvail--) - distances[curPos++] = 0; - break; + *distances++ = 0; + return; } { UInt32 size = p->hashBufPosLimit - p->hashBufPos; @@ -341,13 +347,14 @@ void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) if (size2 < size) size = size2; } + #ifndef MFMT_GM_INLINE while (curPos < limit && size-- != 0) { UInt32 *startDistances = distances + curPos; UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++], - pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, - startDistances + 1, p->numHashBytes - 1) - startDistances); + pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, + startDistances + 1, p->numHashBytes - 1) - startDistances); *startDistances = num - 1; curPos += num; cyclicBufferPos++; @@ -358,7 +365,7 @@ void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) { UInt32 posRes; curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, - distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes); + distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos), size, &posRes); p->hashBufPos += posRes - pos; cyclicBufferPos += posRes - pos; p->buffer += posRes - pos; @@ -374,10 +381,11 @@ void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) p->cyclicBufferPos = cyclicBufferPos; } } + distances[0] = curPos; } -void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) +static void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) { CMtSync *sync = &p->hashSync; if (!sync->needStart) @@ -391,7 +399,7 @@ void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) { UInt32 subValue = p->pos - p->cyclicBufferSize; - MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2); + MatchFinder_Normalize3(subValue, p->son, (size_t)p->cyclicBufferSize * 2); p->pos -= subValue; } @@ -430,15 +438,15 @@ void BtThreadFunc(CMatchFinderMt *mt) void MatchFinderMt_Construct(CMatchFinderMt *p) { - p->hashBuf = 0; + p->hashBuf = NULL; MtSync_Construct(&p->hashSync); MtSync_Construct(&p->btSync); } -void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) +static void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) { alloc->Free(alloc, p->hashBuf); - p->hashBuf = 0; + p->hashBuf = NULL; } void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) @@ -451,14 +459,15 @@ void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) #define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks) #define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks) -static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } -static unsigned MY_STD_CALL BtThreadFunc2(void *p) +static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } +static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE BtThreadFunc2(void *p) { Byte allocaDummy[0x180]; - int i = 0; + unsigned i = 0; for (i = 0; i < 16; i++) - allocaDummy[i] = (Byte)i; - BtThreadFunc((CMatchFinderMt *)p); + allocaDummy[i] = (Byte)0; + if (allocaDummy[0] == 0) + BtThreadFunc((CMatchFinderMt *)p); return 0; } @@ -469,10 +478,10 @@ SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddB p->historySize = historySize; if (kMtBtBlockSize <= matchMaxLen * 4) return SZ_ERROR_PARAM; - if (p->hashBuf == 0) + if (!p->hashBuf) { p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); - if (p->hashBuf == 0) + if (!p->hashBuf) return SZ_ERROR_MEM; p->btBuf = p->hashBuf + kHashBufferSize; } @@ -492,8 +501,11 @@ void MatchFinderMt_Init(CMatchFinderMt *p) CMatchFinder *mf = p->MatchFinder; p->btBufPos = p->btBufPosLimit = 0; p->hashBufPos = p->hashBufPosLimit = 0; - MatchFinder_Init(mf); - p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf); + + /* Init without data reading. We don't want to read data in this thread */ + MatchFinder_Init_2(mf, False); + + p->pointerToCurPos = Inline_MatchFinder_GetPointerToCurrentPos(mf); p->btNumAvailBytes = 0; p->lzPos = p->historySize + 1; @@ -518,13 +530,13 @@ void MatchFinderMt_ReleaseStream(CMatchFinderMt *p) /* p->MatchFinder->ReleaseStream(); */ } -void MatchFinderMt_Normalize(CMatchFinderMt *p) +static void MatchFinderMt_Normalize(CMatchFinderMt *p) { MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize); p->lzPos = p->historySize + 1; } -void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) +static void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) { UInt32 blockIndex; MtSync_GetNextBlock(&p->btSync); @@ -536,34 +548,29 @@ void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) MatchFinderMt_Normalize(p); } -const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) +static const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) { return p->pointerToCurPos; } #define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p); -UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) +static UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) { GET_NEXT_BLOCK_IF_REQUIRED; return p->btNumAvailBytes; } -Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index) +static UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { - return p->pointerToCurPos[index]; -} - -UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) -{ - UInt32 hash2Value, curMatch2; + UInt32 h2, curMatch2; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH2_CALC - curMatch2 = hash[hash2Value]; - hash[hash2Value] = lzPos; + curMatch2 = hash[h2]; + hash[h2] = lzPos; if (curMatch2 >= matchMinPos) if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) @@ -571,23 +578,23 @@ UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) *distances++ = 2; *distances++ = lzPos - curMatch2 - 1; } + return distances; } -UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +static UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { - UInt32 hash2Value, hash3Value, curMatch2, curMatch3; + UInt32 h2, h3, curMatch2, curMatch3; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH3_CALC - curMatch2 = hash[ hash2Value]; - curMatch3 = hash[kFix3HashSize + hash3Value]; + curMatch2 = hash[ h2]; + curMatch3 = hash[kFix3HashSize + h3]; - hash[ hash2Value] = - hash[kFix3HashSize + hash3Value] = - lzPos; + hash[ h2] = lzPos; + hash[kFix3HashSize + h3] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { @@ -600,43 +607,45 @@ UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) distances[0] = 2; distances += 2; } + if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { *distances++ = 3; *distances++ = lzPos - curMatch3 - 1; } + return distances; } /* -UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +static UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) { - UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4; + UInt32 h2, h3, h4, curMatch2, curMatch3, curMatch4; UInt32 *hash = p->hash; const Byte *cur = p->pointerToCurPos; UInt32 lzPos = p->lzPos; MT_HASH4_CALC - curMatch2 = hash[ hash2Value]; - curMatch3 = hash[kFix3HashSize + hash3Value]; - curMatch4 = hash[kFix4HashSize + hash4Value]; + curMatch2 = hash[ h2]; + curMatch3 = hash[kFix3HashSize + h3]; + curMatch4 = hash[kFix4HashSize + h4]; - hash[ hash2Value] = - hash[kFix3HashSize + hash3Value] = - hash[kFix4HashSize + hash4Value] = - lzPos; + hash[ h2] = lzPos; + hash[kFix3HashSize + h3] = lzPos; + hash[kFix4HashSize + h4] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch2 - 1; if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) { - distances[0] = (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; + distances[0] = (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; return distances + 2; } distances[0] = 2; distances += 2; } + if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) { distances[1] = lzPos - curMatch3 - 1; @@ -658,13 +667,14 @@ UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) *distances++ = 4; *distances++ = lzPos - curMatch4 - 1; } + return distances; } */ #define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++; -UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) +static UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; @@ -682,7 +692,7 @@ UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) return len; } -UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) +static UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) { const UInt32 *btBuf = p->btBuf + p->btBufPos; UInt32 len = *btBuf++; @@ -690,6 +700,7 @@ UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) if (len == 0) { + /* change for bt5 ! */ if (p->btNumAvailBytes-- >= 4) len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances)); } @@ -705,7 +716,7 @@ UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) *distances2++ = *btBuf++; } while ((len -= 2) != 0); - len = (UInt32)(distances2 - (distances)); + len = (UInt32)(distances2 - (distances)); } INCREASE_LZ_POS return len; @@ -715,41 +726,41 @@ UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) #define SKIP_HEADER_MT(n) SKIP_HEADER2_MT if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash; #define SKIP_FOOTER_MT } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0); -void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) +static void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER2_MT { p->btNumAvailBytes--; SKIP_FOOTER_MT } -void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) +static void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(2) - UInt32 hash2Value; + UInt32 h2; MT_HASH2_CALC - hash[hash2Value] = p->lzPos; + hash[h2] = p->lzPos; SKIP_FOOTER_MT } -void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) +static void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(3) - UInt32 hash2Value, hash3Value; + UInt32 h2, h3; MT_HASH3_CALC - hash[kFix3HashSize + hash3Value] = - hash[ hash2Value] = + hash[kFix3HashSize + h3] = + hash[ h2] = p->lzPos; SKIP_FOOTER_MT } /* -void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) +static void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) { SKIP_HEADER_MT(4) - UInt32 hash2Value, hash3Value, hash4Value; + UInt32 h2, h3, h4; MT_HASH4_CALC - hash[kFix4HashSize + hash4Value] = - hash[kFix3HashSize + hash3Value] = - hash[ hash2Value] = + hash[kFix4HashSize + h4] = + hash[kFix3HashSize + h3] = + hash[ h2] = p->lzPos; SKIP_FOOTER_MT } @@ -758,11 +769,11 @@ void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) { vTable->Init = (Mf_Init_Func)MatchFinderMt_Init; - vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte; vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes; vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches; - switch(p->MatchFinder->numHashBytes) + + switch (p->MatchFinder->numHashBytes) { case 2: p->GetHeadsFunc = GetHeads2; @@ -778,7 +789,6 @@ void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) default: /* case 4: */ p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4; - /* p->GetHeadsFunc = GetHeads4; */ p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3; vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip; break; diff --git a/lzma/C/LzFindMt.h b/lzma/C/LzFindMt.h index 17ed237d64..46b6924ad7 100644 --- a/lzma/C/LzFindMt.h +++ b/lzma/C/LzFindMt.h @@ -1,5 +1,5 @@ /* LzFindMt.h -- multithreaded Match finder for LZ algorithms -2009-02-07 : Igor Pavlov : Public domain */ +2015-05-03 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_MT_H #define __LZ_FIND_MT_H @@ -7,9 +7,7 @@ #include "LzFind.h" #include "Threads.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN #define kMtHashBlockSize (1 << 13) #define kMtHashNumBlocks (1 << 3) @@ -77,7 +75,7 @@ typedef struct _CMatchFinderMt UInt32 matchMaxLen; UInt32 numHashBytes; UInt32 pos; - Byte *buffer; + const Byte *buffer; UInt32 cyclicBufferPos; UInt32 cyclicBufferSize; /* it must be historySize + 1 */ UInt32 cutValue; @@ -98,8 +96,6 @@ SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddB void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/LzHash.h b/lzma/C/LzHash.h index b2f0e3c245..2191444072 100644 --- a/lzma/C/LzHash.h +++ b/lzma/C/LzHash.h @@ -1,5 +1,5 @@ /* LzHash.h -- HASH functions for LZ algorithms -2009-02-07 : Igor Pavlov : Public domain */ +2015-04-12 : Igor Pavlov : Public domain */ #ifndef __LZ_HASH_H #define __LZ_HASH_H @@ -12,43 +12,46 @@ #define kFix4HashSize (kHash2Size + kHash3Size) #define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) -#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); +#define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8); #define HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - hash2Value = temp & (kHash2Size - 1); \ - hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + h2 = temp & (kHash2Size - 1); \ + hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } #define HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - hash2Value = temp & (kHash2Size - 1); \ - hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ - hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; } #define HASH5_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - hash2Value = temp & (kHash2Size - 1); \ - hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ - hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ - hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ - hash4Value &= (kHash4Size - 1); } + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + temp ^= (p->crc[cur[3]] << 5); \ + h4 = temp & (kHash4Size - 1); \ + hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; } -/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ -#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; +/* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; #define MT_HASH2_CALC \ - hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); #define MT_HASH3_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - hash2Value = temp & (kHash2Size - 1); \ - hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + h2 = temp & (kHash2Size - 1); \ + h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } #define MT_HASH4_CALC { \ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ - hash2Value = temp & (kHash2Size - 1); \ - hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ - hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + h2 = temp & (kHash2Size - 1); \ + temp ^= ((UInt32)cur[2] << 8); \ + h3 = temp & (kHash3Size - 1); \ + h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } #endif diff --git a/lzma/C/Lzma2Dec.c b/lzma/C/Lzma2Dec.c index 7c4eb4449e..b6884571c2 100644 --- a/lzma/C/Lzma2Dec.c +++ b/lzma/C/Lzma2Dec.c @@ -1,8 +1,10 @@ /* Lzma2Dec.c -- LZMA2 Decoder -2010-12-15 : Igor Pavlov : Public domain */ +2015-11-09 : Igor Pavlov : Public domain */ /* #define SHOW_DEBUG_INFO */ +#include "Precomp.h" + #ifdef SHOW_DEBUG_INFO #include #endif @@ -97,12 +99,12 @@ void Lzma2Dec_Init(CLzma2Dec *p) static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) { - switch(p->state) + switch (p->state) { case LZMA2_STATE_CONTROL: p->control = b; - PRF(printf("\n %4X ", p->decoder.dicPos)); - PRF(printf(" %2X", b)); + PRF(printf("\n %4X ", (unsigned)p->decoder.dicPos)); + PRF(printf(" %2X", (unsigned)b)); if (p->control == 0) return LZMA2_STATE_FINISHED; if (LZMA2_IS_UNCOMPRESSED_STATE(p)) @@ -122,7 +124,7 @@ static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) case LZMA2_STATE_UNPACK1: p->unpackSize |= (UInt32)b; p->unpackSize++; - PRF(printf(" %8d", p->unpackSize)); + PRF(printf(" %8u", (unsigned)p->unpackSize)); return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; case LZMA2_STATE_PACK0: @@ -132,13 +134,13 @@ static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) case LZMA2_STATE_PACK1: p->packSize |= (UInt32)b; p->packSize++; - PRF(printf(" %8d", p->packSize)); + PRF(printf(" %8u", (unsigned)p->packSize)); return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); case LZMA2_STATE_PROP: { - int lc, lp; + unsigned lc, lp; if (b >= (9 * 5 * 5)) return LZMA2_STATE_ERROR; lc = b % 9; @@ -177,13 +179,16 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, while (p->state != LZMA2_STATE_FINISHED) { SizeT dicPos = p->decoder.dicPos; + if (p->state == LZMA2_STATE_ERROR) return SZ_ERROR_DATA; + if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return SZ_OK; } + if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) { if (*srcLen == inSize) @@ -193,8 +198,15 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, } (*srcLen)++; p->state = Lzma2Dec_UpdateState(p, *src++); + + if (dicPos == dicLimit && p->state != LZMA2_STATE_FINISHED) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } continue; } + { SizeT destSizeCur = dicLimit - dicPos; SizeT srcSizeCur = inSize - *srcLen; @@ -220,7 +232,10 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, if (initDic) p->needInitProp = p->needInitState = True; else if (p->needInitDic) + { + p->state = LZMA2_STATE_ERROR; return SZ_ERROR_DATA; + } p->needInitDic = False; LzmaDec_InitDicAndState(&p->decoder, initDic, False); } @@ -229,7 +244,10 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, srcSizeCur = destSizeCur; if (srcSizeCur == 0) + { + p->state = LZMA2_STATE_ERROR; return SZ_ERROR_DATA; + } LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); @@ -245,17 +263,21 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, if (p->state == LZMA2_STATE_DATA) { - int mode = LZMA2_GET_LZMA_MODE(p); + unsigned mode = LZMA2_GET_LZMA_MODE(p); Bool initDic = (mode == 3); - Bool initState = (mode > 0); + Bool initState = (mode != 0); if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) + { + p->state = LZMA2_STATE_ERROR; return SZ_ERROR_DATA; + } LzmaDec_InitDicAndState(&p->decoder, initDic, initState); p->needInitDic = False; p->needInitState = False; p->state = LZMA2_STATE_DATA_CONT; } + if (srcSizeCur > p->packSize) srcSizeCur = (SizeT)p->packSize; @@ -274,16 +296,22 @@ SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, if (srcSizeCur == 0 && outSizeProcessed == 0) { - if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK || - p->unpackSize != 0 || p->packSize != 0) + if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + || p->unpackSize != 0 + || p->packSize != 0) + { + p->state = LZMA2_STATE_ERROR; return SZ_ERROR_DATA; + } p->state = LZMA2_STATE_CONTROL; } + if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) *status = LZMA_STATUS_NOT_FINISHED; } } } + *status = LZMA_STATUS_FINISHED_WITH_MARK; return SZ_OK; } diff --git a/lzma/C/Lzma2Dec.h b/lzma/C/Lzma2Dec.h index 827698deeb..026cdefe99 100644 --- a/lzma/C/Lzma2Dec.h +++ b/lzma/C/Lzma2Dec.h @@ -1,14 +1,12 @@ /* Lzma2Dec.h -- LZMA2 Decoder -2009-05-03 : Igor Pavlov : Public domain */ +2015-05-13 : Igor Pavlov : Public domain */ #ifndef __LZMA2_DEC_H #define __LZMA2_DEC_H #include "LzmaDec.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN /* ---------- State Interface ---------- */ @@ -17,7 +15,7 @@ typedef struct CLzmaDec decoder; UInt32 packSize; UInt32 unpackSize; - int state; + unsigned state; Byte control; Bool needInitDic; Bool needInitState; @@ -77,8 +75,6 @@ Returns: SRes Lzma2Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, Byte prop, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/LzmaDec.c b/lzma/C/LzmaDec.c index 72451d1ffd..651d1f2843 100644 --- a/lzma/C/LzmaDec.c +++ b/lzma/C/LzmaDec.c @@ -1,5 +1,7 @@ /* LzmaDec.c -- LZMA Decoder -2010-12-15 : Igor Pavlov : Public domain */ +2015-06-23 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include "LzmaDec.h" @@ -44,6 +46,13 @@ i -= 0x40; } #endif +#define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) +#define MATCHED_LITER_DEC \ + matchByte <<= 1; \ + bit = (matchByte & offs); \ + probLit = prob + offs + bit + symbol; \ + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) @@ -105,14 +114,14 @@ #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 -#define LZMA_LIT_SIZE 768 - -#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) +#define LZMA_LIT_SIZE 0x300 #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif +#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + #define LZMA_DIC_MIN (1 << 12) /* First LZMA-symbol is always decoded. @@ -124,8 +133,8 @@ Out: p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished - = kMatchSpecLenStart + 1 : Flush marker - = kMatchSpecLenStart + 2 : State Init Marker + = kMatchSpecLenStart + 1 : Flush marker (unused now) + = kMatchSpecLenStart + 2 : State Init Marker (unused now) */ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) @@ -163,38 +172,62 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte unsigned symbol; UPDATE_0(prob); prob = probs + Literal; - if (checkDicSize != 0 || processedPos != 0) - prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + - (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + if (processedPos != 0 || checkDicSize != 0) + prob += ((UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + processedPos++; if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; - do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); + #ifdef _LZMA_SIZE_OPT + do { NORMAL_LITER_DEC } while (symbol < 0x100); + #else + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + #endif } else { - unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; + #ifdef _LZMA_SIZE_OPT do { unsigned bit; CLzmaProb *probLit; - matchByte <<= 1; - bit = (matchByte & offs); - probLit = prob + offs + bit + symbol; - GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + MATCHED_LITER_DEC } while (symbol < 0x100); + #else + { + unsigned bit; + CLzmaProb *probLit; + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + } + #endif } + dic[dicPos++] = (Byte)symbol; - processedPos++; continue; } - else + { UPDATE_1(prob); prob = probs + IsRep + state; @@ -217,7 +250,7 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte IF_BIT_0(prob) { UPDATE_0(prob); - dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; @@ -258,6 +291,8 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } + + #ifdef _LZMA_SIZE_OPT { unsigned limit, offset; CLzmaProb *probLen = prob + LenChoice; @@ -290,6 +325,42 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte TREE_DECODE(probLen, limit, len); len += offset; } + #else + { + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + len -= 8; + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + TREE_DECODE(probLen, (1 << kLenNumHighBits), len); + len += kLenNumLowSymbols + kLenNumMidSymbols; + } + } + } + #endif if (state >= kNumStates) { @@ -300,7 +371,7 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; - int numDirectBits = (int)(((distance >> 1) - 1)); + unsigned numDirectBits = (unsigned)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { @@ -359,6 +430,7 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte } } } + rep3 = rep2; rep2 = rep1; rep1 = rep0; @@ -366,26 +438,39 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte if (checkDicSize == 0) { if (distance >= processedPos) + { + p->dicPos = dicPos; return SZ_ERROR_DATA; + } } else if (distance >= checkDicSize) + { + p->dicPos = dicPos; return SZ_ERROR_DATA; + } state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; - if (limit == dicPos) - return SZ_ERROR_DATA; { - SizeT rem = limit - dicPos; - unsigned curLen = ((rem < len) ? (unsigned)rem : len); - SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + SizeT rem; + unsigned curLen; + SizeT pos; + + if ((rem = limit - dicPos) == 0) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + + curLen = ((rem < len) ? (unsigned)rem : len); + pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); processedPos += curLen; len -= curLen; - if (pos + curLen <= dicBufSize) + if (curLen <= dicBufSize - pos) { Byte *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; @@ -409,7 +494,9 @@ static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte } } while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; p->range = range; p->code = code; @@ -433,9 +520,10 @@ static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; unsigned len = p->remainLen; - UInt32 rep0 = p->reps[0]; - if (limit - dicPos < len) - len = (unsigned)(limit - dicPos); + SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ + SizeT rem = limit - dicPos; + if (rem < len) + len = (unsigned)(rem); if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) p->checkDicSize = p->prop.dicSize; @@ -445,7 +533,7 @@ static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) while (len != 0) { len--; - dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; @@ -463,17 +551,19 @@ static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); - if (p->processedPos >= p->prop.dicSize) + + if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); if (p->remainLen > kMatchSpecLenStart) - { p->remainLen = kMatchSpecLenStart; - } + return 0; } @@ -490,12 +580,12 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; - CLzmaProb *probs = p->probs; + const CLzmaProb *probs = p->probs; unsigned state = p->state; ELzmaDummy res; { - CLzmaProb *prob; + const CLzmaProb *prob; UInt32 bound; unsigned ttt; unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); @@ -509,9 +599,9 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) - prob += (LZMA_LIT_SIZE * - ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + - (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + prob += ((UInt32)LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); if (state < kNumLitStates) { @@ -521,13 +611,13 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + - ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + (p->dicPos < p->reps[0] ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; - CLzmaProb *probLit; + const CLzmaProb *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; @@ -597,7 +687,7 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS } { unsigned limit, offset; - CLzmaProb *probLen = prob + LenChoice; + const CLzmaProb *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; @@ -637,7 +727,7 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { - int numDirectBits = ((posSlot >> 1) - 1); + unsigned numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ @@ -676,13 +766,6 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS } -static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) -{ - p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); - p->range = 0xFFFFFFFF; - p->needFlush = 0; -} - void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { p->needFlush = 1; @@ -707,8 +790,8 @@ void LzmaDec_Init(CLzmaDec *p) static void LzmaDec_InitStateReal(CLzmaDec *p) { - UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); - UInt32 i; + SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); + SizeT i; CLzmaProb *probs = p->probs; for (i = 0; i < numProbs; i++) probs[i] = kBitModelTotal >> 1; @@ -730,7 +813,7 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *sr { int checkEndMarkNow; - if (p->needFlush != 0) + if (p->needFlush) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; @@ -741,8 +824,13 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *sr } if (p->tempBuf[0] != 0) return SZ_ERROR_DATA; - - LzmaDec_InitRc(p, p->tempBuf); + p->code = + ((UInt32)p->tempBuf[1] << 24) + | ((UInt32)p->tempBuf[2] << 16) + | ((UInt32)p->tempBuf[3] << 8) + | ((UInt32)p->tempBuf[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; p->tempBufSize = 0; } @@ -826,7 +914,16 @@ SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *sr p->buf = p->tempBuf; if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) return SZ_ERROR_DATA; - lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + + { + unsigned kkk = (unsigned)(p->buf - p->tempBuf); + if (rem < kkk) + return SZ_ERROR_FAIL; /* some internal error */ + rem -= kkk; + if (lookAhead < rem) + return SZ_ERROR_FAIL; /* some internal error */ + lookAhead -= rem; + } (*srcLen) += lookAhead; src += lookAhead; inSize -= lookAhead; @@ -881,13 +978,13 @@ SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *sr void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->probs); - p->probs = 0; + p->probs = NULL; } static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) { alloc->Free(alloc, p->dic); - p->dic = 0; + p->dic = NULL; } void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) @@ -925,12 +1022,12 @@ SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); - if (p->probs == 0 || numProbs != p->numProbs) + if (!p->probs || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); p->numProbs = numProbs; - if (p->probs == 0) + if (!p->probs) return SZ_ERROR_MEM; } return SZ_OK; @@ -951,12 +1048,22 @@ SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAll SizeT dicBufSize; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); - dicBufSize = propNew.dicSize; - if (p->dic == 0 || dicBufSize != p->dicBufSize) + + { + UInt32 dictSize = propNew.dicSize; + SizeT mask = ((UInt32)1 << 12) - 1; + if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; + else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;; + dicBufSize = ((SizeT)dictSize + mask) & ~mask; + if (dicBufSize < dictSize) + dicBufSize = dictSize; + } + + if (!p->dic || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); - if (p->dic == 0) + if (!p->dic) { LzmaDec_FreeProbs(p, alloc); return SZ_ERROR_MEM; diff --git a/lzma/C/LzmaDec.h b/lzma/C/LzmaDec.h index 6741a644b6..2633abeac9 100644 --- a/lzma/C/LzmaDec.h +++ b/lzma/C/LzmaDec.h @@ -1,14 +1,12 @@ /* LzmaDec.h -- LZMA Decoder -2009-02-07 : Igor Pavlov : Public domain */ +2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H -#include "Types.h" +#include "7zTypes.h" -#ifdef __cplusplus -extern "C" { -#endif +EXTERN_C_BEGIN /* #define _LZMA_PROB32 */ /* _LZMA_PROB32 can increase the speed on some CPUs, @@ -224,8 +222,6 @@ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ELzmaStatus *status, ISzAlloc *alloc); -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/C/LzmaEnc.c b/lzma/C/LzmaEnc.c index 870399d35a..11de42c09d 100644 --- a/lzma/C/LzmaEnc.c +++ b/lzma/C/LzmaEnc.c @@ -1,5 +1,7 @@ /* LzmaEnc.c -- LZMA Encoder -2011-01-27 : Igor Pavlov : Public domain */ +2015-11-08 : Igor Pavlov : Public domain */ + +#include "Precomp.h" #include @@ -18,9 +20,12 @@ #endif #ifdef SHOW_STAT -static int ttt = 0; +static unsigned g_STAT_OFFSET = 0; #endif +#define kMaxHistorySize ((UInt32)3 << 29) +/* #define kMaxHistorySize ((UInt32)7 << 29) */ + #define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) #define kBlockSize (9 << 10) @@ -46,7 +51,7 @@ void LzmaEncProps_Init(CLzmaEncProps *p) { p->level = 5; p->dictSize = p->mc = 0; - p->reduceSize = (UInt32)(Int32)-1; + p->reduceSize = (UInt64)(Int64)-1; p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; p->writeEndMark = 0; } @@ -56,24 +61,28 @@ void LzmaEncProps_Normalize(CLzmaEncProps *p) int level = p->level; if (level < 0) level = 5; p->level = level; + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); if (p->dictSize > p->reduceSize) { unsigned i; - for (i = 15; i <= 30; i++) + for (i = 11; i <= 30; i++) { - if (p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } - if (p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } + if ((UInt32)p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } + if ((UInt32)p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } } } + if (p->lc < 0) p->lc = 3; if (p->lp < 0) p->lp = 0; if (p->pb < 0) p->pb = 2; + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); if (p->numHashBytes < 0) p->numHashBytes = 4; - if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) p->numThreads = #ifndef _7ZIP_ST @@ -90,17 +99,18 @@ UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) return props.dictSize; } +#if (_MSC_VER >= 1400) +/* BSR code is fast for some new CPUs */ /* #define LZMA_LOG_BSR */ -/* Define it for Intel's CPU */ - +#endif #ifdef LZMA_LOG_BSR -#define kDicLogSizeMaxCompress 30 +#define kDicLogSizeMaxCompress 32 #define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } -UInt32 GetPosSlot1(UInt32 pos) +static UInt32 GetPosSlot1(UInt32 pos) { UInt32 res; BSR2_RET(pos, res); @@ -111,27 +121,44 @@ UInt32 GetPosSlot1(UInt32 pos) #else -#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kNumLogBits (9 + sizeof(size_t) / 2) +/* #define kNumLogBits (11 + sizeof(size_t) / 8 * 3) */ + #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) -void LzmaEnc_FastPosInit(Byte *g_FastPos) +static void LzmaEnc_FastPosInit(Byte *g_FastPos) { - int c = 2, slotFast; + unsigned slot; g_FastPos[0] = 0; g_FastPos[1] = 1; + g_FastPos += 2; - for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + for (slot = 2; slot < kNumLogBits * 2; slot++) { - UInt32 k = (1 << ((slotFast >> 1) - 1)); - UInt32 j; - for (j = 0; j < k; j++, c++) - g_FastPos[c] = (Byte)slotFast; + size_t k = ((size_t)1 << ((slot >> 1) - 1)); + size_t j; + for (j = 0; j < k; j++) + g_FastPos[j] = (Byte)slot; + g_FastPos += k; } } +/* we can use ((limit - pos) >> 31) only if (pos < ((UInt32)1 << 31)) */ +/* #define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ res = p->g_FastPos[pos >> i] + (i * 2); } +*/ + +/* +#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ + (0 - (((((UInt32)1 << (kNumLogBits)) - 1) - (pos >> 6)) >> 31))); \ + res = p->g_FastPos[pos >> i] + (i * 2); } +*/ + +#define BSR2_RET(pos, res) { UInt32 i = (pos < (1 << (kNumLogBits + 6))) ? 6 : 6 + kNumLogBits - 1; \ + res = p->g_FastPos[pos >> i] + (i * 2); } + /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ p->g_FastPos[pos >> 6] + 12 : \ @@ -211,6 +238,7 @@ typedef struct #define kNumStates 12 + typedef struct { CLzmaProb choice; @@ -220,14 +248,16 @@ typedef struct CLzmaProb high[kLenNumHighSymbols]; } CLenEnc; + typedef struct { CLenEnc p; - UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; UInt32 tableSize; + UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; UInt32 counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; + typedef struct { UInt32 range; @@ -242,10 +272,14 @@ typedef struct SRes res; } CRangeEnc; + typedef struct { CLzmaProb *litProbs; + UInt32 state; + UInt32 reps[LZMA_NUM_REPS]; + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; @@ -259,15 +293,49 @@ typedef struct CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; - - UInt32 reps[LZMA_NUM_REPS]; - UInt32 state; } CSaveState; + typedef struct { - IMatchFinder matchFinder; void *matchFinderObj; + IMatchFinder matchFinder; + + UInt32 optimumEndIndex; + UInt32 optimumCurrentIndex; + + UInt32 longestMatchLength; + UInt32 numPairs; + UInt32 numAvail; + + UInt32 numFastBytes; + UInt32 additionalOffset; + UInt32 reps[LZMA_NUM_REPS]; + UInt32 state; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + unsigned lclp; + + CLzmaProb *litProbs; + + Bool fastMode; + Bool writeEndMark; + Bool finished; + Bool multiThread; + Bool needInit; + + UInt64 nowPos64; + + UInt32 matchPriceCount; + UInt32 alignPriceCount; + + UInt32 distTableSize; + + UInt32 dictSize; + SRes result; + + CRangeEnc rc; #ifndef _7ZIP_ST Bool mtMode; @@ -280,12 +348,6 @@ typedef struct Byte pad[128]; #endif - UInt32 optimumEndIndex; - UInt32 optimumCurrentIndex; - - UInt32 longestMatchLength; - UInt32 numPairs; - UInt32 numAvail; COptimal opt[kNumOpts]; #ifndef LZMA_LOG_BSR @@ -294,22 +356,10 @@ typedef struct UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; - UInt32 numFastBytes; - UInt32 additionalOffset; - UInt32 reps[LZMA_NUM_REPS]; - UInt32 state; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; UInt32 alignPrices[kAlignTableSize]; - UInt32 alignPriceCount; - - UInt32 distTableSize; - - unsigned lc, lp, pb; - unsigned lpMask, pbMask; - - CLzmaProb *litProbs; CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep[kNumStates]; @@ -325,26 +375,14 @@ typedef struct CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; - unsigned lclp; - - Bool fastMode; - - CRangeEnc rc; - - Bool writeEndMark; - UInt64 nowPos64; - UInt32 matchPriceCount; - Bool finished; - Bool multiThread; - - SRes result; - UInt32 dictSize; - - int needInit; - CSaveState saveState; + + #ifndef _7ZIP_ST + Byte pad2[128]; + #endif } CLzmaEnc; + void LzmaEnc_SaveState(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; @@ -368,7 +406,7 @@ void LzmaEnc_SaveState(CLzmaEncHandle pp) memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); - memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); + memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << p->lclp) * sizeof(CLzmaProb)); } void LzmaEnc_RestoreState(CLzmaEncHandle pp) @@ -394,7 +432,7 @@ void LzmaEnc_RestoreState(CLzmaEncHandle pp) memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); memcpy(dest->reps, p->reps, sizeof(p->reps)); - memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); + memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << dest->lclp) * sizeof(CLzmaProb)); } SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) @@ -403,9 +441,13 @@ SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) CLzmaEncProps props = *props2; LzmaEncProps_Normalize(&props); - if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || - props.dictSize > ((UInt32)1 << kDicLogSizeMaxCompress) || props.dictSize > ((UInt32)1 << 30)) + if (props.lc > LZMA_LC_MAX + || props.lp > LZMA_LP_MAX + || props.pb > LZMA_PB_MAX + || props.dictSize > ((UInt64)1 << kDicLogSizeMaxCompress) + || props.dictSize > kMaxHistorySize) return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; { unsigned fb = props.fb; @@ -419,7 +461,7 @@ SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) p->lp = props.lp; p->pb = props.pb; p->fastMode = (props.algo == 0); - p->matchFinderBase.btMode = props.btMode; + p->matchFinderBase.btMode = (Byte)(props.btMode ? 1 : 0); { UInt32 numHashBytes = 4; if (props.btMode) @@ -463,8 +505,8 @@ static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, static void RangeEnc_Construct(CRangeEnc *p) { - p->outStream = 0; - p->bufBase = 0; + p->outStream = NULL; + p->bufBase = NULL; } #define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) @@ -472,10 +514,10 @@ static void RangeEnc_Construct(CRangeEnc *p) #define RC_BUF_SIZE (1 << 16) static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) { - if (p->bufBase == 0) + if (!p->bufBase) { p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); - if (p->bufBase == 0) + if (!p->bufBase) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; } @@ -516,7 +558,7 @@ static void RangeEnc_FlushStream(CRangeEnc *p) static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) { - if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) + if ((UInt32)p->low < (UInt32)0xFF000000 || (unsigned)(p->low >> 32) != 0) { Byte temp = p->cache; do @@ -542,7 +584,7 @@ static void RangeEnc_FlushData(CRangeEnc *p) RangeEnc_ShiftLow(p); } -static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, unsigned numBits) { do { @@ -605,7 +647,7 @@ static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, while (symbol < 0x10000); } -void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) { UInt32 i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) @@ -641,7 +683,7 @@ void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) #define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] -static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, const UInt32 *ProbPrices) { UInt32 price = 0; symbol |= 0x100; @@ -654,7 +696,7 @@ static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *Pro return price; } -static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) +static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, const UInt32 *ProbPrices) { UInt32 price = 0; UInt32 offs = 0x100; @@ -698,7 +740,7 @@ static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLeve } } -static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) { UInt32 price = 0; symbol |= (1 << numBitLevels); @@ -710,7 +752,7 @@ static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 s return price; } -static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) { UInt32 price = 0; UInt32 m = 1; @@ -761,7 +803,7 @@ static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posSt } } -static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) +static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, const UInt32 *ProbPrices) { UInt32 a0 = GET_PRICE_0a(p->choice); UInt32 a1 = GET_PRICE_1a(p->choice); @@ -784,20 +826,20 @@ static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UIn prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } -static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, const UInt32 *ProbPrices) { LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); p->counters[posState] = p->tableSize; } -static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, const UInt32 *ProbPrices) { UInt32 posState; for (posState = 0; posState < numPosStates; posState++) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } -static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, const UInt32 *ProbPrices) { LenEnc_Encode(&p->p, rc, symbol, posState); if (updatePrice) @@ -811,9 +853,10 @@ static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 static void MovePos(CLzmaEnc *p, UInt32 num) { #ifdef SHOW_STAT - ttt += num; - printf("\n MovePos %d", num); + g_STAT_OFFSET += num; + printf("\n MovePos %u", num); #endif + if (num != 0) { p->additionalOffset += num; @@ -826,28 +869,32 @@ static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) UInt32 lenRes = 0, numPairs; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + #ifdef SHOW_STAT - printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); - ttt++; + printf("\n i = %u numPairs = %u ", g_STAT_OFFSET, numPairs / 2); + g_STAT_OFFSET++; { UInt32 i; for (i = 0; i < numPairs; i += 2) - printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + printf("%2u %6u | ", p->matches[i], p->matches[i + 1]); } #endif + if (numPairs > 0) { lenRes = p->matches[numPairs - 2]; if (lenRes == p->numFastBytes) { - const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; - UInt32 distance = p->matches[numPairs - 1] + 1; UInt32 numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { - const Byte *pby2 = pby - distance; - for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + const Byte *pbyCur = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + const Byte *pby = pbyCur + lenRes; + ptrdiff_t dif = (ptrdiff_t)-1 - p->matches[numPairs - 1]; + const Byte *pbyLim = pbyCur + numAvail; + for (; pby != pbyLim && *pby == pby[dif]; pby++); + lenRes = (UInt32)(pby - pbyCur); } } } @@ -932,7 +979,7 @@ static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) return p->optimumCurrentIndex; } -#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * (UInt32)0x300) static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { @@ -976,7 +1023,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) UInt32 lenTest; const Byte *data2; reps[i] = p->reps[i]; - data2 = data - (reps[i] + 1); + data2 = data - reps[i] - 1; if (data[0] != data2[0] || data[1] != data2[1]) { repLens[i] = 0; @@ -1120,12 +1167,12 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) cur = 0; #ifdef SHOW_STAT2 - if (position >= 0) + /* if (position >= 0) */ { unsigned i; printf("\n pos = %4X", position); for (i = cur; i <= lenEnd; i++) - printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + printf("\nprice[%4X] = %u", position - cur + i, p->opt[i].price); } #endif @@ -1277,7 +1324,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) /* try Literal + rep0 */ UInt32 temp; UInt32 lenTest2; - const Byte *data2 = data - (reps[0] + 1); + const Byte *data2 = data - reps[0] - 1; UInt32 limit = p->numFastBytes + 1; if (limit > numAvailFull) limit = numAvailFull; @@ -1320,7 +1367,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) UInt32 lenTest; UInt32 lenTestTemp; UInt32 price; - const Byte *data2 = data - (reps[repIndex] + 1); + const Byte *data2 = data - reps[repIndex] - 1; if (data[0] != data2[0] || data[1] != data2[1]) continue; for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); @@ -1350,13 +1397,13 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; - UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { + UInt32 nextRepMatchPrice; UInt32 state2 = kRepNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = @@ -1437,16 +1484,16 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) if (/*_maxMode && */lenTest == matches[offs]) { /* Try Match + Literal + Rep0 */ - const Byte *data2 = data - (curBack + 1); + const Byte *data2 = data - curBack - 1; UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; - UInt32 nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { + UInt32 nextRepMatchPrice; UInt32 state2 = kMatchNextStates[state]; UInt32 posStateNext = (position + lenTest) & p->pbMask; UInt32 curAndLenCharPrice = curAndLenPrice + @@ -1520,7 +1567,7 @@ static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len; - const Byte *data2 = data - (p->reps[i] + 1); + const Byte *data2 = data - p->reps[i] - 1; if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++); @@ -1589,7 +1636,7 @@ static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) for (i = 0; i < LZMA_NUM_REPS; i++) { UInt32 len, limit; - const Byte *data2 = data - (p->reps[i] + 1); + const Byte *data2 = data - p->reps[i] - 1; if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; @@ -1685,6 +1732,7 @@ void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); MatchFinder_Construct(&p->matchFinderBase); + #ifndef _7ZIP_ST MatchFinderMt_Construct(&p->matchFinderMt); p->matchFinderMt.MatchFinder = &p->matchFinderBase; @@ -1701,15 +1749,15 @@ void LzmaEnc_Construct(CLzmaEnc *p) #endif LzmaEnc_InitPriceTables(p->ProbPrices); - p->litProbs = 0; - p->saveState.litProbs = 0; + p->litProbs = NULL; + p->saveState.litProbs = NULL; } CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) { void *p; p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); - if (p != 0) + if (p) LzmaEnc_Construct((CLzmaEnc *)p); return p; } @@ -1718,8 +1766,8 @@ void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->litProbs); alloc->Free(alloc, p->saveState.litProbs); - p->litProbs = 0; - p->saveState.litProbs = 0; + p->litProbs = NULL; + p->saveState.litProbs = NULL; } void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) @@ -1727,6 +1775,7 @@ void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); #endif + MatchFinder_Free(&p->matchFinderBase, allocBig); LzmaEnc_FreeLits(p, alloc); RangeEnc_Free(&p->rc, alloc); @@ -1763,7 +1812,7 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize ReadMatchDistances(p, &numPairs); RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); p->state = kLiteralNextStates[p->state]; - curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + curByte = *(p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; nowPos32++; @@ -1780,7 +1829,7 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize len = GetOptimum(p, nowPos32, &pos); #ifdef SHOW_STAT2 - printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); + printf("\n pos = %4X, len = %u pos = %u", nowPos32, len, pos); #endif posState = nowPos32 & p->pbMask; @@ -1889,7 +1938,7 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) break; } - else if (processed >= (1 << 15)) + else if (processed >= (1 << 17)) { p->nowPos64 += nowPos32 - startPos32; return CheckErrors(p); @@ -1905,22 +1954,21 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 beforeSize = kNumOpts; - Bool btMode; if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; - btMode = (p->matchFinderBase.btMode != 0); + #ifndef _7ZIP_ST - p->mtMode = (p->multiThread && !p->fastMode && btMode); + p->mtMode = (p->multiThread && !p->fastMode && (p->matchFinderBase.btMode != 0)); #endif { unsigned lclp = p->lc + p->lp; - if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + if (!p->litProbs || !p->saveState.litProbs || p->lclp != lclp) { LzmaEnc_FreeLits(p, alloc); - p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); - p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); - if (p->litProbs == 0 || p->saveState.litProbs == 0) + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + if (!p->litProbs || !p->saveState.litProbs) { LzmaEnc_FreeLits(p, alloc); return SZ_ERROR_MEM; @@ -1929,7 +1977,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, I } } - p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + p->matchFinderBase.bigHash = (Byte)(p->dictSize > kBigHashDicLimit ? 1 : 0); if (beforeSize + p->dictSize < keepWindowSize) beforeSize = keepWindowSize - p->dictSize; @@ -1949,6 +1997,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, I p->matchFinderObj = &p->matchFinderBase; MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); } + return SZ_OK; } @@ -1977,9 +2026,10 @@ void LzmaEnc_Init(CLzmaEnc *p) } { - UInt32 num = 0x300 << (p->lp + p->lc); + UInt32 num = (UInt32)0x300 << (p->lp + p->lc); + CLzmaProb *probs = p->litProbs; for (i = 0; i < num; i++) - p->litProbs[i] = kProbInitValue; + probs[i] = kProbInitValue; } { @@ -2086,10 +2136,11 @@ void LzmaEnc_Finish(CLzmaEncHandle pp) if (p->mtMode) MatchFinderMt_ReleaseStream(&p->matchFinderMt); #else - (void)pp; + UNUSED_VAR(pp); #endif } + typedef struct { ISeqOutStream funcTable; @@ -2119,12 +2170,14 @@ UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); } + const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) { const CLzmaEnc *p = (CLzmaEnc *)pp; return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; } + SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) { @@ -2159,23 +2212,23 @@ SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, return res; } + static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) { SRes res = SZ_OK; #ifndef _7ZIP_ST Byte allocaDummy[0x300]; - int i = 0; - for (i = 0; i < 16; i++) - allocaDummy[i] = (Byte)i; + allocaDummy[0] = 0; + allocaDummy[1] = allocaDummy[0]; #endif for (;;) { res = LzmaEnc_CodeOneBlock(p, False, 0, 0); - if (res != SZ_OK || p->finished != 0) + if (res != SZ_OK || p->finished) break; - if (progress != 0) + if (progress) { res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); if (res != SZ_OK) @@ -2185,10 +2238,19 @@ static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) } } } + LzmaEnc_Finish(p); + + /* + if (res == S_OK && !Inline_MatchFinder_IsFinishedOK(&p->matchFinderBase)) + res = SZ_ERROR_FAIL; + } + */ + return res; } + SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { @@ -2196,28 +2258,27 @@ SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *i return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); } + SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) { CLzmaEnc *p = (CLzmaEnc *)pp; - int i; + unsigned i; UInt32 dictSize = p->dictSize; if (*size < LZMA_PROPS_SIZE) return SZ_ERROR_PARAM; *size = LZMA_PROPS_SIZE; props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); - for (i = 11; i <= 30; i++) + if (dictSize >= ((UInt32)1 << 22)) { - if (dictSize <= ((UInt32)2 << i)) - { - dictSize = (2 << i); - break; - } - if (dictSize <= ((UInt32)3 << i)) - { - dictSize = (3 << i); - break; - } + UInt32 kDictMask = ((UInt32)1 << 20) - 1; + if (dictSize < (UInt32)0xFFFFFFFF - kDictMask) + dictSize = (dictSize + kDictMask) & ~kDictMask; + } + else for (i = 11; i <= 30; i++) + { + if (dictSize <= ((UInt32)2 << i)) { dictSize = (2 << i); break; } + if (dictSize <= ((UInt32)3 << i)) { dictSize = (3 << i); break; } } for (i = 0; i < 4; i++) @@ -2225,6 +2286,7 @@ SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) return SZ_OK; } + SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { @@ -2233,19 +2295,22 @@ SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte CSeqOutStreamBuf outStream; - LzmaEnc_SetInputBuf(p, src, srcLen); - outStream.funcTable.Write = MyWrite; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = writeEndMark; - p->rc.outStream = &outStream.funcTable; + res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); + if (res == SZ_OK) + { res = LzmaEnc_Encode2(p, progress); + if (res == SZ_OK && p->nowPos64 != srcLen) + res = SZ_ERROR_FAIL; + } *destLen -= outStream.rem; if (outStream.overflow) @@ -2253,13 +2318,14 @@ SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte return res; } + SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); SRes res; - if (p == 0) + if (!p) return SZ_ERROR_MEM; res = LzmaEnc_SetProps(p, props); diff --git a/lzma/C/LzmaEnc.h b/lzma/C/LzmaEnc.h index 7573ba01bf..c2806b45f4 100644 --- a/lzma/C/LzmaEnc.h +++ b/lzma/C/LzmaEnc.h @@ -1,10 +1,10 @@ /* LzmaEnc.h -- LZMA Encoder -2011-01-27 : Igor Pavlov : Public domain */ +2013-01-18 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H -#include "Types.h" +#include "7zTypes.h" EXTERN_C_BEGIN @@ -16,7 +16,7 @@ typedef struct _CLzmaEncProps UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version (1 << 12) <= dictSize <= (1 << 30) for 64-bit version default = (1 << 24) */ - UInt32 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. + UInt64 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. Encoder uses this value to reduce dictionary size */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ diff --git a/lzma/C/Ppmd.h b/lzma/C/Ppmd.h new file mode 100644 index 0000000000..25ce2d28fb --- /dev/null +++ b/lzma/C/Ppmd.h @@ -0,0 +1,85 @@ +/* Ppmd.h -- PPMD codec common code +2013-01-18 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#ifndef __PPMD_H +#define __PPMD_H + +#include "CpuArch.h" + +EXTERN_C_BEGIN + +#ifdef MY_CPU_32BIT + #define PPMD_32BIT +#endif + +#define PPMD_INT_BITS 7 +#define PPMD_PERIOD_BITS 7 +#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS)) + +#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift)) +#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2) +#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob)) +#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob)) + +#define PPMD_N1 4 +#define PPMD_N2 4 +#define PPMD_N3 4 +#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4) +#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4) + +#pragma pack(push, 1) +/* Most compilers works OK here even without #pragma pack(push, 1), but some GCC compilers need it. */ + +/* SEE-contexts for PPM-contexts with masked symbols */ +typedef struct +{ + UInt16 Summ; /* Freq */ + Byte Shift; /* Speed of Freq change; low Shift is for fast change */ + Byte Count; /* Count to next change of Shift */ +} CPpmd_See; + +#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \ + { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); } + +typedef struct +{ + Byte Symbol; + Byte Freq; + UInt16 SuccessorLow; + UInt16 SuccessorHigh; +} CPpmd_State; + +#pragma pack(pop) + +typedef + #ifdef PPMD_32BIT + CPpmd_State * + #else + UInt32 + #endif + CPpmd_State_Ref; + +typedef + #ifdef PPMD_32BIT + void * + #else + UInt32 + #endif + CPpmd_Void_Ref; + +typedef + #ifdef PPMD_32BIT + Byte * + #else + UInt32 + #endif + CPpmd_Byte_Ref; + +#define PPMD_SetAllBitsIn256Bytes(p) \ + { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \ + p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }} + +EXTERN_C_END + +#endif diff --git a/lzma/C/Ppmd7.c b/lzma/C/Ppmd7.c new file mode 100644 index 0000000000..735651f5d8 --- /dev/null +++ b/lzma/C/Ppmd7.c @@ -0,0 +1,710 @@ +/* Ppmd7.c -- PPMdH codec +2015-09-28 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#include "Precomp.h" + +#include + +#include "Ppmd7.h" + +const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + +#define MAX_FREQ 124 +#define UNIT_SIZE 12 + +#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) +#define U2I(nu) (p->Units2Indx[(nu) - 1]) +#define I2U(indx) (p->Indx2Units[indx]) + +#ifdef PPMD_32BIT + #define REF(ptr) (ptr) +#else + #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) +#endif + +#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) + +#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref)) +#define STATS(ctx) Ppmd7_GetStats(p, ctx) +#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx) +#define SUFFIX(ctx) CTX((ctx)->Suffix) + +typedef CPpmd7_Context * CTX_PTR; + +struct CPpmd7_Node_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd7_Node_ * + #else + UInt32 + #endif + CPpmd7_Node_Ref; + +typedef struct CPpmd7_Node_ +{ + UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */ + UInt16 NU; + CPpmd7_Node_Ref Next; /* must be at offset >= 4 */ + CPpmd7_Node_Ref Prev; +} CPpmd7_Node; + +#ifdef PPMD_32BIT + #define NODE(ptr) (ptr) +#else + #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs))) +#endif + +void Ppmd7_Construct(CPpmd7 *p) +{ + unsigned i, k, m; + + p->Base = 0; + + for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) + { + unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); + do { p->Units2Indx[k++] = (Byte)i; } while (--step); + p->Indx2Units[i] = (Byte)k; + } + + p->NS2BSIndx[0] = (0 << 1); + p->NS2BSIndx[1] = (1 << 1); + memset(p->NS2BSIndx + 2, (2 << 1), 9); + memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); + + for (i = 0; i < 3; i++) + p->NS2Indx[i] = (Byte)i; + for (m = i, k = 1; i < 256; i++) + { + p->NS2Indx[i] = (Byte)m; + if (--k == 0) + k = (++m) - 2; + } + + memset(p->HB2Flag, 0, 0x40); + memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40); +} + +void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->Base); + p->Size = 0; + p->Base = 0; +} + +Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc) +{ + if (p->Base == 0 || p->Size != size) + { + Ppmd7_Free(p, alloc); + p->AlignOffset = + #ifdef PPMD_32BIT + (4 - size) & 3; + #else + 4 - (size & 3); + #endif + if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size + #ifndef PPMD_32BIT + + UNIT_SIZE + #endif + )) == 0) + return False; + p->Size = size; + } + return True; +} + +static void InsertNode(CPpmd7 *p, void *node, unsigned indx) +{ + *((CPpmd_Void_Ref *)node) = p->FreeList[indx]; + p->FreeList[indx] = REF(node); +} + +static void *RemoveNode(CPpmd7 *p, unsigned indx) +{ + CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]); + p->FreeList[indx] = *node; + return node; +} + +static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx) +{ + unsigned i, nu = I2U(oldIndx) - I2U(newIndx); + ptr = (Byte *)ptr + U2B(I2U(newIndx)); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); + } + InsertNode(p, ptr, i); +} + +static void GlueFreeBlocks(CPpmd7 *p) +{ + #ifdef PPMD_32BIT + CPpmd7_Node headItem; + CPpmd7_Node_Ref head = &headItem; + #else + CPpmd7_Node_Ref head = p->AlignOffset + p->Size; + #endif + + CPpmd7_Node_Ref n = head; + unsigned i; + + p->GlueCount = 255; + + /* create doubly-linked list of free blocks */ + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + UInt16 nu = I2U(i); + CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i]; + p->FreeList[i] = 0; + while (next != 0) + { + CPpmd7_Node *node = NODE(next); + node->Next = n; + n = NODE(n)->Prev = next; + next = *(const CPpmd7_Node_Ref *)node; + node->Stamp = 0; + node->NU = (UInt16)nu; + } + } + NODE(head)->Stamp = 1; + NODE(head)->Next = n; + NODE(n)->Prev = head; + if (p->LoUnit != p->HiUnit) + ((CPpmd7_Node *)p->LoUnit)->Stamp = 1; + + /* Glue free blocks */ + while (n != head) + { + CPpmd7_Node *node = NODE(n); + UInt32 nu = (UInt32)node->NU; + for (;;) + { + CPpmd7_Node *node2 = NODE(n) + nu; + nu += node2->NU; + if (node2->Stamp != 0 || nu >= 0x10000) + break; + NODE(node2->Prev)->Next = node2->Next; + NODE(node2->Next)->Prev = node2->Prev; + node->NU = (UInt16)nu; + } + n = node->Next; + } + + /* Fill lists of free blocks */ + for (n = NODE(head)->Next; n != head;) + { + CPpmd7_Node *node = NODE(n); + unsigned nu; + CPpmd7_Node_Ref next = node->Next; + for (nu = node->NU; nu > 128; nu -= 128, node += 128) + InsertNode(p, node, PPMD_NUM_INDEXES - 1); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, node + k, nu - k - 1); + } + InsertNode(p, node, i); + n = next; + } +} + +static void *AllocUnitsRare(CPpmd7 *p, unsigned indx) +{ + unsigned i; + void *retVal; + if (p->GlueCount == 0) + { + GlueFreeBlocks(p); + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + } + i = indx; + do + { + if (++i == PPMD_NUM_INDEXES) + { + UInt32 numBytes = U2B(I2U(indx)); + p->GlueCount--; + return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); + } + } + while (p->FreeList[i] == 0); + retVal = RemoveNode(p, i); + SplitBlock(p, retVal, i, indx); + return retVal; +} + +static void *AllocUnits(CPpmd7 *p, unsigned indx) +{ + UInt32 numBytes; + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + numBytes = U2B(I2U(indx)); + if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) + { + void *retVal = p->LoUnit; + p->LoUnit += numBytes; + return retVal; + } + return AllocUnitsRare(p, indx); +} + +#define MyMem12Cpy(dest, src, num) \ + { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \ + do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while (--n); } + +static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU) +{ + unsigned i0 = U2I(oldNU); + unsigned i1 = U2I(newNU); + if (i0 == i1) + return oldPtr; + if (p->FreeList[i1] != 0) + { + void *ptr = RemoveNode(p, i1); + MyMem12Cpy(ptr, oldPtr, newNU); + InsertNode(p, oldPtr, i0); + return ptr; + } + SplitBlock(p, oldPtr, i0, i1); + return oldPtr; +} + +#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) + +static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) +{ + (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); + (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); +} + +static void RestartModel(CPpmd7 *p) +{ + unsigned i, k, m; + + memset(p->FreeList, 0, sizeof(p->FreeList)); + p->Text = p->Base + p->AlignOffset; + p->HiUnit = p->Text + p->Size; + p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; + p->GlueCount = 0; + + p->OrderFall = p->MaxOrder; + p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; + p->PrevSuccess = 0; + + p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ + p->MinContext->Suffix = 0; + p->MinContext->NumStats = 256; + p->MinContext->SummFreq = 256 + 1; + p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ + p->LoUnit += U2B(256 / 2); + p->MinContext->Stats = REF(p->FoundState); + for (i = 0; i < 256; i++) + { + CPpmd_State *s = &p->FoundState[i]; + s->Symbol = (Byte)i; + s->Freq = 1; + SetSuccessor(s, 0); + } + + for (i = 0; i < 128; i++) + for (k = 0; k < 8; k++) + { + UInt16 *dest = p->BinSumm[i] + k; + UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2)); + for (m = 0; m < 64; m += 8) + dest[m] = val; + } + + for (i = 0; i < 25; i++) + for (k = 0; k < 16; k++) + { + CPpmd_See *s = &p->See[i][k]; + s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4)); + s->Count = 4; + } +} + +void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder) +{ + p->MaxOrder = maxOrder; + RestartModel(p); + p->DummySee.Shift = PPMD_PERIOD_BITS; + p->DummySee.Summ = 0; /* unused */ + p->DummySee.Count = 64; /* unused */ +} + +static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip) +{ + CPpmd_State upState; + CTX_PTR c = p->MinContext; + CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); + CPpmd_State *ps[PPMD7_MAX_ORDER]; + unsigned numPs = 0; + + if (!skip) + ps[numPs++] = p->FoundState; + + while (c->Suffix) + { + CPpmd_Void_Ref successor; + CPpmd_State *s; + c = SUFFIX(c); + if (c->NumStats != 1) + { + for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); + } + else + s = ONE_STATE(c); + successor = SUCCESSOR(s); + if (successor != upBranch) + { + c = CTX(successor); + if (numPs == 0) + return c; + break; + } + ps[numPs++] = s; + } + + upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch); + SetSuccessor(&upState, upBranch + 1); + + if (c->NumStats == 1) + upState.Freq = ONE_STATE(c)->Freq; + else + { + UInt32 cf, s0; + CPpmd_State *s; + for (s = STATS(c); s->Symbol != upState.Symbol; s++); + cf = s->Freq - 1; + s0 = c->SummFreq - c->NumStats - cf; + upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0)))); + } + + do + { + /* Create Child */ + CTX_PTR c1; /* = AllocContext(p); */ + if (p->HiUnit != p->LoUnit) + c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); + else if (p->FreeList[0] != 0) + c1 = (CTX_PTR)RemoveNode(p, 0); + else + { + c1 = (CTX_PTR)AllocUnitsRare(p, 0); + if (!c1) + return NULL; + } + c1->NumStats = 1; + *ONE_STATE(c1) = upState; + c1->Suffix = REF(c); + SetSuccessor(ps[--numPs], REF(c1)); + c = c1; + } + while (numPs != 0); + + return c; +} + +static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) +{ + CPpmd_State tmp = *t1; + *t1 = *t2; + *t2 = tmp; +} + +static void UpdateModel(CPpmd7 *p) +{ + CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); + CTX_PTR c; + unsigned s0, ns; + + if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) + { + c = SUFFIX(p->MinContext); + + if (c->NumStats == 1) + { + CPpmd_State *s = ONE_STATE(c); + if (s->Freq < 32) + s->Freq++; + } + else + { + CPpmd_State *s = STATS(c); + if (s->Symbol != p->FoundState->Symbol) + { + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s[0].Freq >= s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + s--; + } + } + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + } + + if (p->OrderFall == 0) + { + p->MinContext = p->MaxContext = CreateSuccessors(p, True); + if (p->MinContext == 0) + { + RestartModel(p); + return; + } + SetSuccessor(p->FoundState, REF(p->MinContext)); + return; + } + + *p->Text++ = p->FoundState->Symbol; + successor = REF(p->Text); + if (p->Text >= p->UnitsStart) + { + RestartModel(p); + return; + } + + if (fSuccessor) + { + if (fSuccessor <= successor) + { + CTX_PTR cs = CreateSuccessors(p, False); + if (cs == NULL) + { + RestartModel(p); + return; + } + fSuccessor = REF(cs); + } + if (--p->OrderFall == 0) + { + successor = fSuccessor; + p->Text -= (p->MaxContext != p->MinContext); + } + } + else + { + SetSuccessor(p->FoundState, successor); + fSuccessor = REF(p->MinContext); + } + + s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1); + + for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c)) + { + unsigned ns1; + UInt32 cf, sf; + if ((ns1 = c->NumStats) != 1) + { + if ((ns1 & 1) == 0) + { + /* Expand for one UNIT */ + unsigned oldNU = ns1 >> 1; + unsigned i = U2I(oldNU); + if (i != U2I(oldNU + 1)) + { + void *ptr = AllocUnits(p, i + 1); + void *oldPtr; + if (!ptr) + { + RestartModel(p); + return; + } + oldPtr = STATS(c); + MyMem12Cpy(ptr, oldPtr, oldNU); + InsertNode(p, oldPtr, i); + c->Stats = STATS_REF(ptr); + } + } + c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1))); + } + else + { + CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0); + if (!s) + { + RestartModel(p); + return; + } + *s = *ONE_STATE(c); + c->Stats = REF(s); + if (s->Freq < MAX_FREQ / 4 - 1) + s->Freq <<= 1; + else + s->Freq = MAX_FREQ - 4; + c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3)); + } + cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6); + sf = (UInt32)s0 + c->SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf) + (cf >= 4 * sf); + c->SummFreq += 3; + } + else + { + cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf); + c->SummFreq = (UInt16)(c->SummFreq + cf); + } + { + CPpmd_State *s = STATS(c) + ns1; + SetSuccessor(s, successor); + s->Symbol = p->FoundState->Symbol; + s->Freq = (Byte)cf; + c->NumStats = (UInt16)(ns1 + 1); + } + } + p->MaxContext = p->MinContext = CTX(fSuccessor); +} + +static void Rescale(CPpmd7 *p) +{ + unsigned i, adder, sumFreq, escFreq; + CPpmd_State *stats = STATS(p->MinContext); + CPpmd_State *s = p->FoundState; + { + CPpmd_State tmp = *s; + for (; s != stats; s--) + s[0] = s[-1]; + *s = tmp; + } + escFreq = p->MinContext->SummFreq - s->Freq; + s->Freq += 4; + adder = (p->OrderFall != 0); + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq = s->Freq; + + i = p->MinContext->NumStats - 1; + do + { + escFreq -= (++s)->Freq; + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq += s->Freq; + if (s[0].Freq > s[-1].Freq) + { + CPpmd_State *s1 = s; + CPpmd_State tmp = *s1; + do + s1[0] = s1[-1]; + while (--s1 != stats && tmp.Freq > s1[-1].Freq); + *s1 = tmp; + } + } + while (--i); + + if (s->Freq == 0) + { + unsigned numStats = p->MinContext->NumStats; + unsigned n0, n1; + do { i++; } while ((--s)->Freq == 0); + escFreq += i; + p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i); + if (p->MinContext->NumStats == 1) + { + CPpmd_State tmp = *stats; + do + { + tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1)); + escFreq >>= 1; + } + while (escFreq > 1); + InsertNode(p, stats, U2I(((numStats + 1) >> 1))); + *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; + return; + } + n0 = (numStats + 1) >> 1; + n1 = (p->MinContext->NumStats + 1) >> 1; + if (n0 != n1) + p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); + } + p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); + p->FoundState = STATS(p->MinContext); +} + +CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq) +{ + CPpmd_See *see; + unsigned nonMasked = p->MinContext->NumStats - numMasked; + if (p->MinContext->NumStats != 256) + { + see = p->See[p->NS2Indx[nonMasked - 1]] + + (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) + + 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) + + 4 * (numMasked > nonMasked) + + p->HiBitsFlag; + { + unsigned r = (see->Summ >> see->Shift); + see->Summ = (UInt16)(see->Summ - r); + *escFreq = r + (r == 0); + } + } + else + { + see = &p->DummySee; + *escFreq = 1; + } + return see; +} + +static void NextContext(CPpmd7 *p) +{ + CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); + if (p->OrderFall == 0 && (Byte *)c > p->Text) + p->MinContext = p->MaxContext = c; + else + UpdateModel(p); +} + +void Ppmd7_Update1(CPpmd7 *p) +{ + CPpmd_State *s = p->FoundState; + s->Freq += 4; + p->MinContext->SummFreq += 4; + if (s[0].Freq > s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + p->FoundState = --s; + if (s->Freq > MAX_FREQ) + Rescale(p); + } + NextContext(p); +} + +void Ppmd7_Update1_0(CPpmd7 *p) +{ + p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq); + p->RunLength += p->PrevSuccess; + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + NextContext(p); +} + +void Ppmd7_UpdateBin(CPpmd7 *p) +{ + p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0)); + p->PrevSuccess = 1; + p->RunLength++; + NextContext(p); +} + +void Ppmd7_Update2(CPpmd7 *p) +{ + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + p->RunLength = p->InitRL; + UpdateModel(p); +} diff --git a/lzma/C/Ppmd7.h b/lzma/C/Ppmd7.h new file mode 100644 index 0000000000..56e81eb123 --- /dev/null +++ b/lzma/C/Ppmd7.h @@ -0,0 +1,140 @@ +/* Ppmd7.h -- PPMdH compression codec +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +/* This code supports virtual RangeDecoder and includes the implementation +of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H. +If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */ + +#ifndef __PPMD7_H +#define __PPMD7_H + +#include "Ppmd.h" + +EXTERN_C_BEGIN + +#define PPMD7_MIN_ORDER 2 +#define PPMD7_MAX_ORDER 64 + +#define PPMD7_MIN_MEM_SIZE (1 << 11) +#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3) + +struct CPpmd7_Context_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd7_Context_ * + #else + UInt32 + #endif + CPpmd7_Context_Ref; + +typedef struct CPpmd7_Context_ +{ + UInt16 NumStats; + UInt16 SummFreq; + CPpmd_State_Ref Stats; + CPpmd7_Context_Ref Suffix; +} CPpmd7_Context; + +#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) + +typedef struct +{ + CPpmd7_Context *MinContext, *MaxContext; + CPpmd_State *FoundState; + unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag; + Int32 RunLength, InitRL; /* must be 32-bit at least */ + + UInt32 Size; + UInt32 GlueCount; + Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; + UInt32 AlignOffset; + + Byte Indx2Units[PPMD_NUM_INDEXES]; + Byte Units2Indx[128]; + CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; + Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256]; + CPpmd_See DummySee, See[25][16]; + UInt16 BinSumm[128][64]; +} CPpmd7; + +void Ppmd7_Construct(CPpmd7 *p); +Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc); +void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc); +void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder); +#define Ppmd7_WasAllocated(p) ((p)->Base != NULL) + + +/* ---------- Internal Functions ---------- */ + +extern const Byte PPMD7_kExpEscape[16]; + +#ifdef PPMD_32BIT + #define Ppmd7_GetPtr(p, ptr) (ptr) + #define Ppmd7_GetContext(p, ptr) (ptr) + #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats) +#else + #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs))) + #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs))) + #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats))) +#endif + +void Ppmd7_Update1(CPpmd7 *p); +void Ppmd7_Update1_0(CPpmd7 *p); +void Ppmd7_Update2(CPpmd7 *p); +void Ppmd7_UpdateBin(CPpmd7 *p); + +#define Ppmd7_GetBinSumm(p) \ + &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \ + p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \ + (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \ + 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \ + ((p->RunLength >> 26) & 0x20)] + +CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale); + + +/* ---------- Decode ---------- */ + +typedef struct +{ + UInt32 (*GetThreshold)(void *p, UInt32 total); + void (*Decode)(void *p, UInt32 start, UInt32 size); + UInt32 (*DecodeBit)(void *p, UInt32 size0); +} IPpmd7_RangeDec; + +typedef struct +{ + IPpmd7_RangeDec p; + UInt32 Range; + UInt32 Code; + IByteIn *Stream; +} CPpmd7z_RangeDec; + +void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p); +Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p); +#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0) + +int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc); + + +/* ---------- Encode ---------- */ + +typedef struct +{ + UInt64 Low; + UInt32 Range; + Byte Cache; + UInt64 CacheSize; + IByteOut *Stream; +} CPpmd7z_RangeEnc; + +void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p); +void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p); + +void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol); + +EXTERN_C_END + +#endif diff --git a/lzma/C/Ppmd7Dec.c b/lzma/C/Ppmd7Dec.c new file mode 100644 index 0000000000..3d01d76440 --- /dev/null +++ b/lzma/C/Ppmd7Dec.c @@ -0,0 +1,189 @@ +/* Ppmd7Dec.c -- PPMdH Decoder +2010-03-12 : Igor Pavlov : Public domain +This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */ + +#include "Precomp.h" + +#include "Ppmd7.h" + +#define kTopValue (1 << 24) + +Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p) +{ + unsigned i; + p->Code = 0; + p->Range = 0xFFFFFFFF; + if (p->Stream->Read((void *)p->Stream) != 0) + return False; + for (i = 0; i < 4; i++) + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + return (p->Code < 0xFFFFFFFF); +} + +static UInt32 Range_GetThreshold(void *pp, UInt32 total) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + return (p->Code) / (p->Range /= total); +} + +static void Range_Normalize(CPpmd7z_RangeDec *p) +{ + if (p->Range < kTopValue) + { + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + p->Range <<= 8; + if (p->Range < kTopValue) + { + p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream); + p->Range <<= 8; + } + } +} + +static void Range_Decode(void *pp, UInt32 start, UInt32 size) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + p->Code -= start * p->Range; + p->Range *= size; + Range_Normalize(p); +} + +static UInt32 Range_DecodeBit(void *pp, UInt32 size0) +{ + CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp; + UInt32 newBound = (p->Range >> 14) * size0; + UInt32 symbol; + if (p->Code < newBound) + { + symbol = 0; + p->Range = newBound; + } + else + { + symbol = 1; + p->Code -= newBound; + p->Range -= newBound; + } + Range_Normalize(p); + return symbol; +} + +void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p) +{ + p->p.GetThreshold = Range_GetThreshold; + p->p.Decode = Range_Decode; + p->p.DecodeBit = Range_DecodeBit; +} + + +#define MASK(sym) ((signed char *)charMask)[sym] + +int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc) +{ + size_t charMask[256 / sizeof(size_t)]; + if (p->MinContext->NumStats != 1) + { + CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext); + unsigned i; + UInt32 count, hiCnt; + if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) + { + Byte symbol; + rc->Decode(rc, 0, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update1_0(p); + return symbol; + } + p->PrevSuccess = 0; + i = p->MinContext->NumStats - 1; + do + { + if ((hiCnt += (++s)->Freq) > count) + { + Byte symbol; + rc->Decode(rc, hiCnt - s->Freq, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update1(p); + return symbol; + } + } + while (--i); + if (count >= p->MinContext->SummFreq) + return -2; + p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]; + rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt); + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(s->Symbol) = 0; + i = p->MinContext->NumStats - 1; + do { MASK((--s)->Symbol) = 0; } while (--i); + } + else + { + UInt16 *prob = Ppmd7_GetBinSumm(p); + if (rc->DecodeBit(rc, *prob) == 0) + { + Byte symbol; + *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); + symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol; + Ppmd7_UpdateBin(p); + return symbol; + } + *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); + p->InitEsc = PPMD7_kExpEscape[*prob >> 10]; + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0; + p->PrevSuccess = 0; + } + for (;;) + { + CPpmd_State *ps[256], *s; + UInt32 freqSum, count, hiCnt; + CPpmd_See *see; + unsigned i, num, numMasked = p->MinContext->NumStats; + do + { + p->OrderFall++; + if (!p->MinContext->Suffix) + return -1; + p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix); + } + while (p->MinContext->NumStats == numMasked); + hiCnt = 0; + s = Ppmd7_GetStats(p, p->MinContext); + i = 0; + num = p->MinContext->NumStats - numMasked; + do + { + int k = (int)(MASK(s->Symbol)); + hiCnt += (s->Freq & k); + ps[i] = s++; + i -= k; + } + while (i != num); + + see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum); + freqSum += hiCnt; + count = rc->GetThreshold(rc, freqSum); + + if (count < hiCnt) + { + Byte symbol; + CPpmd_State **pps = ps; + for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); + s = *pps; + rc->Decode(rc, hiCnt - s->Freq, s->Freq); + Ppmd_See_Update(see); + p->FoundState = s; + symbol = s->Symbol; + Ppmd7_Update2(p); + return symbol; + } + if (count >= freqSum) + return -2; + rc->Decode(rc, hiCnt, freqSum - hiCnt); + see->Summ = (UInt16)(see->Summ + freqSum); + do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); + } +} diff --git a/lzma/C/Precomp.h b/lzma/C/Precomp.h new file mode 100644 index 0000000000..edb5814439 --- /dev/null +++ b/lzma/C/Precomp.h @@ -0,0 +1,10 @@ +/* Precomp.h -- StdAfx +2013-11-12 : Igor Pavlov : Public domain */ + +#ifndef __7Z_PRECOMP_H +#define __7Z_PRECOMP_H + +#include "Compiler.h" +/* #include "7zTypes.h" */ + +#endif diff --git a/lzma/C/Threads.c b/lzma/C/Threads.c index 4be44fb0f8..ece07e618a 100644 --- a/lzma/C/Threads.c +++ b/lzma/C/Threads.c @@ -1,7 +1,9 @@ /* Threads.c -- multithreading library -2009-09-20 : Igor Pavlov : Public domain */ +2014-09-21 : Igor Pavlov : Public domain */ -#ifndef _WIN32_WCE +#include "Precomp.h" + +#ifndef UNDER_CE #include #endif @@ -29,14 +31,21 @@ WRes Handle_WaitObject(HANDLE h) { return (WRes)WaitForSingleObject(h, INFINITE) WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param) { - unsigned threadId; /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ - *p = - #ifdef UNDER_CE - CreateThread(0, 0, func, param, 0, &threadId); - #else - (HANDLE)_beginthreadex(NULL, 0, func, param, 0, &threadId); - #endif - /* maybe we must use errno here, but probably GetLastError() is also OK. */ + /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ + + #ifdef UNDER_CE + + DWORD threadId; + *p = CreateThread(0, 0, func, param, 0, &threadId); + + #else + + unsigned threadId; + *p = (HANDLE)_beginthreadex(NULL, 0, func, param, 0, &threadId); + + #endif + + /* maybe we must use errno here, but probably GetLastError() is also OK. */ return HandleToWRes(*p); } diff --git a/lzma/C/Threads.h b/lzma/C/Threads.h index 6a7afa8299..e927208d79 100644 --- a/lzma/C/Threads.h +++ b/lzma/C/Threads.h @@ -1,15 +1,17 @@ /* Threads.h -- multithreading library -2009-03-27 : Igor Pavlov : Public domain */ +2013-11-12 : Igor Pavlov : Public domain */ #ifndef __7Z_THREADS_H #define __7Z_THREADS_H -#include "Types.h" - -#ifdef __cplusplus -extern "C" { +#ifdef _WIN32 +#include #endif +#include "7zTypes.h" + +EXTERN_C_BEGIN + WRes HandlePtr_Close(HANDLE *h); WRes Handle_WaitObject(HANDLE h); @@ -18,7 +20,15 @@ typedef HANDLE CThread; #define Thread_WasCreated(p) (*(p) != NULL) #define Thread_Close(p) HandlePtr_Close(p) #define Thread_Wait(p) Handle_WaitObject(*(p)) -typedef unsigned THREAD_FUNC_RET_TYPE; + +typedef +#ifdef UNDER_CE + DWORD +#else + unsigned +#endif + THREAD_FUNC_RET_TYPE; + #define THREAD_FUNC_CALL_TYPE MY_STD_CALL #define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE typedef THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE * THREAD_FUNC_TYPE)(void *); @@ -52,8 +62,6 @@ WRes CriticalSection_Init(CCriticalSection *p); #define CriticalSection_Enter(p) EnterCriticalSection(p) #define CriticalSection_Leave(p) LeaveCriticalSection(p) -#ifdef __cplusplus -} -#endif +EXTERN_C_END #endif diff --git a/lzma/CMakeLists.txt b/lzma/CMakeLists.txt index 7cd330cc8a..9c9de9152e 100644 --- a/lzma/CMakeLists.txt +++ b/lzma/CMakeLists.txt @@ -6,21 +6,27 @@ if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fomit-frame-pointer" ) endif( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_7ZIP_PPMD_SUPPPORT" ) + set( LZMA_FILES + C/7zArcIn.c C/7zBuf.c C/7zCrc.c C/7zCrcOpt.c C/7zDec.c - C/7zIn.c C/7zStream.c C/Bcj2.c C/Bra.c C/Bra86.c + C/BraIA64.c C/CpuArch.c + C/Delta.c C/LzFind.c C/Lzma2Dec.c C/LzmaDec.c - C/LzmaEnc.c ) + C/LzmaEnc.c + C/Ppmd7.c + C/Ppmd7Dec.c ) if( WIN32 ) set( LZMA_FILES ${LZMA_FILES} C/LzFindMt.c C/Threads.c ) diff --git a/lzma/history.txt b/lzma/history.txt index 36d01d7d96..6abb8248cd 100644 --- a/lzma/history.txt +++ b/lzma/history.txt @@ -1,6 +1,62 @@ HISTORY of the LZMA SDK ----------------------- +15.12 2015-11-19 +------------------------- +- The BUG in C version of 7z decoder was fixed: + 7zDec.c : SzDecodeLzma2() + 7z decoder could mistakenly report about decoding error for some 7z archives + that use LZMA2 compression method. + The probability to get that mistaken decoding error report was about + one error per 16384 solid blocks for solid blocks larger than 16 KB (compressed size). +- The BUG (in 9.26-15.11) in C version of 7z decoder was fixed: + 7zArcIn.c : SzReadHeader2() + 7z decoder worked incorrectly for 7z archives that contain + empty solid blocks, that can be placed to 7z archive, if some file is + unavailable for reading during archive creation. + + +15.09 beta 2015-10-16 +------------------------- +- The BUG in LZMA / LZMA2 encoding code was fixed. + The BUG in LzFind.c::MatchFinder_ReadBlock() function. + If input data size is larger than (4 GiB - dictionary_size), + the following code worked incorrectly: + - LZMA : LzmaEnc_MemEncode(), LzmaEncode() : LZMA encoding functions + for compressing from memory to memory. + That BUG is not related to LZMA encoder version that works via streams. + - LZMA2 : multi-threaded version of LZMA2 encoder worked incorrectly, if + default value of chunk size (CLzma2EncProps::blockSize) is changed + to value larger than (4 GiB - dictionary_size). + + +9.38 beta 2015-01-03 +------------------------- +- The BUG in 9.31-9.37 was fixed: + IArchiveGetRawProps interface was disabled for 7z archives. +- The BUG in 9.26-9.36 was fixed: + Some code in CPP\7zip\Archive\7z\ worked correctly only under Windows. + + +9.36 beta 2014-12-26 +------------------------- +- The BUG in command line version was fixed: + 7-Zip created temporary archive in current folder during update archive + operation, if -w{Path} switch was not specified. + The fixed 7-Zip creates temporary archive in folder that contains updated archive. +- The BUG in 9.33-9.35 was fixed: + 7-Zip silently ignored file reading errors during 7z or gz archive creation, + and the created archive contained only part of file that was read before error. + The fixed 7-Zip stops archive creation and it reports about error. + + +9.35 beta 2014-12-07 +------------------------- +- 7zr.exe now support AES encryption. +- SFX mudules were added to LZMA SDK +- Some bugs were fixed. + + 9.21 beta 2011-04-11 ------------------------- - New class FString for file names at file systems. diff --git a/lzma/lzma.txt b/lzma/lzma.txt index 9d3cef0031..4e74e8b5b7 100644 --- a/lzma/lzma.txt +++ b/lzma/lzma.txt @@ -1,18 +1,27 @@ -LZMA SDK 9.22 -------------- +LZMA SDK 15.13 +-------------- -LZMA SDK provides the documentation, samples, header files, libraries, -and tools you need to develop applications that use LZMA compression. - -LZMA is default and general compression method of 7z format -in 7-Zip compression program (www.7-zip.org). LZMA provides high -compression ratio and very fast decompression. +LZMA SDK provides the documentation, samples, header files, +libraries, and tools you need to develop applications that +use 7z / LZMA / LZMA2 / XZ compression. LZMA is an improved version of famous LZ77 compression algorithm. It was improved in way of maximum increasing of compression ratio, keeping high decompression speed and low memory requirements for decompressing. +LZMA2 is a LZMA based compression method. LZMA2 provides better +multithreading support for compression than LZMA and some other improvements. + +7z is a file format for data compression and file archiving. +7z is a main file format for 7-Zip compression program (www.7-zip.org). +7z format supports different compression methods: LZMA, LZMA2 and others. +7z also supports AES-256 based encryption. + +XZ is a file format for data compression that uses LZMA2 compression. +XZ format provides additional features: SHA/CRC check, filters for +improved compression ratio, splitting to blocks and streams, + LICENSE @@ -24,8 +33,9 @@ Some code in LZMA SDK is based on public domain code from another developers: 1) PPMd var.H (2001): Dmitry Shkarin 2) SHA-256: Wei Dai (Crypto++ library) -You can copy, modify, distribute and perform LZMA SDK code, even for commercial purposes, -all without asking permission. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute the +original LZMA SDK code, either in source code form or as a compiled binary, for +any purpose, commercial or non-commercial, and by any means. LZMA SDK code is compatible with open source licenses, for example, you can include it to GNU GPL or GNU LGPL code. @@ -34,10 +44,20 @@ include it to GNU GPL or GNU LGPL code. LZMA SDK Contents ----------------- -LZMA SDK includes: + Source code: - - ANSI-C/C++/C#/Java source code for LZMA compressing and decompressing - - Compiled file->file LZMA compressing/decompressing program for Windows system + - C / C++ / C# / Java - LZMA compression and decompression + - C / C++ - LZMA2 compression and decompression + - C / C++ - XZ compression and decompression + - C - 7z decompression + - C++ - 7z compression and decompression + - C - small SFXs for installers (7z decompression) + - C++ - SFXs and SFXs for installers (7z decompression) + + Precomiled binaries: + + - console programs for lzma / 7z / xz compression and decompression + - SFX modules for installers. UNIX/Linux version @@ -51,70 +71,94 @@ In some UNIX/Linux versions you must compile LZMA with static libraries. To compile with static libraries, you can use LIB = -lm -static +Also you can use p7zip (port of 7-Zip for POSIX systems like Unix or Linux): + + http://p7zip.sourceforge.net/ + Files ---------------------- -lzma.txt - LZMA SDK description (this file) -7zFormat.txt - 7z Format description -7zC.txt - 7z ANSI-C Decoder description -methods.txt - Compression method IDs for .7z -lzma.exe - Compiled file->file LZMA encoder/decoder for Windows -7zr.exe - 7-Zip with 7z/lzma/xz support. -history.txt - history of the LZMA SDK +----- + +DOC/7zC.txt - 7z ANSI-C Decoder description +DOC/7zFormat.txt - 7z Format description +DOC/installer.txt - information about 7-Zip for installers +DOC/lzma.txt - LZMA compression description +DOC/lzma-sdk.txt - LZMA SDK description (this file) +DOC/lzma-history.txt - history of LZMA SDK +DOC/lzma-specification.txt - Specification of LZMA +DOC/Methods.txt - Compression method IDs for .7z + +bin/installer/ - example script to create installer that uses SFX module, + +bin/7zdec.exe - simplified 7z archive decoder +bin/7zr.exe - 7-Zip console program (reduced version) +bin/x64/7zr.exe - 7-Zip console program (reduced version) (x64 version) +bin/lzma.exe - file->file LZMA encoder/decoder for Windows +bin/7zS2.sfx - small SFX module for installers (GUI version) +bin/7zS2con.sfx - small SFX module for installers (Console version) +bin/7zSD.sfx - SFX module for installers. + + +7zDec.exe +--------- +7zDec.exe is simplified 7z archive decoder. +It supports only LZMA, LZMA2, and PPMd methods. +7zDec decodes whole solid block from 7z archive to RAM. +The RAM consumption can be high. + + Source code structure --------------------- -C/ - C files - 7zCrc*.* - CRC code - Alloc.* - Memory allocation functions - Bra*.* - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code - LzFind.* - Match finder for LZ (LZMA) encoders - LzFindMt.* - Match finder for LZ (LZMA) encoders for multithreading encoding - LzHash.h - Additional file for LZ match finder - LzmaDec.* - LZMA decoding - LzmaEnc.* - LZMA encoding - LzmaLib.* - LZMA Library for DLL calling - Types.h - Basic types for another .c files - Threads.* - The code for multithreading. - LzmaLib - LZMA Library (.DLL for Windows) - - LzmaUtil - LZMA Utility (file->file LZMA encoder/decoder). +Asm/ - asm files (optimized code for CRC calculation and Intel-AES encryption) - Archive - files related to archiving - 7z - 7z ANSI-C Decoder +C/ - C files (compression / decompression and other) + Util/ + 7z - 7z decoder program (decoding 7z files) + Lzma - LZMA program (file->file LZMA encoder/decoder). + LzmaLib - LZMA library (.DLL for Windows) + SfxSetup - small SFX module for installers CPP/ -- CPP files Common - common files for C++ projects Windows - common files for Windows related code - 7zip - files related to 7-Zip Project - - Common - common files for 7-Zip - - Compress - files related to compression/decompression + 7zip - files related to 7-Zip Archive - files related to archiving Common - common files for archive handling 7z - 7z C++ Encoder/Decoder - Bundles - Modules that are bundles of other modules + Bundles - Modules that are bundles of other modules (files) - Alone7z - 7zr.exe: Standalone version of 7z.exe that supports only 7z/LZMA/BCJ/BCJ2 - LzmaCon - lzma.exe: LZMA compression/decompression - Format7zR - 7zr.dll: Reduced version of 7za.dll: extracting/compressing to 7z/LZMA/BCJ/BCJ2 - Format7zExtractR - 7zxr.dll: Reduced version of 7zxa.dll: extracting from 7z/LZMA/BCJ/BCJ2. + Alone7z - 7zr.exe: Standalone 7-Zip console program (reduced version) + Format7zExtractR - 7zxr.dll: Reduced version of 7z DLL: extracting from 7z/LZMA/BCJ/BCJ2. + Format7zR - 7zr.dll: Reduced version of 7z DLL: extracting/compressing to 7z/LZMA/BCJ/BCJ2 + LzmaCon - lzma.exe: LZMA compression/decompression + LzmaSpec - example code for LZMA Specification + SFXCon - 7zCon.sfx: Console 7z SFX module + SFXSetup - 7zS.sfx: 7z SFX module for installers + SFXWin - 7z.sfx: GUI 7z SFX module - UI - User Interface files + Common - common files for 7-Zip + + Compress - files for compression/decompression + + Crypto - files for encryption / decompression + + UI - User Interface files - Client7z - Test application for 7za.dll, 7zr.dll, 7zxr.dll + Client7z - Test application for 7za.dll, 7zr.dll, 7zxr.dll Common - Common UI files - Console - Code for console archiver - + Console - Code for console program (7z.exe) + Explorer - Some code from 7-Zip Shell extension + FileManager - Some GUI code from 7-Zip File Manager + GUI - Some GUI code from 7-Zip CS/ - C# files @@ -134,8 +178,9 @@ Java/ - Java files RangeCoder - Range Coder (special code of compression/decompression) -C/C++ source code of LZMA SDK is part of 7-Zip project. -7-Zip source code can be downloaded from 7-Zip's SourceForge page: +Note: + Asm / C / C++ source code of LZMA SDK is part of 7-Zip's source code. + 7-Zip's source code can be downloaded from 7-Zip's SourceForge page: http://sourceforge.net/projects/sevenzip/ @@ -146,8 +191,8 @@ LZMA features - Variable dictionary size (up to 1 GB) - Estimated compressing speed: about 2 MB/s on 2 GHz CPU - Estimated decompressing speed: - - 20-30 MB/s on 2 GHz Core 2 or AMD Athlon 64 - - 1-2 MB/s on 200 MHz ARM, MIPS, PowerPC or other simple RISC + - 20-30 MB/s on modern 2 GHz cpu + - 1-2 MB/s on 200 MHz simple RISC cpu: (ARM, MIPS, PowerPC) - Small memory requirements for decompressing (16 KB + DictionarySize) - Small code size for decompressing: 5-8 KB @@ -156,7 +201,7 @@ implemented in any modern 32-bit CPU (or on 16-bit CPU with some conditions). Some critical operations that affect the speed of LZMA decompression: 1) 32*16 bit integer multiply - 2) Misspredicted branches (penalty mostly depends from pipeline length) + 2) Mispredicted branches (penalty mostly depends from pipeline length) 3) 32-bit shift and arithmetic operations The speed of LZMA decompressing mostly depends from CPU speed. @@ -304,298 +349,6 @@ compressible. For some ISAs (for example, for MIPS) it's impossible to get gain from such filter. -LZMA compressed file format ---------------------------- -Offset Size Description - 0 1 Special LZMA properties (lc,lp, pb in encoded form) - 1 4 Dictionary size (little endian) - 5 8 Uncompressed size (little endian). -1 means unknown size - 13 Compressed data - - -ANSI-C LZMA Decoder -~~~~~~~~~~~~~~~~~~~ - -Please note that interfaces for ANSI-C code were changed in LZMA SDK 4.58. -If you want to use old interfaces you can download previous version of LZMA SDK -from sourceforge.net site. - -To use ANSI-C LZMA Decoder you need the following files: -1) LzmaDec.h + LzmaDec.c + Types.h -LzmaUtil/LzmaUtil.c is example application that uses these files. - - -Memory requirements for LZMA decoding -------------------------------------- - -Stack usage of LZMA decoding function for local variables is not -larger than 200-400 bytes. - -LZMA Decoder uses dictionary buffer and internal state structure. -Internal state structure consumes - state_size = (4 + (1.5 << (lc + lp))) KB -by default (lc=3, lp=0), state_size = 16 KB. - - -How To decompress data ----------------------- - -LZMA Decoder (ANSI-C version) now supports 2 interfaces: -1) Single-call Decompressing -2) Multi-call State Decompressing (zlib-like interface) - -You must use external allocator: -Example: -void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } -void SzFree(void *p, void *address) { p = p; free(address); } -ISzAlloc alloc = { SzAlloc, SzFree }; - -You can use p = p; operator to disable compiler warnings. - - -Single-call Decompressing -------------------------- -When to use: RAM->RAM decompressing -Compile files: LzmaDec.h + LzmaDec.c + Types.h -Compile defines: no defines -Memory Requirements: - - Input buffer: compressed size - - Output buffer: uncompressed size - - LZMA Internal Structures: state_size (16 KB for default settings) - -Interface: - int LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, - const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, - ELzmaStatus *status, ISzAlloc *alloc); - In: - dest - output data - destLen - output data size - src - input data - srcLen - input data size - propData - LZMA properties (5 bytes) - propSize - size of propData buffer (5 bytes) - finishMode - It has meaning only if the decoding reaches output limit (*destLen). - LZMA_FINISH_ANY - Decode just destLen bytes. - LZMA_FINISH_END - Stream must be finished after (*destLen). - You can use LZMA_FINISH_END, when you know that - current output buffer covers last bytes of stream. - alloc - Memory allocator. - - Out: - destLen - processed output size - srcLen - processed input size - - Output: - SZ_OK - status: - LZMA_STATUS_FINISHED_WITH_MARK - LZMA_STATUS_NOT_FINISHED - LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK - SZ_ERROR_DATA - Data error - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_UNSUPPORTED - Unsupported properties - SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). - - If LZMA decoder sees end_marker before reaching output limit, it returns OK result, - and output value of destLen will be less than output buffer size limit. - - You can use multiple checks to test data integrity after full decompression: - 1) Check Result and "status" variable. - 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. - 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. - You must use correct finish mode in that case. */ - - -Multi-call State Decompressing (zlib-like interface) ----------------------------------------------------- - -When to use: file->file decompressing -Compile files: LzmaDec.h + LzmaDec.c + Types.h - -Memory Requirements: - - Buffer for input stream: any size (for example, 16 KB) - - Buffer for output stream: any size (for example, 16 KB) - - LZMA Internal Structures: state_size (16 KB for default settings) - - LZMA dictionary (dictionary size is encoded in LZMA properties header) - -1) read LZMA properties (5 bytes) and uncompressed size (8 bytes, little-endian) to header: - unsigned char header[LZMA_PROPS_SIZE + 8]; - ReadFile(inFile, header, sizeof(header) - -2) Allocate CLzmaDec structures (state + dictionary) using LZMA properties - - CLzmaDec state; - LzmaDec_Constr(&state); - res = LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc); - if (res != SZ_OK) - return res; - -3) Init LzmaDec structure before any new LZMA stream. And call LzmaDec_DecodeToBuf in loop - - LzmaDec_Init(&state); - for (;;) - { - ... - int res = LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, - const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode); - ... - } - - -4) Free all allocated structures - LzmaDec_Free(&state, &g_Alloc); - -For full code example, look at C/LzmaUtil/LzmaUtil.c code. - - -How To compress data --------------------- - -Compile files: LzmaEnc.h + LzmaEnc.c + Types.h + -LzFind.c + LzFind.h + LzFindMt.c + LzFindMt.h + LzHash.h - -Memory Requirements: - - (dictSize * 11.5 + 6 MB) + state_size - -Lzma Encoder can use two memory allocators: -1) alloc - for small arrays. -2) allocBig - for big arrays. - -For example, you can use Large RAM Pages (2 MB) in allocBig allocator for -better compression speed. Note that Windows has bad implementation for -Large RAM Pages. -It's OK to use same allocator for alloc and allocBig. - - -Single-call Compression with callbacks --------------------------------------- - -Check C/LzmaUtil/LzmaUtil.c as example, - -When to use: file->file decompressing - -1) you must implement callback structures for interfaces: -ISeqInStream -ISeqOutStream -ICompressProgress -ISzAlloc - -static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); } -static void SzFree(void *p, void *address) { p = p; MyFree(address); } -static ISzAlloc g_Alloc = { SzAlloc, SzFree }; - - CFileSeqInStream inStream; - CFileSeqOutStream outStream; - - inStream.funcTable.Read = MyRead; - inStream.file = inFile; - outStream.funcTable.Write = MyWrite; - outStream.file = outFile; - - -2) Create CLzmaEncHandle object; - - CLzmaEncHandle enc; - - enc = LzmaEnc_Create(&g_Alloc); - if (enc == 0) - return SZ_ERROR_MEM; - - -3) initialize CLzmaEncProps properties; - - LzmaEncProps_Init(&props); - - Then you can change some properties in that structure. - -4) Send LZMA properties to LZMA Encoder - - res = LzmaEnc_SetProps(enc, &props); - -5) Write encoded properties to header - - Byte header[LZMA_PROPS_SIZE + 8]; - size_t headerSize = LZMA_PROPS_SIZE; - UInt64 fileSize; - int i; - - res = LzmaEnc_WriteProperties(enc, header, &headerSize); - fileSize = MyGetFileLength(inFile); - for (i = 0; i < 8; i++) - header[headerSize++] = (Byte)(fileSize >> (8 * i)); - MyWriteFileAndCheck(outFile, header, headerSize) - -6) Call encoding function: - res = LzmaEnc_Encode(enc, &outStream.funcTable, &inStream.funcTable, - NULL, &g_Alloc, &g_Alloc); - -7) Destroy LZMA Encoder Object - LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc); - - -If callback function return some error code, LzmaEnc_Encode also returns that code -or it can return the code like SZ_ERROR_READ, SZ_ERROR_WRITE or SZ_ERROR_PROGRESS. - - -Single-call RAM->RAM Compression --------------------------------- - -Single-call RAM->RAM Compression is similar to Compression with callbacks, -but you provide pointers to buffers instead of pointers to stream callbacks: - -HRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, - CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, - ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); - -Return code: - SZ_OK - OK - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_PARAM - Incorrect paramater - SZ_ERROR_OUTPUT_EOF - output buffer overflow - SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) - - - -Defines -------- - -_LZMA_SIZE_OPT - Enable some optimizations in LZMA Decoder to get smaller executable code. - -_LZMA_PROB32 - It can increase the speed on some 32-bit CPUs, but memory usage for - some structures will be doubled in that case. - -_LZMA_UINT32_IS_ULONG - Define it if int is 16-bit on your compiler and long is 32-bit. - -_LZMA_NO_SYSTEM_SIZE_T - Define it if you don't want to use size_t type. - - -_7ZIP_PPMD_SUPPPORT - Define it if you don't want to support PPMD method in AMSI-C .7z decoder. - - -C++ LZMA Encoder/Decoder -~~~~~~~~~~~~~~~~~~~~~~~~ -C++ LZMA code use COM-like interfaces. So if you want to use it, -you can study basics of COM/OLE. -C++ LZMA code is just wrapper over ANSI-C code. - - -C++ Notes -~~~~~~~~~~~~~~~~~~~~~~~~ -If you use some C++ code folders in 7-Zip (for example, C++ code for .7z handling), -you must check that you correctly work with "new" operator. -7-Zip can be compiled with MSVC 6.0 that doesn't throw "exception" from "new" operator. -So 7-Zip uses "CPP\Common\NewHandler.cpp" that redefines "new" operator: -operator new(size_t size) -{ - void *p = ::malloc(size); - if (p == 0) - throw CNewException(); - return p; -} -If you use MSCV that throws exception for "new" operator, you can compile without -"NewHandler.cpp". So standard exception will be used. Actually some code of -7-Zip catches any exception in internal code and converts it to HRESULT code. -So you don't need to catch CNewException, if you call COM interfaces of 7-Zip. --- diff --git a/lzma/lzmalib.vcproj b/lzma/lzmalib.vcproj index 2736f31a6a..f1e085f3d8 100644 --- a/lzma/lzmalib.vcproj +++ b/lzma/lzmalib.vcproj @@ -1,12 +1,11 @@ + + @@ -303,10 +306,6 @@ RelativePath=".\C\7zDec.c" > - - @@ -323,10 +322,18 @@ RelativePath=".\C\Bra86.c" > + + + + @@ -347,6 +354,14 @@ RelativePath=".\C\LzmaEnc.c" > + + + + @@ -381,10 +396,18 @@ RelativePath=".\C\Bra.h" > + + + + @@ -409,6 +432,18 @@ RelativePath=".\C\LzmaEnc.h" > + + + + + + diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 6541e9a02d..611a667af1 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -27,7 +27,8 @@ II. Implementation Semantics II.A : Storage and Retrieval of Data ------------------------------------ -No changes. +Any TEXTMAP lump in the described namespaces must be encoded in ISO 8859-1 which +as of this writing is the only character encoding supported by ZDoom. ----------------------------------- II.B : Storage Within Archive Files @@ -202,7 +203,18 @@ Note: All fields default to false unless mentioned otherwise. // 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 - moreids = ; // Additional sector IDs/tags, specified as a space separated list of numbers (e.g. "2 666 1003 4505") + moreids = ; // Additional sector IDs/tags, specified as a space separated list of numbers (e.g. "2 666 1003 4505") + damageamount = ; // Amount of damage inflicted by this sector, default = 0. If this is 0, all other damage properties will be ignored. + // Setting damage through these properties will override any damage set through 'special'. + // Setting damageamount to a negative value will create a healing sector. + damagetype = ; // Damage type for sector damage, Default = "None". (generic damage) + damageinterval = ; // Interval in tics between damage application, default = 32. + leakiness = ; // Probability of leaking through radiation suit (0 = never, 256 = always), default = 0. + damageterraineffect = ; // Will spawn a terrain splash when damage is inflicted. Default = false. + damagehazard = ; // Changes damage model to Strife's delayed damage for the given sector. Default = false. + floorterrain = ; // Sets the terrain for the sector's floor. Default = 'use the flat texture's terrain definition.' + ceilingterrain = ; // Sets the terrain for the sector's ceiling. Default = 'use the flat texture's terrain definition.' + * Note about dropactors @@ -237,6 +249,7 @@ Note: All fields default to false unless mentioned otherwise. scalex = ; // Vertical scaling on thing. Default = 0 (ignored). scaley = ; // Horizontal scaling on thing. Default = 0 (ignored). scale = ; // Vertical and horizontal scaling on thing. Default = 0 (ignored). + floatbobphase = ; // Sets the thing's floatbobphase. Valid phase values are 0-63. Default = -1 (use actor class default). * Note about arg0str @@ -375,6 +388,10 @@ Changed language describing the DIALOGUE lump to mention USDF as an option. 1.25 19.04.2015 Added 'moreids' for linedefs and sectors. +1.26 05.01.2016 +added clarification about character encoding +added sector damage properties. + =============================================================================== EOF =============================================================================== diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0bbdebcc3..ec211fe01e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,6 +187,7 @@ else( WIN32 ) if( GTK2_FOUND ) set( ZDOOM_LIBS ${ZDOOM_LIBS} ${GTK2_LIBRARIES} ) include_directories( ${GTK2_INCLUDE_DIRS} ) + link_directories( ${GTK2_LIBRARY_DIRS} ) else( GTK2_FOUND ) set( NO_GTK ON ) endif( GTK2_FOUND ) @@ -496,6 +497,11 @@ if( NOT MSVC ) add_definitions( -D__forceinline=inline ) endif( NOT MSVC ) +# Fix stat in v140_xp (broken in RTM and Update 1 so far) +if( MSVC AND MSVC_VERSION EQUAL 1900 AND CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) + add_definitions( -D_stat64i32=VS14Stat ) +endif( MSVC AND MSVC_VERSION EQUAL 1900 AND CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) + if( UNIX ) CHECK_LIBRARY_EXISTS( rt clock_gettime "" CLOCK_GETTIME_IN_RT ) if( NOT CLOCK_GETTIME_IN_RT ) @@ -585,9 +591,7 @@ set( PLAT_WIN32_SOURCES set( PLAT_POSIX_SOURCES posix/i_cd.cpp posix/i_movie.cpp - posix/i_steam.cpp - posix/i_system.cpp - posix/st_start.cpp ) + posix/i_steam.cpp ) set( PLAT_SDL_SOURCES posix/sdl/crashcatcher.c posix/sdl/hardware.cpp @@ -595,8 +599,10 @@ set( PLAT_SDL_SOURCES posix/sdl/i_input.cpp posix/sdl/i_joystick.cpp posix/sdl/i_main.cpp + posix/sdl/i_system.cpp posix/sdl/i_timer.cpp - posix/sdl/sdlvideo.cpp ) + posix/sdl/sdlvideo.cpp + posix/sdl/st_start.cpp ) set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm posix/osx/zdoom.icns ) @@ -605,8 +611,11 @@ set( PLAT_COCOA_SOURCES posix/cocoa/i_input.mm posix/cocoa/i_joystick.cpp posix/cocoa/i_main.mm + posix/cocoa/i_system.mm posix/cocoa/i_timer.cpp - posix/cocoa/i_video.mm ) + posix/cocoa/i_video.mm + posix/cocoa/st_console.mm + posix/cocoa/st_start.mm ) if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) @@ -909,6 +918,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE nodebuild_extract.cpp nodebuild_gl.cpp nodebuild_utility.cpp + pathexpander.cpp p_3dfloors.cpp p_3dmidtex.cpp p_acs.cpp @@ -989,6 +999,8 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE wi_stuff.cpp zstrformat.cpp zstring.cpp + GuillotineBinPack.cpp + SkylineBinPack.cpp g_doom/a_doommisc.cpp g_heretic/a_hereticmisc.cpp g_hexen/a_hexenmisc.cpp @@ -1086,6 +1098,7 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE sound/music_fluidsynth_mididevice.cpp sound/music_softsynth_mididevice.cpp sound/music_timidity_mididevice.cpp + sound/music_wildmidi_mididevice.cpp sound/music_win_mididevice.cpp sound/oalsound.cpp sound/sndfile_decoder.cpp @@ -1129,6 +1142,11 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE timidity/playmidi.cpp timidity/resample.cpp timidity/timidity.cpp + wildmidi/file_io.cpp + wildmidi/gus_pat.cpp + wildmidi/reverb.cpp + wildmidi/wildmidi_lib.cpp + wildmidi/wm_error.cpp xlat/parse_xlat.cpp fragglescript/t_fspic.cpp fragglescript/t_func.cpp @@ -1243,18 +1261,20 @@ if( APPLE ) LINK_FLAGS "-framework Carbon -framework Cocoa -framework IOKit -framework OpenGL" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" ) - # Fix fmod link so that it can be found in the app bundle. - find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) - find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) - execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" - COMMAND grep "libfmodex.dylib (compat" - COMMAND head -n1 - COMMAND awk "{print $1}" - OUTPUT_VARIABLE FMOD_LINK - OUTPUT_STRIP_TRAILING_WHITESPACE ) - add_custom_command( TARGET zdoom POST_BUILD - COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/libfmodex.dylib "$" - COMMENT "Relinking FMOD Ex" ) + if( NOT NO_FMOD ) + # Fix fmod link so that it can be found in the app bundle. + find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) + execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" + COMMAND grep "libfmodex.dylib (compat" + COMMAND head -n1 + COMMAND awk "{print $1}" + OUTPUT_VARIABLE FMOD_LINK + OUTPUT_STRIP_TRAILING_WHITESPACE ) + add_custom_command( TARGET zdoom POST_BUILD + COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/libfmodex.dylib "$" + COMMENT "Relinking FMOD Ex" ) + endif( NOT NO_FMOD ) endif( APPLE ) source_group("Assembly Files\\ia32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/asm_ia32/.+") @@ -1264,6 +1284,8 @@ source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURC source_group("Audio Files\\OPL Synth\\DOSBox" FILES oplsynth/dosbox/opl.cpp oplsynth/dosbox/opl.h) source_group("Audio Files\\Timidity\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.h$") source_group("Audio Files\\Timidity\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/timidity/.+\\.cpp$") +source_group("Audio Files\\WildMidi\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.h$") +source_group("Audio Files\\WildMidi\\Source" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/wildmidi/.+\\.cpp$") source_group("Decorate++" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/thingdef/.+") source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fragglescript/.+") source_group("Games\\Doom Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_doom/.+") diff --git a/src/GuillotineBinPack.cpp b/src/GuillotineBinPack.cpp new file mode 100644 index 0000000000..5efbbf8a18 --- /dev/null +++ b/src/GuillotineBinPack.cpp @@ -0,0 +1,634 @@ +/** @file GuillotineBinPack.cpp + @author Jukka Jylänki + + @brief Implements different bin packer algorithms that use the GUILLOTINE data structure. + + This work is released to Public Domain, do whatever you want with it. +*/ + +#include +#include +#include "templates.h" +#include "GuillotineBinPack.h" + +using namespace std; + +GuillotineBinPack::GuillotineBinPack() +:binWidth(0), +binHeight(0) +{ +} + +GuillotineBinPack::GuillotineBinPack(int width, int height) +{ + Init(width, height); +} + +void GuillotineBinPack::Init(int width, int height) +{ + binWidth = width; + binHeight = height; + +#ifdef _DEBUG + disjointRects.Clear(); +#endif + + // Clear any memory of previously packed rectangles. + usedRectangles.Clear(); + + // We start with a single big free rectangle that spans the whole bin. + Rect n; + n.x = 0; + n.y = 0; + n.width = width; + n.height = height; + + freeRectangles.Clear(); + freeRectangles.Push(n); +} + +void GuillotineBinPack::Insert(TArray &rects, TArray &dst, bool merge, + FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) +{ + dst.Clear(); + + // Remember variables about the best packing choice we have made so far during the iteration process. + int bestFreeRect = 0; + int bestRect = 0; + bool bestFlipped = false; + + // Pack rectangles one at a time until we have cleared the rects array of all rectangles. + // rects will get destroyed in the process. + while(rects.Size() > 0) + { + // Stores the penalty score of the best rectangle placement - bigger=worse, smaller=better. + int bestScore = INT_MAX; + + for(unsigned i = 0; i < freeRectangles.Size(); ++i) + { + for(unsigned j = 0; j < rects.Size(); ++j) + { + // If this rectangle is a perfect match, we pick it instantly. + if (rects[j].width == freeRectangles[i].width && rects[j].height == freeRectangles[i].height) + { + bestFreeRect = i; + bestRect = j; + bestFlipped = false; + bestScore = INT_MIN; + i = freeRectangles.Size(); // Force a jump out of the outer loop as well - we got an instant fit. + break; + } + // If flipping this rectangle is a perfect match, pick that then. + else if (rects[j].height == freeRectangles[i].width && rects[j].width == freeRectangles[i].height) + { + bestFreeRect = i; + bestRect = j; + bestFlipped = true; + bestScore = INT_MIN; + i = freeRectangles.Size(); // Force a jump out of the outer loop as well - we got an instant fit. + break; + } + // Try if we can fit the rectangle upright. + else if (rects[j].width <= freeRectangles[i].width && rects[j].height <= freeRectangles[i].height) + { + int score = ScoreByHeuristic(rects[j].width, rects[j].height, freeRectangles[i], rectChoice); + if (score < bestScore) + { + bestFreeRect = i; + bestRect = j; + bestFlipped = false; + bestScore = score; + } + } + // If not, then perhaps flipping sideways will make it fit? + else if (rects[j].height <= freeRectangles[i].width && rects[j].width <= freeRectangles[i].height) + { + int score = ScoreByHeuristic(rects[j].height, rects[j].width, freeRectangles[i], rectChoice); + if (score < bestScore) + { + bestFreeRect = i; + bestRect = j; + bestFlipped = true; + bestScore = score; + } + } + } + } + + // If we didn't manage to find any rectangle to pack, abort. + if (bestScore == INT_MAX) + return; + + // Otherwise, we're good to go and do the actual packing. + Rect newNode; + newNode.x = freeRectangles[bestFreeRect].x; + newNode.y = freeRectangles[bestFreeRect].y; + newNode.width = rects[bestRect].width; + newNode.height = rects[bestRect].height; + + if (bestFlipped) + std::swap(newNode.width, newNode.height); + + // Remove the free space we lost in the bin. + SplitFreeRectByHeuristic(freeRectangles[bestFreeRect], newNode, splitMethod); + freeRectangles.Delete(bestFreeRect); + + // Remove the rectangle we just packed from the input list. + rects.Delete(bestRect); + + // Perform a Rectangle Merge step if desired. + if (merge) + MergeFreeList(); + + // Remember the new used rectangle. + usedRectangles.Push(newNode); + + // Check that we're really producing correct packings here. + assert(disjointRects.Add(newNode) == true); + } +} + +/// @return True if r fits inside freeRect (possibly rotated). +bool Fits(const RectSize &r, const Rect &freeRect) +{ + return (r.width <= freeRect.width && r.height <= freeRect.height) || + (r.height <= freeRect.width && r.width <= freeRect.height); +} + +/// @return True if r fits perfectly inside freeRect, i.e. the leftover area is 0. +bool FitsPerfectly(const RectSize &r, const Rect &freeRect) +{ + return (r.width == freeRect.width && r.height == freeRect.height) || + (r.height == freeRect.width && r.width == freeRect.height); +} + +/* +// A helper function for GUILLOTINE-MAXFITTING. Counts how many rectangles fit into the given rectangle +// after it has been split. +void CountNumFitting(const Rect &freeRect, int width, int height, const TArray &rects, + int usedRectIndex, bool splitHorizontal, int &score1, int &score2) +{ + const int w = freeRect.width - width; + const int h = freeRect.height - height; + + Rect bottom; + bottom.x = freeRect.x; + bottom.y = freeRect.y + height; + bottom.height = h; + + Rect right; + right.x = freeRect.x + width; + right.y = freeRect.y; + right.width = w; + + if (splitHorizontal) + { + bottom.width = freeRect.width; + right.height = height; + } + else // Split vertically + { + bottom.width = width; + right.height = freeRect.height; + } + + int fitBottom = 0; + int fitRight = 0; + for(size_t i = 0; i < rects.size(); ++i) + if (i != usedRectIndex) + { + if (FitsPerfectly(rects[i], bottom)) + fitBottom |= 0x10000000; + if (FitsPerfectly(rects[i], right)) + fitRight |= 0x10000000; + + if (Fits(rects[i], bottom)) + ++fitBottom; + if (Fits(rects[i], right)) + ++fitRight; + } + + score1 = min(fitBottom, fitRight); + score2 = max(fitBottom, fitRight); +} +*/ +/* +// Implements GUILLOTINE-MAXFITTING, an experimental heuristic that's really cool but didn't quite work in practice. +void GuillotineBinPack::InsertMaxFitting(TArray &rects, TArray &dst, bool merge, + FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) +{ + dst.clear(); + int bestRect = 0; + bool bestFlipped = false; + bool bestSplitHorizontal = false; + + // Pick rectangles one at a time and pack the one that leaves the most choices still open. + while(rects.size() > 0 && freeRectangles.size() > 0) + { + int bestScore1 = -1; + int bestScore2 = -1; + + ///\todo Different sort predicates. + clb::sort::QuickSort(&freeRectangles[0], freeRectangles.size(), CompareRectShortSide); + + Rect &freeRect = freeRectangles[0]; + + for(size_t j = 0; j < rects.size(); ++j) + { + int score1; + int score2; + + if (rects[j].width == freeRect.width && rects[j].height == freeRect.height) + { + bestRect = j; + bestFlipped = false; + bestScore1 = bestScore2 = std::numeric_limits::max(); + break; + } + else if (rects[j].width <= freeRect.width && rects[j].height <= freeRect.height) + { + CountNumFitting(freeRect, rects[j].width, rects[j].height, rects, j, false, score1, score2); + + if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2)) + { + bestRect = j; + bestScore1 = score1; + bestScore2 = score2; + bestFlipped = false; + bestSplitHorizontal = false; + } + + CountNumFitting(freeRect, rects[j].width, rects[j].height, rects, j, true, score1, score2); + + if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2)) + { + bestRect = j; + bestScore1 = score1; + bestScore2 = score2; + bestFlipped = false; + bestSplitHorizontal = true; + } + } + + if (rects[j].height == freeRect.width && rects[j].width == freeRect.height) + { + bestRect = j; + bestFlipped = true; + bestScore1 = bestScore2 = std::numeric_limits::max(); + break; + } + else if (rects[j].height <= freeRect.width && rects[j].width <= freeRect.height) + { + CountNumFitting(freeRect, rects[j].height, rects[j].width, rects, j, false, score1, score2); + + if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2)) + { + bestRect = j; + bestScore1 = score1; + bestScore2 = score2; + bestFlipped = true; + bestSplitHorizontal = false; + } + + CountNumFitting(freeRect, rects[j].height, rects[j].width, rects, j, true, score1, score2); + + if (score1 > bestScore1 || (score1 == bestScore1 && score2 > bestScore2)) + { + bestRect = j; + bestScore1 = score1; + bestScore2 = score2; + bestFlipped = true; + bestSplitHorizontal = true; + } + } + } + + if (bestScore1 >= 0) + { + Rect newNode; + newNode.x = freeRect.x; + newNode.y = freeRect.y; + newNode.width = rects[bestRect].width; + newNode.height = rects[bestRect].height; + if (bestFlipped) + std::swap(newNode.width, newNode.height); + + assert(disjointRects.Disjoint(newNode)); + SplitFreeRectAlongAxis(freeRect, newNode, bestSplitHorizontal); + + rects.erase(rects.begin() + bestRect); + + if (merge) + MergeFreeList(); + + usedRectangles.push_back(newNode); +#ifdef _DEBUG + disjointRects.Add(newNode); +#endif + } + + freeRectangles.erase(freeRectangles.begin()); + } +} +*/ + +Rect GuillotineBinPack::Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice, + GuillotineSplitHeuristic splitMethod) +{ + // Find where to put the new rectangle. + int freeNodeIndex = 0; + Rect newRect = FindPositionForNewNode(width, height, rectChoice, &freeNodeIndex); + + // Abort if we didn't have enough space in the bin. + if (newRect.height == 0) + return newRect; + + // Remove the space that was just consumed by the new rectangle. + SplitFreeRectByHeuristic(freeRectangles[freeNodeIndex], newRect, splitMethod); + freeRectangles.Delete(freeNodeIndex); + + // Perform a Rectangle Merge step if desired. + if (merge) + MergeFreeList(); + + // Remember the new used rectangle. + usedRectangles.Push(newRect); + + // Check that we're really producing correct packings here. + assert(disjointRects.Add(newRect) == true); + + return newRect; +} + +/// Computes the ratio of used surface area to the total bin area. +float GuillotineBinPack::Occupancy() const +{ + ///\todo The occupancy rate could be cached/tracked incrementally instead + /// of looping through the list of packed rectangles here. + unsigned long usedSurfaceArea = 0; + for(unsigned i = 0; i < usedRectangles.Size(); ++i) + usedSurfaceArea += usedRectangles[i].width * usedRectangles[i].height; + + return (float)usedSurfaceArea / (binWidth * binHeight); +} + +/// Returns the heuristic score value for placing a rectangle of size width*height into freeRect. Does not try to rotate. +int GuillotineBinPack::ScoreByHeuristic(int width, int height, const Rect &freeRect, FreeRectChoiceHeuristic rectChoice) +{ + switch(rectChoice) + { + case RectBestAreaFit: return ScoreBestAreaFit(width, height, freeRect); + case RectBestShortSideFit: return ScoreBestShortSideFit(width, height, freeRect); + case RectBestLongSideFit: return ScoreBestLongSideFit(width, height, freeRect); + case RectWorstAreaFit: return ScoreWorstAreaFit(width, height, freeRect); + case RectWorstShortSideFit: return ScoreWorstShortSideFit(width, height, freeRect); + case RectWorstLongSideFit: return ScoreWorstLongSideFit(width, height, freeRect); + default: assert(false); return INT_MAX; + } +} + +int GuillotineBinPack::ScoreBestAreaFit(int width, int height, const Rect &freeRect) +{ + return freeRect.width * freeRect.height - width * height; +} + +int GuillotineBinPack::ScoreBestShortSideFit(int width, int height, const Rect &freeRect) +{ + int leftoverHoriz = abs(freeRect.width - width); + int leftoverVert = abs(freeRect.height - height); + int leftover = MIN(leftoverHoriz, leftoverVert); + return leftover; +} + +int GuillotineBinPack::ScoreBestLongSideFit(int width, int height, const Rect &freeRect) +{ + int leftoverHoriz = abs(freeRect.width - width); + int leftoverVert = abs(freeRect.height - height); + int leftover = MAX(leftoverHoriz, leftoverVert); + return leftover; +} + +int GuillotineBinPack::ScoreWorstAreaFit(int width, int height, const Rect &freeRect) +{ + return -ScoreBestAreaFit(width, height, freeRect); +} + +int GuillotineBinPack::ScoreWorstShortSideFit(int width, int height, const Rect &freeRect) +{ + return -ScoreBestShortSideFit(width, height, freeRect); +} + +int GuillotineBinPack::ScoreWorstLongSideFit(int width, int height, const Rect &freeRect) +{ + return -ScoreBestLongSideFit(width, height, freeRect); +} + +Rect GuillotineBinPack::FindPositionForNewNode(int width, int height, FreeRectChoiceHeuristic rectChoice, int *nodeIndex) +{ + Rect bestNode; + memset(&bestNode, 0, sizeof(Rect)); + + int bestScore = INT_MAX; + + /// Try each free rectangle to find the best one for placement. + for(unsigned i = 0; i < freeRectangles.Size(); ++i) + { + // If this is a perfect fit upright, choose it immediately. + if (width == freeRectangles[i].width && height == freeRectangles[i].height) + { + bestNode.x = freeRectangles[i].x; + bestNode.y = freeRectangles[i].y; + bestNode.width = width; + bestNode.height = height; + bestScore = INT_MIN; + *nodeIndex = i; + assert(disjointRects.Disjoint(bestNode)); + break; + } + // If this is a perfect fit sideways, choose it. +/* else if (height == freeRectangles[i].width && width == freeRectangles[i].height) + { + bestNode.x = freeRectangles[i].x; + bestNode.y = freeRectangles[i].y; + bestNode.width = height; + bestNode.height = width; + bestScore = INT_MIN; + *nodeIndex = i; + assert(disjointRects.Disjoint(bestNode)); + break; + } +*/ // Does the rectangle fit upright? + else if (width <= freeRectangles[i].width && height <= freeRectangles[i].height) + { + int score = ScoreByHeuristic(width, height, freeRectangles[i], rectChoice); + + if (score < bestScore) + { + bestNode.x = freeRectangles[i].x; + bestNode.y = freeRectangles[i].y; + bestNode.width = width; + bestNode.height = height; + bestScore = score; + *nodeIndex = i; + assert(disjointRects.Disjoint(bestNode)); + } + } + // Does the rectangle fit sideways? +/* else if (height <= freeRectangles[i].width && width <= freeRectangles[i].height) + { + int score = ScoreByHeuristic(height, width, freeRectangles[i], rectChoice); + + if (score < bestScore) + { + bestNode.x = freeRectangles[i].x; + bestNode.y = freeRectangles[i].y; + bestNode.width = height; + bestNode.height = width; + bestScore = score; + *nodeIndex = i; + assert(disjointRects.Disjoint(bestNode)); + } + } +*/ } + return bestNode; +} + +void GuillotineBinPack::SplitFreeRectByHeuristic(const Rect &freeRect, const Rect &placedRect, GuillotineSplitHeuristic method) +{ + // Compute the lengths of the leftover area. + const int w = freeRect.width - placedRect.width; + const int h = freeRect.height - placedRect.height; + + // Placing placedRect into freeRect results in an L-shaped free area, which must be split into + // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line. + // We have two choices: horizontal or vertical. + + // Use the given heuristic to decide which choice to make. + + bool splitHorizontal; + switch(method) + { + case SplitShorterLeftoverAxis: + // Split along the shorter leftover axis. + splitHorizontal = (w <= h); + break; + case SplitLongerLeftoverAxis: + // Split along the longer leftover axis. + splitHorizontal = (w > h); + break; + case SplitMinimizeArea: + // Maximize the larger area == minimize the smaller area. + // Tries to make the single bigger rectangle. + splitHorizontal = (placedRect.width * h > w * placedRect.height); + break; + case SplitMaximizeArea: + // Maximize the smaller area == minimize the larger area. + // Tries to make the rectangles more even-sized. + splitHorizontal = (placedRect.width * h <= w * placedRect.height); + break; + case SplitShorterAxis: + // Split along the shorter total axis. + splitHorizontal = (freeRect.width <= freeRect.height); + break; + case SplitLongerAxis: + // Split along the longer total axis. + splitHorizontal = (freeRect.width > freeRect.height); + break; + default: + splitHorizontal = true; + assert(false); + } + + // Perform the actual split. + SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal); +} + +/// This function will add the two generated rectangles into the freeRectangles array. The caller is expected to +/// remove the original rectangle from the freeRectangles array after that. +void GuillotineBinPack::SplitFreeRectAlongAxis(const Rect &freeRect, const Rect &placedRect, bool splitHorizontal) +{ + // Form the two new rectangles. + Rect bottom; + bottom.x = freeRect.x; + bottom.y = freeRect.y + placedRect.height; + bottom.height = freeRect.height - placedRect.height; + + Rect right; + right.x = freeRect.x + placedRect.width; + right.y = freeRect.y; + right.width = freeRect.width - placedRect.width; + + if (splitHorizontal) + { + bottom.width = freeRect.width; + right.height = placedRect.height; + } + else // Split vertically + { + bottom.width = placedRect.width; + right.height = freeRect.height; + } + + // Add the new rectangles into the free rectangle pool if they weren't degenerate. + if (bottom.width > 0 && bottom.height > 0) + freeRectangles.Push(bottom); + if (right.width > 0 && right.height > 0) + freeRectangles.Push(right); + + assert(disjointRects.Disjoint(bottom)); + assert(disjointRects.Disjoint(right)); +} + +void GuillotineBinPack::MergeFreeList() +{ +#ifdef _DEBUG + DisjointRectCollection test; + for(unsigned i = 0; i < freeRectangles.Size(); ++i) + assert(test.Add(freeRectangles[i]) == true); +#endif + + // Do a Theta(n^2) loop to see if any pair of free rectangles could me merged into one. + // Note that we miss any opportunities to merge three rectangles into one. (should call this function again to detect that) + for(unsigned i = 0; i < freeRectangles.Size(); ++i) + for(unsigned j = i+1; j < freeRectangles.Size(); ++j) + { + if (freeRectangles[i].width == freeRectangles[j].width && freeRectangles[i].x == freeRectangles[j].x) + { + if (freeRectangles[i].y == freeRectangles[j].y + freeRectangles[j].height) + { + freeRectangles[i].y -= freeRectangles[j].height; + freeRectangles[i].height += freeRectangles[j].height; + freeRectangles.Delete(j); + --j; + } + else if (freeRectangles[i].y + freeRectangles[i].height == freeRectangles[j].y) + { + freeRectangles[i].height += freeRectangles[j].height; + freeRectangles.Delete(j); + --j; + } + } + else if (freeRectangles[i].height == freeRectangles[j].height && freeRectangles[i].y == freeRectangles[j].y) + { + if (freeRectangles[i].x == freeRectangles[j].x + freeRectangles[j].width) + { + freeRectangles[i].x -= freeRectangles[j].width; + freeRectangles[i].width += freeRectangles[j].width; + freeRectangles.Delete(j); + --j; + } + else if (freeRectangles[i].x + freeRectangles[i].width == freeRectangles[j].x) + { + freeRectangles[i].width += freeRectangles[j].width; + freeRectangles.Delete(j); + --j; + } + } + } + +#ifdef _DEBUG + test.Clear(); + for(unsigned i = 0; i < freeRectangles.Size(); ++i) + assert(test.Add(freeRectangles[i]) == true); +#endif +} diff --git a/src/GuillotineBinPack.h b/src/GuillotineBinPack.h new file mode 100644 index 0000000000..54cd77a9ea --- /dev/null +++ b/src/GuillotineBinPack.h @@ -0,0 +1,135 @@ +/** @file GuillotineBinPack.h + @author Jukka Jylänki + + @brief Implements different bin packer algorithms that use the GUILLOTINE data structure. + + This work is released to Public Domain, do whatever you want with it. +*/ +#pragma once + +#include "tarray.h" + +#include "Rect.h" + +/** GuillotineBinPack implements different variants of bin packer algorithms that use the GUILLOTINE data structure + to keep track of the free space of the bin where rectangles may be placed. */ +class GuillotineBinPack +{ +public: + /// The initial bin size will be (0,0). Call Init to set the bin size. + GuillotineBinPack(); + + /// Initializes a new bin of the given size. + GuillotineBinPack(int width, int height); + + /// (Re)initializes the packer to an empty bin of width x height units. Call whenever + /// you need to restart with a new bin. + void Init(int width, int height); + + /// Specifies the different choice heuristics that can be used when deciding which of the free subrectangles + /// to place the to-be-packed rectangle into. + enum FreeRectChoiceHeuristic + { + RectBestAreaFit, ///< -BAF + RectBestShortSideFit, ///< -BSSF + RectBestLongSideFit, ///< -BLSF + RectWorstAreaFit, ///< -WAF + RectWorstShortSideFit, ///< -WSSF + RectWorstLongSideFit ///< -WLSF + }; + + /// Specifies the different choice heuristics that can be used when the packer needs to decide whether to + /// subdivide the remaining free space in horizontal or vertical direction. + enum GuillotineSplitHeuristic + { + SplitShorterLeftoverAxis, ///< -SLAS + SplitLongerLeftoverAxis, ///< -LLAS + SplitMinimizeArea, ///< -MINAS, Try to make a single big rectangle at the expense of making the other small. + SplitMaximizeArea, ///< -MAXAS, Try to make both remaining rectangles as even-sized as possible. + SplitShorterAxis, ///< -SAS + SplitLongerAxis ///< -LAS + }; + + /// Inserts a single rectangle into the bin. The packer might rotate the rectangle, in which case the returned + /// struct will have the width and height values swapped. + /// @param merge If true, performs free Rectangle Merge procedure after packing the new rectangle. This procedure + /// tries to defragment the list of disjoint free rectangles to improve packing performance, but also takes up + /// some extra time. + /// @param rectChoice The free rectangle choice heuristic rule to use. + /// @param splitMethod The free rectangle split heuristic rule to use. + Rect Insert(int width, int height, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); + + /// Inserts a list of rectangles into the bin. + /// @param rects The list of rectangles to add. This list will be destroyed in the packing process. + /// @param dst The outputted list of rectangles. Note that the indices will not correspond to the input indices. + /// @param merge If true, performs Rectangle Merge operations during the packing process. + /// @param rectChoice The free rectangle choice heuristic rule to use. + /// @param splitMethod The free rectangle split heuristic rule to use. + void Insert(TArray &rects, TArray &dst, bool merge, + FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); + +// Implements GUILLOTINE-MAXFITTING, an experimental heuristic that's really cool but didn't quite work in practice. +// void InsertMaxFitting(TArray &rects, TArray &dst, bool merge, +// FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod); + + /// Computes the ratio of used/total surface area. 0.00 means no space is yet used, 1.00 means the whole bin is used. + float Occupancy() const; + + /// Returns the internal list of disjoint rectangles that track the free area of the bin. You may alter this vector + /// any way desired, as long as the end result still is a list of disjoint rectangles. + TArray &GetFreeRectangles() { return freeRectangles; } + + /// Returns the list of packed rectangles. You may alter this vector at will, for example, you can move a Rect from + /// this list to the Free Rectangles list to free up space on-the-fly, but notice that this causes fragmentation. + TArray &GetUsedRectangles() { return usedRectangles; } + + /// Performs a Rectangle Merge operation. This procedure looks for adjacent free rectangles and merges them if they + /// can be represented with a single rectangle. Takes up Theta(|freeRectangles|^2) time. + void MergeFreeList(); + +#ifdef _DEBUG + void DelDisjoint(const Rect &r) { disjointRects.Del(r); } +#endif + +private: + int binWidth; + int binHeight; + + /// Stores a list of all the rectangles that we have packed so far. This is used only to compute the Occupancy ratio, + /// so if you want to have the packer consume less memory, this can be removed. + TArray usedRectangles; + + /// Stores a list of rectangles that represents the free area of the bin. This rectangles in this list are disjoint. + TArray freeRectangles; + +#ifdef _DEBUG + /// Used to track that the packer produces proper packings. + DisjointRectCollection disjointRects; +#endif + + /// Goes through the list of free rectangles and finds the best one to place a rectangle of given size into. + /// Running time is Theta(|freeRectangles|). + /// @param nodeIndex [out] The index of the free rectangle in the freeRectangles array into which the new + /// rect was placed. + /// @return A Rect structure that represents the placement of the new rect into the best free rectangle. + Rect FindPositionForNewNode(int width, int height, FreeRectChoiceHeuristic rectChoice, int *nodeIndex); + + static int ScoreByHeuristic(int width, int height, const Rect &freeRect, FreeRectChoiceHeuristic rectChoice); + // The following functions compute (penalty) score values if a rect of the given size was placed into the + // given free rectangle. In these score values, smaller is better. + + static int ScoreBestAreaFit(int width, int height, const Rect &freeRect); + static int ScoreBestShortSideFit(int width, int height, const Rect &freeRect); + static int ScoreBestLongSideFit(int width, int height, const Rect &freeRect); + + static int ScoreWorstAreaFit(int width, int height, const Rect &freeRect); + static int ScoreWorstShortSideFit(int width, int height, const Rect &freeRect); + static int ScoreWorstLongSideFit(int width, int height, const Rect &freeRect); + + /// Splits the given L-shaped free rectangle into two new free rectangles after placedRect has been placed into it. + /// Determines the split axis by using the given heuristic. + void SplitFreeRectByHeuristic(const Rect &freeRect, const Rect &placedRect, GuillotineSplitHeuristic method); + + /// Splits the given L-shaped free rectangle into two new free rectangles along the given fixed split axis. + void SplitFreeRectAlongAxis(const Rect &freeRect, const Rect &placedRect, bool splitHorizontal); +}; diff --git a/src/Rect.h b/src/Rect.h new file mode 100644 index 0000000000..8b7ba1e2a6 --- /dev/null +++ b/src/Rect.h @@ -0,0 +1,94 @@ +/** @file Rect.h + @author Jukka Jylänki + + This work is released to Public Domain, do whatever you want with it. +*/ +#pragma once + +#include + +struct RectSize +{ + int width; + int height; +}; + +struct Rect +{ + int x; + int y; + int width; + int height; +}; + +/// Performs a lexicographic compare on (rect short side, rect long side). +/// @return -1 if the smaller side of a is shorter than the smaller side of b, 1 if the other way around. +/// If they are equal, the larger side length is used as a tie-breaker. +/// If the rectangles are of same size, returns 0. +int CompareRectShortSide(const Rect &a, const Rect &b); + +/// Performs a lexicographic compare on (x, y, width, height). +int NodeSortCmp(const Rect &a, const Rect &b); + +/// Returns true if a is contained in b. +bool IsContainedIn(const Rect &a, const Rect &b); + +#ifdef _DEBUG +class DisjointRectCollection +{ +public: + TArray rects; + + bool Add(const Rect &r) + { + // Degenerate rectangles are ignored. + if (r.width == 0 || r.height == 0) + return true; + + if (!Disjoint(r)) + return false; + rects.Push(r); + return true; + } + + bool Del(const Rect &r) + { + for(unsigned i = 0; i < rects.Size(); ++i) + { + if(r.x == rects[i].x && r.y == rects[i].y && r.width == rects[i].width && r.height == rects[i].height) + { + rects.Delete(i); + return true; + } + } + return false; + } + + void Clear() + { + rects.Clear(); + } + + bool Disjoint(const Rect &r) const + { + // Degenerate rectangles are ignored. + if (r.width == 0 || r.height == 0) + return true; + + for(unsigned i = 0; i < rects.Size(); ++i) + if (!Disjoint(rects[i], r)) + return false; + return true; + } + + static bool Disjoint(const Rect &a, const Rect &b) + { + if (a.x + a.width <= b.x || + b.x + b.width <= a.x || + a.y + a.height <= b.y || + b.y + b.height <= a.y) + return true; + return false; + } +}; +#endif diff --git a/src/SkylineBinPack.cpp b/src/SkylineBinPack.cpp new file mode 100644 index 0000000000..cb0be54e13 --- /dev/null +++ b/src/SkylineBinPack.cpp @@ -0,0 +1,398 @@ +/** @file SkylineBinPack.cpp + @author Jukka Jylänki + + @brief Implements different bin packer algorithms that use the SKYLINE data structure. + + This work is released to Public Domain, do whatever you want with it. +*/ + +#include +#include +#include "templates.h" + +#include "SkylineBinPack.h" + +using namespace std; + +SkylineBinPack::SkylineBinPack() +:binWidth(0), +binHeight(0) +{ +} + +SkylineBinPack::SkylineBinPack(int width, int height, bool useWasteMap) +{ + Init(width, height, useWasteMap); +} + +void SkylineBinPack::Init(int width, int height, bool useWasteMap_) +{ + binWidth = width; + binHeight = height; + + useWasteMap = useWasteMap_; + +#ifdef _DEBUG + disjointRects.Clear(); +#endif + + usedSurfaceArea = 0; + skyLine.Clear(); + SkylineNode node; + node.x = 0; + node.y = 0; + node.width = binWidth; + skyLine.Push(node); + + if (useWasteMap) + { + wasteMap.Init(width, height); + wasteMap.GetFreeRectangles().Clear(); + } +} + +void SkylineBinPack::Insert(TArray &rects, TArray &dst) +{ + dst.Clear(); + + while(rects.Size() > 0) + { + Rect bestNode; + int bestScore1 = INT_MAX; + int bestScore2 = INT_MAX; + int bestSkylineIndex = -1; + int bestRectIndex = -1; + for(unsigned i = 0; i < rects.Size(); ++i) + { + Rect newNode; + int score1; + int score2; + int index; + newNode = FindPositionForNewNodeMinWaste(rects[i].width, rects[i].height, score2, score1, index); + assert(disjointRects.Disjoint(newNode)); + if (newNode.height != 0) + { + if (score1 < bestScore1 || (score1 == bestScore1 && score2 < bestScore2)) + { + bestNode = newNode; + bestScore1 = score1; + bestScore2 = score2; + bestSkylineIndex = index; + bestRectIndex = i; + } + } + } + + if (bestRectIndex == -1) + return; + + // Perform the actual packing. + assert(disjointRects.Disjoint(bestNode)); +#ifdef _DEBUG + disjointRects.Add(bestNode); +#endif + AddSkylineLevel(bestSkylineIndex, bestNode); + usedSurfaceArea += rects[bestRectIndex].width * rects[bestRectIndex].height; + rects.Delete(bestRectIndex); + dst.Push(bestNode); + } +} + +Rect SkylineBinPack::Insert(int width, int height) +{ + // First try to pack this rectangle into the waste map, if it fits. + Rect node = wasteMap.Insert(width, height, true, GuillotineBinPack::RectBestShortSideFit, + GuillotineBinPack::SplitMaximizeArea); + assert(disjointRects.Disjoint(node)); + + if (node.height != 0) + { + Rect newNode; + newNode.x = node.x; + newNode.y = node.y; + newNode.width = node.width; + newNode.height = node.height; + usedSurfaceArea += width * height; + assert(disjointRects.Disjoint(newNode)); +#ifdef _DEBUG + disjointRects.Add(newNode); +#endif + return newNode; + } + + return InsertBottomLeft(width, height); +} + +bool SkylineBinPack::RectangleFits(int skylineNodeIndex, int width, int height, int &y) const +{ + int x = skyLine[skylineNodeIndex].x; + if (x + width > binWidth) + return false; + int widthLeft = width; + int i = skylineNodeIndex; + y = skyLine[skylineNodeIndex].y; + while(widthLeft > 0) + { + y = MAX(y, skyLine[i].y); + if (y + height > binHeight) + return false; + widthLeft -= skyLine[i].width; + ++i; + assert(i < (int)skyLine.Size() || widthLeft <= 0); + } + return true; +} + +int SkylineBinPack::ComputeWastedArea(int skylineNodeIndex, int width, int height, int y) const +{ + int wastedArea = 0; + const int rectLeft = skyLine[skylineNodeIndex].x; + const int rectRight = rectLeft + width; + for(; skylineNodeIndex < (int)skyLine.Size() && skyLine[skylineNodeIndex].x < rectRight; ++skylineNodeIndex) + { + if (skyLine[skylineNodeIndex].x >= rectRight || skyLine[skylineNodeIndex].x + skyLine[skylineNodeIndex].width <= rectLeft) + break; + + int leftSide = skyLine[skylineNodeIndex].x; + int rightSide = MIN(rectRight, leftSide + skyLine[skylineNodeIndex].width); + assert(y >= skyLine[skylineNodeIndex].y); + wastedArea += (rightSide - leftSide) * (y - skyLine[skylineNodeIndex].y); + } + return wastedArea; +} + +bool SkylineBinPack::RectangleFits(int skylineNodeIndex, int width, int height, int &y, int &wastedArea) const +{ + bool fits = RectangleFits(skylineNodeIndex, width, height, y); + if (fits) + wastedArea = ComputeWastedArea(skylineNodeIndex, width, height, y); + + return fits; +} + +void SkylineBinPack::AddWasteMapArea(int skylineNodeIndex, int width, int height, int y) +{ + int wastedArea = 0; + const int rectLeft = skyLine[skylineNodeIndex].x; + const int rectRight = rectLeft + width; + for(; skylineNodeIndex < (int)skyLine.Size() && skyLine[skylineNodeIndex].x < rectRight; ++skylineNodeIndex) + { + if (skyLine[skylineNodeIndex].x >= rectRight || skyLine[skylineNodeIndex].x + skyLine[skylineNodeIndex].width <= rectLeft) + break; + + int leftSide = skyLine[skylineNodeIndex].x; + int rightSide = MIN(rectRight, leftSide + skyLine[skylineNodeIndex].width); + assert(y >= skyLine[skylineNodeIndex].y); + + Rect waste; + waste.x = leftSide; + waste.y = skyLine[skylineNodeIndex].y; + waste.width = rightSide - leftSide; + waste.height = y - skyLine[skylineNodeIndex].y; + + assert(disjointRects.Disjoint(waste)); + wasteMap.GetFreeRectangles().Push(waste); + } +} + +void SkylineBinPack::AddWaste(const Rect &waste) +{ + wasteMap.GetFreeRectangles().Push(waste); +#ifdef _DEBUG + disjointRects.Del(waste); + wasteMap.DelDisjoint(waste); +#endif +} + +void SkylineBinPack::AddSkylineLevel(int skylineNodeIndex, const Rect &rect) +{ + // First track all wasted areas and mark them into the waste map if we're using one. + if (useWasteMap) + AddWasteMapArea(skylineNodeIndex, rect.width, rect.height, rect.y); + + SkylineNode newNode; + newNode.x = rect.x; + newNode.y = rect.y + rect.height; + newNode.width = rect.width; + skyLine.Insert(skylineNodeIndex, newNode); + + assert(newNode.x + newNode.width <= binWidth); + assert(newNode.y <= binHeight); + + for(unsigned i = skylineNodeIndex+1; i < skyLine.Size(); ++i) + { + assert(skyLine[i-1].x <= skyLine[i].x); + + if (skyLine[i].x < skyLine[i-1].x + skyLine[i-1].width) + { + int shrink = skyLine[i-1].x + skyLine[i-1].width - skyLine[i].x; + + skyLine[i].x += shrink; + skyLine[i].width -= shrink; + + if (skyLine[i].width <= 0) + { + skyLine.Delete(i); + --i; + } + else + break; + } + else + break; + } + MergeSkylines(); +} + +void SkylineBinPack::MergeSkylines() +{ + for(unsigned i = 0; i < skyLine.Size()-1; ++i) + if (skyLine[i].y == skyLine[i+1].y) + { + skyLine[i].width += skyLine[i+1].width; + skyLine.Delete(i+1); + --i; + } +} + +Rect SkylineBinPack::InsertBottomLeft(int width, int height) +{ + int bestHeight; + int bestWidth; + int bestIndex; + Rect newNode = FindPositionForNewNodeBottomLeft(width, height, bestHeight, bestWidth, bestIndex); + + if (bestIndex != -1) + { + assert(disjointRects.Disjoint(newNode)); + // Perform the actual packing. + AddSkylineLevel(bestIndex, newNode); + + usedSurfaceArea += width * height; +#ifdef _DEBUG + disjointRects.Add(newNode); +#endif + } + else + memset(&newNode, 0, sizeof(Rect)); + + return newNode; +} + +Rect SkylineBinPack::FindPositionForNewNodeBottomLeft(int width, int height, int &bestHeight, int &bestWidth, int &bestIndex) const +{ + bestHeight = INT_MAX; + bestIndex = -1; + // Used to break ties if there are nodes at the same level. Then pick the narrowest one. + bestWidth = INT_MAX; + Rect newNode = { 0, 0, 0, 0 }; + for(unsigned i = 0; i < skyLine.Size(); ++i) + { + int y; + if (RectangleFits(i, width, height, y)) + { + if (y + height < bestHeight || (y + height == bestHeight && skyLine[i].width < bestWidth)) + { + bestHeight = y + height; + bestIndex = i; + bestWidth = skyLine[i].width; + newNode.x = skyLine[i].x; + newNode.y = y; + newNode.width = width; + newNode.height = height; + assert(disjointRects.Disjoint(newNode)); + } + } +/* if (RectangleFits(i, height, width, y)) + { + if (y + width < bestHeight || (y + width == bestHeight && skyLine[i].width < bestWidth)) + { + bestHeight = y + width; + bestIndex = i; + bestWidth = skyLine[i].width; + newNode.x = skyLine[i].x; + newNode.y = y; + newNode.width = height; + newNode.height = width; + assert(disjointRects.Disjoint(newNode)); + } + } +*/ } + + return newNode; +} + +Rect SkylineBinPack::InsertMinWaste(int width, int height) +{ + int bestHeight; + int bestWastedArea; + int bestIndex; + Rect newNode = FindPositionForNewNodeMinWaste(width, height, bestHeight, bestWastedArea, bestIndex); + + if (bestIndex != -1) + { + assert(disjointRects.Disjoint(newNode)); + // Perform the actual packing. + AddSkylineLevel(bestIndex, newNode); + + usedSurfaceArea += width * height; +#ifdef _DEBUG + disjointRects.Add(newNode); +#endif + } + else + memset(&newNode, 0, sizeof(newNode)); + + return newNode; +} + +Rect SkylineBinPack::FindPositionForNewNodeMinWaste(int width, int height, int &bestHeight, int &bestWastedArea, int &bestIndex) const +{ + bestHeight = INT_MAX; + bestWastedArea = INT_MAX; + bestIndex = -1; + Rect newNode; + memset(&newNode, 0, sizeof(newNode)); + for(unsigned i = 0; i < skyLine.Size(); ++i) + { + int y; + int wastedArea; + + if (RectangleFits(i, width, height, y, wastedArea)) + { + if (wastedArea < bestWastedArea || (wastedArea == bestWastedArea && y + height < bestHeight)) + { + bestHeight = y + height; + bestWastedArea = wastedArea; + bestIndex = i; + newNode.x = skyLine[i].x; + newNode.y = y; + newNode.width = width; + newNode.height = height; + assert(disjointRects.Disjoint(newNode)); + } + } +/* if (RectangleFits(i, height, width, y, wastedArea)) + { + if (wastedArea < bestWastedArea || (wastedArea == bestWastedArea && y + width < bestHeight)) + { + bestHeight = y + width; + bestWastedArea = wastedArea; + bestIndex = i; + newNode.x = skyLine[i].x; + newNode.y = y; + newNode.width = height; + newNode.height = width; + assert(disjointRects.Disjoint(newNode)); + } + }*/ + } + + return newNode; +} + +/// Computes the ratio of used surface area. +float SkylineBinPack::Occupancy() const +{ + return (float)usedSurfaceArea / (binWidth * binHeight); +} diff --git a/src/SkylineBinPack.h b/src/SkylineBinPack.h new file mode 100644 index 0000000000..937c88de60 --- /dev/null +++ b/src/SkylineBinPack.h @@ -0,0 +1,91 @@ +/** @file SkylineBinPack.h + @author Jukka Jylänki + + @brief Implements different bin packer algorithms that use the SKYLINE data structure. + + This work is released to Public Domain, do whatever you want with it. +*/ +#pragma once + +#include "tarray.h" + +#include "Rect.h" +#include "GuillotineBinPack.h" + +/** Implements bin packing algorithms that use the SKYLINE data structure to store the bin contents. Uses + GuillotineBinPack as the waste map. */ +class SkylineBinPack +{ +public: + /// Instantiates a bin of size (0,0). Call Init to create a new bin. + SkylineBinPack(); + + /// Instantiates a bin of the given size. + SkylineBinPack(int binWidth, int binHeight, bool useWasteMap); + + /// (Re)initializes the packer to an empty bin of width x height units. Call whenever + /// you need to restart with a new bin. + void Init(int binWidth, int binHeight, bool useWasteMap); + + /// Inserts the given list of rectangles in an offline/batch mode, possibly rotated. + /// @param rects The list of rectangles to insert. This vector will be destroyed in the process. + /// @param dst [out] This list will contain the packed rectangles. The indices will not correspond to that of rects. + /// @param method The rectangle placement rule to use when packing. + void Insert(TArray &rects, TArray &dst); + + /// Inserts a single rectangle into the bin, possibly rotated. + Rect Insert(int width, int height); + + /// Adds a rectangle to the waste list. It must have been previously returned by + /// Insert or the results are undefined. + void AddWaste(const Rect &rect); + + /// Computes the ratio of used surface area to the total bin area. + float Occupancy() const; + +private: + int binWidth; + int binHeight; + +#ifdef _DEBUG + DisjointRectCollection disjointRects; +#endif + + /// Represents a single level (a horizontal line) of the skyline/horizon/envelope. + struct SkylineNode + { + /// The starting x-coordinate (leftmost). + int x; + + /// The y-coordinate of the skyline level line. + int y; + + /// The line width. The ending coordinate (inclusive) will be x+width-1. + int width; + }; + + TArray skyLine; + + unsigned long usedSurfaceArea; + + /// If true, we use the GuillotineBinPack structure to recover wasted areas into a waste map. + bool useWasteMap; + GuillotineBinPack wasteMap; + + Rect InsertBottomLeft(int width, int height); + Rect InsertMinWaste(int width, int height); + + Rect FindPositionForNewNodeBottomLeft(int width, int height, int &bestHeight, int &bestWidth, int &bestIndex) const; + Rect FindPositionForNewNodeMinWaste(int width, int height, int &bestHeight, int &bestWastedArea, int &bestIndex) const; + + bool RectangleFits(int skylineNodeIndex, int width, int height, int &y) const; + bool RectangleFits(int skylineNodeIndex, int width, int height, int &y, int &wastedArea) const; + int ComputeWastedArea(int skylineNodeIndex, int width, int height, int y) const; + + void AddWasteMapArea(int skylineNodeIndex, int width, int height, int y); + + void AddSkylineLevel(int skylineNodeIndex, const Rect &rect); + + /// Merges all skyline nodes that are at the same level. + void MergeSkylines(); +}; diff --git a/src/actor.h b/src/actor.h index 52c88e9018..4deb349a78 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1,1187 +1,1367 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Map Objects, MObj, definition and handling. -// -//----------------------------------------------------------------------------- - - -#ifndef __P_MOBJ_H__ -#define __P_MOBJ_H__ - -// Basics. -#include "tables.h" - -// We need the thinker_t stuff. -#include "dthinker.h" - - -// States are tied to finite states are tied to animation frames. -#include "info.h" - -#include "doomdef.h" -#include "textures/textures.h" -#include "r_data/renderstyle.h" -#include "s_sound.h" -#include "memarena.h" -#include "g_level.h" -#include "tflags.h" - -struct subsector_t; -// -// NOTES: AActor -// -// Actors are used to tell the refresh where to draw an image, -// tell the world simulation when objects are contacted, -// and tell the sound driver how to position a sound. -// -// The refresh uses the next and prev links to follow -// lists of things in sectors as they are being drawn. -// The sprite, frame, and angle elements determine which patch_t -// is used to draw the sprite if it is visible. -// The sprite and frame values are almost always set -// from state_t structures. -// The statescr.exe utility generates the states.h and states.c -// files that contain the sprite/frame numbers from the -// statescr.txt source file. -// The xyz origin point represents a point at the bottom middle -// of the sprite (between the feet of a biped). -// This is the default origin position for patch_ts grabbed -// with lumpy.exe. -// A walking creature will have its z equal to the floor -// it is standing on. -// -// The sound code uses the x,y, and sometimes z fields -// to do stereo positioning of any sound emitted by the actor. -// -// The play simulation uses the blocklinks, x,y,z, radius, height -// to determine when AActors are touching each other, -// touching lines in the map, or hit by trace lines (gunshots, -// lines of sight, etc). -// The AActor->flags element has various bit flags -// used by the simulation. -// -// Every actor is linked into a single sector -// based on its origin coordinates. -// The subsector_t is found with R_PointInSubsector(x,y), -// and the sector_t can be found with subsector->sector. -// The sector links are only used by the rendering code, -// the play simulation does not care about them at all. -// -// Any actor that needs to be acted upon by something else -// in the play world (block movement, be shot, etc) will also -// need to be linked into the blockmap. -// If the thing has the MF_NOBLOCK flag set, it will not use -// the block links. It can still interact with other things, -// but only as the instigator (missiles will run into other -// things, but nothing can run into a missile). -// Each block in the grid is 128*128 units, and knows about -// every line_t that it contains a piece of, and every -// interactable actor that has its origin contained. -// -// A valid actor is an actor that has the proper subsector_t -// filled in for its xy coordinates and is linked into the -// sector from which the subsector was made, or has the -// MF_NOSECTOR flag set (the subsector_t needs to be valid -// even if MF_NOSECTOR is set), and is linked into a blockmap -// block or has the MF_NOBLOCKMAP flag set. -// Links should only be modified by the P_[Un]SetThingPosition() -// functions. -// Do not change the MF_NO* flags while a thing is valid. -// -// Any questions? -// - -// --- mobj.flags --- -enum ActorFlag -{ - MF_SPECIAL = 0x00000001, // call P_SpecialThing when touched - MF_SOLID = 0x00000002, - MF_SHOOTABLE = 0x00000004, - MF_NOSECTOR = 0x00000008, // don't use the sector links - // (invisible but touchable) - MF_NOBLOCKMAP = 0x00000010, // don't use the blocklinks - // (inert but displayable) - MF_AMBUSH = 0x00000020, // not activated by sound; deaf monster - MF_JUSTHIT = 0x00000040, // try to attack right back - MF_JUSTATTACKED = 0x00000080, // take at least one step before attacking - MF_SPAWNCEILING = 0x00000100, // hang from ceiling instead of floor - MF_NOGRAVITY = 0x00000200, // don't apply gravity every tic - -// movement flags - MF_DROPOFF = 0x00000400, // allow jumps from high places - MF_PICKUP = 0x00000800, // for players to pick up items - MF_NOCLIP = 0x00001000, // player cheat - MF_INCHASE = 0x00002000, // [RH] used by A_Chase and A_Look to avoid recursion - MF_FLOAT = 0x00004000, // allow moves to any height, no gravity - MF_TELEPORT = 0x00008000, // don't cross lines or look at heights - MF_MISSILE = 0x00010000, // don't hit same species, explode on block - - MF_DROPPED = 0x00020000, // dropped by a demon, not level spawned - MF_SHADOW = 0x00040000, // actor is hard for monsters to see - MF_NOBLOOD = 0x00080000, // don't bleed when shot (use puff) - MF_CORPSE = 0x00100000, // don't stop moving halfway off a step - MF_INFLOAT = 0x00200000, // floating to a height for a move, don't - // auto float to target's height - MF_INBOUNCE = 0x00200000, // used by Heretic bouncing missiles - - MF_COUNTKILL = 0x00400000, // count towards intermission kill total - MF_COUNTITEM = 0x00800000, // count towards intermission item total - - MF_SKULLFLY = 0x01000000, // skull in flight - MF_NOTDMATCH = 0x02000000, // don't spawn in death match (key cards) - - MF_SPAWNSOUNDSOURCE = 0x04000000, // Plays missile's see sound at spawning object. - MF_FRIENDLY = 0x08000000, // [RH] Friendly monsters for Strife (and MBF) - MF_UNMORPHED = 0x10000000, // [RH] Actor is the unmorphed version of something else - MF_NOLIFTDROP = 0x20000000, // [RH] Used with MF_NOGRAVITY to avoid dropping with lifts - MF_STEALTH = 0x40000000, // [RH] Andy Baker's stealth monsters - MF_ICECORPSE = 0x80000000, // a frozen corpse (for blasting) [RH] was 0x800000 - - // --- dummies for unknown/unimplemented Strife flags --- - MF_STRIFEx8000000 = 0, // seems related to MF_SHADOW -}; - -// --- mobj.flags2 --- -enum ActorFlag2 -{ - MF2_DONTREFLECT = 0x00000001, // this projectile cannot be reflected - MF2_WINDTHRUST = 0x00000002, // gets pushed around by the wind specials - MF2_DONTSEEKINVISIBLE=0x00000004, // For seeker missiles: Don't home in on invisible/shadow targets - MF2_BLASTED = 0x00000008, // actor will temporarily take damage from impact - MF2_FLY = 0x00000010, // fly mode is active - MF2_FLOORCLIP = 0x00000020, // if feet are allowed to be clipped - MF2_SPAWNFLOAT = 0x00000040, // spawn random float z - MF2_NOTELEPORT = 0x00000080, // does not teleport - MF2_RIP = 0x00000100, // missile rips through solid targets - MF2_PUSHABLE = 0x00000200, // can be pushed by other moving actors - MF2_SLIDE = 0x00000400, // slides against walls - MF2_ONMOBJ = 0x00000800, // actor is resting on top of another actor - MF2_PASSMOBJ = 0x00001000, // Enable z block checking. If on, - // this flag will allow the actor to - // pass over/under other actors. - MF2_CANNOTPUSH = 0x00002000, // cannot push other pushable mobjs - MF2_THRUGHOST = 0x00004000, // missile will pass through ghosts [RH] was 8 - MF2_BOSS = 0x00008000, // mobj is a major boss - - MF2_DONTTRANSLATE = 0x00010000, // Don't apply palette translations - MF2_NODMGTHRUST = 0x00020000, // does not thrust target when damaging - MF2_TELESTOMP = 0x00040000, // mobj can stomp another - MF2_FLOATBOB = 0x00080000, // use float bobbing z movement - MF2_THRUACTORS = 0x00100000, // performs no actor<->actor collision checks - MF2_IMPACT = 0x00200000, // an MF_MISSILE mobj can activate SPAC_IMPACT - MF2_PUSHWALL = 0x00400000, // mobj can push walls - MF2_MCROSS = 0x00800000, // can activate monster cross lines - MF2_PCROSS = 0x01000000, // can activate projectile cross lines - MF2_CANTLEAVEFLOORPIC=0x02000000, // stay within a certain floor type - MF2_NONSHOOTABLE = 0x04000000, // mobj is totally non-shootable, - // but still considered solid - MF2_INVULNERABLE = 0x08000000, // mobj is invulnerable - MF2_DORMANT = 0x10000000, // thing is dormant - MF2_ARGSDEFINED = 0x20000000, // Internal flag used by DECORATE to signal that the - // args should not be taken from the mapthing definition - MF2_SEEKERMISSILE = 0x40000000, // is a seeker (for reflection) - MF2_REFLECTIVE = 0x80000000, // reflects missiles -}; - -// --- mobj.flags3 --- -enum ActorFlag3 -{ - MF3_FLOORHUGGER = 0x00000001, // Missile stays on floor - MF3_CEILINGHUGGER = 0x00000002, // Missile stays on ceiling - MF3_NORADIUSDMG = 0x00000004, // Actor does not take radius damage - MF3_GHOST = 0x00000008, // Actor is a ghost - MF3_ALWAYSPUFF = 0x00000010, // Puff always appears, even when hit nothing - MF3_SPECIALFLOORCLIP= 0x00000020, // Actor uses floorclip for special effect (e.g. Wraith) - MF3_DONTSPLASH = 0x00000040, // Thing doesn't make a splash - MF3_NOSIGHTCHECK = 0x00000080, // Go after first acceptable target without checking sight - MF3_DONTOVERLAP = 0x00000100, // Don't pass over/under other things with this bit set - MF3_DONTMORPH = 0x00000200, // Immune to arti_egg - MF3_DONTSQUASH = 0x00000400, // Death ball can't squash this actor - MF3_EXPLOCOUNT = 0x00000800, // Don't explode until special2 counts to special1 - MF3_FULLVOLACTIVE = 0x00001000, // Active sound is played at full volume - MF3_ISMONSTER = 0x00002000, // Actor is a monster - MF3_SKYEXPLODE = 0x00004000, // Explode missile when hitting sky - MF3_STAYMORPHED = 0x00008000, // Monster cannot unmorph - MF3_DONTBLAST = 0x00010000, // Actor cannot be pushed by blasting - MF3_CANBLAST = 0x00020000, // Actor is not a monster but can be blasted - MF3_NOTARGET = 0x00040000, // This actor not targetted when it hurts something else - MF3_DONTGIB = 0x00080000, // Don't gib this corpse - MF3_NOBLOCKMONST = 0x00100000, // Can cross ML_BLOCKMONSTERS lines - MF3_CRASHED = 0x00200000, // Actor entered its crash state - MF3_FULLVOLDEATH = 0x00400000, // DeathSound is played full volume (for missiles) - MF3_AVOIDMELEE = 0x00800000, // Avoids melee attacks (same as MBF's monster_backing but must be explicitly set) - MF3_SCREENSEEKER = 0x01000000, // Fails the IsOkayToAttack test if potential target is outside player FOV - MF3_FOILINVUL = 0x02000000, // Actor can hurt MF2_INVULNERABLE things - MF3_NOTELEOTHER = 0x04000000, // Monster is unaffected by teleport other artifact - MF3_BLOODLESSIMPACT = 0x08000000, // Projectile does not leave blood - MF3_NOEXPLODEFLOOR = 0x10000000, // Missile stops at floor instead of exploding - MF3_WARNBOT = 0x20000000, // Missile warns bot - MF3_PUFFONACTORS = 0x40000000, // Puff appears even when hit bleeding actors - MF3_HUNTPLAYERS = 0x80000000, // Used with TIDtoHate, means to hate players too -}; - -// --- mobj.flags4 --- -enum ActorFlag4 -{ - MF4_NOHATEPLAYERS = 0x00000001, // Ignore player attacks - MF4_QUICKTORETALIATE= 0x00000002, // Always switch targets when hurt - MF4_NOICEDEATH = 0x00000004, // Actor never enters an ice death, not even the generic one - MF4_BOSSDEATH = 0x00000008, // A_FreezeDeathChunks calls A_BossDeath - MF4_RANDOMIZE = 0x00000010, // Missile has random initial tic count - MF4_NOSKIN = 0x00000020, // Player cannot use skins - MF4_FIXMAPTHINGPOS = 0x00000040, // Fix this actor's position when spawned as a map thing - MF4_ACTLIKEBRIDGE = 0x00000080, // Pickups can "stand" on this actor / cannot be moved by any sector action. - MF4_STRIFEDAMAGE = 0x00000100, // Strife projectiles only do up to 4x damage, not 8x - - MF4_CANUSEWALLS = 0x00000200, // Can activate 'use' specials - MF4_MISSILEMORE = 0x00000400, // increases the chance of a missile attack - MF4_MISSILEEVENMORE = 0x00000800, // significantly increases the chance of a missile attack - MF4_FORCERADIUSDMG = 0x00001000, // if put on an object it will override MF3_NORADIUSDMG - MF4_DONTFALL = 0x00002000, // Doesn't have NOGRAVITY disabled when dying. - MF4_SEESDAGGERS = 0x00004000, // This actor can see you striking with a dagger - MF4_INCOMBAT = 0x00008000, // Don't alert others when attacked by a dagger - MF4_LOOKALLAROUND = 0x00010000, // Monster has eyes in the back of its head - MF4_STANDSTILL = 0x00020000, // Monster should not chase targets unless attacked? - MF4_SPECTRAL = 0x00040000, - MF4_SCROLLMOVE = 0x00080000, // velocity has been applied by a scroller - MF4_NOSPLASHALERT = 0x00100000, // Splashes don't alert this monster - MF4_SYNCHRONIZED = 0x00200000, // For actors spawned at load-time only: Do not randomize tics - MF4_NOTARGETSWITCH = 0x00400000, // monster never switches target until current one is dead - MF4_VFRICTION = 0x00800000, // Internal flag used by A_PainAttack to push a monster down - MF4_DONTHARMCLASS = 0x01000000, // Don't hurt one's own kind with explosions (hitscans, too?) - MF4_SHIELDREFLECT = 0x02000000, - MF4_DEFLECT = 0x04000000, // different projectile reflection styles - MF4_ALLOWPARTICLES = 0x08000000, // this puff type can be replaced by particles - MF4_NOEXTREMEDEATH = 0x10000000, // this projectile or weapon never gibs its victim - MF4_EXTREMEDEATH = 0x20000000, // this projectile or weapon always gibs its victim - MF4_FRIGHTENED = 0x40000000, // Monster runs away from player - MF4_BOSSSPAWNED = 0x80000000, // Spawned by a boss spawn cube -}; - -// --- mobj.flags5 --- - -enum ActorFlag5 -{ - MF5_DONTDRAIN = 0x00000001, // cannot be drained health from. - /* = 0x00000002, reserved for use by scripting branch */ - MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. - 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 - MF5_CHASEGOAL = 0x00000080, // Walks to goal instead of target if a valid goal is set. - MF5_BLOODSPLATTER = 0x00000100, // Blood splatter like in Raven's games. - MF5_OLDRADIUSDMG = 0x00000200, // Use old radius damage code (for barrels and boss brain) - MF5_DEHEXPLOSION = 0x00000400, // Use the DEHACKED explosion options when this projectile explodes - MF5_PIERCEARMOR = 0x00000800, // Armor doesn't protect against damage from this actor - MF5_NOBLOODDECALS = 0x00001000, // Actor bleeds but doesn't spawn blood decals - MF5_USESPECIAL = 0x00002000, // Actor executes its special when being 'used'. - MF5_NOPAIN = 0x00004000, // If set the pain state won't be entered - MF5_ALWAYSFAST = 0x00008000, // always uses 'fast' attacking logic - MF5_NEVERFAST = 0x00010000, // never uses 'fast' attacking logic - MF5_ALWAYSRESPAWN = 0x00020000, // always respawns, regardless of skill setting - MF5_NEVERRESPAWN = 0x00040000, // never respawns, regardless of skill setting - MF5_DONTRIP = 0x00080000, // Ripping projectiles explode when hitting this actor - MF5_NOINFIGHTING = 0x00100000, // This actor doesn't switch target when it's hurt - MF5_NOINTERACTION = 0x00200000, // Thing is completely excluded from any gameplay related checks - MF5_NOTIMEFREEZE = 0x00400000, // Actor is not affected by time freezer - MF5_PUFFGETSOWNER = 0x00800000, // [BB] Sets the owner of the puff to the player who fired it - MF5_SPECIALFIREDAMAGE=0x01000000, // Special treatment of PhoenixFX1 turned into a flag to remove - // dependence of main engine code of specific actor types. - MF5_SUMMONEDMONSTER = 0x02000000, // To mark the friendly Minotaur. Hopefully to be generalized later. - MF5_NOVERTICALMELEERANGE=0x04000000,// Does not check vertical distance for melee range - MF5_BRIGHT = 0x08000000, // Actor is always rendered fullbright - MF5_CANTSEEK = 0x10000000, // seeker missiles cannot home in on this actor - MF5_INCONVERSATION = 0x20000000, // Actor is having a conversation - MF5_PAINLESS = 0x40000000, // Actor always inflicts painless damage. - MF5_MOVEWITHSECTOR = 0x80000000, // P_ChangeSector() will still process this actor if it has MF_NOBLOCKMAP -}; - -// --- mobj.flags6 --- -enum ActorFlag6 -{ - MF6_NOBOSSRIP = 0x00000001, // For rippermissiles: Don't rip through bosses. - MF6_THRUSPECIES = 0x00000002, // Actors passes through other of the same species. - MF6_MTHRUSPECIES = 0x00000004, // Missile passes through actors of its shooter's species. - MF6_FORCEPAIN = 0x00000008, // forces target into painstate (unless it has the NOPAIN flag) - MF6_NOFEAR = 0x00000010, // Not scared of frightening players - MF6_BUMPSPECIAL = 0x00000020, // Actor executes its special when being collided (as the ST flag) - MF6_DONTHARMSPECIES = 0x00000040, // Don't hurt one's own species with explosions (hitscans, too?) - MF6_STEPMISSILE = 0x00000080, // Missile can "walk" up steps - MF6_NOTELEFRAG = 0x00000100, // [HW] Actor can't be telefragged - MF6_TOUCHY = 0x00000200, // From MBF: killough 11/98: dies when solids touch it - MF6_CANJUMP = 0x00000400, // From MBF: a dedicated flag instead of the BOUNCES+FLOAT+sentient combo - MF6_JUMPDOWN = 0x00000800, // From MBF: generalization of dog behavior wrt. dropoffs. - MF6_VULNERABLE = 0x00001000, // Actor can be damaged (even if not shootable). - MF6_ARMED = 0x00002000, // From MBF: Object is armed (for MF6_TOUCHY objects) - MF6_FALLING = 0x00004000, // From MBF: Object is falling (for pseudotorque simulation) - MF6_LINEDONE = 0x00008000, // From MBF: Object has already run a line effect - MF6_NOTRIGGER = 0x00010000, // actor cannot trigger any line actions - MF6_SHATTERING = 0x00020000, // marks an ice corpse for forced shattering - MF6_KILLED = 0x00040000, // Something that was killed (but not necessarily a corpse) - MF6_BLOCKEDBYSOLIDACTORS = 0x00080000, // Blocked by solid actors, even if not solid itself - MF6_ADDITIVEPOISONDAMAGE = 0x00100000, - MF6_ADDITIVEPOISONDURATION = 0x00200000, - MF6_NOMENU = 0x00400000, // Player class should not appear in the class selection menu. - MF6_BOSSCUBE = 0x00800000, // Actor spawned by A_BrainSpit, flagged for timefreeze reasons. - MF6_SEEINVISIBLE = 0x01000000, // Monsters can see invisible player. - 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. - MF6_RELATIVETOFLOOR = 0x80000000, // [RC] Make flying actors be affected by lifts. -}; - -// --- mobj.flags7 --- -enum ActorFlag7 -{ - MF7_NEVERTARGET = 0x00000001, // can not be targetted at all, even if monster friendliness is considered. - MF7_NOTELESTOMP = 0x00000002, // cannot telefrag under any circumstances (even when set by MAPINFO) - MF7_ALWAYSTELEFRAG = 0x00000004, // will unconditionally be telefragged when in the way. Overrides all other settings. - MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag - MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag - MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. - MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. - MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. - MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. - MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. - MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. - MF7_THRUREFLECT = 0x00000800, // Actors who are reflective cause the missiles to not slow down or change angles. - MF7_MIRRORREFLECT = 0x00001000, // Actor is turned directly 180 degrees around when reflected. - MF7_AIMREFLECT = 0x00002000, // Actor is directly reflected straight back at the one who fired the projectile. - MF7_HITTARGET = 0x00004000, // The actor the projectile dies on is set to target, provided it's targetable anyway. - MF7_HITMASTER = 0x00008000, // Same as HITTARGET, except it's master instead of target. - MF7_HITTRACER = 0x00010000, // Same as HITTARGET, but for tracer. - MF7_FLYCHEAT = 0x00020000, // must be part of the actor so that it can be tracked properly - MF7_NODECAL = 0x00040000, // [ZK] Forces puff to have no impact decal - MF7_FORCEDECAL = 0x00080000, // [ZK] Forces puff's decal to override the weapon's. - MF7_LAXTELEFRAGDMG = 0x00100000, // [MC] Telefrag damage can be reduced. -}; - -// --- mobj.renderflags --- -enum ActorRenderFlag -{ - RF_XFLIP = 0x0001, // Flip sprite horizontally - RF_YFLIP = 0x0002, // Flip sprite vertically - RF_ONESIDED = 0x0004, // Wall/floor sprite is visible from front only - RF_FULLBRIGHT = 0x0010, // Sprite is drawn at full brightness - - RF_RELMASK = 0x0300, // ---Relative z-coord for bound actors (these obey texture pegging) - RF_RELABSOLUTE = 0x0000, // Actor z is absolute - RF_RELUPPER = 0x0100, // Actor z is relative to upper part of wall - RF_RELLOWER = 0x0200, // Actor z is relative to lower part of wall - RF_RELMID = 0x0300, // Actor z is relative to middle part of wall - - RF_CLIPMASK = 0x0c00, // ---Clipping for bound actors - RF_CLIPFULL = 0x0000, // Clip sprite to full height of wall - RF_CLIPUPPER = 0x0400, // Clip sprite to upper part of wall - RF_CLIPMID = 0x0800, // Clip sprite to mid part of wall - RF_CLIPLOWER = 0x0c00, // Clip sprite to lower part of wall - - RF_DECALMASK = RF_RELMASK|RF_CLIPMASK, - - RF_SPRITETYPEMASK = 0x7000, // ---Different sprite types, not all implemented - RF_FACESPRITE = 0x0000, // Face sprite - RF_WALLSPRITE = 0x1000, // Wall sprite - RF_FLOORSPRITE = 0x2000, // Floor sprite - RF_VOXELSPRITE = 0x3000, // Voxel object - RF_INVISIBLE = 0x8000, // Don't bother drawing this actor - - RF_FORCEYBILLBOARD = 0x10000, // [BB] OpenGL only: draw with y axis billboard, i.e. anchored to the floor (overrides gl_billboard_mode setting) - RF_FORCEXYBILLBOARD = 0x20000, // [BB] OpenGL only: draw with xy axis billboard, i.e. unanchored (overrides gl_billboard_mode setting) -}; - -#define TRANSLUC25 (FRACUNIT/4) -#define TRANSLUC33 (FRACUNIT/3) -#define TRANSLUC50 (FRACUNIT/2) -#define TRANSLUC66 ((FRACUNIT*2)/3) -#define TRANSLUC75 ((FRACUNIT*3)/4) - -// also #defines OPAQUE -#ifndef OPAQUE -#define OPAQUE (FRACUNIT) -#endif - -// This translucency value produces the closest match to Heretic's TINTTAB. -// ~40% of the value of the overlaid image shows through. -#define HR_SHADOW (0x6800) - -// Hexen's TINTTAB is the same as Heretic's, just reversed. -#define HX_SHADOW (0x9800) -#define HX_ALTSHADOW (0x6800) - -// This could easily be a bool but then it'd be much harder to find later. ;) -enum replace_t -{ - NO_REPLACE = 0, - ALLOW_REPLACE = 1 -}; - -enum ActorBounceFlag -{ - BOUNCE_Walls = 1<<0, // bounces off of walls - BOUNCE_Floors = 1<<1, // bounces off of floors - 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 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 - // the compatibility handlers for the old bounce flags. - BOUNCE_NoWallSound = 1<<8, // don't make noise when bouncing off a wall - BOUNCE_Quiet = 1<<9, // Strife's grenades don't make a bouncing sound - BOUNCE_ExplodeOnWater = 1<<10, // explodes when hitting a water surface - BOUNCE_CanBounceWater = 1<<11, // can bounce on water - // 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, - - // The three "standard" types of bounciness are: - // HERETIC - Missile will only bounce off the floor once and then enter - // its death state. It does not bounce off walls at all. - // HEXEN - Missile bounces off of walls and floors indefinitely. - // DOOM - Like Hexen, but the bounce turns off if its vertical velocity - // is too low. - BOUNCE_None = 0, - BOUNCE_Heretic = BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_HereticType, - BOUNCE_Doom = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff, - BOUNCE_Hexen = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors, - BOUNCE_Grenade = BOUNCE_MBF | BOUNCE_Doom, // Bounces on walls and flats like ZDoom bounce. - BOUNCE_Classic = BOUNCE_MBF | BOUNCE_Floors | BOUNCE_Ceilings, // Bounces on flats only, but - // does not die when bouncing. - - // combined types - BOUNCE_DoomCompat = BOUNCE_Doom | BOUNCE_UseSeeSound, - BOUNCE_HereticCompat = BOUNCE_Heretic | BOUNCE_UseSeeSound, - BOUNCE_HexenCompat = BOUNCE_Hexen | BOUNCE_UseSeeSound - - // The distinction between BOUNCE_Actors and BOUNCE_AllActors: A missile with - // BOUNCE_Actors set will bounce off of reflective and "non-sentient" actors. - // A missile that also has BOUNCE_AllActors set will bounce off of any actor. - // For compatibility reasons when BOUNCE_Actors was implied by the bounce type - // being "Doom" or "Hexen" and BOUNCE_AllActors was the separate - // MF5_BOUNCEONACTORS, you must set BOUNCE_Actors for BOUNCE_AllActors to have - // an effect. - - -}; - -// [TP] Flagset definitions -typedef TFlags ActorFlags; -typedef TFlags ActorFlags2; -typedef TFlags ActorFlags3; -typedef TFlags ActorFlags4; -typedef TFlags ActorFlags5; -typedef TFlags ActorFlags6; -typedef TFlags ActorFlags7; -typedef TFlags ActorRenderFlags; -typedef TFlags ActorBounceFlags; -DEFINE_TFLAGS_OPERATORS (ActorFlags) -DEFINE_TFLAGS_OPERATORS (ActorFlags2) -DEFINE_TFLAGS_OPERATORS (ActorFlags3) -DEFINE_TFLAGS_OPERATORS (ActorFlags4) -DEFINE_TFLAGS_OPERATORS (ActorFlags5) -DEFINE_TFLAGS_OPERATORS (ActorFlags6) -DEFINE_TFLAGS_OPERATORS (ActorFlags7) -DEFINE_TFLAGS_OPERATORS (ActorRenderFlags) -DEFINE_TFLAGS_OPERATORS (ActorBounceFlags) - -// Used to affect the logic for thing activation through death, USESPECIAL and BUMPSPECIAL -// "thing" refers to what has the flag and the special, "trigger" refers to what used or bumped it -enum EThingSpecialActivationType -{ - THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator - THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special - THINGSPEC_ThingTargets = 1<<1, // The thing changes its target to the trigger - THINGSPEC_TriggerTargets = 1<<2, // The trigger changes its target to the thing - THINGSPEC_MonsterTrigger = 1<<3, // The thing can be triggered by a monster - THINGSPEC_MissileTrigger = 1<<4, // The thing can be triggered by a projectile - THINGSPEC_ClearSpecial = 1<<5, // Clears special after successful activation - THINGSPEC_NoDeathSpecial = 1<<6, // Don't activate special on death - THINGSPEC_TriggerActs = 1<<7, // The trigger is the activator of the special - // (overrides LEVEL_ACTOWNSPECIAL Hexen hack) - THINGSPEC_Activate = 1<<8, // The thing is activated when triggered - THINGSPEC_Deactivate = 1<<9, // The thing is deactivated when triggered - THINGSPEC_Switch = 1<<10, // The thing is alternatively activated and deactivated when triggered -}; - -// [RH] Like msecnode_t, but for the blockmap -struct FBlockNode -{ - AActor *Me; // actor this node references - int BlockIndex; // index into blocklinks for the block this node is in - FBlockNode **PrevActor; // previous actor in this block - FBlockNode *NextActor; // next actor in this block - FBlockNode **PrevBlock; // previous block this actor is in - FBlockNode *NextBlock; // next block this actor is in - - static FBlockNode *Create (AActor *who, int x, int y); - void Release (); - - static FBlockNode *FreeBlocks; -}; - -class FDecalBase; -class AInventory; - -inline AActor *GetDefaultByName (const char *name) -{ - return (AActor *)(PClass::FindClass(name)->Defaults); -} - -inline AActor *GetDefaultByType (const PClass *type) -{ - return (AActor *)(type->Defaults); -} - -template -inline T *GetDefault () -{ - return (T *)(RUNTIME_CLASS(T)->Defaults); -} - -struct line_t; -struct secplane_t; -struct FStrifeDialogueNode; - -enum -{ - AMETA_BASE = 0x12000, - - AMETA_Obituary, // string (player was killed by this actor) - AMETA_HitObituary, // string (player was killed by this actor in melee) - AMETA_DeathHeight, // fixed (height on normal death) - AMETA_BurnHeight, // fixed (height on burning death) - AMETA_StrifeName, // string (for named Strife objects) - AMETA_BloodColor, // colorized blood - AMETA_GibHealth, // negative health below which this monster dies an extreme death - AMETA_WoundHealth, // health needed to enter wound state - AMETA_FastSpeed, // Speed in fast mode - AMETA_RDFactor, // Radius damage factor - AMETA_CameraHeight, // Height of camera when used as such - AMETA_HowlSound, // Sound being played when electrocuted or poisoned - AMETA_BloodType, // Blood replacement type - AMETA_BloodType2, // Bloodsplatter replacement type - AMETA_BloodType3, // AxeBlood replacement type -}; - -struct FDropItem -{ - FName Name; - int probability; - int amount; - FDropItem * Next; -}; - -class FDropItemPtrArray : public TArray -{ -public: - ~FDropItemPtrArray() - { - Clear(); - } - - void Clear(); -}; - -extern FDropItemPtrArray DropItemList; - -void FreeDropItemChain(FDropItem *chain); -int StoreDropItemChain(FDropItem *chain); - - - -// Map Object definition. -class AActor : public DThinker -{ - DECLARE_CLASS (AActor, DThinker) - HAS_OBJECT_POINTERS -public: - AActor () throw(); - AActor (const AActor &other) throw(); - AActor &operator= (const AActor &other); - void Destroy (); - ~AActor (); - - void Serialize (FArchive &arc); - - static AActor *StaticSpawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement, bool SpawningMapThing = false); - - inline AActor *GetDefault () const - { - return (AActor *)(RUNTIME_TYPE(this)->Defaults); - } - - FDropItem *GetDropItems(); - - // Return true if the monster should use a missile attack, false for melee - bool SuggestMissileAttack (fixed_t dist); - - // Adjusts the angle for deflection/reflection of incoming missiles - // Returns true if the missile should be allowed to explode anyway - bool AdjustReflectionAngle (AActor *thing, angle_t &angle); - - // Returns true if this actor is within melee range of its target - bool CheckMeleeRange(); - - 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); - - virtual void Tick (); - - // Called when actor dies - 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, FName damagetype); - - // Like DoSpecialDamage, but called on the actor receiving the damage. - virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); - - // Centaurs and ettins squeal when electrocuted, poisoned, or "holy"-ed - // Made a metadata property so no longer virtual - void Howl (); - - // Actor just hit the floor - virtual void HitFloor (); - - // plays bouncing sound - void PlayBounceSound(bool onfloor); - - // Called when an actor with MF_MISSILE and MF2_FLOORBOUNCE hits the floor - virtual bool FloorBounceMissile (secplane_t &plane); - - // Called when an actor is to be reflected by a disc of repulsion. - // Returns true to continue normal blast processing. - virtual bool SpecialBlastHandling (AActor *source, fixed_t strength); - - // Called by RoughBlockCheck - bool IsOkayToAttack (AActor *target); - - // Plays the actor's ActiveSound if its voice isn't already making noise. - void PlayActiveSound (); - - // Actor had MF_SKULLFLY set and rammed into something - // Returns false to stop moving and true to keep moving - virtual bool Slam (AActor *victim); - - // Called by PIT_CheckThing() and needed for some Hexen things. - // Returns -1 for normal behavior, 0 to return false, and 1 to return true. - // I'm not sure I like it this way, but it will do for now. - virtual int SpecialMissileHit (AActor *victim); - - // Returns true if it's okay to switch target to "other" after being attacked by it. - virtual bool OkayToSwitchTarget (AActor *other); - - // Something just touched this actor. - virtual void Touch (AActor *toucher); - - // Adds the item to this actor's inventory and sets its Owner. - virtual void AddInventory (AInventory *item); - - // Removes the item from the inventory list. - virtual void RemoveInventory (AInventory *item); - - // Take the amount value of an item from the inventory list. - // If nothing is left, the item may be destroyed. - // Returns true if the initial item count is positive. - virtual bool TakeInventory (const PClass *itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); - - // Uses an item and removes it from the inventory. - virtual bool UseInventory (AInventory *item); - - // 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; - - // Finds the first item of a particular type. - AInventory *FindInventory (const PClass *type, bool subclass = false); - AInventory *FindInventory (FName type); - template T *FindInventory () - { - return static_cast (FindInventory (RUNTIME_CLASS(T))); - } - - // Adds one item of a particular type. Returns NULL if it could not be added. - AInventory *GiveInventoryType (const PClass *type); - - // Returns the first item held with IF_INVBAR set. - AInventory *FirstInv (); - - // Tries to give the actor some ammo. - bool GiveAmmo (const PClass *type, int amount); - - // Destroys all the inventory the actor is holding. - void DestroyAllInventory (); - - // Set the alphacolor field properly - void SetShade (DWORD rgb); - void SetShade (int r, int g, int b); - - // Plays a conversation animation - void ConversationAnimation (int animnum); - - // Make this actor hate the same things as another actor - void CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth=true); - - // Moves the other actor's inventory to this one - void ObtainInventory (AActor *other); - - // Die. Now. - virtual bool Massacre (); - - // Transforms the actor into a finely-ground paste - virtual bool Grind(bool items); - - // Get this actor's team - int GetTeam(); - - // Is the other actor on my team? - bool IsTeammate (AActor *other); - - // Is the other actor my friend? - bool IsFriend (AActor *other); - - // 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(); - - // Return starting health adjusted by skill level - int SpawnHealth(); - int GibHealth(); - - inline bool isMissile(bool precise=true) - { - return (flags&MF_MISSILE) || (precise && GetDefault()->flags&MF_MISSILE); - } - - // Check for monsters that count as kill but excludes all friendlies. - bool CountsAsKill() const - { - return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY); - } - - bool intersects(AActor *other) const - { - fixed_t blockdist = radius + other->radius; - return ( abs(x - other->x) < blockdist && abs(y - other->y) < blockdist); - } - - PalEntry GetBloodColor() const - { - return (PalEntry)GetClass()->Meta.GetMetaInt(AMETA_BloodColor); - } - - // These also set CF_INTERPVIEW for players. - void SetPitch(int p, bool interpolate, bool forceclamp = false); - void SetAngle(angle_t ang, bool interpolate); - void SetRoll(angle_t roll, bool interpolate); - - const PClass *GetBloodType(int type = 0) const - { - const PClass *bloodcls; - if (type == 0) - { - bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType, NAME_Blood)); - } - else if (type == 1) - { - bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType2, NAME_BloodSplatter)); - } - else if (type == 2) - { - bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType3, NAME_AxeBlood)); - } - else return NULL; - - if (bloodcls != NULL) - { - bloodcls = bloodcls->GetReplacement(); - } - return bloodcls; - } - - inline void SetFriendPlayer(player_t *player); - - bool IsVisibleToPlayer() const; - - // Calculate amount of missile damage - virtual int GetMissileDamage(int mask, int add); - - bool CanSeek(AActor *target) const; - - fixed_t GetGravity() const; - bool IsSentient() const; - 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. - fixed_t x,y,z; - AActor *snext, **sprev; // links in sector (if needed) - angle_t angle; - WORD sprite; // used to find patch_t and flip value - BYTE frame; // sprite frame to draw - fixed_t scaleX, scaleY; // Scaling values; FRACUNIT is normal size - FRenderStyle RenderStyle; // Style to draw this actor with - ActorRenderFlags renderflags; // Different rendering flags - FTextureID picnum; // Draw this instead of sprite if valid - DWORD effects; // [RH] see p_effect.h - fixed_t alpha; - DWORD fillcolor; // Color to draw when STYLE_Shaded - -// interaction info - fixed_t pitch; - angle_t roll; // This was fixed_t before, which is probably wrong - FBlockNode *BlockNode; // links in blocks (if needed) - struct sector_t *Sector; - subsector_t * subsector; - fixed_t floorz, ceilingz; // closest together of contacted secs - fixed_t dropoffz; // killough 11/98: the lowest floor over all contacted Sectors. - - struct sector_t *floorsector; - FTextureID floorpic; // contacted sec floorpic - struct sector_t *ceilingsector; - FTextureID ceilingpic; // contacted sec ceilingpic - fixed_t radius, height; // for movement checking - fixed_t projectilepassheight; // height for clipping projectile movement against this actor - fixed_t velx, vely, velz; // velocity - SDWORD tics; // state tic counter - FState *state; - SDWORD Damage; // For missiles and monster railgun - int projectileKickback; - ActorFlags flags; - ActorFlags2 flags2; // Heretic flags - ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable - ActorFlags4 flags4; // [RH] Even more flags! - ActorFlags5 flags5; // OMG! We need another one. - ActorFlags6 flags6; // Shit! Where did all the flags go? - ActorFlags7 flags7; // WHO WANTS TO BET ON 8!? - - // [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 weaponspecial; // Special info for weapons. - int health; - BYTE movedir; // 0-7 - SBYTE visdir; - SWORD movecount; // when 0, select a new dir - SWORD strafecount; // for MF3_AVOIDMELEE - TObjPtr target; // thing being chased/attacked (or NULL) - // also the originator for missiles - TObjPtr lastenemy; // Last known enemy -- killough 2/15/98 - TObjPtr LastHeard; // [RH] Last actor this one heard - SDWORD reactiontime; // if non 0, don't attack yet; used by - // player to freeze a bit after teleporting - SDWORD threshold; // if > 0, the target will be chased - // no matter what (even if shot) - player_t *player; // only valid if type of APlayerPawn - TObjPtr LastLookActor; // Actor last looked for (if TIDtoHate != 0) - fixed_t SpawnPoint[3]; // For nightmare respawn - WORD SpawnAngle; - int StartHealth; - BYTE WeaveIndexXY; // Separated from special2 because it's used by globally accessible functions. - BYTE WeaveIndexZ; - int skillrespawncount; - int TIDtoHate; // TID of things to hate (0 if none) - FNameNoInit Species; // For monster families - TObjPtr tracer; // Thing being chased/attacked for tracers - TObjPtr master; // Thing which spawned this one (prevents mutual attacks) - fixed_t floorclip; // value to use for floor clipping - int tid; // thing identifier - 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 - BYTE boomwaterlevel; // splash information for non-swimmable water sectors - BYTE MinMissileChance;// [RH] If a random # is > than this, then missile attack. - SBYTE LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0) - ActorBounceFlags BounceFlags; // which bouncing type? - DWORD SpawnFlags; // Increased to DWORD because of Doom 64 - fixed_t meleerange; // specifies how far a melee attack reaches. - fixed_t meleethreshold; // Distance below which a monster doesn't try to shoot missiles anynore - // but instead tries to come closer for a melee attack. - // This is not the same as meleerange - fixed_t maxtargetrange; // any target farther away cannot be attacked - fixed_t bouncefactor; // Strife's grenades use 50%, Hexen's Flechettes 70. - fixed_t wallbouncefactor; // The bounce factor for walls can be different. - int bouncecount; // Strife's grenades only bounce twice before exploding - fixed_t gravity; // [GRB] Gravity factor - fixed_t Friction; - int FastChaseStrafeCount; - fixed_t pushfactor; - int lastpush; - int activationtype; // How the thing behaves when activated with USESPECIAL or BUMPSPECIAL - int lastbump; // Last time the actor was bumped, used to control BUMPSPECIAL - int Score; // manipulated by score items, ACS or DECORATE. The engine doesn't use this itself for anything. - FString * Tag; // Strife's tag name. - int DesignatedTeam; // Allow for friendly fire cacluations to be done on non-players. - - AActor *BlockingMobj; // Actor that blocked the last move - line_t *BlockingLine; // Line that blocked the last move - - int PoisonDamage; // Damage received per tic from poison. - FNameNoInit PoisonDamageType; // Damage type dealt by poison. - int PoisonDuration; // Duration left for receiving poison damage. - int PoisonPeriod; // How often poison damage is applied. (Every X tics.) - - int PoisonDamageReceived; // Damage received per tic from poison. - FNameNoInit PoisonDamageTypeReceived; // Damage type received by poison. - int PoisonDurationReceived; // Duration left for receiving poison damage. - int PoisonPeriodReceived; // How often poison damage is applied. (Every X tics.) - TObjPtr Poisoner; // Last source of received poison damage. - - // a linked list of sectors where this object appears - struct msecnode_t *touching_sectorlist; // phares 3/14/98 - - TObjPtr Inventory; // [RH] This actor's inventory - DWORD InventoryID; // A unique ID to keep track of inventory items - - BYTE smokecounter; - BYTE FloatBobPhase; - BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc) - DWORD Translation; - - // [RH] Stuff that used to be part of an Actor Info - FSoundIDNoInit SeeSound; - FSoundIDNoInit AttackSound; - FSoundIDNoInit PainSound; - FSoundIDNoInit DeathSound; - FSoundIDNoInit ActiveSound; - FSoundIDNoInit UseSound; // [RH] Sound to play when an actor is used. - FSoundIDNoInit BounceSound; - FSoundIDNoInit WallBounceSound; - FSoundIDNoInit CrushPainSound; - - fixed_t Speed; - fixed_t FloatSpeed; - fixed_t MaxDropOffHeight, MaxStepHeight; - SDWORD Mass; - SWORD PainChance; - int PainThreshold; - FNameNoInit DamageType; - FNameNoInit DamageTypeReceived; - fixed_t DamageFactor; - fixed_t DamageMultiply; - - FNameNoInit PainType; - FNameNoInit DeathType; - const PClass *TeleFogSourceType; - const PClass *TeleFogDestType; - int RipperLevel; - int RipLevelMin; - int RipLevelMax; - - FState *SpawnState; - FState *SeeState; - FState *MeleeState; - FState *MissileState; - - - int ConversationRoot; // THe root of the current dialogue - FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used." - - // [RH] Decal(s) this weapon/projectile generates on impact. - FDecalBase *DecalGenerator; - - // [RH] Used to interpolate the view to get >35 FPS - fixed_t PrevX, PrevY, PrevZ; - angle_t PrevAngle; - - // ThingIDs - static void ClearTIDHashes (); - void AddToHash (); - void RemoveFromHash (); - -private: - static AActor *TIDHash[128]; - static inline int TIDHASH (int key) { return key & 127; } - static FSharedStringArena mStringPropertyData; - - friend class FActorIterator; - friend bool P_IsTIDUsed(int tid); - - sector_t *LinkToWorldForMapThing (); - -public: - void LinkToWorld (bool buggy=false); - void LinkToWorld (sector_t *sector); - void UnlinkFromWorld (); - void AdjustFloorClip (); - void SetOrigin (fixed_t x, fixed_t y, fixed_t z); - bool InStateSequence(FState * newstate, FState * basestate); - int GetTics(FState * newstate); - bool SetState (FState *newstate, bool nofunction=false); - virtual bool UpdateWaterLevel (fixed_t oldz, bool splash=true); - bool isFast(); - bool isSlow(); - void SetIdle(bool nofunction=false); - void ClearCounters(); - FState *GetRaiseState(); - void Revive(); - - FState *FindState (FName label) const - { - return GetClass()->ActorInfo->FindState(1, &label); - } - - FState *FindState (FName label, FName sublabel, bool exact = false) const - { - FName names[] = { label, sublabel }; - 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; -}; - -class FActorIterator -{ -public: - FActorIterator (int i) : base (NULL), id (i) - { - } - FActorIterator (int i, AActor *start) : base (start), id (i) - { - } - AActor *Next () - { - if (id == 0) - return NULL; - if (!base) - base = AActor::TIDHash[id & 127]; - else - base = base->inext; - - while (base && base->tid != id) - base = base->inext; - - return base; - } -private: - AActor *base; - int id; -}; - -template -class TActorIterator : public FActorIterator -{ -public: - TActorIterator (int id) : FActorIterator (id) {} - T *Next () - { - AActor *actor; - do - { - actor = FActorIterator::Next (); - } while (actor && !actor->IsKindOf (RUNTIME_CLASS(T))); - return static_cast(actor); - } -}; - -class NActorIterator : public FActorIterator -{ - const PClass *type; -public: - NActorIterator (const PClass *cls, int id) : FActorIterator (id) { type = cls; } - NActorIterator (FName cls, int id) : FActorIterator (id) { type = PClass::FindClass(cls); } - NActorIterator (const char *cls, int id) : FActorIterator (id) { type = PClass::FindClass(cls); } - AActor *Next () - { - AActor *actor; - if (type == NULL) return NULL; - do - { - actor = FActorIterator::Next (); - } while (actor && !actor->IsKindOf (type)); - return actor; - } -}; - -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); -} - -AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); -AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); - -template -inline T *Spawn (fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) -{ - return static_cast(AActor::StaticSpawn (RUNTIME_CLASS(T), x, y, z, allowreplacement)); -} - - -void PrintMiscActorInfo(AActor * query); - -#define S_FREETARGMOBJ 1 - -#endif // __P_MOBJ_H__ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Map Objects, MObj, definition and handling. +// +//----------------------------------------------------------------------------- + + +#ifndef __P_MOBJ_H__ +#define __P_MOBJ_H__ + +// Basics. +#include "tables.h" + +// We need the thinker_t stuff. +#include "dthinker.h" + + +// States are tied to finite states are tied to animation frames. +#include "info.h" + +#include "doomdef.h" +#include "textures/textures.h" +#include "r_data/renderstyle.h" +#include "s_sound.h" +#include "memarena.h" +#include "g_level.h" +#include "tflags.h" + +struct subsector_t; +// +// NOTES: AActor +// +// Actors are used to tell the refresh where to draw an image, +// tell the world simulation when objects are contacted, +// and tell the sound driver how to position a sound. +// +// The refresh uses the next and prev links to follow +// lists of things in sectors as they are being drawn. +// The sprite, frame, and angle elements determine which patch_t +// is used to draw the sprite if it is visible. +// The sprite and frame values are almost always set +// from state_t structures. +// The statescr.exe utility generates the states.h and states.c +// files that contain the sprite/frame numbers from the +// statescr.txt source file. +// The xyz origin point represents a point at the bottom middle +// of the sprite (between the feet of a biped). +// This is the default origin position for patch_ts grabbed +// with lumpy.exe. +// A walking creature will have its z equal to the floor +// it is standing on. +// +// The sound code uses the x,y, and sometimes z fields +// to do stereo positioning of any sound emitted by the actor. +// +// The play simulation uses the blocklinks, x,y,z, radius, height +// to determine when AActors are touching each other, +// touching lines in the map, or hit by trace lines (gunshots, +// lines of sight, etc). +// The AActor->flags element has various bit flags +// used by the simulation. +// +// Every actor is linked into a single sector +// based on its origin coordinates. +// The subsector_t is found with R_PointInSubsector(x,y), +// and the sector_t can be found with subsector->sector. +// The sector links are only used by the rendering code, +// the play simulation does not care about them at all. +// +// Any actor that needs to be acted upon by something else +// in the play world (block movement, be shot, etc) will also +// need to be linked into the blockmap. +// If the thing has the MF_NOBLOCK flag set, it will not use +// the block links. It can still interact with other things, +// but only as the instigator (missiles will run into other +// things, but nothing can run into a missile). +// Each block in the grid is 128*128 units, and knows about +// every line_t that it contains a piece of, and every +// interactable actor that has its origin contained. +// +// A valid actor is an actor that has the proper subsector_t +// filled in for its xy coordinates and is linked into the +// sector from which the subsector was made, or has the +// MF_NOSECTOR flag set (the subsector_t needs to be valid +// even if MF_NOSECTOR is set), and is linked into a blockmap +// block or has the MF_NOBLOCKMAP flag set. +// Links should only be modified by the P_[Un]SetThingPosition() +// functions. +// Do not change the MF_NO* flags while a thing is valid. +// +// Any questions? +// + +// --- mobj.flags --- +enum ActorFlag +{ + MF_SPECIAL = 0x00000001, // call P_SpecialThing when touched + MF_SOLID = 0x00000002, + MF_SHOOTABLE = 0x00000004, + MF_NOSECTOR = 0x00000008, // don't use the sector links + // (invisible but touchable) + MF_NOBLOCKMAP = 0x00000010, // don't use the blocklinks + // (inert but displayable) + MF_AMBUSH = 0x00000020, // not activated by sound; deaf monster + MF_JUSTHIT = 0x00000040, // try to attack right back + MF_JUSTATTACKED = 0x00000080, // take at least one step before attacking + MF_SPAWNCEILING = 0x00000100, // hang from ceiling instead of floor + MF_NOGRAVITY = 0x00000200, // don't apply gravity every tic + +// movement flags + MF_DROPOFF = 0x00000400, // allow jumps from high places + MF_PICKUP = 0x00000800, // for players to pick up items + MF_NOCLIP = 0x00001000, // player cheat + MF_INCHASE = 0x00002000, // [RH] used by A_Chase and A_Look to avoid recursion + MF_FLOAT = 0x00004000, // allow moves to any height, no gravity + MF_TELEPORT = 0x00008000, // don't cross lines or look at heights + MF_MISSILE = 0x00010000, // don't hit same species, explode on block + + MF_DROPPED = 0x00020000, // dropped by a demon, not level spawned + MF_SHADOW = 0x00040000, // actor is hard for monsters to see + MF_NOBLOOD = 0x00080000, // don't bleed when shot (use puff) + MF_CORPSE = 0x00100000, // don't stop moving halfway off a step + MF_INFLOAT = 0x00200000, // floating to a height for a move, don't + // auto float to target's height + MF_INBOUNCE = 0x00200000, // used by Heretic bouncing missiles + + MF_COUNTKILL = 0x00400000, // count towards intermission kill total + MF_COUNTITEM = 0x00800000, // count towards intermission item total + + MF_SKULLFLY = 0x01000000, // skull in flight + MF_NOTDMATCH = 0x02000000, // don't spawn in death match (key cards) + + MF_SPAWNSOUNDSOURCE = 0x04000000, // Plays missile's see sound at spawning object. + MF_FRIENDLY = 0x08000000, // [RH] Friendly monsters for Strife (and MBF) + MF_UNMORPHED = 0x10000000, // [RH] Actor is the unmorphed version of something else + MF_NOLIFTDROP = 0x20000000, // [RH] Used with MF_NOGRAVITY to avoid dropping with lifts + MF_STEALTH = 0x40000000, // [RH] Andy Baker's stealth monsters + MF_ICECORPSE = 0x80000000, // a frozen corpse (for blasting) [RH] was 0x800000 + + // --- dummies for unknown/unimplemented Strife flags --- + MF_STRIFEx8000000 = 0, // seems related to MF_SHADOW +}; + +// --- mobj.flags2 --- +enum ActorFlag2 +{ + MF2_DONTREFLECT = 0x00000001, // this projectile cannot be reflected + MF2_WINDTHRUST = 0x00000002, // gets pushed around by the wind specials + MF2_DONTSEEKINVISIBLE=0x00000004, // For seeker missiles: Don't home in on invisible/shadow targets + MF2_BLASTED = 0x00000008, // actor will temporarily take damage from impact + MF2_FLY = 0x00000010, // fly mode is active + MF2_FLOORCLIP = 0x00000020, // if feet are allowed to be clipped + MF2_SPAWNFLOAT = 0x00000040, // spawn random float z + MF2_NOTELEPORT = 0x00000080, // does not teleport + MF2_RIP = 0x00000100, // missile rips through solid targets + MF2_PUSHABLE = 0x00000200, // can be pushed by other moving actors + MF2_SLIDE = 0x00000400, // slides against walls + MF2_ONMOBJ = 0x00000800, // actor is resting on top of another actor + MF2_PASSMOBJ = 0x00001000, // Enable z block checking. If on, + // this flag will allow the actor to + // pass over/under other actors. + MF2_CANNOTPUSH = 0x00002000, // cannot push other pushable mobjs + MF2_THRUGHOST = 0x00004000, // missile will pass through ghosts [RH] was 8 + MF2_BOSS = 0x00008000, // mobj is a major boss + + MF2_DONTTRANSLATE = 0x00010000, // Don't apply palette translations + MF2_NODMGTHRUST = 0x00020000, // does not thrust target when damaging + MF2_TELESTOMP = 0x00040000, // mobj can stomp another + MF2_FLOATBOB = 0x00080000, // use float bobbing z movement + MF2_THRUACTORS = 0x00100000, // performs no actor<->actor collision checks + MF2_IMPACT = 0x00200000, // an MF_MISSILE mobj can activate SPAC_IMPACT + MF2_PUSHWALL = 0x00400000, // mobj can push walls + MF2_MCROSS = 0x00800000, // can activate monster cross lines + MF2_PCROSS = 0x01000000, // can activate projectile cross lines + MF2_CANTLEAVEFLOORPIC=0x02000000, // stay within a certain floor type + MF2_NONSHOOTABLE = 0x04000000, // mobj is totally non-shootable, + // but still considered solid + MF2_INVULNERABLE = 0x08000000, // mobj is invulnerable + MF2_DORMANT = 0x10000000, // thing is dormant + MF2_ARGSDEFINED = 0x20000000, // Internal flag used by DECORATE to signal that the + // args should not be taken from the mapthing definition + MF2_SEEKERMISSILE = 0x40000000, // is a seeker (for reflection) + MF2_REFLECTIVE = 0x80000000, // reflects missiles +}; + +// --- mobj.flags3 --- +enum ActorFlag3 +{ + MF3_FLOORHUGGER = 0x00000001, // Missile stays on floor + MF3_CEILINGHUGGER = 0x00000002, // Missile stays on ceiling + MF3_NORADIUSDMG = 0x00000004, // Actor does not take radius damage + MF3_GHOST = 0x00000008, // Actor is a ghost + MF3_ALWAYSPUFF = 0x00000010, // Puff always appears, even when hit nothing + MF3_SPECIALFLOORCLIP= 0x00000020, // Actor uses floorclip for special effect (e.g. Wraith) + MF3_DONTSPLASH = 0x00000040, // Thing doesn't make a splash + MF3_NOSIGHTCHECK = 0x00000080, // Go after first acceptable target without checking sight + MF3_DONTOVERLAP = 0x00000100, // Don't pass over/under other things with this bit set + MF3_DONTMORPH = 0x00000200, // Immune to arti_egg + MF3_DONTSQUASH = 0x00000400, // Death ball can't squash this actor + MF3_EXPLOCOUNT = 0x00000800, // Don't explode until special2 counts to special1 + MF3_FULLVOLACTIVE = 0x00001000, // Active sound is played at full volume + MF3_ISMONSTER = 0x00002000, // Actor is a monster + MF3_SKYEXPLODE = 0x00004000, // Explode missile when hitting sky + MF3_STAYMORPHED = 0x00008000, // Monster cannot unmorph + MF3_DONTBLAST = 0x00010000, // Actor cannot be pushed by blasting + MF3_CANBLAST = 0x00020000, // Actor is not a monster but can be blasted + MF3_NOTARGET = 0x00040000, // This actor not targetted when it hurts something else + MF3_DONTGIB = 0x00080000, // Don't gib this corpse + MF3_NOBLOCKMONST = 0x00100000, // Can cross ML_BLOCKMONSTERS lines + MF3_CRASHED = 0x00200000, // Actor entered its crash state + MF3_FULLVOLDEATH = 0x00400000, // DeathSound is played full volume (for missiles) + MF3_AVOIDMELEE = 0x00800000, // Avoids melee attacks (same as MBF's monster_backing but must be explicitly set) + MF3_SCREENSEEKER = 0x01000000, // Fails the IsOkayToAttack test if potential target is outside player FOV + MF3_FOILINVUL = 0x02000000, // Actor can hurt MF2_INVULNERABLE things + MF3_NOTELEOTHER = 0x04000000, // Monster is unaffected by teleport other artifact + MF3_BLOODLESSIMPACT = 0x08000000, // Projectile does not leave blood + MF3_NOEXPLODEFLOOR = 0x10000000, // Missile stops at floor instead of exploding + MF3_WARNBOT = 0x20000000, // Missile warns bot + MF3_PUFFONACTORS = 0x40000000, // Puff appears even when hit bleeding actors + MF3_HUNTPLAYERS = 0x80000000, // Used with TIDtoHate, means to hate players too +}; + +// --- mobj.flags4 --- +enum ActorFlag4 +{ + MF4_NOHATEPLAYERS = 0x00000001, // Ignore player attacks + MF4_QUICKTORETALIATE= 0x00000002, // Always switch targets when hurt + MF4_NOICEDEATH = 0x00000004, // Actor never enters an ice death, not even the generic one + MF4_BOSSDEATH = 0x00000008, // A_FreezeDeathChunks calls A_BossDeath + MF4_RANDOMIZE = 0x00000010, // Missile has random initial tic count + MF4_NOSKIN = 0x00000020, // Player cannot use skins + MF4_FIXMAPTHINGPOS = 0x00000040, // Fix this actor's position when spawned as a map thing + MF4_ACTLIKEBRIDGE = 0x00000080, // Pickups can "stand" on this actor / cannot be moved by any sector action. + MF4_STRIFEDAMAGE = 0x00000100, // Strife projectiles only do up to 4x damage, not 8x + + MF4_CANUSEWALLS = 0x00000200, // Can activate 'use' specials + MF4_MISSILEMORE = 0x00000400, // increases the chance of a missile attack + MF4_MISSILEEVENMORE = 0x00000800, // significantly increases the chance of a missile attack + MF4_FORCERADIUSDMG = 0x00001000, // if put on an object it will override MF3_NORADIUSDMG + MF4_DONTFALL = 0x00002000, // Doesn't have NOGRAVITY disabled when dying. + MF4_SEESDAGGERS = 0x00004000, // This actor can see you striking with a dagger + MF4_INCOMBAT = 0x00008000, // Don't alert others when attacked by a dagger + MF4_LOOKALLAROUND = 0x00010000, // Monster has eyes in the back of its head + MF4_STANDSTILL = 0x00020000, // Monster should not chase targets unless attacked? + MF4_SPECTRAL = 0x00040000, + MF4_SCROLLMOVE = 0x00080000, // velocity has been applied by a scroller + MF4_NOSPLASHALERT = 0x00100000, // Splashes don't alert this monster + MF4_SYNCHRONIZED = 0x00200000, // For actors spawned at load-time only: Do not randomize tics + MF4_NOTARGETSWITCH = 0x00400000, // monster never switches target until current one is dead + MF4_VFRICTION = 0x00800000, // Internal flag used by A_PainAttack to push a monster down + MF4_DONTHARMCLASS = 0x01000000, // Don't hurt one's own kind with explosions (hitscans, too?) + MF4_SHIELDREFLECT = 0x02000000, + MF4_DEFLECT = 0x04000000, // different projectile reflection styles + MF4_ALLOWPARTICLES = 0x08000000, // this puff type can be replaced by particles + MF4_NOEXTREMEDEATH = 0x10000000, // this projectile or weapon never gibs its victim + MF4_EXTREMEDEATH = 0x20000000, // this projectile or weapon always gibs its victim + MF4_FRIGHTENED = 0x40000000, // Monster runs away from player + MF4_BOSSSPAWNED = 0x80000000, // Spawned by a boss spawn cube +}; + +// --- mobj.flags5 --- + +enum ActorFlag5 +{ + MF5_DONTDRAIN = 0x00000001, // cannot be drained health from. + /* = 0x00000002, reserved for use by scripting branch */ + MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. + 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 + MF5_CHASEGOAL = 0x00000080, // Walks to goal instead of target if a valid goal is set. + MF5_BLOODSPLATTER = 0x00000100, // Blood splatter like in Raven's games. + MF5_OLDRADIUSDMG = 0x00000200, // Use old radius damage code (for barrels and boss brain) + MF5_DEHEXPLOSION = 0x00000400, // Use the DEHACKED explosion options when this projectile explodes + MF5_PIERCEARMOR = 0x00000800, // Armor doesn't protect against damage from this actor + MF5_NOBLOODDECALS = 0x00001000, // Actor bleeds but doesn't spawn blood decals + MF5_USESPECIAL = 0x00002000, // Actor executes its special when being 'used'. + MF5_NOPAIN = 0x00004000, // If set the pain state won't be entered + MF5_ALWAYSFAST = 0x00008000, // always uses 'fast' attacking logic + MF5_NEVERFAST = 0x00010000, // never uses 'fast' attacking logic + MF5_ALWAYSRESPAWN = 0x00020000, // always respawns, regardless of skill setting + MF5_NEVERRESPAWN = 0x00040000, // never respawns, regardless of skill setting + MF5_DONTRIP = 0x00080000, // Ripping projectiles explode when hitting this actor + MF5_NOINFIGHTING = 0x00100000, // This actor doesn't switch target when it's hurt + MF5_NOINTERACTION = 0x00200000, // Thing is completely excluded from any gameplay related checks + MF5_NOTIMEFREEZE = 0x00400000, // Actor is not affected by time freezer + MF5_PUFFGETSOWNER = 0x00800000, // [BB] Sets the owner of the puff to the player who fired it + MF5_SPECIALFIREDAMAGE=0x01000000, // Special treatment of PhoenixFX1 turned into a flag to remove + // dependence of main engine code of specific actor types. + MF5_SUMMONEDMONSTER = 0x02000000, // To mark the friendly Minotaur. Hopefully to be generalized later. + MF5_NOVERTICALMELEERANGE=0x04000000,// Does not check vertical distance for melee range + MF5_BRIGHT = 0x08000000, // Actor is always rendered fullbright + MF5_CANTSEEK = 0x10000000, // seeker missiles cannot home in on this actor + MF5_INCONVERSATION = 0x20000000, // Actor is having a conversation + MF5_PAINLESS = 0x40000000, // Actor always inflicts painless damage. + MF5_MOVEWITHSECTOR = 0x80000000, // P_ChangeSector() will still process this actor if it has MF_NOBLOCKMAP +}; + +// --- mobj.flags6 --- +enum ActorFlag6 +{ + MF6_NOBOSSRIP = 0x00000001, // For rippermissiles: Don't rip through bosses. + MF6_THRUSPECIES = 0x00000002, // Actors passes through other of the same species. + MF6_MTHRUSPECIES = 0x00000004, // Missile passes through actors of its shooter's species. + MF6_FORCEPAIN = 0x00000008, // forces target into painstate (unless it has the NOPAIN flag) + MF6_NOFEAR = 0x00000010, // Not scared of frightening players + MF6_BUMPSPECIAL = 0x00000020, // Actor executes its special when being collided (as the ST flag) + MF6_DONTHARMSPECIES = 0x00000040, // Don't hurt one's own species with explosions (hitscans, too?) + MF6_STEPMISSILE = 0x00000080, // Missile can "walk" up steps + MF6_NOTELEFRAG = 0x00000100, // [HW] Actor can't be telefragged + MF6_TOUCHY = 0x00000200, // From MBF: killough 11/98: dies when solids touch it + MF6_CANJUMP = 0x00000400, // From MBF: a dedicated flag instead of the BOUNCES+FLOAT+sentient combo + MF6_JUMPDOWN = 0x00000800, // From MBF: generalization of dog behavior wrt. dropoffs. + MF6_VULNERABLE = 0x00001000, // Actor can be damaged (even if not shootable). + MF6_ARMED = 0x00002000, // From MBF: Object is armed (for MF6_TOUCHY objects) + MF6_FALLING = 0x00004000, // From MBF: Object is falling (for pseudotorque simulation) + MF6_LINEDONE = 0x00008000, // From MBF: Object has already run a line effect + MF6_NOTRIGGER = 0x00010000, // actor cannot trigger any line actions + MF6_SHATTERING = 0x00020000, // marks an ice corpse for forced shattering + MF6_KILLED = 0x00040000, // Something that was killed (but not necessarily a corpse) + MF6_BLOCKEDBYSOLIDACTORS = 0x00080000, // Blocked by solid actors, even if not solid itself + MF6_ADDITIVEPOISONDAMAGE = 0x00100000, + MF6_ADDITIVEPOISONDURATION = 0x00200000, + MF6_NOMENU = 0x00400000, // Player class should not appear in the class selection menu. + MF6_BOSSCUBE = 0x00800000, // Actor spawned by A_BrainSpit, flagged for timefreeze reasons. + MF6_SEEINVISIBLE = 0x01000000, // Monsters can see invisible player. + 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. + MF6_RELATIVETOFLOOR = 0x80000000, // [RC] Make flying actors be affected by lifts. +}; + +// --- mobj.flags7 --- +enum ActorFlag7 +{ + MF7_NEVERTARGET = 0x00000001, // can not be targetted at all, even if monster friendliness is considered. + MF7_NOTELESTOMP = 0x00000002, // cannot telefrag under any circumstances (even when set by MAPINFO) + MF7_ALWAYSTELEFRAG = 0x00000004, // will unconditionally be telefragged when in the way. Overrides all other settings. + MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag + MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag + MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters. + MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat. + MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode. + MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. + MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. + MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. + MF7_THRUREFLECT = 0x00000800, // Actors who are reflective cause the missiles to not slow down or change angles. + MF7_MIRRORREFLECT = 0x00001000, // Actor is turned directly 180 degrees around when reflected. + MF7_AIMREFLECT = 0x00002000, // Actor is directly reflected straight back at the one who fired the projectile. + MF7_HITTARGET = 0x00004000, // The actor the projectile dies on is set to target, provided it's targetable anyway. + MF7_HITMASTER = 0x00008000, // Same as HITTARGET, except it's master instead of target. + MF7_HITTRACER = 0x00010000, // Same as HITTARGET, but for tracer. + MF7_FLYCHEAT = 0x00020000, // must be part of the actor so that it can be tracked properly + MF7_NODECAL = 0x00040000, // [ZK] Forces puff to have no impact decal + MF7_FORCEDECAL = 0x00080000, // [ZK] Forces puff's decal to override the weapon's. + MF7_LAXTELEFRAGDMG = 0x00100000, // [MC] Telefrag damage can be reduced. + MF7_ICESHATTER = 0x00200000, // [MC] Shatters ice corpses regardless of damagetype. +}; + +// --- mobj.renderflags --- +enum ActorRenderFlag +{ + RF_XFLIP = 0x0001, // Flip sprite horizontally + RF_YFLIP = 0x0002, // Flip sprite vertically + RF_ONESIDED = 0x0004, // Wall/floor sprite is visible from front only + RF_FULLBRIGHT = 0x0010, // Sprite is drawn at full brightness + + RF_RELMASK = 0x0300, // ---Relative z-coord for bound actors (these obey texture pegging) + RF_RELABSOLUTE = 0x0000, // Actor z is absolute + RF_RELUPPER = 0x0100, // Actor z is relative to upper part of wall + RF_RELLOWER = 0x0200, // Actor z is relative to lower part of wall + RF_RELMID = 0x0300, // Actor z is relative to middle part of wall + + RF_CLIPMASK = 0x0c00, // ---Clipping for bound actors + RF_CLIPFULL = 0x0000, // Clip sprite to full height of wall + RF_CLIPUPPER = 0x0400, // Clip sprite to upper part of wall + RF_CLIPMID = 0x0800, // Clip sprite to mid part of wall + RF_CLIPLOWER = 0x0c00, // Clip sprite to lower part of wall + + RF_DECALMASK = RF_RELMASK|RF_CLIPMASK, + + RF_SPRITETYPEMASK = 0x7000, // ---Different sprite types, not all implemented + RF_FACESPRITE = 0x0000, // Face sprite + RF_WALLSPRITE = 0x1000, // Wall sprite + RF_FLOORSPRITE = 0x2000, // Floor sprite + RF_VOXELSPRITE = 0x3000, // Voxel object + RF_INVISIBLE = 0x8000, // Don't bother drawing this actor + + RF_FORCEYBILLBOARD = 0x10000, // [BB] OpenGL only: draw with y axis billboard, i.e. anchored to the floor (overrides gl_billboard_mode setting) + RF_FORCEXYBILLBOARD = 0x20000, // [BB] OpenGL only: draw with xy axis billboard, i.e. unanchored (overrides gl_billboard_mode setting) +}; + +#define TRANSLUC25 (FRACUNIT/4) +#define TRANSLUC33 (FRACUNIT/3) +#define TRANSLUC50 (FRACUNIT/2) +#define TRANSLUC66 ((FRACUNIT*2)/3) +#define TRANSLUC75 ((FRACUNIT*3)/4) + +// also #defines OPAQUE +#ifndef OPAQUE +#define OPAQUE (FRACUNIT) +#endif + +// This translucency value produces the closest match to Heretic's TINTTAB. +// ~40% of the value of the overlaid image shows through. +#define HR_SHADOW (0x6800) + +// Hexen's TINTTAB is the same as Heretic's, just reversed. +#define HX_SHADOW (0x9800) +#define HX_ALTSHADOW (0x6800) + +// This could easily be a bool but then it'd be much harder to find later. ;) +enum replace_t +{ + NO_REPLACE = 0, + ALLOW_REPLACE = 1 +}; + +enum ActorBounceFlag +{ + BOUNCE_Walls = 1<<0, // bounces off of walls + BOUNCE_Floors = 1<<1, // bounces off of floors + 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 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 + // the compatibility handlers for the old bounce flags. + BOUNCE_NoWallSound = 1<<8, // don't make noise when bouncing off a wall + BOUNCE_Quiet = 1<<9, // Strife's grenades don't make a bouncing sound + BOUNCE_ExplodeOnWater = 1<<10, // explodes when hitting a water surface + BOUNCE_CanBounceWater = 1<<11, // can bounce on water + // 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, + + // The three "standard" types of bounciness are: + // HERETIC - Missile will only bounce off the floor once and then enter + // its death state. It does not bounce off walls at all. + // HEXEN - Missile bounces off of walls and floors indefinitely. + // DOOM - Like Hexen, but the bounce turns off if its vertical velocity + // is too low. + BOUNCE_None = 0, + BOUNCE_Heretic = BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_HereticType, + BOUNCE_Doom = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors | BOUNCE_AutoOff, + BOUNCE_Hexen = BOUNCE_Walls | BOUNCE_Floors | BOUNCE_Ceilings | BOUNCE_Actors, + BOUNCE_Grenade = BOUNCE_MBF | BOUNCE_Doom, // Bounces on walls and flats like ZDoom bounce. + BOUNCE_Classic = BOUNCE_MBF | BOUNCE_Floors | BOUNCE_Ceilings, // Bounces on flats only, but + // does not die when bouncing. + + // combined types + BOUNCE_DoomCompat = BOUNCE_Doom | BOUNCE_UseSeeSound, + BOUNCE_HereticCompat = BOUNCE_Heretic | BOUNCE_UseSeeSound, + BOUNCE_HexenCompat = BOUNCE_Hexen | BOUNCE_UseSeeSound + + // The distinction between BOUNCE_Actors and BOUNCE_AllActors: A missile with + // BOUNCE_Actors set will bounce off of reflective and "non-sentient" actors. + // A missile that also has BOUNCE_AllActors set will bounce off of any actor. + // For compatibility reasons when BOUNCE_Actors was implied by the bounce type + // being "Doom" or "Hexen" and BOUNCE_AllActors was the separate + // MF5_BOUNCEONACTORS, you must set BOUNCE_Actors for BOUNCE_AllActors to have + // an effect. + + +}; + +// [TP] Flagset definitions +typedef TFlags ActorFlags; +typedef TFlags ActorFlags2; +typedef TFlags ActorFlags3; +typedef TFlags ActorFlags4; +typedef TFlags ActorFlags5; +typedef TFlags ActorFlags6; +typedef TFlags ActorFlags7; +typedef TFlags ActorRenderFlags; +typedef TFlags ActorBounceFlags; +DEFINE_TFLAGS_OPERATORS (ActorFlags) +DEFINE_TFLAGS_OPERATORS (ActorFlags2) +DEFINE_TFLAGS_OPERATORS (ActorFlags3) +DEFINE_TFLAGS_OPERATORS (ActorFlags4) +DEFINE_TFLAGS_OPERATORS (ActorFlags5) +DEFINE_TFLAGS_OPERATORS (ActorFlags6) +DEFINE_TFLAGS_OPERATORS (ActorFlags7) +DEFINE_TFLAGS_OPERATORS (ActorRenderFlags) +DEFINE_TFLAGS_OPERATORS (ActorBounceFlags) + +// Used to affect the logic for thing activation through death, USESPECIAL and BUMPSPECIAL +// "thing" refers to what has the flag and the special, "trigger" refers to what used or bumped it +enum EThingSpecialActivationType +{ + THINGSPEC_Default = 0, // Normal behavior: a player must be the trigger, and is the activator + THINGSPEC_ThingActs = 1, // The thing itself is the activator of the special + THINGSPEC_ThingTargets = 1<<1, // The thing changes its target to the trigger + THINGSPEC_TriggerTargets = 1<<2, // The trigger changes its target to the thing + THINGSPEC_MonsterTrigger = 1<<3, // The thing can be triggered by a monster + THINGSPEC_MissileTrigger = 1<<4, // The thing can be triggered by a projectile + THINGSPEC_ClearSpecial = 1<<5, // Clears special after successful activation + THINGSPEC_NoDeathSpecial = 1<<6, // Don't activate special on death + THINGSPEC_TriggerActs = 1<<7, // The trigger is the activator of the special + // (overrides LEVEL_ACTOWNSPECIAL Hexen hack) + THINGSPEC_Activate = 1<<8, // The thing is activated when triggered + THINGSPEC_Deactivate = 1<<9, // The thing is deactivated when triggered + THINGSPEC_Switch = 1<<10, // The thing is alternatively activated and deactivated when triggered +}; + +// [RH] Like msecnode_t, but for the blockmap +struct FBlockNode +{ + AActor *Me; // actor this node references + int BlockIndex; // index into blocklinks for the block this node is in + FBlockNode **PrevActor; // previous actor in this block + FBlockNode *NextActor; // next actor in this block + FBlockNode **PrevBlock; // previous block this actor is in + FBlockNode *NextBlock; // next block this actor is in + + static FBlockNode *Create (AActor *who, int x, int y); + void Release (); + + static FBlockNode *FreeBlocks; +}; + +class FDecalBase; +class AInventory; + +inline AActor *GetDefaultByName (const char *name) +{ + return (AActor *)(PClass::FindClass(name)->Defaults); +} + +inline AActor *GetDefaultByType (const PClass *type) +{ + return (AActor *)(type->Defaults); +} + +template +inline T *GetDefault () +{ + return (T *)(RUNTIME_CLASS(T)->Defaults); +} + +struct line_t; +struct secplane_t; +struct FStrifeDialogueNode; + +enum +{ + AMETA_BASE = 0x12000, + + AMETA_Obituary, // string (player was killed by this actor) + AMETA_HitObituary, // string (player was killed by this actor in melee) + AMETA_DeathHeight, // fixed (height on normal death) + AMETA_BurnHeight, // fixed (height on burning death) + AMETA_StrifeName, // string (for named Strife objects) + AMETA_BloodColor, // colorized blood + AMETA_GibHealth, // negative health below which this monster dies an extreme death + AMETA_WoundHealth, // health needed to enter wound state + AMETA_FastSpeed, // Speed in fast mode + AMETA_RDFactor, // Radius damage factor + AMETA_CameraHeight, // Height of camera when used as such + AMETA_HowlSound, // Sound being played when electrocuted or poisoned + AMETA_BloodType, // Blood replacement type + AMETA_BloodType2, // Bloodsplatter replacement type + AMETA_BloodType3, // AxeBlood replacement type +}; + +struct fixedvec3 +{ + fixed_t x, y, z; + + operator FVector3() + { + return FVector3(FIXED2FLOAT(x), FIXED2FLOAT(y), FIXED2FLOAT(z)); + } + + operator TVector3() + { + return TVector3(FIXED2DBL(x), FIXED2DBL(y), FIXED2DBL(z)); + } +}; + +struct fixedvec2 +{ + fixed_t x, y; + + operator FVector2() + { + return FVector2(FIXED2FLOAT(x), FIXED2FLOAT(y)); + } + + operator TVector2() + { + return TVector2(FIXED2DBL(x), FIXED2DBL(y)); + } +}; + +struct FDropItem +{ + FName Name; + int probability; + int amount; + FDropItem * Next; +}; + +class FDropItemPtrArray : public TArray +{ +public: + ~FDropItemPtrArray() + { + Clear(); + } + + void Clear(); +}; + +extern FDropItemPtrArray DropItemList; + +void FreeDropItemChain(FDropItem *chain); +int StoreDropItemChain(FDropItem *chain); +fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); // since we cannot include p_local here... +angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); // same reason here with r_defs.h + + +// Map Object definition. +class AActor : public DThinker +{ + DECLARE_CLASS (AActor, DThinker) + HAS_OBJECT_POINTERS +public: + AActor () throw(); + AActor (const AActor &other) throw(); + AActor &operator= (const AActor &other); + void Destroy (); + ~AActor (); + + void Serialize (FArchive &arc); + + static AActor *StaticSpawn (const PClass *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement, bool SpawningMapThing = false); + + inline AActor *GetDefault () const + { + return (AActor *)(RUNTIME_TYPE(this)->Defaults); + } + + FDropItem *GetDropItems(); + + // Return true if the monster should use a missile attack, false for melee + bool SuggestMissileAttack (fixed_t dist); + + // Adjusts the angle for deflection/reflection of incoming missiles + // Returns true if the missile should be allowed to explode anyway + bool AdjustReflectionAngle (AActor *thing, angle_t &angle); + + // Returns true if this actor is within melee range of its target + bool CheckMeleeRange(); + + 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); + + virtual void Tick (); + + // Called when actor dies + 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, FName damagetype); + + // Like DoSpecialDamage, but called on the actor receiving the damage. + virtual int TakeSpecialDamage (AActor *inflictor, AActor *source, int damage, FName damagetype); + + // Centaurs and ettins squeal when electrocuted, poisoned, or "holy"-ed + // Made a metadata property so no longer virtual + void Howl (); + + // Actor just hit the floor + virtual void HitFloor (); + + // plays bouncing sound + void PlayBounceSound(bool onfloor); + + // Called when an actor with MF_MISSILE and MF2_FLOORBOUNCE hits the floor + virtual bool FloorBounceMissile (secplane_t &plane); + + // Called when an actor is to be reflected by a disc of repulsion. + // Returns true to continue normal blast processing. + virtual bool SpecialBlastHandling (AActor *source, fixed_t strength); + + // Called by RoughBlockCheck + bool IsOkayToAttack (AActor *target); + + // Plays the actor's ActiveSound if its voice isn't already making noise. + void PlayActiveSound (); + + // Actor had MF_SKULLFLY set and rammed into something + // Returns false to stop moving and true to keep moving + virtual bool Slam (AActor *victim); + + // Called by PIT_CheckThing() and needed for some Hexen things. + // Returns -1 for normal behavior, 0 to return false, and 1 to return true. + // I'm not sure I like it this way, but it will do for now. + virtual int SpecialMissileHit (AActor *victim); + + // Returns true if it's okay to switch target to "other" after being attacked by it. + virtual bool OkayToSwitchTarget (AActor *other); + + // Something just touched this actor. + virtual void Touch (AActor *toucher); + + // Adds the item to this actor's inventory and sets its Owner. + virtual void AddInventory (AInventory *item); + + // Removes the item from the inventory list. + virtual void RemoveInventory (AInventory *item); + + // Take the amount value of an item from the inventory list. + // If nothing is left, the item may be destroyed. + // Returns true if the initial item count is positive. + virtual bool TakeInventory (const PClass *itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); + + // Uses an item and removes it from the inventory. + virtual bool UseInventory (AInventory *item); + + // 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; + + // Finds the first item of a particular type. + AInventory *FindInventory (const PClass *type, bool subclass = false); + AInventory *FindInventory (FName type); + template T *FindInventory () + { + return static_cast (FindInventory (RUNTIME_CLASS(T))); + } + + // Adds one item of a particular type. Returns NULL if it could not be added. + AInventory *GiveInventoryType (const PClass *type); + + // Returns the first item held with IF_INVBAR set. + AInventory *FirstInv (); + + // Tries to give the actor some ammo. + bool GiveAmmo (const PClass *type, int amount); + + // Destroys all the inventory the actor is holding. + void DestroyAllInventory (); + + // Set the alphacolor field properly + void SetShade (DWORD rgb); + void SetShade (int r, int g, int b); + + // Plays a conversation animation + void ConversationAnimation (int animnum); + + // Make this actor hate the same things as another actor + void CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth=true); + + // Moves the other actor's inventory to this one + void ObtainInventory (AActor *other); + + // Die. Now. + virtual bool Massacre (); + + // Transforms the actor into a finely-ground paste + virtual bool Grind(bool items); + + // Get this actor's team + int GetTeam(); + + // Is the other actor on my team? + bool IsTeammate (AActor *other); + + // Is the other actor my friend? + bool IsFriend (AActor *other); + + // 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(); + + // Return starting health adjusted by skill level + int SpawnHealth(); + int GibHealth(); + + inline bool isMissile(bool precise=true) + { + return (flags&MF_MISSILE) || (precise && GetDefault()->flags&MF_MISSILE); + } + + // Check for monsters that count as kill but excludes all friendlies. + bool CountsAsKill() const + { + return (flags & MF_COUNTKILL) && !(flags & MF_FRIENDLY); + } + + bool intersects(AActor *other) const + { + fixed_t blockdist = radius + other->radius; + return ( abs(x - other->x) < blockdist && abs(y - other->y) < blockdist); + } + + PalEntry GetBloodColor() const + { + return (PalEntry)GetClass()->Meta.GetMetaInt(AMETA_BloodColor); + } + + // These also set CF_INTERPVIEW for players. + void SetPitch(int p, bool interpolate, bool forceclamp = false); + void SetAngle(angle_t ang, bool interpolate); + void SetRoll(angle_t roll, bool interpolate); + + const PClass *GetBloodType(int type = 0) const + { + const PClass *bloodcls; + if (type == 0) + { + bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType, NAME_Blood)); + } + else if (type == 1) + { + bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType2, NAME_BloodSplatter)); + } + else if (type == 2) + { + bloodcls = PClass::FindClass((ENamedName)GetClass()->Meta.GetMetaInt(AMETA_BloodType3, NAME_AxeBlood)); + } + else return NULL; + + if (bloodcls != NULL) + { + bloodcls = bloodcls->GetReplacement(); + } + return bloodcls; + } + + // 'absolute' is reserved for a linked portal implementation which needs + // to distinguish between portal-aware and portal-unaware distance calculation. + fixed_t AproxDistance(AActor *other, bool absolute = false) + { + return P_AproxDistance(x - other->x, y - other->y); + } + + // same with 'ref' here. + fixed_t AproxDistance(fixed_t otherx, fixed_t othery, AActor *ref = NULL) + { + return P_AproxDistance(x - otherx, y - othery); + } + + fixed_t AproxDistance(AActor *other, fixed_t xadd, fixed_t yadd, bool absolute = false) + { + return P_AproxDistance(x - other->x + xadd, y - other->y + yadd); + } + + fixed_t AproxDistance3D(AActor *other, bool absolute = false) + { + return P_AproxDistance(AproxDistance(other), z - other->z); + } + + // more precise, but slower version, being used in a few places + fixed_t Distance2D(AActor *other, bool absolute = false) + { + return xs_RoundToInt(FVector2(x - other->x, y - other->y).Length()); + } + + // a full 3D version of the above + fixed_t Distance3D(AActor *other, bool absolute = false) + { + return xs_RoundToInt(FVector3(x - other->x, y - other->y, z - other->z).Length()); + } + + angle_t AngleTo(AActor *other, bool absolute = false) const + { + return R_PointToAngle2(x, y, other->x, other->y); + } + + angle_t AngleTo(AActor *other, fixed_t oxofs, fixed_t oyofs, bool absolute = false) const + { + return R_PointToAngle2(x, y, other->x + oxofs, other->y + oyofs); + } + + fixed_t AngleTo(fixed_t otherx, fixed_t othery, AActor *ref = NULL) + { + return R_PointToAngle2(x, y, otherx, othery); + } + + fixed_t AngleXYTo(fixed_t myx, fixed_t myy, AActor *other, bool absolute = false) + { + return R_PointToAngle2(myx, myy, other->x, other->y); + } + + fixedvec2 Vec2To(AActor *other) const + { + fixedvec2 ret = { other->x - x, other->y - y }; + return ret; + } + + fixedvec3 Vec3To(AActor *other) const + { + fixedvec3 ret = { other->x - x, other->y - y, other->z - z }; + return ret; + } + + fixedvec2 Vec2Offset(fixed_t dx, fixed_t dy, bool absolute = false) const + { + fixedvec2 ret = { x + dx, y + dy }; + return ret; + } + + + fixedvec2 Vec2Angle(fixed_t length, angle_t angle, bool absolute = false) const + { + fixedvec2 ret = { x + FixedMul(length, finecosine[angle >> ANGLETOFINESHIFT]), + y + FixedMul(length, finesine[angle >> ANGLETOFINESHIFT]) }; + return ret; + } + + fixedvec3 Vec3Offset(fixed_t dx, fixed_t dy, fixed_t dz, bool absolute = false) const + { + fixedvec3 ret = { x + dx, y + dy, z + dz }; + return ret; + } + + fixedvec3 Vec3Angle(fixed_t length, angle_t angle, fixed_t dz, bool absolute = false) const + { + fixedvec3 ret = { x + FixedMul(length, finecosine[angle >> ANGLETOFINESHIFT]), + y + FixedMul(length, finesine[angle >> ANGLETOFINESHIFT]), z + dz }; + return ret; + } + + void Move(fixed_t dx, fixed_t dy, fixed_t dz) + { + SetOrigin(x + dx, y + dy, z + dz, true); + } + + void SetOrigin(const fixedvec3 & npos, bool moving) + { + SetOrigin(npos.x, npos.y, npos.z, moving); + } + + inline void SetFriendPlayer(player_t *player); + + bool IsVisibleToPlayer() const; + + // Calculate amount of missile damage + virtual int GetMissileDamage(int mask, int add); + + bool CanSeek(AActor *target) const; + + fixed_t GetGravity() const; + bool IsSentient() const; + 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. + fixed_t x,y,z; + AActor *snext, **sprev; // links in sector (if needed) + angle_t angle; + WORD sprite; // used to find patch_t and flip value + BYTE frame; // sprite frame to draw + fixed_t scaleX, scaleY; // Scaling values; FRACUNIT is normal size + FRenderStyle RenderStyle; // Style to draw this actor with + ActorRenderFlags renderflags; // Different rendering flags + FTextureID picnum; // Draw this instead of sprite if valid + DWORD effects; // [RH] see p_effect.h + fixed_t alpha; + DWORD fillcolor; // Color to draw when STYLE_Shaded + +// interaction info + fixed_t pitch; + angle_t roll; // This was fixed_t before, which is probably wrong + FBlockNode *BlockNode; // links in blocks (if needed) + struct sector_t *Sector; + subsector_t * subsector; + fixed_t floorz, ceilingz; // closest together of contacted secs + fixed_t dropoffz; // killough 11/98: the lowest floor over all contacted Sectors. + + struct sector_t *floorsector; + FTextureID floorpic; // contacted sec floorpic + int floorterrain; + struct sector_t *ceilingsector; + FTextureID ceilingpic; // contacted sec ceilingpic + fixed_t radius, height; // for movement checking + fixed_t projectilepassheight; // height for clipping projectile movement against this actor + fixed_t velx, vely, velz; // velocity + SDWORD tics; // state tic counter + FState *state; + SDWORD Damage; // For missiles and monster railgun + int projectileKickback; + ActorFlags flags; + ActorFlags2 flags2; // Heretic flags + ActorFlags3 flags3; // [RH] Hexen/Heretic actor-dependant behavior made flaggable + ActorFlags4 flags4; // [RH] Even more flags! + ActorFlags5 flags5; // OMG! We need another one. + ActorFlags6 flags6; // Shit! Where did all the flags go? + ActorFlags7 flags7; // WHO WANTS TO BET ON 8!? + + // [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 weaponspecial; // Special info for weapons. + int health; + BYTE movedir; // 0-7 + SBYTE visdir; + SWORD movecount; // when 0, select a new dir + SWORD strafecount; // for MF3_AVOIDMELEE + TObjPtr target; // thing being chased/attacked (or NULL) + // also the originator for missiles + TObjPtr lastenemy; // Last known enemy -- killough 2/15/98 + TObjPtr LastHeard; // [RH] Last actor this one heard + SDWORD reactiontime; // if non 0, don't attack yet; used by + // player to freeze a bit after teleporting + SDWORD threshold; // if > 0, the target will be chased + // no matter what (even if shot) + player_t *player; // only valid if type of APlayerPawn + TObjPtr LastLookActor; // Actor last looked for (if TIDtoHate != 0) + fixed_t SpawnPoint[3]; // For nightmare respawn + WORD SpawnAngle; + int StartHealth; + BYTE WeaveIndexXY; // Separated from special2 because it's used by globally accessible functions. + BYTE WeaveIndexZ; + int skillrespawncount; + int TIDtoHate; // TID of things to hate (0 if none) + FNameNoInit Species; // For monster families + TObjPtr tracer; // Thing being chased/attacked for tracers + TObjPtr master; // Thing which spawned this one (prevents mutual attacks) + fixed_t floorclip; // value to use for floor clipping + int tid; // thing identifier + 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 + BYTE boomwaterlevel; // splash information for non-swimmable water sectors + BYTE MinMissileChance;// [RH] If a random # is > than this, then missile attack. + SBYTE LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0) + ActorBounceFlags BounceFlags; // which bouncing type? + DWORD SpawnFlags; // Increased to DWORD because of Doom 64 + fixed_t meleerange; // specifies how far a melee attack reaches. + fixed_t meleethreshold; // Distance below which a monster doesn't try to shoot missiles anynore + // but instead tries to come closer for a melee attack. + // This is not the same as meleerange + fixed_t maxtargetrange; // any target farther away cannot be attacked + fixed_t bouncefactor; // Strife's grenades use 50%, Hexen's Flechettes 70. + fixed_t wallbouncefactor; // The bounce factor for walls can be different. + int bouncecount; // Strife's grenades only bounce twice before exploding + fixed_t gravity; // [GRB] Gravity factor + fixed_t Friction; + int FastChaseStrafeCount; + fixed_t pushfactor; + int lastpush; + int activationtype; // How the thing behaves when activated with USESPECIAL or BUMPSPECIAL + int lastbump; // Last time the actor was bumped, used to control BUMPSPECIAL + int Score; // manipulated by score items, ACS or DECORATE. The engine doesn't use this itself for anything. + FString * Tag; // Strife's tag name. + int DesignatedTeam; // Allow for friendly fire cacluations to be done on non-players. + + AActor *BlockingMobj; // Actor that blocked the last move + line_t *BlockingLine; // Line that blocked the last move + + int PoisonDamage; // Damage received per tic from poison. + FNameNoInit PoisonDamageType; // Damage type dealt by poison. + int PoisonDuration; // Duration left for receiving poison damage. + int PoisonPeriod; // How often poison damage is applied. (Every X tics.) + + int PoisonDamageReceived; // Damage received per tic from poison. + FNameNoInit PoisonDamageTypeReceived; // Damage type received by poison. + int PoisonDurationReceived; // Duration left for receiving poison damage. + int PoisonPeriodReceived; // How often poison damage is applied. (Every X tics.) + TObjPtr Poisoner; // Last source of received poison damage. + + // a linked list of sectors where this object appears + struct msecnode_t *touching_sectorlist; // phares 3/14/98 + + TObjPtr Inventory; // [RH] This actor's inventory + DWORD InventoryID; // A unique ID to keep track of inventory items + + BYTE smokecounter; + BYTE FloatBobPhase; + BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc) + DWORD Translation; + + // [RH] Stuff that used to be part of an Actor Info + FSoundIDNoInit SeeSound; + FSoundIDNoInit AttackSound; + FSoundIDNoInit PainSound; + FSoundIDNoInit DeathSound; + FSoundIDNoInit ActiveSound; + FSoundIDNoInit UseSound; // [RH] Sound to play when an actor is used. + FSoundIDNoInit BounceSound; + FSoundIDNoInit WallBounceSound; + FSoundIDNoInit CrushPainSound; + + fixed_t Speed; + fixed_t FloatSpeed; + fixed_t MaxDropOffHeight, MaxStepHeight; + SDWORD Mass; + SWORD PainChance; + int PainThreshold; + FNameNoInit DamageType; + FNameNoInit DamageTypeReceived; + fixed_t DamageFactor; + fixed_t DamageMultiply; + + FNameNoInit PainType; + FNameNoInit DeathType; + const PClass *TeleFogSourceType; + const PClass *TeleFogDestType; + int RipperLevel; + int RipLevelMin; + int RipLevelMax; + + FState *SpawnState; + FState *SeeState; + FState *MeleeState; + FState *MissileState; + + + int ConversationRoot; // THe root of the current dialogue + FStrifeDialogueNode *Conversation; // [RH] The dialogue to show when this actor is "used." + + // [RH] Decal(s) this weapon/projectile generates on impact. + FDecalBase *DecalGenerator; + + // [RH] Used to interpolate the view to get >35 FPS + fixed_t PrevX, PrevY, PrevZ; + angle_t PrevAngle; + + // ThingIDs + static void ClearTIDHashes (); + void AddToHash (); + void RemoveFromHash (); + +private: + static AActor *TIDHash[128]; + static inline int TIDHASH (int key) { return key & 127; } + static FSharedStringArena mStringPropertyData; + + friend class FActorIterator; + friend bool P_IsTIDUsed(int tid); + + sector_t *LinkToWorldForMapThing (); + +public: + void LinkToWorld (bool buggy=false); + void LinkToWorld (sector_t *sector); + void UnlinkFromWorld (); + void AdjustFloorClip (); + void SetOrigin (fixed_t x, fixed_t y, fixed_t z, bool moving = false); + bool InStateSequence(FState * newstate, FState * basestate); + int GetTics(FState * newstate); + bool SetState (FState *newstate, bool nofunction=false); + virtual bool UpdateWaterLevel (fixed_t oldz, bool splash=true); + bool isFast(); + bool isSlow(); + void SetIdle(bool nofunction=false); + void ClearCounters(); + FState *GetRaiseState(); + void Revive(); + + FState *FindState (FName label) const + { + return GetClass()->ActorInfo->FindState(1, &label); + } + + FState *FindState (FName label, FName sublabel, bool exact = false) const + { + FName names[] = { label, sublabel }; + 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; + + fixed_t X() const + { + return x; + } + fixed_t Y() const + { + return y; + } + fixed_t Z() const + { + return z; + } + fixed_t Top() const + { + return z + height; + } + void SetZ(fixed_t newz, bool moving = true) + { + z = newz; + } + +}; + +class FActorIterator +{ +public: + FActorIterator (int i) : base (NULL), id (i) + { + } + FActorIterator (int i, AActor *start) : base (start), id (i) + { + } + AActor *Next () + { + if (id == 0) + return NULL; + if (!base) + base = AActor::TIDHash[id & 127]; + else + base = base->inext; + + while (base && base->tid != id) + base = base->inext; + + return base; + } +private: + AActor *base; + int id; +}; + +template +class TActorIterator : public FActorIterator +{ +public: + TActorIterator (int id) : FActorIterator (id) {} + T *Next () + { + AActor *actor; + do + { + actor = FActorIterator::Next (); + } while (actor && !actor->IsKindOf (RUNTIME_CLASS(T))); + return static_cast(actor); + } +}; + +class NActorIterator : public FActorIterator +{ + const PClass *type; +public: + NActorIterator (const PClass *cls, int id) : FActorIterator (id) { type = cls; } + NActorIterator (FName cls, int id) : FActorIterator (id) { type = PClass::FindClass(cls); } + NActorIterator (const char *cls, int id) : FActorIterator (id) { type = PClass::FindClass(cls); } + AActor *Next () + { + AActor *actor; + if (type == NULL) return NULL; + do + { + actor = FActorIterator::Next (); + } while (actor && !actor->IsKindOf (type)); + return actor; + } +}; + +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); +} + +inline AActor *Spawn (const PClass *type, const fixedvec3 &pos, replace_t allowreplacement) +{ + return AActor::StaticSpawn (type, pos.x, pos.y, pos.z, allowreplacement); +} + +AActor *Spawn (const char *type, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); +AActor *Spawn (FName classname, fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement); + +inline AActor *Spawn (const char *type, const fixedvec3 &pos, replace_t allowreplacement) +{ + return Spawn (type, pos.x, pos.y, pos.z, allowreplacement); +} + +inline AActor *Spawn (FName classname, const fixedvec3 &pos, replace_t allowreplacement) +{ + return Spawn (classname, pos.x, pos.y, pos.z, allowreplacement); +} + + +template +inline T *Spawn (fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) +{ + return static_cast(AActor::StaticSpawn (RUNTIME_CLASS(T), x, y, z, allowreplacement)); +} + +template +inline T *Spawn (const fixedvec3 &pos, replace_t allowreplacement) +{ + return static_cast(AActor::StaticSpawn (RUNTIME_CLASS(T), pos.x, pos.y, pos.z, allowreplacement)); +} + +void PrintMiscActorInfo(AActor * query); + +#define S_FREETARGMOBJ 1 + +#endif // __P_MOBJ_H__ diff --git a/src/am_map.cpp b/src/am_map.cpp index 0133a2493a..64d5420b0c 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -360,6 +360,20 @@ static FColorCVar *cv_overlay[] = { &am_ovsecretsectorcolor }; +CCMD(am_restorecolors) +{ + for (unsigned i = 0; i < countof(cv_standard); i++) + { + cv_standard[i]->ResetToDefault(); + } + for (unsigned i = 0; i < countof(cv_overlay); i++) + { + cv_overlay[i]->ResetToDefault(); + } +} + + + #define NOT_USED 1,0,0 // use almost black as indicator for an unused color static unsigned char DoomColors[]= { @@ -984,8 +998,8 @@ void AM_restoreScaleAndLoc () } else { - m_x = (players[consoleplayer].camera->x >> FRACTOMAPBITS) - m_w/2; - m_y = (players[consoleplayer].camera->y >> FRACTOMAPBITS)- m_h/2; + m_x = (players[consoleplayer].camera->X() >> FRACTOMAPBITS) - m_w/2; + m_y = (players[consoleplayer].camera->Y() >> FRACTOMAPBITS)- m_h/2; } m_x2 = m_x + m_w; m_y2 = m_y + m_h; @@ -1235,8 +1249,8 @@ void AM_initVariables () if (playeringame[pnum]) break; assert(pnum >= 0 && pnum < MAXPLAYERS); - m_x = (players[pnum].camera->x >> FRACTOMAPBITS) - m_w/2; - m_y = (players[pnum].camera->y >> FRACTOMAPBITS) - m_h/2; + m_x = (players[pnum].camera->X() >> FRACTOMAPBITS) - m_w/2; + m_y = (players[pnum].camera->Y() >> FRACTOMAPBITS) - m_h/2; AM_changeWindowLoc(); // for saving & restoring @@ -1557,25 +1571,25 @@ void AM_doFollowPlayer () fixed_t sx, sy; if (players[consoleplayer].camera != NULL && - (f_oldloc.x != players[consoleplayer].camera->x || - f_oldloc.y != players[consoleplayer].camera->y)) + (f_oldloc.x != players[consoleplayer].camera->X() || + f_oldloc.y != players[consoleplayer].camera->Y())) { - m_x = (players[consoleplayer].camera->x >> FRACTOMAPBITS) - m_w/2; - m_y = (players[consoleplayer].camera->y >> FRACTOMAPBITS) - m_h/2; + m_x = (players[consoleplayer].camera->X() >> FRACTOMAPBITS) - m_w/2; + m_y = (players[consoleplayer].camera->Y() >> FRACTOMAPBITS) - m_h/2; m_x2 = m_x + m_w; m_y2 = m_y + m_h; // do the parallax parchment scrolling. - sx = (players[consoleplayer].camera->x - f_oldloc.x) >> FRACTOMAPBITS; - sy = (f_oldloc.y - players[consoleplayer].camera->y) >> FRACTOMAPBITS; + sx = (players[consoleplayer].camera->X() - f_oldloc.x) >> FRACTOMAPBITS; + sy = (f_oldloc.y - players[consoleplayer].camera->Y()) >> FRACTOMAPBITS; if (am_rotate == 1 || (am_rotate == 2 && viewactive)) { AM_rotate (&sx, &sy, players[consoleplayer].camera->angle - ANG90); } AM_ScrollParchment (sx, sy); - f_oldloc.x = players[consoleplayer].camera->x; - f_oldloc.y = players[consoleplayer].camera->y; + f_oldloc.x = players[consoleplayer].camera->X(); + f_oldloc.y = players[consoleplayer].camera->Y(); } } @@ -1935,8 +1949,6 @@ void AM_drawSubsectors() scalex = sec->GetXScale(sector_t::floor); scaley = sec->GetYScale(sector_t::floor); -#ifdef _3DFLOORS - if (sec->e->XFloor.ffloors.Size()) { secplane_t *floorplane = &sec->floorplane; @@ -1997,7 +2009,6 @@ void AM_drawSubsectors() floorlight = *light->p_lightlevel; colormap = light->extra_colormap; } -#endif if (maptex == skyflatnum) { continue; @@ -2060,17 +2071,17 @@ static bool AM_CheckSecret(line_t *line) { if (line->frontsector != NULL) { - if (line->frontsector->secretsector) + if (line->frontsector->wasSecret()) { - if (am_map_secrets!=0 && !(line->frontsector->special&SECRET_MASK)) return true; + if (am_map_secrets!=0 && !line->frontsector->isSecret()) return true; if (am_map_secrets==2 && !(line->flags & ML_SECRET)) return true; } } if (line->backsector != NULL) { - if (line->backsector->secretsector) + if (line->backsector->wasSecret()) { - if (am_map_secrets!=0 && !(line->backsector->special&SECRET_MASK)) return true; + if (am_map_secrets!=0 && !line->backsector->isSecret()) return true; if (am_map_secrets==2 && !(line->flags & ML_SECRET)) return true; } } @@ -2153,8 +2164,6 @@ void AM_showSS() } } -#ifdef _3DFLOORS - //============================================================================= // // Determines if a 3D floor boundary should be drawn @@ -2214,7 +2223,154 @@ bool AM_Check3DFloors(line_t *line) // All 3D floors could be matched so let's not draw a boundary. return false; } -#endif + +// [TP] Check whether a sector can trigger a special that satisfies the provided function. +// If found, specialptr and argsptr will be filled by the special and the arguments +// If needUseActivated is true, the special must be activated by use. +bool AM_checkSectorActions (sector_t *sector, bool (*function)(int, int *), int *specialptr, int **argsptr, bool needUseActivated) +{ + for (ASectorAction* action = sector->SecActTarget; action; action = barrier_cast(action->tracer)) + { + if ((action->IsActivatedByUse() || false == needUseActivated) + && (*function)(action->special, action->args) + && action->CanTrigger (players[consoleplayer].mo)) + { + *specialptr = action->special; + *argsptr = action->args; + return true; + } + } + + return false; +} + +// [TP] Check whether there's a boundary on the provided line for a special that satisfies the provided function. +// It's a boundary if the line can activate the special or the line's bordering sectors can activate it. +// If found, specialptr and argsptr will be filled with special and args if given. +bool AM_checkSpecialBoundary (line_t &line, bool (*function)(int, int *), int *specialptr = NULL, int **argsptr = NULL) +{ + if (specialptr == NULL) + { + static int sink; + specialptr = &sink; + } + + if (argsptr == NULL) + { + static int *sink; + argsptr = &sink; + } + + // Check if the line special qualifies for this + if ((line.activation & SPAC_PlayerActivate) && (*function)(line.special, line.args)) + { + *specialptr = line.special; + *argsptr = line.args; + return true; + } + + // Check sector actions in the line's front sector -- the action has to be use-activated in order to + // show up if this is a one-sided line, because the player cannot trigger sector actions by crossing + // a one-sided line (since that's impossible, duh). + if (AM_checkSectorActions(line.frontsector, function, specialptr, argsptr, line.backsector == NULL)) + return true; + + // If it has a back sector, check sector actions in that. + return (line.backsector && AM_checkSectorActions(line.backsector, function, specialptr, argsptr, false)); +} + +bool AM_isTeleportSpecial (int special, int *) +{ + return (special == Teleport || + special == Teleport_NoFog || + special == Teleport_ZombieChanger || + special == Teleport_Line); +} + +bool AM_isTeleportBoundary (line_t &line) +{ + return AM_checkSpecialBoundary(line, &AM_isTeleportSpecial); +} + +bool AM_isExitSpecial (int special, int *) +{ + return (special == Teleport_NewMap || + special == Teleport_EndGame || + special == Exit_Normal || + special == Exit_Secret); +} + +bool AM_isExitBoundary (line_t& line) +{ + return AM_checkSpecialBoundary(line, &AM_isExitSpecial); +} + +bool AM_isTriggerSpecial (int special, int *) +{ + return LineSpecialsInfo[special] != NULL + && LineSpecialsInfo[special]->max_args >= 0 + && special != Door_Open + && special != Door_Close + && special != Door_CloseWaitOpen + && special != Door_Raise + && special != Door_Animated + && special != Generic_Door; +} + +bool AM_isTriggerBoundary (line_t &line) +{ + return AM_checkSpecialBoundary(line, &AM_isTriggerSpecial); +} + +bool AM_isLockSpecial (int special, int* args) +{ + return special == Door_LockedRaise + || special == ACS_LockedExecute + || special == ACS_LockedExecuteDoor + || (special == Door_Animated && args[3] != 0) + || (special == Generic_Door && args[4] != 0) + || (special == FS_Execute && args[2] != 0); +} + +bool AM_isLockBoundary (line_t &line, int *lockptr = NULL) +{ + if (lockptr == NULL) + { + static int sink; + lockptr = &sink; + } + + if (line.locknumber) + { + *lockptr = line.locknumber; + return true; + } + + int special; + int *args; + bool result = AM_checkSpecialBoundary(line, &AM_isLockSpecial, &special, &args); + + if (result) + { + switch (special) + { + case FS_Execute: + *lockptr = args[2]; + break; + + case Door_Animated: + case Door_LockedRaise: + *lockptr = args[3]; + break; + + default: + *lockptr = args[4]; + break; + } + } + + return result; +} //============================================================================= // @@ -2263,49 +2419,19 @@ void AM_drawWalls (bool allmap) AM_drawMline(&l, AMColors.SecretWallColor); else AM_drawMline(&l, AMColors.WallColor); - } - else if (lines[i].locknumber > 0 && AMColors.displayLocks) - { // [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 = AMColors[AMColors.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) && - (lines[i].activation & SPAC_PlayerActivate) && - AMColors.isValid(AMColors.IntraTeleportColor)) + } + else if (AM_isTeleportBoundary(lines[i]) && AMColors.isValid(AMColors.IntraTeleportColor)) { // intra-level teleporters AM_drawMline(&l, AMColors.IntraTeleportColor); } - else if ((lines[i].special == Teleport_NewMap || - lines[i].special == Teleport_EndGame || - lines[i].special == Exit_Normal || - lines[i].special == Exit_Secret) && - AMColors.isValid(AMColors.InterTeleportColor)) + else if (AM_isExitBoundary(lines[i]) && AMColors.isValid(AMColors.InterTeleportColor)) { // inter-level/game-ending teleporters AM_drawMline(&l, AMColors.InterTeleportColor); } - else if (lines[i].special == Door_LockedRaise || - lines[i].special == ACS_LockedExecute || - lines[i].special == ACS_LockedExecuteDoor || - (lines[i].special == Door_Animated && lines[i].args[3] != 0) || - (lines[i].special == Generic_Door && lines[i].args[4] != 0)) + else if (AM_isLockBoundary(lines[i], &lock)) { if (AMColors.displayLocks) { - int P_GetMapColorForLock(int lock); - - if (lines[i].special==Door_LockedRaise || lines[i].special==Door_Animated) - lock=lines[i].args[3]; - else lock=lines[i].args[4]; - color = P_GetMapColorForLock(lock); AMColor c; @@ -2320,14 +2446,9 @@ void AM_drawWalls (bool allmap) AM_drawMline (&l, AMColors.LockedColor); // locked special } } - else if (am_showtriggerlines && AMColors.isValid(AMColors.SpecialWallColor) && lines[i].special != 0 - && lines[i].special != Door_Open - && lines[i].special != Door_Close - && lines[i].special != Door_CloseWaitOpen - && lines[i].special != Door_Raise - && lines[i].special != Door_Animated - && lines[i].special != Generic_Door - && (lines[i].activation & SPAC_PlayerActivate)) + else if (am_showtriggerlines + && AMColors.isValid(AMColors.SpecialWallColor) + && AM_isTriggerBoundary(lines[i])) { AM_drawMline(&l, AMColors.SpecialWallColor); // wall with special non-door action the player can do } @@ -2345,12 +2466,10 @@ void AM_drawWalls (bool allmap) { AM_drawMline(&l, AMColors.CDWallColor); // ceiling level change } -#ifdef _3DFLOORS else if (AM_Check3DFloors(&lines[i])) { AM_drawMline(&l, AMColors.EFWallColor); // Extra floor border } -#endif else if (am_cheat > 0 && am_cheat < 4) { AM_drawMline(&l, AMColors.TSWallColor); @@ -2493,8 +2612,8 @@ void AM_drawPlayers () mline_t *arrow; int numarrowlines; - pt.x = players[consoleplayer].camera->x >> FRACTOMAPBITS; - pt.y = players[consoleplayer].camera->y >> FRACTOMAPBITS; + pt.x = players[consoleplayer].camera->X() >> FRACTOMAPBITS; + pt.y = players[consoleplayer].camera->Y() >> FRACTOMAPBITS; if (am_rotate == 1 || (am_rotate == 2 && viewactive)) { angle = ANG90; @@ -2556,8 +2675,8 @@ void AM_drawPlayers () if (p->mo != NULL) { - pt.x = p->mo->x >> FRACTOMAPBITS; - pt.y = p->mo->y >> FRACTOMAPBITS; + pt.x = p->mo->X() >> FRACTOMAPBITS; + pt.y = p->mo->Y() >> FRACTOMAPBITS; angle = p->mo->angle; if (am_rotate == 1 || (am_rotate == 2 && viewactive)) @@ -2588,8 +2707,8 @@ void AM_drawKeys () while ((key = it.Next()) != NULL) { - p.x = key->x >> FRACTOMAPBITS; - p.y = key->y >> FRACTOMAPBITS; + p.x = key->X() >> FRACTOMAPBITS; + p.y = key->Y() >> FRACTOMAPBITS; angle = key->angle; if (am_rotate == 1 || (am_rotate == 2 && viewactive)) @@ -2633,8 +2752,8 @@ void AM_drawThings () { if (am_cheat > 0 || !(t->flags6 & MF6_NOTONAUTOMAP)) { - p.x = t->x >> FRACTOMAPBITS; - p.y = t->y >> FRACTOMAPBITS; + p.x = t->X() >> FRACTOMAPBITS; + p.y = t->Y() >> FRACTOMAPBITS; if (am_showthingsprites > 0 && t->sprite > 0) { @@ -2860,7 +2979,7 @@ void AM_drawAuthorMarkers () marked->subsector->flags & SSECF_DRAWN : marked->Sector->MoreFlags & SECF_DRAWN))) { - DrawMarker (tex, marked->x >> FRACTOMAPBITS, marked->y >> FRACTOMAPBITS, 0, + DrawMarker (tex, marked->X() >> FRACTOMAPBITS, marked->Y() >> FRACTOMAPBITS, 0, flip, mark->scaleX, mark->scaleY, mark->Translation, mark->alpha, mark->fillcolor, mark->RenderStyle); } diff --git a/src/b_func.cpp b/src/b_func.cpp index f93625612f..cc8d9808a6 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -30,17 +30,17 @@ bool DBot::Reachable (AActor *rtarget) if (player->mo == rtarget) return false; - if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget->x, rtarget->y) - - rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y)) + if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget) - + rtarget->Sector->floorplane.ZatPoint (rtarget)) < player->mo->height) //Where rtarget is, player->mo can't be. return false; sector_t *last_s = player->mo->Sector; - fixed_t last_z = last_s->floorplane.ZatPoint (player->mo->x, player->mo->y); - fixed_t estimated_dist = P_AproxDistance (player->mo->x - rtarget->x, player->mo->y - rtarget->y); + fixed_t last_z = last_s->floorplane.ZatPoint (player->mo); + fixed_t estimated_dist = player->mo->AproxDistance(rtarget); bool reachable = true; - FPathTraverse it(player->mo->x+player->mo->velx, player->mo->y+player->mo->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); + FPathTraverse it(player->mo->X()+player->mo->velx, player->mo->Y()+player->mo->vely, rtarget->X(), rtarget->Y(), PT_ADDLINES|PT_ADDTHINGS); intercept_t *in; while ((in = it.Next())) { @@ -96,7 +96,7 @@ bool DBot::Reachable (AActor *rtarget) thing = in->d.thing; if (thing == player->mo) //Can't reach self in this case. continue; - if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT))) + if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget) <= (last_z+MAXMOVEHEIGHT))) { return true; } @@ -123,7 +123,7 @@ bool DBot::Check_LOS (AActor *to, angle_t vangle) if (vangle == 0) return false; //Looker seems to be blind. - return absangle(R_PointToAngle2 (player->mo->x, player->mo->y, to->x, to->y) - player->mo->angle) <= vangle/2; + return absangle(player->mo->AngleTo(to) - player->mo->angle) <= vangle/2; } //------------------------------------- @@ -165,10 +165,8 @@ void DBot::Dofire (ticcmd_t *cmd) //MAKEME: Decrease the rocket suicides even more. no_fire = true; - //angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); //Distance to enemy. - dist = P_AproxDistance ((player->mo->x + player->mo->velx) - (enemy->x + enemy->velx), - (player->mo->y + player->mo->vely) - (enemy->y + enemy->vely)); + dist = player->mo->AproxDistance(enemy, player->mo->velx - enemy->velx, player->mo->vely - enemy->vely); //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) @@ -219,17 +217,17 @@ void DBot::Dofire (ticcmd_t *cmd) } // prediction aiming shootmissile: - dist = P_AproxDistance (player->mo->x - enemy->x, player->mo->y - enemy->y); + dist = player->mo->AproxDistance (enemy); m = dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed; - bglobal.SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - angle = R_PointToAngle2 (player->mo->x, player->mo->y, bglobal.body1->x, bglobal.body1->y); + bglobal.SetBodyAt (enemy->X() + enemy->velx*m*2, enemy->Y() + enemy->vely*m*2, enemy->Z(), 1); + angle = player->mo->AngleTo(bglobal.body1); if (Check_LOS (enemy, SHOOTFOV)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - angle = R_PointToAngle2 (player->mo->x, player->mo->y, enemy->x, enemy->y); + angle = player->mo->AngleTo(enemy); aiming_penalty = 0; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; @@ -265,7 +263,6 @@ shootmissile: cmd->ucmd.buttons |= BT_ATTACK; } //Prevents bot from jerking, when firing automatic things with low skill. - //player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); } bool FCajunMaster::IsLeader (player_t *player) @@ -331,8 +328,7 @@ AActor *DBot::Choose_Mate () { if (P_CheckSight (player->mo, client->mo, SF_IGNOREVISIBILITY)) { - test = P_AproxDistance (client->mo->x - player->mo->x, - client->mo->y - player->mo->y); + test = client->mo->AproxDistance(player->mo); if (test < closest_dist) { @@ -402,8 +398,7 @@ AActor *DBot::Find_enemy () if (Check_LOS (client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. //if(P_CheckSight(player->mo, players[count].mo)) { - temp = P_AproxDistance (client->mo->x - player->mo->x, - client->mo->y - player->mo->y); + temp = client->mo->AproxDistance(player->mo); //Too dark? if (temp > DARK_DIST && @@ -432,7 +427,7 @@ void FCajunMaster::SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum) { if (body1) { - body1->SetOrigin (x, y, z); + body1->SetOrigin (x, y, z, false); } else { @@ -443,7 +438,7 @@ void FCajunMaster::SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum) { if (body2) { - body2->SetOrigin (x, y, z); + body2->SetOrigin (x, y, z, false); } else { @@ -464,16 +459,13 @@ void FCajunMaster::SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum) //Emulates missile travel. Returns distance travelled. fixed_t FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd) { - AActor *th = Spawn ("CajunTrace", source->x, source->y, source->z + 4*8*FRACUNIT, NO_REPLACE); + AActor *th = Spawn ("CajunTrace", source->X(), source->Y(), source->Z() + 4*8*FRACUNIT, NO_REPLACE); th->target = source; // where it came from float speed = (float)th->Speed; - FVector3 velocity; - velocity[0] = FIXED2FLOAT(dest->x - source->x); - velocity[1] = FIXED2FLOAT(dest->y - source->y); - velocity[2] = FIXED2FLOAT(dest->z - source->z); + TVector3 velocity = source->Vec3To(dest); velocity.MakeUnit(); th->velx = FLOAT2FIXED(velocity[0] * speed); th->vely = FLOAT2FIXED(velocity[1] * speed); @@ -484,8 +476,8 @@ fixed_t FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd) while (dist < SAFE_SELF_MISDIST) { dist += th->Speed; - th->SetOrigin (th->x + th->velx, th->y + th->vely, th->z + th->velz); - if (!CleanAhead (th, th->x, th->y, cmd)) + th->Move(th->velx, th->vely, th->velz); + if (!CleanAhead (th, th->X(), th->Y(), cmd)) break; } th->Destroy (); @@ -499,30 +491,30 @@ angle_t DBot::FireRox (AActor *enemy, ticcmd_t *cmd) AActor *actor; int m; - bglobal.SetBodyAt (player->mo->x + FixedMul(player->mo->velx, 5*FRACUNIT), - player->mo->y + FixedMul(player->mo->vely, 5*FRACUNIT), - player->mo->z + (player->mo->height / 2), 2); + bglobal.SetBodyAt (player->mo->X() + FixedMul(player->mo->velx, 5*FRACUNIT), + player->mo->Y() + FixedMul(player->mo->vely, 5*FRACUNIT), + player->mo->Z() + (player->mo->height / 2), 2); actor = bglobal.body2; - dist = P_AproxDistance (actor->x-enemy->x, actor->y-enemy->y); + dist = actor->AproxDistance (enemy); if (dist < SAFE_SELF_MISDIST) return 0; //Predict. m = (((dist+1)/FRACUNIT) / GetDefaultByName("Rocket")->Speed); - bglobal.SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)), - enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); + bglobal.SetBodyAt (enemy->X() + FixedMul(enemy->velx, (m+2*FRACUNIT)), + enemy->Y() + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); //try the predicted location if (P_CheckSight (actor, bglobal.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile { FCheckPosition tm; - if (bglobal.SafeCheckPosition (player->mo, actor->x, actor->y, tm)) + if (bglobal.SafeCheckPosition (player->mo, actor->X(), actor->Y(), tm)) { if (bglobal.FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST) { - ang = R_PointToAngle2 (actor->x, actor->y, bglobal.body1->x, bglobal.body1->y); + ang = actor->AngleTo(bglobal.body1); return ang; } } @@ -532,7 +524,7 @@ angle_t DBot::FireRox (AActor *enemy, ticcmd_t *cmd) { if (bglobal.FakeFire (player->mo, enemy, cmd) >= SAFE_SELF_MISDIST) { - ang = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); + ang = player->mo->AngleTo(enemy); return ang; } } diff --git a/src/b_game.cpp b/src/b_game.cpp index e136f3f29b..5f852d043d 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -423,8 +423,8 @@ void FCajunMaster::RemoveAllBots (bool fromlist) } } } + FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, players[i].mo, true, i, true); ClearPlayer (i, !fromlist); - FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, i); } } diff --git a/src/b_move.cpp b/src/b_move.cpp index fd04054577..cee409265e 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -35,7 +35,7 @@ void DBot::Roam (ticcmd_t *cmd) if (Reachable(dest)) { // Straight towards it. - angle = R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y); + angle = player->mo->AngleTo(dest); } else if (player->mo->movedir < 8) // turn towards movement direction if not there yet { @@ -67,8 +67,8 @@ bool DBot::Move (ticcmd_t *cmd) if ((unsigned)player->mo->movedir >= 8) I_Error ("Weird bot movedir!"); - tryx = player->mo->x + 8*xspeed[player->mo->movedir]; - tryy = player->mo->y + 8*yspeed[player->mo->movedir]; + tryx = player->mo->X() + 8*xspeed[player->mo->movedir]; + tryy = player->mo->Y() + 8*yspeed[player->mo->movedir]; try_ok = bglobal.CleanAhead (player->mo, tryx, tryy, cmd); @@ -124,9 +124,6 @@ bool DBot::TryWalk (ticcmd_t *cmd) void DBot::NewChaseDir (ticcmd_t *cmd) { - fixed_t deltax; - fixed_t deltay; - dirtype_t d[3]; int tdir; @@ -145,19 +142,18 @@ void DBot::NewChaseDir (ticcmd_t *cmd) olddir = (dirtype_t)player->mo->movedir; turnaround = opposite[olddir]; - deltax = dest->x - player->mo->x; - deltay = dest->y - player->mo->y; + fixedvec2 delta = player->mo->Vec2To(dest); - if (deltax > 10*FRACUNIT) + if (delta.x > 10*FRACUNIT) d[1] = DI_EAST; - else if (deltax < -10*FRACUNIT) + else if (delta.x < -10*FRACUNIT) d[1] = DI_WEST; else d[1] = DI_NODIR; - if (deltay < -10*FRACUNIT) + if (delta.y < -10*FRACUNIT) d[2] = DI_SOUTH; - else if (deltay > 10*FRACUNIT) + else if (delta.y > 10*FRACUNIT) d[2] = DI_NORTH; else d[2] = DI_NODIR; @@ -165,14 +161,14 @@ void DBot::NewChaseDir (ticcmd_t *cmd) // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { - player->mo->movedir = diags[((deltay<0)<<1)+(deltax>0)]; + player->mo->movedir = diags[((delta.y<0)<<1)+(delta.x>0)]; if (player->mo->movedir != turnaround && TryWalk(cmd)) return; } // try other directions if (pr_botnewchasedir() > 200 - || abs(deltay)>abs(deltax)) + || abs(delta.y)>abs(delta.x)) { tdir=d[1]; d[1]=d[2]; @@ -282,7 +278,7 @@ bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cm if ( !(thing->flags & MF_TELEPORT) && - tm.ceilingz - thing->z < thing->height) + tm.ceilingz - thing->Z() < thing->height) return false; // mobj must lower itself to fit // jump out of water @@ -290,7 +286,7 @@ bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cm // maxstep=37*FRACUNIT; if ( !(thing->flags & MF_TELEPORT) && - (tm.floorz - thing->z > maxstep ) ) + (tm.floorz - thing->Z() > maxstep ) ) return false; // too big a step up @@ -346,28 +342,13 @@ void DBot::Pitch (AActor *target) double aim; double diff; - diff = target->z - player->mo->z; - aim = atan (diff / (double)P_AproxDistance (player->mo->x - target->x, player->mo->y - target->y)); + diff = target->Z() - player->mo->Z(); + aim = atan(diff / (double)player->mo->AproxDistance(target)); player->mo->pitch = -(int)(aim * ANGLE_180/M_PI); } //Checks if a sector is dangerous. bool FCajunMaster::IsDangerous (sector_t *sec) { - int special; - - return - sec->damage - || sec->special & DAMAGE_MASK - || (special = sec->special & 0xff, special == dLight_Strobe_Hurt) - || special == dDamage_Hellslime - || special == dDamage_Nukage - || special == dDamage_End - || special == dDamage_SuperHellslime - || special == dDamage_LavaWimpy - || special == dDamage_LavaHefty - || special == dScroll_EastLavaDamage - || special == sLight_Strobe_Hurt - || special == Damage_InstantDeath - || special == sDamage_SuperHellslime; + return sec->damageamount > 0; } diff --git a/src/b_think.cpp b/src/b_think.cpp index 34baeee9c2..de74c04dbe 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -81,7 +81,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) int r; stuck = false; - dist = dest ? P_AproxDistance(player->mo->x-dest->x, player->mo->y-dest->y) : 0; + dist = dest ? player->mo->AproxDistance(dest) : 0; if (missile && ((!missile->velx || !missile->vely) || !Check_LOS(missile, SHOOTFOV*3/2))) @@ -96,14 +96,14 @@ void DBot::ThinkForMove (ticcmd_t *cmd) player->mo->pitch += 80; //HOW TO MOVE: - if (missile && (P_AproxDistance(player->mo->x-missile->x, player->mo->y-missile->y)mo->AproxDistance(missile)mo->x, player->mo->y, missile->x, missile->y); + angle = player->mo->AngleTo(missile); cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000) + if ((player->mo->AproxDistance(oldx, oldy)<50000) && t_strafe<=0) { t_strafe = 5; @@ -156,7 +156,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) t_fight = AFTERTICS; if (t_strafe <= 0 && - (P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000 + (player->mo->AproxDistance(oldx, oldy)<50000 || ((pr_botmove()%30)==10)) ) { @@ -165,10 +165,10 @@ void DBot::ThinkForMove (ticcmd_t *cmd) sleft = !sleft; } - angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); + angle = player->mo->AngleTo(enemy); if (player->ReadyWeapon == NULL || - P_AproxDistance(player->mo->x-enemy->x, player->mo->y-enemy->y) > + player->mo->AproxDistance(enemy) > player->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) @@ -206,9 +206,9 @@ void DBot::ThinkForMove (ticcmd_t *cmd) goto roam; } - angle = R_PointToAngle2(player->mo->x, player->mo->y, mate->x, mate->y); + angle = player->mo->AngleTo(mate); - matedist = P_AproxDistance(player->mo->x - mate->x, player->mo->y - mate->y); + matedist = player->mo->AproxDistance(mate); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -241,7 +241,7 @@ void DBot::ThinkForMove (ticcmd_t *cmd) (pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. else //hide while t_fight, but keep view at enemy. - angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); + angle = player->mo->AngleTo(enemy); } //Just a monster, so kill it. else dest = enemy; @@ -303,8 +303,8 @@ void DBot::ThinkForMove (ticcmd_t *cmd) if (t_fight<(AFTERTICS/2)) player->mo->flags |= MF_DROPOFF; - oldx = player->mo->x; - oldy = player->mo->y; + oldx = player->mo->X(); + oldy = player->mo->Y(); } //BOT_WhatToGet diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index a40f1f1a2e..3cfbf1c650 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -422,7 +422,7 @@ CCMD (take) CCMD (gameversion) { - Printf ("%s @ %s\nCommit %s", GetVersionString(), GetGitTime(), GetGitHash()); + Printf ("%s @ %s\nCommit %s\n", GetVersionString(), GetGitTime(), GetGitHash()); } CCMD (print) @@ -944,7 +944,7 @@ static void PrintFilteredActorList(const ActorTypeChecker IsActorType, const cha { Printf ("%s at (%d,%d,%d)\n", mo->GetClass()->TypeName.GetChars(), - mo->x >> FRACBITS, mo->y >> FRACBITS, mo->z >> FRACBITS); + mo->X() >> FRACBITS, mo->Y() >> FRACBITS, mo->Z() >> FRACBITS); } } } @@ -1084,7 +1084,7 @@ CCMD(currentpos) { AActor *mo = players[consoleplayer].mo; Printf("Current player position: (%1.3f,%1.3f,%1.3f), angle: %1.3f, floorheight: %1.3f, sector:%d, lightlevel: %d\n", - FIXED2FLOAT(mo->x), FIXED2FLOAT(mo->y), FIXED2FLOAT(mo->z), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel); + FIXED2FLOAT(mo->X()), FIXED2FLOAT(mo->Y()), FIXED2FLOAT(mo->Z()), mo->angle/float(ANGLE_1), FIXED2FLOAT(mo->floorz), mo->Sector->sectornum, mo->Sector->lightlevel); } //----------------------------------------------------------------------------- @@ -1106,11 +1106,8 @@ static void PrintSecretString(const char *string, bool thislevel) if (*string == ';') string++; if (thislevel && secnum >= 0 && secnum < numsectors) { - if (sectors[secnum].secretsector) - { - if ((sectors[secnum].special & SECRET_MASK)) colstr = TEXTCOLOR_RED; - else colstr = TEXTCOLOR_GREEN; - } + if (sectors[secnum].isSecret()) colstr = TEXTCOLOR_RED; + else if (sectors[secnum].wasSecret()) colstr = TEXTCOLOR_GREEN; else colstr = TEXTCOLOR_ORANGE; } } diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index d72993cb77..8ddd022e94 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -1076,7 +1076,8 @@ FString BuildString (int argc, FString *argv) // %x or %{x} in the command line with argument x. If argument x does not // exist, then the empty string is substituted in its place. // -// Substitution is not done inside of quoted strings. +// Substitution is not done inside of quoted strings, unless that string is +// prepended with a % character. // // To avoid a substitution, use %%. The %% will be replaced by a single %. // @@ -1091,19 +1092,12 @@ FString SubstituteAliasParams (FString &command, FCommandLine &args) char *p = command.LockBuffer(), *start = p; unsigned long argnum; FString buf; + bool inquote = false; while (*p != '\0') { - if (*p == '%' && ((p[1] >= '0' && p[1] <= '9') || p[1] == '{' || p[1] == '%')) + if (p[0] == '%' && ((p[1] >= '0' && p[1] <= '9') || p[1] == '{')) { - if (p[1] == '%') - { - // Do not substitute. Just collapse to a single %. - buf.AppendCStrPart (start, p - start + 1); - start = p = p + 2; - continue; - } - // Do a substitution. Output what came before this. buf.AppendCStrPart (start, p - start); @@ -1115,14 +1109,50 @@ FString SubstituteAliasParams (FString &command, FCommandLine &args) } p = (start += (p[1] == '{' && *start == '}')); } - else if (*p == '"') + else if (p[0] == '%' && p[1] == '%') { - // Don't substitute inside quoted strings. - p++; - while (*p != '\0' && (*p != '"' || *(p-1) == '\\')) + // Do not substitute. Just collapse to a single %. + buf.AppendCStrPart (start, p - start + 1); + start = p = p + 2; + continue; + } + else if (p[0] == '%' && p[1] == '"') + { + // Collapse %" to " and remember that we're in a quote so when we + // see a " character again, we don't start skipping below. + if (!inquote) + { + inquote = true; + buf.AppendCStrPart(start, p - start); + start = p + 1; + } + else + { + inquote = false; + } + p += 2; + } + else if (p[0] == '\\' && p[1] == '"') + { + p += 2; + } + else if (p[0] == '"') + { + // Don't substitute inside quoted strings if it didn't start + // with a %" + if (!inquote) + { p++; - if (*p != '\0') + while (*p != '\0' && (*p != '"' || *(p-1) == '\\')) + p++; + if (*p != '\0') + p++; + } + else + { + inquote = false; p++; + } } else { diff --git a/src/compatibility.cpp b/src/compatibility.cpp index d60677f64f..498fefb2a5 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -145,6 +145,7 @@ static FCompatOption Options[] = { "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 }, { "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 }, { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, + { "pointonline", COMPATF2_POINTONLINE, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/configfile.cpp b/src/configfile.cpp index b8db74eb38..13cfea5178 100644 --- a/src/configfile.cpp +++ b/src/configfile.cpp @@ -368,8 +368,7 @@ bool FConfigFile::DeleteCurrentSection() LastSectionPtr = &sec->Next; } - CurrentSection->~FConfigSection(); - delete[] (char *)CurrentSection; + delete CurrentSection; CurrentSection = sec->Next; return CurrentSection != NULL; diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index a994f7603f..31ff05b649 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -784,7 +784,7 @@ static int PatchThing (int thingy) bool hadStyle = false; FStateDefinitions statedef; bool patchedStates = false; - int oldflags; + ActorFlags oldflags; const PClass *type; SWORD *ednum, dummyed; @@ -1139,28 +1139,28 @@ static int PatchThing (int thingy) } if (vchanged[1]) { - info->flags2 = ActorFlags2::FromInt (value[1]); - if (info->flags2 & 0x00000004) // old BOUNCE1 + if (value[1] & 0x00000004) // old BOUNCE1 { - info->flags2 &= ActorFlags2::FromInt (~4); + value[1] &= ~0x00000004; info->BounceFlags = BOUNCE_DoomCompat; } // Damage types that once were flags but now are not - if (info->flags2 & 0x20000000) + if (value[1] & 0x20000000) { info->DamageType = NAME_Ice; - info->flags2 &= ActorFlags2::FromInt (~0x20000000); + value[1] &= ~0x20000000; } - if (info->flags2 & 0x10000) + if (value[1] & 0x10000000) { info->DamageType = NAME_Fire; - info->flags2 &= ActorFlags2::FromInt (~0x10000); + value[1] &= ~0x10000000; } - if (info->flags2 & 1) + if (value[1] & 0x00000001) { info->gravity = FRACUNIT/4; - info->flags2 &= ActorFlags2::FromInt (~1); + value[1] &= ~0x00000001; } + info->flags2 = ActorFlags2::FromInt (value[1]); } if (vchanged[2]) { @@ -3056,7 +3056,7 @@ bool ADehackedPickup::TryPickup (AActor *&toucher) { return false; } - RealPickup = static_cast(Spawn (type, x, y, z, NO_REPLACE)); + RealPickup = static_cast(Spawn (type, X(), Y(), Z(), NO_REPLACE)); if (RealPickup != NULL) { // The internally spawned item should never count towards statistics. diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 3fc365e8c9..a8d60e1cea 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -435,6 +435,11 @@ int FIWadManager::IdentifyVersion (TArray &wadfiles, const char *iwad, } } } + TArray gog_paths = I_GetGogPaths(); + for (i = 0; i < gog_paths.Size(); ++i) + { + CheckIWAD (gog_paths[i], &wads[0]); + } TArray steam_path = I_GetSteamPath(); for (i = 0; i < steam_path.Size(); ++i) { diff --git a/src/d_main.cpp b/src/d_main.cpp index 98be13e337..3b3cce1f4f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -561,7 +561,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN| COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS|COMPATF_HITSCAN| COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_LIGHT|COMPATF_MASKEDMIDTEX; - w = COMPATF2_BADANGLES|COMPATF2_FLOORMOVE; + w = COMPATF2_BADANGLES|COMPATF2_FLOORMOVE|COMPATF2_POINTONLINE; break; case 3: // Boom compat mode @@ -580,6 +580,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) case 6: // Boom with some added settings to reenable some 'broken' behavior v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_NO_PASSMOBJ| COMPATF_INVISIBILITY|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|COMPATF_WALLRUN|COMPATF_NOTOSSDROPS; + w = COMPATF2_POINTONLINE; break; } @@ -622,6 +623,7 @@ CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX); CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES); CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); +CVAR (Flag, compat_pointonline, compatflags2, COMPATF2_POINTONLINE); //========================================================================== // diff --git a/src/d_net.cpp b/src/d_net.cpp index bc5ccdba95..29303fa04f 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -973,7 +973,7 @@ void NetUpdate (void) { I_StartTic (); D_ProcessEvents (); - if ((maketic - gametic) / ticdup >= BACKUPTICS/2-1) + if (pauseext || (maketic - gametic) / ticdup >= BACKUPTICS/2-1) break; // can't hold any more //Printf ("mk:%i ",maketic); @@ -1204,7 +1204,7 @@ void NetUpdate (void) // Send current network delay // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - newtics - gametic) / ticdup); + netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); if (numtics > 0) { @@ -1810,7 +1810,8 @@ void TryRunTics (void) // If paused, do not eat more CPU time than we need, because it // will all be wasted anyway. - if (pauseext) r_NoInterpolate = true; + if (pauseext) + r_NoInterpolate = true; bool doWait = cl_capfps || r_NoInterpolate /*|| netgame*/; // get real tics @@ -1828,6 +1829,9 @@ void TryRunTics (void) // get available tics NetUpdate (); + if (pauseext) + return; + lowtic = INT_MAX; numplaying = 0; for (i = 0; i < doomcom.numnodes; i++) @@ -1935,7 +1939,7 @@ void TryRunTics (void) C_Ticker (); M_Ticker (); I_GetTime (true); - if (!pauseext) G_Ticker(); + G_Ticker(); gametic++; NetUpdate (); // check for new console commands @@ -2082,6 +2086,33 @@ static int KillAll(const PClass *cls) } return killcount; +} + +static int RemoveClass(const PClass *cls) +{ + AActor *actor; + int removecount = 0; + bool player = false; + TThinkerIterator iterator(cls); + while ((actor = iterator.Next())) + { + if (actor->IsA(cls)) + { + // [MC]Do not remove LIVE players. + if (actor->player != NULL) + { + player = true; + continue; + } + removecount++; + actor->ClearCounters(); + actor->Destroy(); + } + } + if (player) + Printf("Cannot remove live players!\n"); + return removecount; + } // [RH] Execute a special "ticcmd". The type byte should // have already been read, and the stream is positioned @@ -2288,10 +2319,12 @@ void Net_DoCommand (int type, BYTE **stream, int player) else { const AActor *def = GetDefaultByType (typeinfo); - AActor *spawned = Spawn (typeinfo, - source->x + FixedMul (def->radius * 2 + source->radius, finecosine[source->angle>>ANGLETOFINESHIFT]), - source->y + FixedMul (def->radius * 2 + source->radius, finesine[source->angle>>ANGLETOFINESHIFT]), - source->z + 8 * FRACUNIT, ALLOW_REPLACE); + fixedvec3 spawnpos = source->Vec3Offset( + FixedMul (def->radius * 2 + source->radius, finecosine[source->angle>>ANGLETOFINESHIFT]), + FixedMul (def->radius * 2 + source->radius, finesine[source->angle>>ANGLETOFINESHIFT]), + 8 * FRACUNIT); + + AActor *spawned = Spawn (typeinfo, spawnpos, ALLOW_REPLACE); if (spawned != NULL) { if (type == DEM_SUMMONFRIEND || type == DEM_SUMMONFRIEND2 || type == DEM_SUMMONMBF) @@ -2342,8 +2375,8 @@ void Net_DoCommand (int type, BYTE **stream, int player) s = ReadString (stream); - if (Trace (players[player].mo->x, players[player].mo->y, - players[player].mo->z + players[player].mo->height - (players[player].mo->height>>2), + if (Trace (players[player].mo->X(), players[player].mo->Y(), + players[player].mo->Top() - (players[player].mo->height>>2), players[player].mo->Sector, vx, vy, vz, 172*FRACUNIT, 0, ML_BLOCKEVERYTHING, players[player].mo, trace, TRACE_NoSky)) @@ -2555,6 +2588,27 @@ void Net_DoCommand (int type, BYTE **stream, int player) } break; + case DEM_REMOVE: + { + char *classname = ReadString(stream); + int removecount = 0; + const PClass *cls = PClass::FindClass(classname); + if (cls != NULL && cls->ActorInfo != NULL) + { + removecount = RemoveClass(cls); + const PClass *cls_rep = cls->GetReplacement(); + if (cls != cls_rep) + { + removecount += RemoveClass(cls_rep); + } + Printf("Removed %d actors of type %s.\n", removecount, classname); + } + else + { + Printf("%s is not an actor class.\n", classname); + } + } + break; case DEM_CONVREPLY: case DEM_CONVCLOSE: @@ -2680,6 +2734,7 @@ void Net_SkipCommand (int type, BYTE **stream) case DEM_SUMMONFRIEND: case DEM_SUMMONFOE: case DEM_SUMMONMBF: + case DEM_REMOVE: case DEM_SPRAY: case DEM_MORPHEX: case DEM_KILLCLASSCHEAT: diff --git a/src/d_player.h b/src/d_player.h index e27bf1087f..38879a63a4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -221,7 +221,11 @@ enum WF_WEAPONRELOADOK = 1 << 5, // [XA] Okay to reload this weapon. WF_WEAPONZOOMOK = 1 << 6, // [XA] Okay to use weapon zoom function. WF_REFIRESWITCHOK = 1 << 7, // Mirror WF_WEAPONSWITCHOK for A_ReFire -}; + WF_USER1OK = 1 << 8, // [MC] Allow pushing of custom state buttons 1-4 + WF_USER2OK = 1 << 9, + WF_USER3OK = 1 << 10, + WF_USER4OK = 1 << 11, +}; #define WPIECE1 1 #define WPIECE2 2 @@ -405,7 +409,7 @@ public: int lastkilltime; // [RH] For multikills BYTE multicount; BYTE spreecount; // [RH] Keep track of killing sprees - BYTE WeaponState; + WORD WeaponState; AWeapon *ReadyWeapon; AWeapon *PendingWeapon; // WP_NOCHANGE if not changing @@ -418,6 +422,8 @@ public: int killcount, itemcount, secretcount; // for intermission int damagecount, bonuscount;// for screen flashing int hazardcount; // for delayed Strife damage + int hazardinterval; // Frequency of damage infliction + FName hazardtype; // Damage type of last hazardous damage encounter. int poisoncount; // screen flash for poison damage FName poisontype; // type of poison damage to apply FName poisonpaintype; // type of Pain state to enter for poison damage diff --git a/src/d_protocol.h b/src/d_protocol.h index 8b6e777d50..73b042470e 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -164,6 +164,7 @@ enum EDemoCommand DEM_RUNNAMEDSCRIPT, // 65 String: Script name, Byte: Arg count + Always flag; each arg is a 4-byte int DEM_REVERTCAMERA, // 66 DEM_SETSLOTPNUM, // 67 Byte: player number, the rest is the same as DEM_SETSLOT + DEM_REMOVE, // 68 }; // The following are implemented by cht_DoCheat in m_cheat.cpp diff --git a/src/decallib.cpp b/src/decallib.cpp index 0ea3423c4a..784998977c 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -432,7 +432,7 @@ WORD FDecalLib::GetDecalID (FScanner &sc) unsigned long num = strtoul (sc.String, NULL, 10); if (num < 1 || num > 65535) { - sc.MustGetStringName ("Decal ID must be between 1 and 65535"); + sc.ScriptError ("Decal ID must be between 1 and 65535"); } return (WORD)num; } @@ -603,16 +603,18 @@ void FDecalLib::ParseGenerator (FScanner &sc) { const PClass *type; FDecalBase *decal; - AActor *actor; + bool optional = false; // Get name of generator (actor) sc.MustGetString (); + optional = sc.Compare("optional"); + if (optional) sc.MustGetString(); + type = PClass::FindClass (sc.String); if (type == NULL || type->ActorInfo == NULL) { - sc.ScriptError ("%s is not an actor.", sc.String); + if (!optional) sc.ScriptError ("%s is not an actor.", sc.String); } - actor = (AActor *)type->Defaults; // Get name of generated decal sc.MustGetString (); @@ -625,14 +627,17 @@ void FDecalLib::ParseGenerator (FScanner &sc) decal = ScanTreeForName (sc.String, Root); if (decal == NULL) { - sc.ScriptError ("%s has not been defined.", sc.String); + if (!optional) sc.ScriptError ("%s has not been defined.", sc.String); } } - - actor->DecalGenerator = decal; - if (decal != NULL) + if (type != NULL) { - decal->Users.Push (type); + AActor *actor = (AActor *)type->Defaults; + actor->DecalGenerator = decal; + if (decal != NULL) + { + decal->Users.Push(type); + } } } diff --git a/src/dobject.cpp b/src/dobject.cpp index 191fa3c43a..9d674d2268 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -517,8 +517,8 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld) #define SECTOR_CHECK(f,t) \ if (sectors[i].f.p == static_cast(old)) { sectors[i].f = static_cast(notOld); changed++; } SECTOR_CHECK( SoundTarget, AActor ); - SECTOR_CHECK( CeilingSkyBox, ASkyViewpoint ); - SECTOR_CHECK( FloorSkyBox, ASkyViewpoint ); + SECTOR_CHECK( SkyBoxes[sector_t::ceiling], ASkyViewpoint ); + SECTOR_CHECK( SkyBoxes[sector_t::floor], ASkyViewpoint ); SECTOR_CHECK( SecActTarget, ASectorAction ); SECTOR_CHECK( floordata, DSectorEffect ); SECTOR_CHECK( ceilingdata, DSectorEffect ); diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index d52962f115..3812c0b08c 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -640,8 +640,8 @@ size_t DSectorMarker::PropagateMark() { sector_t *sec = §ors[SecNum + i]; GC::Mark(sec->SoundTarget); - GC::Mark(sec->CeilingSkyBox); - GC::Mark(sec->FloorSkyBox); + GC::Mark(sec->SkyBoxes[sector_t::ceiling]); + GC::Mark(sec->SkyBoxes[sector_t::floor]); GC::Mark(sec->SecActTarget); GC::Mark(sec->floordata); GC::Mark(sec->ceilingdata); diff --git a/src/doomdata.h b/src/doomdata.h index 71e581e268..0877aee90f 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -365,6 +365,7 @@ struct FMapThing short pitch; short roll; DWORD RenderStyle; + int FloatbobPhase; }; diff --git a/src/doomdef.h b/src/doomdef.h index c8aa66c173..802591386c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -340,6 +340,7 @@ enum COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW. COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom. COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less + COMPATF2_POINTONLINE = 1 << 3, // Use original but buggy P_PointOnLineSide() and P_PointOnDivlineSide() }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 6fcd50341d..2381ad381d 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -982,7 +982,7 @@ void FParser::SF_ObjX(void) } t_return.type = svt_fixed; // haleyjd: SoM's fixed-point fix - t_return.value.f = mo ? mo->x : 0; // null ptr check + t_return.value.f = mo ? mo->X() : 0; // null ptr check } //========================================================================== @@ -1005,7 +1005,7 @@ void FParser::SF_ObjY(void) } t_return.type = svt_fixed; // haleyjd - t_return.value.f = mo ? mo->y : 0; // null ptr check + t_return.value.f = mo ? mo->Y() : 0; // null ptr check } //========================================================================== @@ -1028,7 +1028,7 @@ void FParser::SF_ObjZ(void) } t_return.type = svt_fixed; // haleyjd - t_return.value.f = mo ? mo->z : 0; // null ptr check + t_return.value.f = mo ? mo->Z() : 0; // null ptr check } @@ -1236,7 +1236,7 @@ void FParser::SF_ObjFlag(void) t_return.type = svt_int; if (mo && flagnum<26) { - t_return.value.i = !!(mo->flags & (1 << flagnum)); + t_return.value.i = !!(mo->flags & ActorFlags::FromInt(1 << flagnum)); } else t_return.value.i = 0; } @@ -1466,8 +1466,8 @@ void FParser::SF_SetCamera(void) angle = t_argc < 2 ? newcamera->angle : (fixed_t)FixedToAngle(fixedvalue(t_argv[1])); newcamera->special1=newcamera->angle; - newcamera->special2=newcamera->z; - newcamera->z = t_argc < 3 ? (newcamera->z + (41 << FRACBITS)) : (intvalue(t_argv[2]) << FRACBITS); + newcamera->special2=newcamera->Z(); + newcamera->SetZ(t_argc < 3 ? (newcamera->Z() + (41 << FRACBITS)) : (intvalue(t_argv[2]) << FRACBITS)); newcamera->angle = angle; if(t_argc < 4) newcamera->pitch = 0; else @@ -1498,7 +1498,7 @@ void FParser::SF_ClearCamera(void) { player->camera=player->mo; cam->angle=cam->special1; - cam->z=cam->special2; + cam->SetZ(cam->special2); } } @@ -3065,7 +3065,7 @@ void FParser::SF_SetWeapon() void FParser::SF_MoveCamera(void) { fixed_t x, y, z; - fixed_t xdist, ydist, zdist, xydist, movespeed; + fixed_t zdist, xydist, movespeed; fixed_t xstep, ystep, zstep, targetheight; angle_t anglespeed, anglestep, angledist, targetangle, mobjangle, bigangle, smallangle; @@ -3097,9 +3097,8 @@ void FParser::SF_MoveCamera(void) anglespeed = (angle_t)FixedToAngle(fixedvalue(t_argv[5])); // figure out how big one step will be - xdist = target->x - cam->x; - ydist = target->y - cam->y; - zdist = targetheight - cam->z; + fixedvec2 dist = cam->Vec2To(target); + zdist = targetheight - cam->Z(); // Angle checking... // 90 @@ -3145,8 +3144,8 @@ void FParser::SF_MoveCamera(void) } // set step variables based on distance and speed - mobjangle = R_PointToAngle2(cam->x, cam->y, target->x, target->y); - xydist = R_PointToDist2(target->x - cam->x, target->y - cam->y); + mobjangle = cam->AngleTo(target); + xydist = cam->Distance2D(target); xstep = FixedMul(finecosine[mobjangle >> ANGLETOFINESHIFT], movespeed); ystep = FixedMul(finesine[mobjangle >> ANGLETOFINESHIFT], movespeed); @@ -3170,19 +3169,19 @@ void FParser::SF_MoveCamera(void) else anglestep = anglespeed; - if(abs(xstep) >= (abs(xdist) - 1)) - x = target->x; + if(abs(xstep) >= (abs(dist.x) - 1)) + x = cam->X() + dist.x; else { - x = cam->x + xstep; + x = cam->X() + xstep; moved = 1; } - if(abs(ystep) >= (abs(ydist) - 1)) - y = target->y; + if(abs(ystep) >= (abs(dist.y) - 1)) + y = cam->Y() + dist.y; else { - y = cam->y + ystep; + y = cam->Y() + ystep; moved = 1; } @@ -3190,7 +3189,7 @@ void FParser::SF_MoveCamera(void) z = targetheight; else { - z = cam->z + zstep; + z = cam->Z() + zstep; moved = 1; } @@ -3212,12 +3211,12 @@ void FParser::SF_MoveCamera(void) cam->radius=8; cam->height=8; - if ((x != cam->x || y != cam->y) && !P_TryMove(cam, x, y, true)) + if ((x != cam->X() || y != cam->Y()) && !P_TryMove(cam, x, y, true)) { Printf("Illegal camera move to (%f, %f)\n", x/65536.f, y/65536.f); return; } - cam->z = z; + cam->SetZ(z); t_return.type = svt_int; t_return.value.i = moved; @@ -3410,13 +3409,10 @@ void FParser::SF_SetObjPosition() if (!mobj) return; - mobj->UnlinkFromWorld(); - - mobj->x = intvalue(t_argv[1]) << FRACBITS; - if(t_argc >= 3) mobj->y = intvalue(t_argv[2]) << FRACBITS; - if(t_argc == 4) mobj->z = intvalue(t_argv[3]) << FRACBITS; - - mobj->LinkToWorld(); + mobj->SetOrigin( + fixedvalue(t_argv[1]), + (t_argc >= 3)? fixedvalue(t_argv[2]) : mobj->Y(), + (t_argc >= 4)? fixedvalue(t_argv[3]) : mobj->Z(), false); } } @@ -4285,7 +4281,7 @@ void FParser::SF_SpawnShot2(void) t_return.type = svt_mobj; - AActor *mo = Spawn (PClass, source->x, source->y, source->z+z, ALLOW_REPLACE); + AActor *mo = Spawn (PClass, source->X(), source->Y(), source->Z()+z, ALLOW_REPLACE); if (mo) { S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM); @@ -4323,44 +4319,12 @@ void FParser::SF_KillInSector() //========================================================================== // // new for GZDoom: Sets a sector's type -// (Sure, this is not particularly useful. But having it made it possible -// to fix a few annoying bugs in some old maps ;) ) // //========================================================================== void FParser::SF_SectorType(void) { - int tagnum, secnum; - sector_t *sector; - - if (CheckArgs(1)) - { - tagnum = intvalue(t_argv[0]); - - // argv is sector tag - secnum = T_FindFirstSectorFromTag(tagnum); - - if(secnum < 0) - { script_error("sector not found with tagnum %i\n", tagnum); return;} - - sector = §ors[secnum]; - - if(t_argc > 1) - { - int i = -1; - int spec = intvalue(t_argv[1]); - - // set all sectors with tag - FSSectorTagIterator itr(tagnum); - while ((i = itr.Next()) >= 0) - { - sectors[i].special = spec; - } - } - - t_return.type = svt_int; - t_return.value.i = sector->special; - } + // I don't think this was ever used publicly so I'm not going to bother fixing it. } //========================================================================== diff --git a/src/fragglescript/t_load.cpp b/src/fragglescript/t_load.cpp index eedb68f7cf..cd40d809a4 100644 --- a/src/fragglescript/t_load.cpp +++ b/src/fragglescript/t_load.cpp @@ -87,7 +87,6 @@ DEFINE_MAP_OPTION(fs_nocheckposition, false) { FFsOptions *opt = info->GetOptData("fragglescript"); - parse.ParseAssign(); if (parse.CheckAssign()) { parse.sc.MustGetNumber(); diff --git a/src/g_doom/a_archvile.cpp b/src/g_doom/a_archvile.cpp index f24ff146e6..9d2e5db0cf 100644 --- a/src/g_doom/a_archvile.cpp +++ b/src/g_doom/a_archvile.cpp @@ -52,7 +52,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Fire) void A_Fire(AActor *self, int height) { AActor *dest; - angle_t an; dest = self->tracer; if (dest == NULL || self->target == NULL) @@ -62,11 +61,8 @@ void A_Fire(AActor *self, int height) if (!P_CheckSight (self->target, dest, 0) ) return; - an = dest->angle >> ANGLETOFINESHIFT; - - self->SetOrigin (dest->x + FixedMul (24*FRACUNIT, finecosine[an]), - dest->y + FixedMul (24*FRACUNIT, finesine[an]), - dest->z + height); + fixedvec3 newpos = dest->Vec3Angle(24 * FRACUNIT, dest->angle, height); + self->SetOrigin(newpos, true); } @@ -86,8 +82,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileTarget) A_FaceTarget (self); - fog = Spawn (fire, self->target->x, self->target->y, - self->target->z, ALLOW_REPLACE); + fog = Spawn (fire, self->target->X(), self->target->Y(), + self->target->Z(), ALLOW_REPLACE); self->tracer = fog; fog->target = self; @@ -117,7 +113,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack) ACTION_PARAM_INT(flags,6); AActor *fire, *target; - angle_t an; if (NULL == (target = self->target)) return; @@ -139,15 +134,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_VileAttack) P_TraceBleed (newdam > 0 ? newdam : dmg, target); - an = self->angle >> ANGLETOFINESHIFT; fire = self->tracer; if (fire != NULL) { // move the fire between the vile and the player - fire->SetOrigin (target->x - FixedMul (24*FRACUNIT, finecosine[an]), - target->y - FixedMul (24*FRACUNIT, finesine[an]), - target->z); + fixedvec3 pos = target->Vec3Angle(-24 * FRACUNIT, self->angle, target->Z()); + fire->SetOrigin (pos, true); P_RadiusAttack (fire, self, blastdmg, blastrad, dmgtype, 0); } diff --git a/src/g_doom/a_doomweaps.cpp b/src/g_doom/a_doomweaps.cpp index 6dbf3d1ec5..79dd6253f7 100644 --- a/src/g_doom/a_doomweaps.cpp +++ b/src/g_doom/a_doomweaps.cpp @@ -59,10 +59,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Punch) if (linetarget) { S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); + self->angle = self->AngleTo(linetarget); } } @@ -218,8 +215,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw) // turn to face target if (!(Flags & SF_NOTURN)) { - angle = R_PointToAngle2(self->x, self->y, - linetarget->x, linetarget->y); + angle = self->AngleTo(linetarget); if (angle - self->angle > ANG180) { if (angle - self->angle < (angle_t)(-ANG90 / 20)) @@ -624,7 +620,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray) if (spray != NULL) { - if (spray->flags6 & MF6_MTHRUSPECIES && spray->GetSpecies() == linetarget->GetSpecies()) + if (spray->flags6 & MF6_MTHRUSPECIES && self->target->GetSpecies() == linetarget->GetSpecies()) { spray->Destroy(); // [MC] Remove it because technically, the spray isn't trying to "hit" them. continue; diff --git a/src/g_doom/a_lostsoul.cpp b/src/g_doom/a_lostsoul.cpp index fdc0257cb9..1182292e00 100644 --- a/src/g_doom/a_lostsoul.cpp +++ b/src/g_doom/a_lostsoul.cpp @@ -36,7 +36,7 @@ void A_SkullAttack(AActor *self, fixed_t speed) an = self->angle >> ANGLETOFINESHIFT; self->velx = FixedMul (speed, finecosine[an]); self->vely = FixedMul (speed, finesine[an]); - dist = P_AproxDistance (dest->x - self->x, dest->y - self->y); + dist = self->AproxDistance (dest); dist = dist / speed; if (dist < 1) diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index b52224c0c2..f361fb78d0 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -103,7 +103,7 @@ void A_PainShootSkull (AActor *self, angle_t angle, const PClass *spawntype, int box.Top() < ld->bbox[BOXBOTTOM] || box.Bottom() > ld->bbox[BOXTOP])) { - if (P_PointOnLineSide(self->x,self->y,ld) != P_PointOnLineSide(x,y,ld)) + if (P_PointOnLineSidePrecise(self->x,self->y,ld) != P_PointOnLineSidePrecise(x,y,ld)) return; // line blocks trajectory // ^ } } diff --git a/src/g_doom/a_revenant.cpp b/src/g_doom/a_revenant.cpp index 0600a59eb2..06fdfa6aa7 100644 --- a/src/g_doom/a_revenant.cpp +++ b/src/g_doom/a_revenant.cpp @@ -77,7 +77,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Tracer) return; // change angle - exact = R_PointToAngle2 (self->x, self->y, dest->x, dest->y); + exact = self->AngleTo(dest); if (exact != self->angle) { @@ -102,10 +102,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Tracer) if (!(self->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))) { // change slope - dist = P_AproxDistance (dest->x - self->x, - dest->y - self->y); - - dist = dist / self->Speed; + dist = self->AproxDistance (dest) / self->Speed; if (dist < 1) dist = 1; diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp index be39b545bf..6e75d80e68 100644 --- a/src/g_doom/a_scriptedmarine.cpp +++ b/src/g_doom/a_scriptedmarine.cpp @@ -276,7 +276,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_M_Saw) S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM); // turn to face target - angle = R_PointToAngle2 (self->x, self->y, linetarget->x, linetarget->y); + angle = self->AngleTo(linetarget); if (angle - self->angle > ANG180) { if (angle - self->angle < (angle_t)(-ANG90/20)) @@ -326,7 +326,8 @@ static void MarinePunch(AActor *self, int damagemul) if (linetarget) { S_Sound (self, CHAN_WEAPON, "*fist", 1, ATTN_NORM); - self->angle = R_PointToAngle2 (self->x, self->y, linetarget->x, linetarget->y); + self->angle = self->AngleTo(linetarget); + } } diff --git a/src/g_doomedmap.cpp b/src/g_doomedmap.cpp index 45224329d8..42157101d0 100644 --- a/src/g_doomedmap.cpp +++ b/src/g_doomedmap.cpp @@ -81,7 +81,7 @@ struct MapinfoEdMapItem { FName classname; // DECORATE is read after MAPINFO so we do not have the actual classes available here yet. short special; - bool argsdefined; + signed char argsdefined; int args[5]; // These are for error reporting. We must store the file information because it's no longer available when these items get resolved. FString filename; @@ -135,7 +135,7 @@ CCMD (dumpmapthings) } else { - Printf("%6d none", infos[i]->Key); + Printf("%6d none\n", infos[i]->Key); } } @@ -181,14 +181,14 @@ void FMapInfoParser::ParseDoomEdNums() editem.special = -1; } memset(editem.args, 0, sizeof(editem.args)); - editem.argsdefined = false; + editem.argsdefined = 0; int minargs = 0; int maxargs = 5; FString specialname; if (sc.CheckString(",")) { - editem.argsdefined = true; // mark args as used - if this is done we need to prevent assignment of map args in P_SpawnMapThing. + editem.argsdefined = 5; // mark args as used - if this is done we need to prevent assignment of map args in P_SpawnMapThing. if (editem.special < 0) editem.special = 0; if (!sc.CheckNumber()) { @@ -221,7 +221,14 @@ void FMapInfoParser::ParseDoomEdNums() editem.args[i] = sc.Number; i++; if (!sc.CheckString(",")) break; + // special check for the ambient sounds which combine the arg being set here with the ones on the mapthing. + if (sc.CheckString("+")) + { + editem.argsdefined = i; + break; + } sc.MustGetNumber(); + } if (specialname.IsNotEmpty() && (i < minargs || i > maxargs)) { diff --git a/src/g_game.cpp b/src/g_game.cpp index 8101ca23d2..ca2233cdbf 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1176,7 +1176,7 @@ void G_Ticker () } if (players[i].mo) { - DWORD sum = rngsum + players[i].mo->x + players[i].mo->y + players[i].mo->z + DWORD sum = rngsum + players[i].mo->X() + players[i].mo->Y() + players[i].mo->Z() + players[i].mo->angle + players[i].mo->pitch; sum ^= players[i].health; consistancy[i][buf] = sum; @@ -1435,13 +1435,13 @@ bool G_CheckSpot (int playernum, FPlayerStart *mthing) if (!players[playernum].mo) { // first spawn of level, before corpses for (i = 0; i < playernum; i++) - if (players[i].mo && players[i].mo->x == x && players[i].mo->y == y) + if (players[i].mo && players[i].mo->X() == x && players[i].mo->Y() == y) return false; return true; } - oldz = players[playernum].mo->z; // [RH] Need to save corpse's z-height - players[playernum].mo->z = z; // [RH] Checks are now full 3-D + oldz = players[playernum].mo->Z(); // [RH] Need to save corpse's z-height + players[playernum].mo->SetZ(z); // [RH] Checks are now full 3-D // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid // corpse to detect collisions with other players in DM starts @@ -1453,7 +1453,7 @@ bool G_CheckSpot (int playernum, FPlayerStart *mthing) players[playernum].mo->flags |= MF_SOLID; i = P_CheckPosition(players[playernum].mo, x, y); players[playernum].mo->flags &= ~MF_SOLID; - players[playernum].mo->z = oldz; // [RH] Restore corpse's height + players[playernum].mo->SetZ(oldz); // [RH] Restore corpse's height if (!i) return false; @@ -1479,8 +1479,7 @@ static fixed_t PlayersRangeFromSpot (FPlayerStart *spot) if (!playeringame[i] || !players[i].mo || players[i].health <= 0) continue; - distance = P_AproxDistance (players[i].mo->x - spot->x, - players[i].mo->y - spot->y); + distance = players[i].mo->AproxDistance (spot->x, spot->y); if (distance < closest) closest = distance; @@ -1713,6 +1712,8 @@ void G_DoPlayerPop(int playernum) // [RH] Make the player disappear FBehavior::StaticStopMyScripts(players[playernum].mo); + // [RH] Let the scripts know the player left + FBehavior::StaticStartTypedScripts(SCRIPT_Disconnect, players[playernum].mo, true, playernum, true); if (players[playernum].mo != NULL) { P_DisconnectEffect(players[playernum].mo); @@ -1726,8 +1727,6 @@ void G_DoPlayerPop(int playernum) players[playernum].mo = NULL; players[playernum].camera = NULL; } - // [RH] Let the scripts know the player left - FBehavior::StaticStartTypedScripts(SCRIPT_Disconnect, NULL, true, playernum); } void G_ScreenShot (char *filename) diff --git a/src/g_heretic/a_chicken.cpp b/src/g_heretic/a_chicken.cpp index 082774a3de..f4ad4c2115 100644 --- a/src/g_heretic/a_chicken.cpp +++ b/src/g_heretic/a_chicken.cpp @@ -178,8 +178,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL1) P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &linetarget); if (linetarget) { - player->mo->angle = R_PointToAngle2 (player->mo->x, - player->mo->y, linetarget->x, linetarget->y); + player->mo->angle = player->mo->AngleTo(linetarget); } P_PlayPeck (player->mo); player->chickenPeck = 12; @@ -211,8 +210,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_BeakAttackPL2) P_LineAttack (player->mo, angle, MELEERANGE, slope, damage, NAME_Melee, "BeakPuff", true, &linetarget); if (linetarget) { - player->mo->angle = R_PointToAngle2 (player->mo->x, - player->mo->y, linetarget->x, linetarget->y); + player->mo->angle = player->mo->AngleTo(linetarget); } P_PlayPeck (player->mo); player->chickenPeck = 12; diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index 17c9621d59..247ccb03fd 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -90,8 +90,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_StaffAttack) { //S_StartSound(player->mo, sfx_stfhit); // turn to face target - self->angle = R_PointToAngle2 (self->x, - self->y, linetarget->x, linetarget->y); + self->angle = self->AngleTo(linetarget); } } @@ -307,8 +306,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GauntletAttack) S_Sound (self, CHAN_AUTO, "weapons/gauntletshit", 1, ATTN_NORM); } // turn to face target - angle = R_PointToAngle2 (self->x, self->y, - linetarget->x, linetarget->y); + angle = self->AngleTo(linetarget); if (angle-self->angle > ANG180) { if ((int)(angle-self->angle) < -ANG90/20) @@ -648,8 +646,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact) } else { // Seek - angle = R_PointToAngle2(self->x, self->y, - target->x, target->y); + self->angle = self->AngleTo(target); newAngle = true; } } @@ -662,8 +659,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DeathBallImpact) if (linetarget && self->target != linetarget) { self->tracer = linetarget; - angle = R_PointToAngle2 (self->x, self->y, - linetarget->x, linetarget->y); + angle = self->AngleTo(linetarget); newAngle = true; break; } @@ -1046,7 +1042,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm) x = self->x + ((pr_storm()&127) - 64) * FRACUNIT; y = self->y + ((pr_storm()&127) - 64) * FRACUNIT; mo = Spawn (x, y, ONCEILINGZ, ALLOW_REPLACE); -#ifdef _3DFLOORS // We used bouncecount to store the 3D floor index in A_HideInCeiling if (!mo) return; fixed_t newz; @@ -1058,7 +1053,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_SkullRodStorm) int moceiling = P_Find3DFloor(NULL, x, y, newz, false, false, newz); if (moceiling >= 0) mo->z = newz - mo->height; -#endif mo->Translation = multiplayer ? TRANSLATION(TRANSLATION_PlayersExtra,self->special2) : 0; mo->target = self->target; @@ -1098,7 +1092,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_RainImpact) DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling) { -#ifdef _3DFLOORS // We use bouncecount to store the 3D floor index fixed_t foo; for (unsigned int i=0; i< self->Sector->e->XFloor.ffloors.Size(); i++) @@ -1106,7 +1099,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling) F3DFloor * rover = self->Sector->e->XFloor.ffloors[i]; if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - if ((foo = rover->bottom.plane->ZatPoint(self->x, self->y)) >= (self->z + self->height)) + if ((foo = rover->bottom.plane->ZatPoint(self)) >= (self->z + self->height)) { self->z = foo + 4*FRACUNIT; self->bouncecount = i; @@ -1114,7 +1107,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_HideInCeiling) } } self->bouncecount = -1; -#endif self->z = self->ceilingz + 4*FRACUNIT; } diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp index dba16b622c..75c05bf2b8 100644 --- a/src/g_heretic/a_ironlich.cpp +++ b/src/g_heretic/a_ironlich.cpp @@ -91,8 +91,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LichAttack) P_TraceBleed (newdam > 0 ? newdam : damage, target, self); return; } - dist = P_AproxDistance (self->x-target->x, self->y-target->y) - > 8*64*FRACUNIT; + dist = self->AproxDistance (target) > 8*64*FRACUNIT; randAttack = pr_atk (); if (randAttack < atkResolve1[dist]) { // Ice ball diff --git a/src/g_hexen/a_blastradius.cpp b/src/g_hexen/a_blastradius.cpp index a78b0b4c87..c7fb17fef0 100644 --- a/src/g_hexen/a_blastradius.cpp +++ b/src/g_hexen/a_blastradius.cpp @@ -33,13 +33,13 @@ void BlastActor (AActor *victim, fixed_t strength, fixed_t speed, AActor * Owner return; } - angle = R_PointToAngle2 (Owner->x, Owner->y, victim->x, victim->y); + angle = Owner->AngleTo(victim); angle >>= ANGLETOFINESHIFT; victim->velx = FixedMul (speed, finecosine[angle]); victim->vely = FixedMul (speed, finesine[angle]); // Spawn blast puff - ang = R_PointToAngle2 (victim->x, victim->y, Owner->x, Owner->y); + ang = victim->AngleTo(Owner); ang >>= ANGLETOFINESHIFT; x = victim->x + FixedMul (victim->radius+FRACUNIT, finecosine[ang]); y = victim->y + FixedMul (victim->radius+FRACUNIT, finesine[ang]); @@ -141,7 +141,7 @@ DEFINE_ACTION_FUNCTION_PARAMS (AActor, A_Blast) { // Must be monster, player, missile, touchy or vulnerable continue; } - dist = P_AproxDistance (self->x - mo->x, self->y - mo->y); + dist = self->AproxDistance (mo); if (dist > radius) { // Out of range continue; diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index 2b469e5afa..39fee109d6 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -262,14 +262,12 @@ static void CHolyTailFollow (AActor *actor, fixed_t dist) child = actor->tracer; if (child) { - an = R_PointToAngle2(actor->x, actor->y, child->x, - child->y)>>ANGLETOFINESHIFT; - oldDistance = P_AproxDistance (child->x-actor->x, child->y-actor->y); + an = actor->AngleTo(child) >> ANGLETOFINESHIFT; + oldDistance = child->AproxDistance (actor); if (P_TryMove (child, actor->x+FixedMul(dist, finecosine[an]), actor->y+FixedMul(dist, finesine[an]), true)) { - newDistance = P_AproxDistance (child->x-actor->x, - child->y-actor->y)-FRACUNIT; + newDistance = child->AproxDistance (actor)-FRACUNIT; if (oldDistance < FRACUNIT) { if (child->z < actor->z) @@ -425,7 +423,7 @@ static void CHolySeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax) deltaZ = -15*FRACUNIT; } } - dist = P_AproxDistance (target->x-actor->x, target->y-actor->y); + dist = actor->AproxDistance (target); dist = dist / actor->Speed; if (dist < 1) { diff --git a/src/g_hexen/a_clericstaff.cpp b/src/g_hexen/a_clericstaff.cpp index 45551f55fa..eeda968fa1 100644 --- a/src/g_hexen/a_clericstaff.cpp +++ b/src/g_hexen/a_clericstaff.cpp @@ -73,9 +73,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, PClass::FindClass ("CStaffPuff"), false, &linetarget); if (linetarget != NULL) { - pmo->angle = R_PointToAngle2 (pmo->x, pmo->y, linetarget->x, linetarget->y); + pmo->angle = pmo->AngleTo(linetarget); if (((linetarget->player && (!linetarget->IsTeammate (pmo) || level.teamdamage != 0))|| linetarget->flags3&MF3_ISMONSTER) - && (!(linetarget->flags2&(MF2_DORMANT+MF2_INVULNERABLE)))) + && (!(linetarget->flags2&(MF2_DORMANT|MF2_INVULNERABLE)))) { newLife = player->health+(damage>>3); newLife = newLife > max ? max : newLife; @@ -103,7 +103,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheck) P_LineAttack (pmo, angle, fixed_t(1.5*MELEERANGE), slope, damage, NAME_Melee, PClass::FindClass ("CStaffPuff"), false, &linetarget); if (linetarget != NULL) { - pmo->angle = R_PointToAngle2 (pmo->x, pmo->y, linetarget->x, linetarget->y); + pmo->angle = pmo->AngleTo(linetarget); if ((linetarget->player && (!linetarget->IsTeammate (pmo) || level.teamdamage != 0)) || linetarget->flags3&MF3_ISMONSTER) { newLife = player->health+(damage>>4); diff --git a/src/g_hexen/a_dragon.cpp b/src/g_hexen/a_dragon.cpp index fdf2428afe..d33e7b07b0 100644 --- a/src/g_hexen/a_dragon.cpp +++ b/src/g_hexen/a_dragon.cpp @@ -59,28 +59,21 @@ static void DragonSeek (AActor *actor, angle_t thresh, angle_t turnMax) angle = actor->angle>>ANGLETOFINESHIFT; actor->velx = FixedMul (actor->Speed, finecosine[angle]); actor->vely = FixedMul (actor->Speed, finesine[angle]); + dist = actor->AproxDistance (target) / actor->Speed; if (actor->z+actor->height < target->z || target->z+target->height < actor->z) { - dist = P_AproxDistance(target->x-actor->x, target->y-actor->y); - dist = dist/actor->Speed; if (dist < 1) { dist = 1; } actor->velz = (target->z - actor->z)/dist; } - else - { - dist = P_AproxDistance (target->x-actor->x, target->y-actor->y); - dist = dist/actor->Speed; - } if (target->flags&MF_SHOOTABLE && pr_dragonseek() < 64) { // attack the destination mobj if it's attackable AActor *oldTarget; - if (absangle(actor->angle-R_PointToAngle2(actor->x, actor->y, - target->x, target->y)) < ANGLE_45/2) + if (absangle(actor->angle - actor->AngleTo(target)) < ANGLE_45/2) { oldTarget = actor->target; actor->target = target; @@ -105,8 +98,7 @@ static void DragonSeek (AActor *actor, angle_t thresh, angle_t turnMax) { AActor *bestActor = NULL; bestAngle = ANGLE_MAX; - angleToTarget = R_PointToAngle2(actor->x, actor->y, - actor->target->x, actor->target->y); + angleToTarget = actor->AngleTo(actor->target); for (i = 0; i < 5; i++) { if (!target->args[i]) @@ -119,8 +111,7 @@ static void DragonSeek (AActor *actor, angle_t thresh, angle_t turnMax) { continue; } - angleToSpot = R_PointToAngle2(actor->x, actor->y, - mo->x, mo->y); + angleToSpot = actor->AngleTo(mo); if (absangle(angleToSpot-angleToTarget) < bestAngle) { bestAngle = absangle(angleToSpot-angleToTarget); @@ -196,8 +187,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_DragonFlight) self->target = NULL; return; } - angle = R_PointToAngle2(self->x, self->y, self->target->x, - self->target->y); + angle = self->AngleTo(self->target); if (absangle(self->angle-angle) < ANGLE_45/2 && self->CheckMeleeRange()) { int damage = pr_dragonflight.HitDice (8); diff --git a/src/g_hexen/a_fighterplayer.cpp b/src/g_hexen/a_fighterplayer.cpp index 5df1839016..82cb8bd054 100644 --- a/src/g_hexen/a_fighterplayer.cpp +++ b/src/g_hexen/a_fighterplayer.cpp @@ -30,7 +30,7 @@ void AdjustPlayerAngle (AActor *pmo, AActor *linetarget) angle_t angle; int difference; - angle = R_PointToAngle2 (pmo->x, pmo->y, linetarget->x, linetarget->y); + angle = pmo->AngleTo(linetarget); difference = (int)angle - (int)pmo->angle; if (abs(difference) > MAX_ANGLE_ADJUST) { diff --git a/src/g_hexen/a_firedemon.cpp b/src/g_hexen/a_firedemon.cpp index c588dd2ae2..c003dc21dc 100644 --- a/src/g_hexen/a_firedemon.cpp +++ b/src/g_hexen/a_firedemon.cpp @@ -158,12 +158,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_FiredChase) { self->special2 = 0; self->velx = self->vely = 0; - dist = P_AproxDistance (self->x - target->x, self->y - target->y); + dist = self->AproxDistance (target); if (dist < FIREDEMON_ATTACK_RANGE) { if (pr_firedemonchase() < 30) { - ang = R_PointToAngle2 (self->x, self->y, target->x, target->y); + ang = self->AngleTo(target); if (pr_firedemonchase() < 128) ang += ANGLE_90; else diff --git a/src/g_hexen/a_flies.cpp b/src/g_hexen/a_flies.cpp index 3c677d5cb3..683cbebe1d 100644 --- a/src/g_hexen/a_flies.cpp +++ b/src/g_hexen/a_flies.cpp @@ -79,7 +79,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FlyBuzz) return; } - angle_t ang = R_PointToAngle2(self->x, self->y, targ->x, targ->y); + angle_t ang = self->AngleTo(targ); self->angle = ang; self->args[0]++; ang >>= ANGLETOFINESHIFT; diff --git a/src/g_hexen/a_healingradius.cpp b/src/g_hexen/a_healingradius.cpp index b827f666b7..d1d39ddd43 100644 --- a/src/g_hexen/a_healingradius.cpp +++ b/src/g_hexen/a_healingradius.cpp @@ -37,7 +37,7 @@ bool AArtiHealingRadius::Use (bool pickup) if (playeringame[i] && players[i].mo != NULL && players[i].mo->health > 0 && - P_AproxDistance (players[i].mo->x - Owner->x, players[i].mo->y - Owner->y) <= HEAL_RADIUS_DIST) + players[i].mo->AproxDistance (Owner) <= HEAL_RADIUS_DIST) { // Q: Is it worth it to make this selectable as a player property? // A: Probably not - but it sure doesn't hurt. diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index 9ca0a70e80..498b4e9554 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -665,8 +665,7 @@ void A_SorcOffense2(AActor *actor) if (mo) { mo->special2 = 35*5/2; // 5 seconds - dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y); - dist = dist/mo->Speed; + dist = mo->AproxDistance(dest) / mo->Speed; if(dist < 1) dist = 1; mo->velz = (dest->z - mo->z) / dist; } diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp index 9d0c098fe4..ad10a464c1 100644 --- a/src/g_hexen/a_hexenspecialdecs.cpp +++ b/src/g_hexen/a_hexenspecialdecs.cpp @@ -109,8 +109,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_PotteryCheck) if (playeringame[i]) { AActor *pmo = players[i].mo; - if (P_CheckSight (self, pmo) && (absangle(R_PointToAngle2 (pmo->x, - pmo->y, self->x, self->y) - pmo->angle) <= ANGLE_45)) + if (P_CheckSight (self, pmo) && (absangle(pmo->AngleTo(self) - pmo->angle) <= ANGLE_45)) { // Previous state (pottery bit waiting state) self->SetState (self->state - 1); return; diff --git a/src/g_hexen/a_korax.cpp b/src/g_hexen/a_korax.cpp index 5323fb8515..a941f1a65e 100644 --- a/src/g_hexen/a_korax.cpp +++ b/src/g_hexen/a_korax.cpp @@ -388,8 +388,7 @@ void A_KSpiritSeeker (AActor *actor, angle_t thresh, angle_t turnMax) deltaZ = -15*FRACUNIT; } } - dist = P_AproxDistance (target->x-actor->x, target->y-actor->y); - dist = dist/actor->Speed; + dist = actor->AproxDistance (target) / actor->Speed; if (dist < 1) { dist = 1; @@ -486,7 +485,7 @@ AActor *P_SpawnKoraxMissile (fixed_t x, fixed_t y, fixed_t z, z -= source->floorclip; th = Spawn (type, x, y, z, ALLOW_REPLACE); th->target = source; // Originator - an = R_PointToAngle2(x, y, dest->x, dest->y); + an = source->AngleXYTo(x, y, dest); if (dest->flags & MF_SHADOW) { // Invisible target an += pr_kmissile.Random2()<<21; @@ -495,8 +494,7 @@ AActor *P_SpawnKoraxMissile (fixed_t x, fixed_t y, fixed_t z, an >>= ANGLETOFINESHIFT; th->velx = FixedMul (th->Speed, finecosine[an]); th->vely = FixedMul (th->Speed, finesine[an]); - dist = P_AproxDistance (dest->x - x, dest->y - y); - dist = dist/th->Speed; + dist = dest->AproxDistance (x, y, source) / th->Speed; if (dist < 1) { dist = 1; diff --git a/src/g_hexen/a_magelightning.cpp b/src/g_hexen/a_magelightning.cpp index 9953cf1d04..a13e3a2741 100644 --- a/src/g_hexen/a_magelightning.cpp +++ b/src/g_hexen/a_magelightning.cpp @@ -190,7 +190,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_LightningClip) } else { - self->angle = R_PointToAngle2(self->x, self->y, target->x, target->y); + self->angle = self->AngleTo(target); self->velx = 0; self->vely = 0; P_ThrustMobj (self, self->angle, self->Speed>>1); diff --git a/src/g_level.cpp b/src/g_level.cpp index 713205f6fb..03474a5b7a 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -533,6 +533,10 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill Printf (TEXTCOLOR_RED "Unloading scripts cannot exit the level again.\n"); return; } + if (gameaction == ga_completed) // do not exit multiple times. + { + return; + } if (levelname == NULL || *levelname == 0) { @@ -694,6 +698,13 @@ void G_DoCompleted (void) gameaction = ga_nothing; + if ( gamestate == GS_DEMOSCREEN + || gamestate == GS_FULLCONSOLE + || gamestate == GS_STARTUP) + { + return; + } + if (gamestate == GS_TITLELEVEL) { level.MapName = nextlevel; @@ -1221,6 +1232,7 @@ void G_FinishTravel () pawn->dropoffz = pawndup->dropoffz; pawn->floorsector = pawndup->floorsector; pawn->floorpic = pawndup->floorpic; + pawn->floorterrain = pawndup->floorterrain; pawn->ceilingsector = pawndup->ceilingsector; pawn->ceilingpic = pawndup->ceilingpic; pawn->floorclip = pawndup->floorclip; @@ -1787,8 +1799,14 @@ void G_ReadSnapshots (PNGHandle *png) DWORD snapver; arc << snapver; - arc << namelen; - arc.Read (mapname, namelen); + if (SaveVersion < 4508) + { + arc << namelen; + arc.Read(mapname, namelen); + mapname[namelen] = 0; + MapName = mapname; + } + else arc << MapName; TheDefaultLevelInfo.snapshotVer = snapver; TheDefaultLevelInfo.snapshot = new FCompressedMemFile; TheDefaultLevelInfo.snapshot->Serialize (arc); diff --git a/src/g_level.h b/src/g_level.h index 496c5c0f4f..2e8e8e4c57 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -339,7 +339,7 @@ struct level_info_t TArray specialactions; TArray PrecacheSounds; - TArray PrecacheTextures; + TArray PrecacheTextures; level_info_t() { @@ -563,8 +563,10 @@ enum ESkillProperty SKILLP_FriendlyHealth, SKILLP_NoPain, SKILLP_ArmorFactor, + SKILLP_HealthFactor, SKILLP_EasyKey, SKILLP_SlowMonsters, + SKILLP_Infight, }; int G_SkillProperty(ESkillProperty prop); const char * G_SkillName(); @@ -602,7 +604,9 @@ struct FSkillInfo fixed_t MonsterHealth; fixed_t FriendlyHealth; bool NoPain; + int Infighting; fixed_t ArmorFactor; + fixed_t HealthFactor; FSkillInfo() {} FSkillInfo(const FSkillInfo &other) diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 5a39d7dbb1..9dace3e237 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -318,7 +318,7 @@ FString level_info_t::LookupLevelName() checkstring[0] = '\0'; } thename = strstr (lookedup, checkstring); - if (thename == NULL || thename == lookedup) + if (thename == NULL) { thename = lookedup; } @@ -1077,15 +1077,8 @@ DEFINE_MAP_OPTION(PrecacheTextures, true) do { parse.sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(parse.sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_ReturnFirst); - if (!tex.isValid()) - { - parse.sc.ScriptMessage("Unknown texture \"%s\"", parse.sc.String); - } - else - { - info->PrecacheTextures.Push(tex); - } + //the texture manager is not initialized here so all we can do is store the texture's name. + info->PrecacheTextures.Push(parse.sc.String); } while (parse.sc.CheckString(",")); } @@ -1333,6 +1326,7 @@ MapFlagHandlers[] = { "compat_badangles", MITYPE_COMPATFLAG, 0, COMPATF2_BADANGLES }, { "compat_floormove", MITYPE_COMPATFLAG, 0, COMPATF2_FLOORMOVE }, { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, + { "compat_pointonline", MITYPE_COMPATFLAG, 0, COMPATF2_POINTONLINE }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp index 0f9d9cd55f..c2348a9e3a 100644 --- a/src/g_raven/a_minotaur.cpp +++ b/src/g_raven/a_minotaur.cpp @@ -185,7 +185,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurDecide) { S_Sound (self, CHAN_WEAPON, "minotaur/sight", 1, ATTN_NORM); } - dist = P_AproxDistance (self->x-target->x, self->y-target->y); + dist = self->AproxDistance (target); if (target->z+target->height > self->z && target->z+target->height < self->z+self->height && dist < (friendly ? 16*64*FRACUNIT : 8*64*FRACUNIT) @@ -390,7 +390,7 @@ void P_MinotaurSlam (AActor *source, AActor *target) fixed_t thrust; int damage; - angle = R_PointToAngle2 (source->x, source->y, target->x, target->y); + angle = source->AngleTo(target); angle >>= ANGLETOFINESHIFT; thrust = 16*FRACUNIT+(pr_minotaurslam()<<10); target->velx += FixedMul (thrust, finecosine[angle]); @@ -489,7 +489,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurLook) mo = player->mo; if (mo == master) continue; if (mo->health <= 0) continue; - dist = P_AproxDistance(self->x - mo->x, self->y - mo->y); + dist = self->AproxDistance(mo); if (dist > MINOTAUR_LOOK_DIST) continue; self->target = mo; break; @@ -514,7 +514,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MinotaurLook) if (!(mo->flags3 & MF3_ISMONSTER)) continue; if (mo->health <= 0) continue; if (!(mo->flags & MF_SHOOTABLE)) continue; - dist = P_AproxDistance (self->x - mo->x, self->y - mo->y); + dist = self->AproxDistance(mo); if (dist > MINOTAUR_LOOK_DIST) continue; if ((mo == master) || (mo == self)) continue; if ((mo->flags5 & MF5_SUMMONEDMONSTER) && (mo->tracer == master)) continue; diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index bc33eb19ca..2be2e179dd 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -72,7 +72,7 @@ bool APowerupGiver::Use (bool pickup) power->Strength = Strength; } - power->ItemFlags |= ItemFlags & (IF_ALWAYSPICKUP|IF_ADDITIVETIME); + power->ItemFlags |= ItemFlags & (IF_ALWAYSPICKUP|IF_ADDITIVETIME|IF_NOTELEPORTFREEZE); if (power->CallTryPickup (Owner)) { return true; @@ -342,6 +342,18 @@ void APowerup::OwnerDied () Destroy (); } +//=========================================================================== +// +// AInventory :: GetNoTeleportFreeze +// +//=========================================================================== + +bool APowerup::GetNoTeleportFreeze () +{ + if (ItemFlags & IF_NOTELEPORTFREEZE) return true; + return Super::GetNoTeleportFreeze(); +} + // Invulnerability Powerup --------------------------------------------------- IMPLEMENT_CLASS (APowerInvulnerable) diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 4e1823f5a0..c16a7a0c12 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -18,6 +18,7 @@ public: virtual AInventory *CreateTossable (); virtual void Serialize (FArchive &arc); virtual void OwnerDied (); + virtual bool GetNoTeleportFreeze(); virtual PalEntry GetBlend (); virtual bool DrawPowerup (int x, int y); diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 576ff6a5cd..29fcbf8cbc 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -272,7 +272,6 @@ FTextureID DBaseDecal::StickToWall (side_t *wall, fixed_t x, fixed_t y, F3DFloor Z -= back->GetPlaneTexZ(sector_t::ceiling); tex = wall->GetTexture(side_t::top); } -#ifdef _3DFLOORS else if (ffloor) // this is a 3d-floor segment - do this only if we know which one! { Sector=ffloor->model; @@ -295,7 +294,6 @@ FTextureID DBaseDecal::StickToWall (side_t *wall, fixed_t x, fixed_t y, F3DFloor tex = ffloor->master->sidedef[0]->GetTexture(side_t::mid); } } -#endif else return FNullTextureID(); CalcFracPos (wall, x, y); diff --git a/src/g_shared/a_lightning.cpp b/src/g_shared/a_lightning.cpp index e7c598d3e8..33d51c02d9 100644 --- a/src/g_shared/a_lightning.cpp +++ b/src/g_shared/a_lightning.cpp @@ -139,7 +139,7 @@ void DLightningThinker::LightningFlash () for (i = numsectors, j = 0; i > 0; --i, ++j, ++tempSec) { // allow combination of the lightning sector specials with bit masks - int special = tempSec->special & 0xff; + int special = tempSec->special; if (tempSec->GetTexture(sector_t::ceiling) == skyflatnum || special == Light_IndoorLightning1 || special == Light_IndoorLightning2 diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 038b630290..eb28310241 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -236,10 +236,12 @@ bool P_GiveBody (AActor *actor, int num, int max) return true; } } - else + else if (num > 0) { if (player->health < max) { + num = FixedMul(num, G_SkillProperty(SKILLP_HealthFactor)); + if (num < 1) num = 1; player->health += num; if (player->health > max) { @@ -680,6 +682,7 @@ AInventory *AInventory::CreateCopy (AActor *other) { AInventory *copy; + Amount = MIN(Amount, MaxAmount); if (GoAway ()) { copy = static_cast(Spawn (GetClass(), 0, 0, 0, NO_REPLACE)); @@ -850,6 +853,25 @@ fixed_t AInventory::GetSpeedFactor () } } +//=========================================================================== +// +// AInventory :: GetNoTeleportFreeze +// +//=========================================================================== + +bool AInventory::GetNoTeleportFreeze () +{ + // do not check the flag here because it's only active when used on PowerUps, not on PowerupGivers. + if (Inventory != NULL) + { + return Inventory->GetNoTeleportFreeze(); + } + else + { + return false; + } +} + //=========================================================================== // // AInventory :: AlterWeaponSprite diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 504a26c73e..413b01db3d 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -137,6 +137,7 @@ enum IF_TOSSED = 1<<22, // Was spawned by P_DropItem (i.e. as a monster drop) IF_ALWAYSRESPAWN = 1<<23, // Always respawn, regardless of dmflag IF_TRANSFER = 1<<24, // All inventory items that the inventory item contains is also transfered to the pickuper + IF_NOTELEPORTFREEZE = 1<<25, // does not 'freeze' the player right after teleporting. }; @@ -201,6 +202,7 @@ public: virtual void AbsorbDamage (int damage, FName damageType, int &newdamage); virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive); virtual fixed_t GetSpeedFactor(); + virtual bool GetNoTeleportFreeze(); virtual int AlterWeaponSprite (visstyle_t *vis); virtual PalEntry GetBlend (); @@ -303,8 +305,7 @@ public: virtual FState *GetReadyState (); virtual FState *GetAtkState (bool hold); virtual FState *GetAltAtkState (bool hold); - virtual FState *GetRelState (); - virtual FState *GetZoomState (); + virtual FState *GetStateForButtonName (FName button); virtual void PostMorphWeapon (); virtual void EndPowerup (); diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 3c8d704aa7..195f3ccbfd 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -129,7 +129,7 @@ void DEarthquake::Tick () AActor *victim = players[i].mo; fixed_t dist; - dist = P_AproxDistance (victim->x - m_Spot->x, victim->y - m_Spot->y); + dist = m_Spot->AproxDistance (victim, true); // Check if in damage radius if (dist < m_DamageRadius && victim->z <= victim->floorz) { @@ -242,8 +242,7 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger { if (quake->m_Spot != NULL) { - fixed_t dist = P_AproxDistance (victim->x - quake->m_Spot->x, - victim->y - quake->m_Spot->y); + fixed_t dist = quake->m_Spot->AproxDistance (victim, true); if (dist < quake->m_TremorRadius) { ++count; diff --git a/src/g_shared/a_randomspawner.cpp b/src/g_shared/a_randomspawner.cpp index adfc09b6e6..7d000f3d48 100644 --- a/src/g_shared/a_randomspawner.cpp +++ b/src/g_shared/a_randomspawner.cpp @@ -15,10 +15,23 @@ #include "gstrings.h" #include "a_action.h" #include "thingdef/thingdef.h" +#include "v_text.h" #define MAX_RANDOMSPAWNERS_RECURSION 32 // Should be largely more than enough, honestly. static FRandom pr_randomspawn("RandomSpawn"); +static bool IsMonster(const FDropItem *di) +{ + const PClass *pclass = PClass::FindClass(di->Name); + + if (NULL == pclass) + { + return false; + } + + return 0 != (GetDefaultByType(pclass)->flags3 & MF3_ISMONSTER); +} + class ARandomSpawner : public AActor { DECLARE_CLASS (ARandomSpawner, AActor) @@ -41,7 +54,7 @@ class ARandomSpawner : public AActor { if (di->Name != NAME_None) { - if (!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER)) + if (!nomonsters || !IsMonster(di)) { if (di->amount < 0) di->amount = 1; // default value is -1, we need a positive value. n += di->amount; // this is how we can weight the list. @@ -62,7 +75,7 @@ class ARandomSpawner : public AActor while (n > -1 && di != NULL) { if (di->Name != NAME_None && - (!nomonsters || !(GetDefaultByType(PClass::FindClass(di->Name))->flags3 & MF3_ISMONSTER))) + (!nomonsters || !IsMonster(di))) { n -= di->amount; if ((di->Next != NULL) && (n > -1)) @@ -106,6 +119,7 @@ class ARandomSpawner : public AActor } else { + Printf(TEXTCOLOR_RED "Unknown item class %s to drop from a random spawner\n", di->Name.GetChars()); Species = NAME_None; } } diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index 570818e194..bb582dd284 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -39,6 +39,14 @@ IMPLEMENT_CLASS (ASectorAction) +ASectorAction::ASectorAction (bool activatedByUse) : + ActivatedByUse (activatedByUse) {} + +bool ASectorAction::IsActivatedByUse() const +{ + return ActivatedByUse; +} + void ASectorAction::Destroy () { // Remove ourself from this sector's list of actions @@ -102,12 +110,17 @@ bool ASectorAction::DoTriggerAction (AActor *triggerer, int activationType) return false; } +bool ASectorAction::CanTrigger (AActor *triggerer) const +{ + return special && + ((triggerer->player && !(flags & MF_FRIENDLY)) || + ((flags & MF_AMBUSH) && (triggerer->flags2 & MF2_MCROSS)) || + ((flags2 & MF2_DORMANT) && (triggerer->flags2 & MF2_PCROSS))); +} + bool ASectorAction::CheckTrigger (AActor *triggerer) const { - if (special && - ((triggerer->player && !(flags & MF_FRIENDLY)) || - ((flags & MF_AMBUSH) && (triggerer->flags2 & MF2_MCROSS)) || - ((flags2 & MF2_DORMANT) && (triggerer->flags2 & MF2_PCROSS)))) + if (CanTrigger(triggerer)) { bool res = !!P_ExecuteSpecial(special, NULL, triggerer, false, args[0], args[1], args[2], args[3], args[4]); @@ -196,6 +209,7 @@ class ASecActUse : public ASectorAction { DECLARE_CLASS (ASecActUse, ASectorAction) public: + ASecActUse() : ASectorAction (true) {} bool DoTriggerAction (AActor *triggerer, int activationType); }; @@ -214,6 +228,7 @@ class ASecActUseWall : public ASectorAction { DECLARE_CLASS (ASecActUseWall, ASectorAction) public: + ASecActUseWall() : ASectorAction (true) {} bool DoTriggerAction (AActor *triggerer, int activationType); }; diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 2688249ea9..8a1c313076 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -100,6 +100,20 @@ public: TObjPtr Mate; }; +// For an EE compatible linedef based definition. +class ASkyCamCompat : public ASkyViewpoint +{ + DECLARE_CLASS (ASkyCamCompat, ASkyViewpoint) + +public: + void BeginPlay () + { + // Do not call the SkyViewpoint's super method because it would trash our setup + AActor::BeginPlay(); + } +}; + + class AStackPoint : public ASkyViewpoint { DECLARE_CLASS (AStackPoint, ASkyViewpoint) diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index d62a2a07bd..a8c8ebd55d 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -37,6 +37,7 @@ #include "p_local.h" #include "p_lnspec.h" #include "farchive.h" +#include "r_sky.h" // arg0 = Visibility*4 for this skybox @@ -66,13 +67,13 @@ void ASkyViewpoint::Destroy () // remove all sector references to ourselves. for (int i = 0; i linecount; i++) - { - refline = Sector->lines[i]; - if (refline->special == Sector_SetPortal && refline->args[1] == 2) - { - // We found the setup linedef for this skybox, so let's use it for our init. - int skybox_id = refline->args[0]; - - // Then, change the alpha - alpha = refline->args[4]; - - // Finally, skyboxify all tagged sectors - // This involves changing their texture to the sky flat, because while - // EE works with any texture for its skybox portals, ZDoom doesn't. - FSectorTagIterator it(skybox_id); - int secnum; - while ((secnum = it.Next()) >= 0) - { - // plane: 0=floor, 1=ceiling, 2=both - if (refline->args[2] == 1 || refline->args[2] == 2) - { - sectors[secnum].CeilingSkyBox = this; - sectors[secnum].SetTexture(sector_t::ceiling, skyflatnum, false); - } - if (refline->args[2] == 0 || refline->args[2] == 2) - { - sectors[secnum].FloorSkyBox = this; - sectors[secnum].SetTexture(sector_t::floor, skyflatnum, false); - } - } - } - } - } - // Do not call the SkyViewpoint's super method because it would trash our setup - AActor::BeginPlay(); -} //--------------------------------------------------------------------------- @@ -185,11 +129,13 @@ void ASkyPicker::PostBeginPlay () { if (0 == (args[1] & 2)) { - Sector->CeilingSkyBox = box; + Sector->SkyBoxes[sector_t::ceiling] = box; + if (box == NULL) Sector->MoreFlags |= SECF_NOCEILINGSKYBOX; // sector should ignore the level's default skybox } if (0 == (args[1] & 1)) { - Sector->FloorSkyBox = box; + Sector->SkyBoxes[sector_t::floor] = box; + if (box == NULL) Sector->MoreFlags |= SECF_NOFLOORSKYBOX; // sector should ignore the level's default skybox } } Destroy (); diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index fbfbbf5502..d3994ed848 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -158,7 +158,7 @@ struct FSpotList while (true) { - distance = P_AproxDistance(Spots[i]->x - x, Spots[i]->y - y); + distance = Spots[i]->AproxDistance(x, y); if ((distance >= mindist) && ((maxdist == 0) || (distance <= maxdist))) break; diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index fae232557b..9c71ab0ea9 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -683,25 +683,15 @@ FState *AWeapon::GetAltAtkState (bool hold) //=========================================================================== // -// AWeapon :: GetRelState +// AWeapon :: GetStateForButtonName // //=========================================================================== -FState *AWeapon::GetRelState () +FState *AWeapon::GetStateForButtonName (FName button) { - return FindState(NAME_Reload); + return FindState(button); } -//=========================================================================== -// -// AWeapon :: GetZoomState -// -//=========================================================================== - -FState *AWeapon::GetZoomState () -{ - return FindState(NAME_Zoom); -} /* Weapon giver ***********************************************************/ diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index f60c8a84e4..997021c5f2 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -134,6 +134,7 @@ DHUDMessage::DHUDMessage (FFont *font, const char *text, float x, float y, int h NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = 0; WrapWidth = 0; + HandleAspect = true; Top = y; Next = NULL; Lines = NULL; @@ -196,6 +197,14 @@ void DHUDMessage::Serialize (FArchive &arc) NoWrap = false; ClipX = ClipY = ClipWidth = ClipHeight = WrapWidth = 0; } + if (SaveVersion >= 4525) + { + arc << HandleAspect; + } + else + { + HandleAspect = true; + } if (arc.IsLoading ()) { Lines = NULL; @@ -257,7 +266,7 @@ void DHUDMessage::CalcClipCoords(int hudheight) else { screen->VirtualToRealCoordsInt(x, y, w, h, - HUDWidth, hudheight, false, true); + HUDWidth, hudheight, false, HandleAspect); ClipLeft = x; ClipTop = y; ClipRight = x + w; diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index b8dde58506..b8416dd5d7 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -94,12 +94,13 @@ public: NoWrap = nowrap; ResetText(SourceText); } - void SetClipRect(int x, int y, int width, int height) + void SetClipRect(int x, int y, int width, int height, bool aspect) { ClipX = x; ClipY = y; ClipWidth = width; ClipHeight = height; + HandleAspect = aspect; } void SetWrapWidth(int wrap) { @@ -119,6 +120,7 @@ protected: int HUDWidth, HUDHeight; int ClipX, ClipY, ClipWidth, ClipHeight, WrapWidth; // in HUD coords int ClipLeft, ClipTop, ClipRight, ClipBot; // in screen coords + bool HandleAspect; EColorRange TextColor; FFont *Font; FRenderStyle Style; diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index b8f419ef05..ea1c8528e2 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -366,7 +366,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags) if (player->mo != NULL) { // The next 12 lines are from the Doom statusbar code. - badguyangle = R_PointToAngle2(player->mo->x, player->mo->y, player->attacker->x, player->attacker->y); + badguyangle = player->mo->AngleTo(player->attacker); if (badguyangle > player->mo->angle) { // whether right or left diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index c0442fc746..b743506332 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1338,17 +1338,17 @@ void DBaseStatusBar::Draw (EHudState state) { if (Scaled) { - y -= Scale (10, SCREENHEIGHT, 200); + y -= Scale (11, SCREENHEIGHT, 200); } else { if (SCREENWIDTH < 640) { - y -= 11; + y -= 12; } else { // Get past the tops of the gargoyles' wings - y -= 26; + y -= 28; } } } diff --git a/src/g_skill.cpp b/src/g_skill.cpp index ddfdbb4cc1..f4cb63ea51 100644 --- a/src/g_skill.cpp +++ b/src/g_skill.cpp @@ -83,6 +83,8 @@ void FMapInfoParser::ParseSkill () skill.FriendlyHealth = FRACUNIT; skill.NoPain = false; skill.ArmorFactor = FRACUNIT; + skill.Infighting = 0; + skill.HealthFactor = FRACUNIT; sc.MustGetString(); skill.Name = sc.String; @@ -266,6 +268,20 @@ void FMapInfoParser::ParseSkill () sc.MustGetFloat(); skill.ArmorFactor = FLOAT2FIXED(sc.Float); } + else if (sc.Compare("HealthFactor")) + { + ParseAssign(); + sc.MustGetFloat(); + skill.HealthFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare("NoInfighting")) + { + skill.Infighting = LEVEL2_NOINFIGHTING; + } + else if (sc.Compare("TotalInfighting")) + { + skill.Infighting = LEVEL2_TOTALINFIGHTING; + } else if (sc.Compare("DefaultSkill")) { if (DefaultSkill >= 0) @@ -384,6 +400,17 @@ int G_SkillProperty(ESkillProperty prop) case SKILLP_ArmorFactor: return AllSkills[gameskill].ArmorFactor; + + case SKILLP_HealthFactor: + return AllSkills[gameskill].HealthFactor; + + case SKILLP_Infight: + // This property also needs to consider the level flags for the same info. + if (level.flags2 & LEVEL2_TOTALINFIGHTING) return 1; + if (level.flags2 & LEVEL2_NOINFIGHTING) return -1; + if (AllSkills[gameskill].Infighting == LEVEL2_TOTALINFIGHTING) return 1; + if (AllSkills[gameskill].Infighting == LEVEL2_NOINFIGHTING) return -1; + return infighting; } } return 0; @@ -463,7 +490,9 @@ FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other) MonsterHealth = other.MonsterHealth; FriendlyHealth = other.FriendlyHealth; NoPain = other.NoPain; + Infighting = other.Infighting; ArmorFactor = other.ArmorFactor; + HealthFactor = other.HealthFactor; return *this; } diff --git a/src/g_strife/a_crusader.cpp b/src/g_strife/a_crusader.cpp index 7ef0b24a18..cf5f03a23b 100644 --- a/src/g_strife/a_crusader.cpp +++ b/src/g_strife/a_crusader.cpp @@ -11,9 +11,9 @@ static bool CrusaderCheckRange (AActor *self) { - if (P_CheckSight (self, self->target) && self->reactiontime == 0) + if (self->reactiontime == 0 && P_CheckSight (self, self->target)) { - return P_AproxDistance (self->x - self->target->x, self->y - self->target->y) < 264*FRACUNIT; + return self->AproxDistance (self->target) < 264*FRACUNIT; } return false; } diff --git a/src/g_strife/a_inquisitor.cpp b/src/g_strife/a_inquisitor.cpp index 70ca805f8d..1a0739d365 100644 --- a/src/g_strife/a_inquisitor.cpp +++ b/src/g_strife/a_inquisitor.cpp @@ -20,7 +20,7 @@ bool InquisitorCheckDistance (AActor *self) { if (self->reactiontime == 0 && P_CheckSight (self, self->target)) { - return P_AproxDistance (self->x - self->target->x, self->y - self->target->y) < 264*FRACUNIT; + return self->AproxDistance (self->target) < 264*FRACUNIT; } return false; } @@ -85,7 +85,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_InquisitorJump) speed = self->Speed * 2/3; self->velx += FixedMul (speed, finecosine[an]); self->vely += FixedMul (speed, finesine[an]); - dist = P_AproxDistance (self->target->x - self->x, self->target->y - self->y); + dist = self->AproxDistance (self->target); dist /= speed; if (dist < 1) { diff --git a/src/g_strife/a_spectral.cpp b/src/g_strife/a_spectral.cpp index 6e2e6488e1..6f80fe42c5 100644 --- a/src/g_strife/a_spectral.cpp +++ b/src/g_strife/a_spectral.cpp @@ -95,7 +95,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Tracer2) return; // change angle - exact = R_PointToAngle2 (self->x, self->y, dest->x, dest->y); + exact = self->AngleTo(dest); if (exact != self->angle) { @@ -120,8 +120,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Tracer2) if (!(self->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER))) { // change slope - dist = P_AproxDistance (dest->x - self->x, dest->y - self->y); - dist /= self->Speed; + dist = self->AproxDistance (dest) / self->Speed; if (dist < 1) { diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 1edea19faa..9f3ebb2723 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -628,13 +628,13 @@ DEFINE_ACTION_FUNCTION(AActor, A_CheckTerrain) { sector_t *sec = self->Sector; - if (self->z == sec->floorplane.ZatPoint (self->x, self->y)) + if (self->z == sec->floorplane.ZatPoint(self)) { - if ((sec->special & 0xFF) == Damage_InstantDeath) + if (sec->special == Damage_InstantDeath) { - P_DamageMobj (self, NULL, NULL, 999, NAME_None); + P_DamageMobj (self, NULL, NULL, 999, NAME_InstantDeath); } - else if ((sec->special & 0xFF) == Scroll_StrifeCurrent) + else if (sec->special == Scroll_StrifeCurrent) { int anglespeed = tagManager.GetFirstSectorTag(sec) - 100; fixed_t speed = (anglespeed % 10) << (FRACBITS - 4); diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index a410584925..f5d4a3576c 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -118,10 +118,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_JabDagger) S_Sound (self, CHAN_WEAPON, linetarget->flags & MF_NOBLOOD ? "misc/metalhit" : "misc/meathit", 1, ATTN_NORM); - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); + self->angle = self->AngleTo(linetarget); self->flags |= MF_JUSTATTACKED; P_DaggerAlert (self, linetarget); } diff --git a/src/info.h b/src/info.h index bdbb69940e..22e3b2f5cd 100644 --- a/src/info.h +++ b/src/info.h @@ -283,7 +283,7 @@ struct FDoomEdEntry { const PClass *Type; short Special; - bool ArgsDefined; + signed char ArgsDefined; int Args[5]; }; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 2a52df58a8..a19d0a479c 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -329,6 +329,7 @@ void DIntermissionScreenText::Drawer () int c; const FRemapTable *range; const char *ch = mText; + const int kerning = SmallFont->GetDefaultKerning(); // Count number of rows in this text. Since it does not word-wrap, we just count // line feed characters. @@ -380,6 +381,7 @@ void DIntermissionScreenText::Drawer () } pic = SmallFont->GetChar (c, &w); + w += kerning; w *= CleanXfac; if (cx + w > SCREENWIDTH) continue; diff --git a/src/keysections.cpp b/src/keysections.cpp index 8d23c95820..1ae87958c1 100644 --- a/src/keysections.cpp +++ b/src/keysections.cpp @@ -177,6 +177,11 @@ void D_LoadWadSettings () { cmd[i] = conf[i]; } + if (i == 0) + { + conf++; + continue; + } cmd[i] = 0; conf += i; if (*conf == '\n') diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 170c650213..3bab8f7e5e 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -149,7 +149,8 @@ void cht_DoCheat (player_t *player, int cheat) case CHT_FLY: if (player->mo != NULL) { - if ((player->mo->flags7 ^= MF7_FLYCHEAT) != 0) + player->mo->flags7 ^= MF7_FLYCHEAT; + if (player->mo->flags7 & MF7_FLYCHEAT) { player->mo->flags |= MF_NOGRAVITY; player->mo->flags2 |= MF2_FLY; @@ -579,7 +580,7 @@ void GiveSpawner (player_t *player, const PClass *type, int amount) } AInventory *item = static_cast - (Spawn (type, player->mo->x, player->mo->y, player->mo->z, NO_REPLACE)); + (Spawn (type, player->mo->X(), player->mo->Y(), player->mo->Z(), NO_REPLACE)); if (item != NULL) { if (amount > 0) diff --git a/src/m_random.h b/src/m_random.h index b5e21c63c9..452ef41fc5 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -57,7 +57,9 @@ public: // Returns a random number in the range [0,mod) int operator() (int mod) { - return GenRand32() % mod; + return (0 == mod) + ? 0 + : (GenRand32() % mod); } // Returns rand# - rand# diff --git a/src/menu/joystickmenu.cpp b/src/menu/joystickmenu.cpp index a7d950ed5a..676bc1767e 100644 --- a/src/menu/joystickmenu.cpp +++ b/src/menu/joystickmenu.cpp @@ -345,8 +345,8 @@ void UpdateJoystickMenu(IJoystickConfig *selected) for(unsigned i=0;imItems.Size();i++) { delete opt->mItems[i]; - opt->mItems.Clear(); } + opt->mItems.Clear(); int i; int itemnum = -1; diff --git a/src/menu/listmenu.cpp b/src/menu/listmenu.cpp index 402c1d5e96..0f50d1e004 100644 --- a/src/menu/listmenu.cpp +++ b/src/menu/listmenu.cpp @@ -385,6 +385,11 @@ FListMenuItemStaticPatch::FListMenuItemStaticPatch(int x, int y, FTextureID patc void FListMenuItemStaticPatch::Drawer(bool selected) { + if (!mTexture.Exists()) + { + return; + } + int x = mXpos; FTexture *tex = TexMan(mTexture); if (mYpos >= 0) @@ -486,7 +491,7 @@ bool FListMenuItemSelectable::MouseEvent(int type, int x, int y) { if (type == DMenu::MOUSE_Release) { - if (DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) + if (NULL != DMenu::CurrentMenu && DMenu::CurrentMenu->MenuEvent(MKEY_Enter, true)) { return true; } diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 73101a76e7..04c10760e4 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -95,6 +95,18 @@ static void DeinitMenus() ClearSaveGames(); } +static FTextureID GetMenuTexture(const char* const name) +{ + const FTextureID texture = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch); + + if (!texture.Exists()) + { + Printf("Missing menu texture: \"%s\"\n", name); + } + + return texture; +} + //============================================================================= // // @@ -235,7 +247,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) else if (sc.Compare("Selector")) { sc.MustGetString(); - desc->mSelector = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + desc->mSelector = GetMenuTexture(sc.String); sc.MustGetStringName(","); sc.MustGetNumber(); desc->mSelectOfsX = sc.Number; @@ -278,7 +290,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) int y = sc.Number; sc.MustGetStringName(","); sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(sc.String); FListMenuItem *it = new FListMenuItemStaticPatch(x, y, tex, centered); desc->mItems.Push(it); @@ -299,7 +311,7 @@ static void ParseListMenuBody(FScanner &sc, FListMenuDescriptor *desc) else if (sc.Compare("PatchItem")) { sc.MustGetString(); - FTextureID tex = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(sc.String); sc.MustGetStringName(","); sc.MustGetString(); int hotkey = sc.String[0]; @@ -1045,7 +1057,7 @@ static void BuildEpisodeMenu() FListMenuItem *it; if (AllEpisodes[i].mPicName.IsNotEmpty()) { - FTextureID tex = TexMan.CheckForTexture(AllEpisodes[i].mPicName, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); it = new FListMenuItemPatch(ld->mXpos, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, tex, NAME_Skillmenu, i); } @@ -1442,7 +1454,7 @@ void M_StartupSkillMenu(FGameStartup *gs) if (skill.PicName.Len() != 0 && pItemText == NULL) { - FTextureID tex = TexMan.CheckForTexture(skill.PicName, FTexture::TEX_MiscPatch); + FTextureID tex = GetMenuTexture(skill.PicName); li = new FListMenuItemPatch(ld->mXpos, y, ld->mLinespacing, skill.Shortcut, tex, action, i); } else diff --git a/src/menu/optionmenuitems.h b/src/menu/optionmenuitems.h index 5bc015369b..0b0b57759d 100644 --- a/src/menu/optionmenuitems.h +++ b/src/menu/optionmenuitems.h @@ -32,6 +32,7 @@ ** */ #include "v_text.h" +#include "gstrings.h" void M_DrawConText (int color, int x, int y, const char *str); @@ -199,6 +200,7 @@ public: { text = (*opt)->mValues[Selection].Text; } + if (*text == '$') text = GStrings(text + 1); screen->DrawText (SmallFont, OptionSettings.mFontColorValue, indent + CURSORSPACE, y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay, TAG_DONE); return indent; @@ -503,6 +505,7 @@ public: int Draw(FOptionMenuDescriptor *desc, int y, int indent, bool selected) { const char *txt = mCurrent? (const char*)mAltText : mLabel; + if (*txt == '$') txt = GStrings(txt + 1); int w = SmallFont->StringWidth(txt) * CleanXfac_1; int x = (screen->GetWidth() - w) / 2; screen->DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true, TAG_DONE); @@ -1045,6 +1048,19 @@ public: return text; } + int Draw(FOptionMenuDescriptor*desc, int y, int indent, bool selected) + { + if (mEntering) + { + // reposition the text so that the cursor is visible when in entering mode. + FString text = Represent(); + int tlen = SmallFont->StringWidth(text) * CleanXfac_1; + int newindent = screen->GetWidth() - tlen - CURSORSPACE; + if (newindent < indent) indent = newindent; + } + return FOptionMenuFieldBase::Draw(desc, y, indent, selected); + } + bool MenuEvent ( int mkey, bool fromcontroller ) { if ( mkey == MKEY_Enter ) @@ -1145,18 +1161,3 @@ private: float mMaximum; float mStep; }; -#ifndef NO_IMP -CCMD(am_restorecolors) -{ - if (DMenu::CurrentMenu != NULL && DMenu::CurrentMenu->IsKindOf(RUNTIME_CLASS(DOptionMenu))) - { - DOptionMenu *m = (DOptionMenu*)DMenu::CurrentMenu; - const FOptionMenuDescriptor *desc = m->GetDescriptor(); - // Find the color cvars by scanning the MapColors menu. - for (unsigned i = 0; i < desc->mItems.Size(); ++i) - { - desc->mItems[i]->SetValue(FOptionMenuItemColorPicker::CPF_RESET, 0); - } - } -} -#endif diff --git a/src/namedef.h b/src/namedef.h index 0d49bba767..ae456883cb 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -221,6 +221,10 @@ xx(Flash) xx(AltFlash) xx(Reload) xx(Zoom) +xx(User1) +xx(User2) +xx(User3) +xx(User4) // State names used by ASwitchableDecoration xx(Active) @@ -396,6 +400,7 @@ xx(Roll) xx(Scale) xx(ScaleX) xx(ScaleY) +xx(Floatbobphase) xx(Blocking) xx(Blockmonsters) @@ -501,6 +506,14 @@ xx(floorplane_a) xx(floorplane_b) xx(floorplane_c) xx(floorplane_d) +xx(damageamount) +xx(damagetype) +xx(damageinterval) +xx(leakiness) +xx(damageterraineffect) +xx(damagehazard) +xx(floorterrain) +xx(ceilingterrain) // USDF keywords xx(Amount) diff --git a/src/oplsynth/mlopl_io.cpp b/src/oplsynth/mlopl_io.cpp index 6914634700..4ec41d6c95 100644 --- a/src/oplsynth/mlopl_io.cpp +++ b/src/oplsynth/mlopl_io.cpp @@ -48,6 +48,7 @@ #define HALF_PI (PI*0.5) EXTERN_CVAR(Int, opl_core) +extern int current_opl_core; OPLio::~OPLio() { @@ -323,7 +324,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) { assert(numchips >= 1 && numchips <= countof(chips)); uint i; - IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3); + IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3); memset(chips, 0, sizeof(chips)); if (IsOPL3) @@ -332,7 +333,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3) } for (i = 0; i < numchips; ++i) { - OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); + OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); if (chip == NULL) { break; diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 79bb102269..aa6cda7ee5 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -53,6 +53,7 @@ #endif // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +void OPL_SetCore(const char *args); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -76,8 +77,9 @@ CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); // //========================================================================== -OPLMIDIDevice::OPLMIDIDevice() +OPLMIDIDevice::OPLMIDIDevice(const char *args) { + OPL_SetCore(args); FullPan = opl_fullpan; FWadLump data = Wads.OpenLumpName("GENMIDI"); OPLloadBank(data); diff --git a/src/oplsynth/music_opldumper_mididevice.cpp b/src/oplsynth/music_opldumper_mididevice.cpp index 46dcdfef21..e6684e1a67 100644 --- a/src/oplsynth/music_opldumper_mididevice.cpp +++ b/src/oplsynth/music_opldumper_mididevice.cpp @@ -262,6 +262,7 @@ protected: //========================================================================== OPLDumperMIDIDevice::OPLDumperMIDIDevice(const char *filename) + : OPLMIDIDevice(NULL) { // Replace the standard OPL device with a disk writer. delete io; diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp index 4d143f4a5a..c99c5c55ba 100644 --- a/src/oplsynth/nukedopl3.cpp +++ b/src/oplsynth/nukedopl3.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2013-2014 Nuke.YKT +* Copyright (C) 2013-2015 Nuke.YKT(Alexey Khokholov) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,53 +27,31 @@ OPL2 ROMs. */ -//version 1.5 +//version 1.6 /* Changelog: v1.1: - Vibrato's sign fix + Vibrato's sign fix. v1.2: - Operator key fix - Corrected 4-operator mode - Corrected rhythm mode - Some small fixes - v1.2.1: - Small envelope generator fix - Removed EX_Get function(not used) - v1.3: - Complete rewrite - (Not released) - v1.4: - New envelope and waveform generator + Operator key fix. + Corrected 4-operator mode. + Corrected rhythm mode. + Some small fixes. + v1.2.1: + Small envelope generator fix. + v1.3: + Complete rewrite. + v1.4: + New envelope and waveform generator. Some small fixes. - (Not released) v1.4.1: - Envelope generator rate calculation fix - (Not released) + Envelope generator rate calculation fix. v1.4.2: Version for ZDoom. v1.5: - Optimizations -*/ - - -/* Verified: - Noise generator. - Waveform generator. - Envelope generator increase table. - Tremolo. -*/ - -/* TODO: - Verify: - kslrom[15] value(is it 128?). - Sustain level = 15. - Vibrato, Phase generator. - Rhythm part. - Envelope generator state switching(decay->sustain when egt = 1 and decay->release). - Feedback. - Register write. - 4-operator. + Optimizations. + v1.6: + Improved emulation output. */ #include @@ -216,19 +194,17 @@ envelope_sinfunc envelope_sin[8] = { }; void envelope_gen_off(opl_slot *slott); -void envelope_gen_change(opl_slot *slott); void envelope_gen_attack(opl_slot *slott); void envelope_gen_decay(opl_slot *slott); void envelope_gen_sustain(opl_slot *slott); void envelope_gen_release(opl_slot *slott); -envelope_genfunc envelope_gen[6] = { +envelope_genfunc envelope_gen[5] = { envelope_gen_off, envelope_gen_attack, envelope_gen_decay, envelope_gen_sustain, - envelope_gen_release, - envelope_gen_change + envelope_gen_release }; enum envelope_gen_num { @@ -252,7 +228,7 @@ Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) { } void envelope_update_ksl(opl_slot *slot) { - Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 1) - ((slot->channel->block ^ 0x07) << 5) - 0x20; + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) - ((0x08 - slot->channel->block) << 5); if (ksl < 0) { ksl = 0; } @@ -281,28 +257,25 @@ void envelope_gen_off(opl_slot *slot) { slot->eg_rout = 0x1ff; } -void envelope_gen_change(opl_slot *slot) { - slot->eg_gen = slot->eg_gennext; - envelope_update_rate(slot); -} - void envelope_gen_attack(opl_slot *slot) { + if (slot->eg_rout == 0x00) { + slot->eg_gen = envelope_gen_num_decay; + envelope_update_rate(slot); + return; + } slot->eg_rout += ((~slot->eg_rout) *slot->eg_inc) >> 3; if (slot->eg_rout < 0x00) { slot->eg_rout = 0x00; } - if (!slot->eg_rout) { - slot->eg_gen = envelope_gen_num_change; - slot->eg_gennext = envelope_gen_num_decay; - } } void envelope_gen_decay(opl_slot *slot) { - slot->eg_rout += slot->eg_inc; if (slot->eg_rout >= slot->reg_sl << 4) { - slot->eg_gen = envelope_gen_num_change; - slot->eg_gennext = envelope_gen_num_sustain; + slot->eg_gen = envelope_gen_num_sustain; + envelope_update_rate(slot); + return; } + slot->eg_rout += slot->eg_inc; } void envelope_gen_sustain(opl_slot *slot) { @@ -312,11 +285,13 @@ void envelope_gen_sustain(opl_slot *slot) { } void envelope_gen_release(opl_slot *slot) { - slot->eg_rout += slot->eg_inc; if (slot->eg_rout >= 0x1ff) { - slot->eg_gen = envelope_gen_num_change; - slot->eg_gennext = envelope_gen_num_off; + slot->eg_gen = envelope_gen_num_off; + slot->eg_rout = 0x1ff; + envelope_update_rate(slot); + return; } + slot->eg_rout += slot->eg_inc; } void envelope_calc(opl_slot *slot) { @@ -324,10 +299,7 @@ void envelope_calc(opl_slot *slot) { rate_h = slot->eg_rate >> 2; rate_l = slot->eg_rate & 3; Bit8u inc = 0; - if (slot->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { - inc = 8; - } - else if (eg_incsh[rate_h] > 0) { + if (eg_incsh[rate_h] > 0) { if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((slot->chip->timer) >> eg_incsh[rate_h]) & 0x07]; } @@ -336,14 +308,19 @@ void envelope_calc(opl_slot *slot) { inc = eg_incstep[eg_incdesc[rate_h]][rate_l][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); } slot->eg_inc = inc; - envelope_gen[slot->eg_gen](slot); slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + envelope_gen[slot->eg_gen](slot); } void eg_keyon(opl_slot *slot, Bit8u type) { if (!slot->key) { - slot->eg_gen = envelope_gen_num_change; - slot->eg_gennext = envelope_gen_num_attack; + slot->eg_gen = envelope_gen_num_attack; + envelope_update_rate(slot); + if ((slot->eg_rate >> 2) == 0x0f) { + slot->eg_gen = envelope_gen_num_decay; + envelope_update_rate(slot); + slot->eg_rout = 0x00; + } slot->pg_phase = 0x00; } slot->key |= type; @@ -353,8 +330,8 @@ void eg_keyoff(opl_slot *slot, Bit8u type) { if (slot->key) { slot->key &= (~type); if (!slot->key) { - slot->eg_gen = envelope_gen_num_change; - slot->eg_gennext = envelope_gen_num_release; + slot->eg_gen = envelope_gen_num_release; + envelope_update_rate(slot); } } } @@ -366,7 +343,7 @@ void eg_keyoff(opl_slot *slot, Bit8u type) { void pg_generate(opl_slot *slot) { Bit16u f_num = slot->channel->f_num; if (slot->reg_vib) { - Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10)&0x07] + (0x01 - slot->chip->dvb)); + Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10) & 0x07] + (0x01 - slot->chip->dvb)); f_num += f_num_high * vibsgn_table[(slot->chip->timer >> 10) & 0x07]; } slot->pg_phase += (((f_num << slot->channel->block) >> 1) * mt[slot->reg_mult]) >> 1; @@ -387,7 +364,7 @@ void n_generate(opl_chip *chip) { // Slot // -void slot_write20(opl_slot *slot,Bit8u data) { +void slot_write20(opl_slot *slot, Bit8u data) { if ((data >> 7) & 0x01) { slot->trem = &slot->chip->tremval; } @@ -434,14 +411,14 @@ void slot_generatephase(opl_slot *slot, Bit16u phase) { } void slot_generate(opl_slot *slot) { - slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9) + (*slot->mod), slot->eg_out); + slot->out = envelope_sin[slot->reg_wf]((Bit16u)(slot->pg_phase >> 9) + (*slot->mod), slot->eg_out); } void slot_generatezm(opl_slot *slot) { - slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9), slot->eg_out); + slot->out = envelope_sin[slot->reg_wf]((Bit16u)(slot->pg_phase >> 9), slot->eg_out); } -void slot_calgfb(opl_slot *slot) { +void slot_calcfb(opl_slot *slot) { slot->prout[1] = slot->prout[0]; slot->prout[0] = slot->out; if (slot->channel->fb != 0x00) { @@ -461,57 +438,61 @@ void chan_setupalg(opl_channel *channel); void chan_updaterhythm(opl_chip *chip, Bit8u data) { chip->rhy = data & 0x3f; if (chip->rhy & 0x20) { - chip->channel[6].out[0] = &chip->slot[13].out; - chip->channel[6].out[1] = &chip->slot[13].out; - chip->channel[6].out[2] = &chip->zeromod; - chip->channel[6].out[3] = &chip->zeromod; - chip->channel[7].out[0] = &chip->slot[14].out; - chip->channel[7].out[1] = &chip->slot[14].out; - chip->channel[7].out[2] = &chip->slot[15].out; - chip->channel[7].out[3] = &chip->slot[15].out; - chip->channel[8].out[0] = &chip->slot[16].out; - chip->channel[8].out[1] = &chip->slot[16].out; - chip->channel[8].out[2] = &chip->slot[17].out; - chip->channel[8].out[3] = &chip->slot[17].out; + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slots[1]->out; + channel6->out[1] = &channel6->slots[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slots[0]->out; + channel7->out[1] = &channel7->slots[0]->out; + channel7->out[2] = &channel7->slots[1]->out; + channel7->out[3] = &channel7->slots[1]->out; + channel8->out[0] = &channel8->slots[0]->out; + channel8->out[1] = &channel8->slots[0]->out; + channel8->out[2] = &channel8->slots[1]->out; + channel8->out[3] = &channel8->slots[1]->out; for (Bit8u chnum = 6; chnum < 9; chnum++) { chip->channel[chnum].chtype = ch_drum; } + chan_setupalg(channel6); //hh if (chip->rhy & 0x01) { - eg_keyon(&chip->slot[14], egk_drum); + eg_keyon(channel7->slots[0], egk_drum); } else { - eg_keyoff(&chip->slot[14], egk_drum); + eg_keyoff(channel7->slots[0], egk_drum); } //tc if (chip->rhy & 0x02) { - eg_keyon(&chip->slot[17], egk_drum); + eg_keyon(channel8->slots[1], egk_drum); } else { - eg_keyoff(&chip->slot[17], egk_drum); + eg_keyoff(channel8->slots[1], egk_drum); } //tom if (chip->rhy & 0x04) { - eg_keyon(&chip->slot[16], egk_drum); + eg_keyon(channel8->slots[0], egk_drum); } else { - eg_keyoff(&chip->slot[16], egk_drum); + eg_keyoff(channel8->slots[0], egk_drum); } //sd if (chip->rhy & 0x08) { - eg_keyon(&chip->slot[15], egk_drum); + eg_keyon(channel7->slots[1], egk_drum); } else { - eg_keyoff(&chip->slot[15], egk_drum); + eg_keyoff(channel7->slots[1], egk_drum); } //bd if (chip->rhy & 0x10) { - eg_keyon(&chip->slot[12], egk_drum); - eg_keyon(&chip->slot[13], egk_drum); + eg_keyon(channel6->slots[0], egk_drum); + eg_keyon(channel6->slots[1], egk_drum); } else { - eg_keyoff(&chip->slot[12], egk_drum); - eg_keyoff(&chip->slot[13], egk_drum); + eg_keyoff(channel6->slots[0], egk_drum); + eg_keyoff(channel6->slots[1], egk_drum); } } else { @@ -566,6 +547,16 @@ void chan_writeb0(opl_channel *channel, Bit8u data) { void chan_setupalg(opl_channel *channel) { if (channel->chtype == ch_drum) { + switch (channel->alg & 0x01) { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + break; + } return; } if (channel->alg & 0x08) { @@ -672,49 +663,39 @@ void chan_writec0(opl_channel *channel, Bit8u data) { } } -void chan_generaterhythm(opl_chip *chip) { - if (chip->rhy & 0x20) { - opl_channel *channel6 = &chip->channel[6]; - opl_channel *channel7 = &chip->channel[7]; - opl_channel *channel8 = &chip->channel[8]; - slot_generate(channel6->slots[0]); - slot_generate(channel6->slots[1]); - Bit16u phase14 = channel7->slots[0]->pg_phase & 0x3ff; - Bit16u phase17 = channel8->slots[1]->pg_phase & 0x3ff; - Bit16u phase = 0x00; - //hh tc phase bit - Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; - //hh - phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1))); - slot_generatephase(channel7->slots[0], phase); - //sd - phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); - slot_generatephase(channel7->slots[1], phase); - //tt - slot_generatezm(channel8->slots[0]); - //tc - phase = 0x100 | (phasebit << 9); - slot_generatephase(channel8->slots[1], phase); - } +void chan_generaterhythm1(opl_chip *chip) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[0]); + Bit16u phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; + Bit16u phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //hh + phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1))); + slot_generatephase(channel7->slots[0], phase); + //tt + slot_generatezm(channel8->slots[0]); } -void chan_generate(opl_channel *channel) { - if (channel->chtype == ch_drum) { - return; - } - if (channel->alg & 0x08) { - return; - } - if (channel->alg & 0x04) { - slot_generate(channel->pair->slots[0]); - slot_generate(channel->pair->slots[1]); - slot_generate(channel->slots[0]); - slot_generate(channel->slots[1]); - } - else { - slot_generate(channel->slots[0]); - slot_generate(channel->slots[1]); - } +void chan_generaterhythm2(opl_chip *chip) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[1]); + Bit16u phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; + Bit16u phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //sd + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + slot_generatephase(channel7->slots[1], phase); + //tc + phase = 0x100 | (phasebit << 9); + slot_generatephase(channel8->slots[1], phase); } void chan_enable(opl_channel *channel) { @@ -782,21 +763,132 @@ Bit16s limshort(Bit32s a) { return (Bit16s)a; } +void chip_generate(opl_chip *chip, Bit16s *buff) { + buff[1] = limshort(chip->mixbuff[1]); + + for (Bit8u ii = 0; ii < 12; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + for (Bit8u ii = 12; ii < 15; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + } + + if (chip->rhy & 0x20) { + chan_generaterhythm1(chip); + } + else { + slot_generate(&chip->slot[12]); + slot_generate(&chip->slot[13]); + slot_generate(&chip->slot[14]); + } + + chip->mixbuff[0] = 0; + for (Bit8u ii = 0; ii < 18; ii++) { + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *chip->channel[ii].out[jj]; + } + if (chip->FullPan) { + chip->mixbuff[0] += (Bit16s)(accm * chip->channel[ii].fcha); + } + else { + chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + } + } + + for (Bit8u ii = 15; ii < 18; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + } + + if (chip->rhy & 0x20) { + chan_generaterhythm2(chip); + } + else { + slot_generate(&chip->slot[15]); + slot_generate(&chip->slot[16]); + slot_generate(&chip->slot[17]); + } + + buff[0] = limshort(chip->mixbuff[0]); + + for (Bit8u ii = 18; ii < 33; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + chip->mixbuff[1] = 0; + for (Bit8u ii = 0; ii < 18; ii++) { + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *chip->channel[ii].out[jj]; + } + if (chip->FullPan) { + chip->mixbuff[1] += (Bit16s)(accm * chip->channel[ii].fchb); + } + else { + chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + } + } + + for (Bit8u ii = 33; ii < 36; ii++) { + slot_calcfb(&chip->slot[ii]); + pg_generate(&chip->slot[ii]); + envelope_calc(&chip->slot[ii]); + slot_generate(&chip->slot[ii]); + } + + n_generate(chip); + + if ((chip->timer & 0x3f) == 0x3f) { + if (!chip->tremdir) { + if (chip->tremtval == 105) { + chip->tremtval--; + chip->tremdir = 1; + } + else { + chip->tremtval++; + } + } + else { + if (chip->tremtval == 0) { + chip->tremtval++; + chip->tremdir = 0; + } + else { + chip->tremtval--; + } + } + chip->tremval = (chip->tremtval >> 2) >> ((1 - chip->dam) << 1); + } + + chip->timer++; +} + void NukedOPL3::Reset() { memset(&opl3, 0, sizeof(opl_chip)); for (Bit8u slotnum = 0; slotnum < 36; slotnum++) { - opl3.slot[slotnum].channel = &opl3.channel[slotnum / 2]; opl3.slot[slotnum].chip = &opl3; opl3.slot[slotnum].mod = &opl3.zeromod; opl3.slot[slotnum].eg_rout = 0x1ff; opl3.slot[slotnum].eg_out = 0x1ff; opl3.slot[slotnum].eg_gen = envelope_gen_num_off; - opl3.slot[slotnum].eg_gennext = envelope_gen_num_off; opl3.slot[slotnum].trem = (Bit8u*)&opl3.zeromod; } for (Bit8u channum = 0; channum < 18; channum++) { - opl3.channel[channum].slots[0] = &opl3.slot[2 * channum]; - opl3.channel[channum].slots[1] = &opl3.slot[2 * channum + 1]; + opl3.channel[channum].slots[0] = &opl3.slot[ch_slot[channum]]; + opl3.channel[channum].slots[1] = &opl3.slot[ch_slot[channum] + 3]; + opl3.slot[ch_slot[channum]].channel = &opl3.channel[channum]; + opl3.slot[ch_slot[channum] + 3].channel = &opl3.channel[channum]; if ((channum % 9) < 3) { opl3.channel[channum].pair = &opl3.channel[channum + 3]; } @@ -816,6 +908,8 @@ void NukedOPL3::Reset() { chan_setupalg(&opl3.channel[channum]); } opl3.noise = 0x306600; + opl3.timer = 0; + opl3.FullPan = FullPan; } void NukedOPL3::WriteReg(int reg, int v) { @@ -903,58 +997,11 @@ void NukedOPL3::WriteReg(int reg, int v) { } void NukedOPL3::Update(float* sndptr, int numsamples) { - Bit32s outa, outb; + Bit16s buffer[2]; for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { - outa = 0; - outb = 0; - for (Bit8u ii = 0; ii < 36; ii++) { - slot_calgfb(&opl3.slot[ii]); - } - chan_generaterhythm(&opl3); - for (Bit8u ii = 0; ii < 18; ii++) { - chan_generate(&opl3.channel[ii]); - Bit16s accm = 0; - for (Bit8u jj = 0; jj < 4; jj++) { - accm += *opl3.channel[ii].out[jj]; - } - if (FullPan) { - outa += (Bit16s)(accm * opl3.channel[ii].fcha); - outb += (Bit16s)(accm * opl3.channel[ii].fchb); - } - else { - outa += (Bit16s)(accm & opl3.channel[ii].cha); - outb += (Bit16s)(accm & opl3.channel[ii].chb); - } - } - for (Bit8u ii = 0; ii < 36; ii++) { - envelope_calc(&opl3.slot[ii]); - pg_generate(&opl3.slot[ii]); - } - n_generate(&opl3); - opl3.timer++; - if (!(opl3.timer & 0x3f)) { - if (!opl3.tremdir) { - if (opl3.tremtval == 105) { - opl3.tremtval--; - opl3.tremdir = 1; - } - else { - opl3.tremtval++; - } - } - else { - if (opl3.tremtval == 0) { - opl3.tremtval++; - opl3.tremdir = 0; - } - else { - opl3.tremtval--; - } - } - opl3.tremval = (opl3.tremtval >> 2) >> ((1 - opl3.dam) << 1); - } - *sndptr++ += (float)(outa / 10240.0); - *sndptr++ += (float)(outb / 10240.0); + chip_generate(&opl3, buffer); + *sndptr++ += (float)(buffer[0] / 10240.0); + *sndptr++ += (float)(buffer[1] / 10240.0); } } diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index ccf37fe143..849646f5f3 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2013-2014 Nuke.YKT +* Copyright (C) 2013-2015 Nuke.YKT(Alexey Khokholov) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ OPL2 ROMs. */ -//version 1.5 +//version 1.6 #include "opl.h" #include "muslib.h" @@ -116,7 +116,7 @@ static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, // ksl table // -static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; +static const Bit8u kslrom[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 }; static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; @@ -133,7 +133,7 @@ static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; static const Bit8u eg_incstep[3][4][8] = { { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, - { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, + { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 0, 1, 0, 1, 1, 1, 0, 1 }, { 0, 1, 1, 1, 0, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 1, 1 } }, { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } }; @@ -149,8 +149,8 @@ static const Bit8s eg_incsh[16] = { // address decoding // -static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - +static const Bit8s ad_slot[0x20] = { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; struct opl_chip; struct opl_slot; @@ -167,7 +167,6 @@ struct opl_slot { Bit16s eg_out; Bit8u eg_inc; Bit8u eg_gen; - Bit8u eg_gennext; Bit8u eg_rate; Bit8u eg_ksl; Bit8u *trem; @@ -217,6 +216,8 @@ struct opl_chip { Bit8u tremdir; Bit32u noise; Bit16s zeromod; + Bit32s mixbuff[2]; + Bit8u FullPan; }; diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index cc76beaa31..e294dec692 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -41,10 +41,9 @@ #include "w_wad.h" #include "sc_man.h" #include "g_level.h" +#include "p_terrain.h" #include "r_data/colormaps.h" -#ifdef _3DFLOORS - //========================================================================== // // 3D Floors @@ -332,17 +331,23 @@ void P_PlayerOnSpecial3DFloor(player_t* player) if(rover->flags & FF_SOLID) { // Player must be on top of the floor to be affected... - if(player->mo->z != rover->top.plane->ZatPoint(player->mo->x, player->mo->y)) continue; + if(player->mo->Z() != rover->top.plane->ZatPoint(player->mo)) continue; } else { //Water and DEATH FOG!!! heh - if (player->mo->z > rover->top.plane->ZatPoint(player->mo->x, player->mo->y) || - (player->mo->z + player->mo->height) < rover->bottom.plane->ZatPoint(player->mo->x, player->mo->y)) + if (player->mo->Z() > rover->top.plane->ZatPoint(player->mo) || + player->mo->Top() < rover->bottom.plane->ZatPoint(player->mo)) continue; } - - if (rover->model->special || rover->model->damage) P_PlayerInSpecialSector(player, rover->model); + + // Apply sector specials + P_PlayerInSpecialSector(player, rover->model); + + // Apply flat specials (using the ceiling!) + P_PlayerOnSpecialFlat( + player, rover->model->GetTerrain(rover->top.isceiling)); + break; } } @@ -367,7 +372,7 @@ bool P_CheckFor3DFloorHit(AActor * mo) if(rover->flags & FF_SOLID && rover->model->SecActTarget) { - if(mo->floorz == rover->top.plane->ZatPoint(mo->x, mo->y)) + if(mo->floorz == rover->top.plane->ZatPoint(mo)) { rover->model->SecActTarget->TriggerAction (mo, SECSPAC_HitFloor); return true; @@ -397,7 +402,7 @@ bool P_CheckFor3DCeilingHit(AActor * mo) if(rover->flags & FF_SOLID && rover->model->SecActTarget) { - if(mo->ceilingz == rover->bottom.plane->ZatPoint(mo->x, mo->y)) + if(mo->ceilingz == rover->bottom.plane->ZatPoint(mo)) { rover->model->SecActTarget->TriggerAction (mo, SECSPAC_HitCeiling); return true; @@ -743,7 +748,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li { fixed_t thingbot, thingtop; - thingbot = thing->z; + thingbot = thing->Z(); thingtop = thingbot + (thing->height==0? 1:thing->height); extsector_t::xfloor *xf[2] = {&linedef->frontsector->e->XFloor, &linedef->backsector->e->XFloor}; @@ -757,6 +762,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li linedef->frontsector->floorplane.ZatPoint(x, y), linedef->backsector->floorplane.ZatPoint(x, y) }; FTextureID highestfloorpic; + int highestfloorterrain = -1; FTextureID lowestceilingpic; highestfloorpic.SetInvalid(); @@ -783,12 +789,13 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li lowestceilingpic = *rover->bottom.texture; } - if(ff_top > highestfloor && delta1 < delta2 && (!restrict || thing->z >= ff_top)) + if(ff_top > highestfloor && delta1 < delta2 && (!restrict || thing->Z() >= ff_top)) { highestfloor = ff_top; highestfloorpic = *rover->top.texture; + highestfloorterrain = rover->model->GetTerrain(rover->top.isceiling); } - if(ff_top > lowestfloor[j] && ff_top <= thing->z + thing->MaxStepHeight) lowestfloor[j] = ff_top; + if(ff_top > lowestfloor[j] && ff_top <= thing->Z() + thing->MaxStepHeight) lowestfloor[j] = ff_top; } } @@ -796,6 +803,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li { open.bottom = highestfloor; open.floorpic = highestfloorpic; + open.floorterrain = highestfloorterrain; } if(lowestceiling < open.top) @@ -951,8 +959,6 @@ int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, b return -1; } -#endif - #include "c_dispatch.h" diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index 8d42560af0..8abd23f032 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -4,8 +4,6 @@ #define CenterSpot(sec) (vertex_t*)&(sec)->soundorg[0] -#define _3DFLOORS - // 3D floor flags. Most are the same as in Legacy but I added some for EDGE's and Vavoom's features as well. typedef enum { @@ -60,8 +58,6 @@ enum VC_COLORMASK = 0x00FFFFFF, }; -#ifdef _3DFLOORS - struct secplane_t; struct FDynamicColormap; @@ -149,39 +145,5 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li secplane_t P_FindFloorPlane(sector_t * sector, fixed_t x, fixed_t y, fixed_t z); int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, bool floor, fixed_t &cmpz); -#else - -// Dummy definitions for disabled 3D floor code - -struct F3DFloor -{ - int dummy; -}; - -struct lightlist_t -{ - int dummy; -}; - -class player_s; -inline void P_PlayerOnSpecial3DFloor(player_t* player) {} - -inline void P_Get3DFloorAndCeiling(AActor * thing, sector_t * sector, fixed_t * floorz, fixed_t * ceilingz, int * floorpic) {} -inline bool P_CheckFor3DFloorHit(AActor * mo) { return false; } -inline bool P_CheckFor3DCeilingHit(AActor * mo) { return false; } -inline void P_Recalculate3DFloors(sector_t *) {} -inline void P_RecalculateAttached3DFloors(sector_t * sec) {} -inline lightlist_t * P_GetPlaneLight(sector_t * , secplane_t * plane, bool underside) { return NULL; } -inline void P_Spawn3DFloors( void ) {} - -struct FLineOpening; - -inline void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *linedef, - fixed_t x, fixed_t y, fixed_t refx, fixed_t refy, bool restrict) {} - -//secplane_t P_FindFloorPlane(sector_t * sector, fixed_t x, fixed_t y, fixed_t z){return sector->floorplane;} - -#endif - #endif \ No newline at end of file diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index 9a9492e08e..3338956f32 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -37,6 +37,7 @@ #include "templates.h" #include "p_local.h" +#include "p_terrain.h" //============================================================================ @@ -275,7 +276,7 @@ bool P_LineOpening_3dMidtex(AActor *thing, const line_t *linedef, FLineOpening & open.abovemidtex = false; if (P_GetMidTexturePosition(linedef, 0, &tt, &tb)) { - if (thing->z + (thing->height/2) < (tt + tb)/2) + if (thing->Z() + (thing->height/2) < (tt + tb)/2) { if (tb < open.top) { @@ -285,14 +286,16 @@ bool P_LineOpening_3dMidtex(AActor *thing, const line_t *linedef, FLineOpening & } else { - if (tt > open.bottom && (!restrict || thing->z >= tt)) + if (tt > open.bottom && (!restrict || thing->Z() >= tt)) { open.bottom = tt; open.abovemidtex = true; open.floorpic = linedef->sidedef[0]->GetTexture(side_t::mid); + open.floorterrain = TerrainTypes[open.floorpic]; + } // returns true if it touches the midtexture - return (abs(thing->z - tt) <= thing->MaxStepHeight); + return (abs(thing->Z() - tt) <= thing->MaxStepHeight); } } return false; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 69045ea5f5..09a62ce49d 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -76,6 +76,8 @@ #include "farchive.h" #include "decallib.h" #include "portal.h" +#include "p_terrain.h" +#include "version.h" #include "g_shared/a_pickups.h" @@ -133,6 +135,16 @@ enum ARMORINFO_ACTUALSAVEAMOUNT, }; +// PickActor +// [JP] I've renamed these flags to something else to avoid confusion with the other PAF_ flags +enum +{ +// PAF_FORCETID, +// PAF_RETURNTID + PICKAF_FORCETID = 1, + PICKAF_RETURNTID = 2, +}; + struct CallReturn { CallReturn(int pc, ScriptFunction *func, FBehavior *module, SDWORD *locals, ACSLocalArrays *arrays, bool discard, unsigned int runaway) @@ -1254,7 +1266,7 @@ static int UseInventory (AActor *activator, const char *type) // //============================================================================ -static int CheckInventory (AActor *activator, const char *type) +static int CheckInventory (AActor *activator, const char *type, bool max) { if (activator == NULL || type == NULL) return 0; @@ -1265,11 +1277,26 @@ static int CheckInventory (AActor *activator, const char *type) } else if (stricmp (type, "Health") == 0) { + if (max) + { + if (activator->IsKindOf (RUNTIME_CLASS (APlayerPawn))) + return static_cast(activator)->MaxHealth; + else + return activator->SpawnHealth(); + } return activator->health; } const PClass *info = PClass::FindClass (type); AInventory *item = activator->FindInventory (info); + + if (max) + { + if (item) + return item->MaxAmount; + else + return ((AInventory *)GetDefaultByType (info))->MaxAmount; + } return item ? item->Amount : 0; } @@ -1418,7 +1445,7 @@ FBehavior *FBehavior::StaticLoadModule (int lumpnum, FileReader *fr, int len) else { delete behavior; - Printf(TEXTCOLOR_RED "%s: invalid ACS module", Wads.GetLumpFullName(lumpnum)); + Printf(TEXTCOLOR_RED "%s: invalid ACS module\n", Wads.GetLumpFullName(lumpnum)); return NULL; } } @@ -1928,11 +1955,14 @@ bool FBehavior::Init(int lumpnum, FileReader * fr, int len) int arraynum = MapVarStore[LittleLong(chunk[2])]; if ((unsigned)arraynum < (unsigned)NumArrays) { - int initsize = MIN (ArrayStore[arraynum].ArraySize, (LittleLong(chunk[1])-4)/4); + // Use unsigned iterator here to avoid issue with GCC 4.9/5.x + // optimizer. Might be some undefined behavior in this code, + // but I don't know what it is. + unsigned int initsize = MIN (ArrayStore[arraynum].ArraySize, (LittleLong(chunk[1])-4)/4); SDWORD *elems = ArrayStore[arraynum].Elements; - for (i = 0; i < initsize; ++i) + for (unsigned int j = 0; j < initsize; ++j) { - elems[i] = LittleLong(chunk[3+i]); + elems[j] = LittleLong(chunk[3+j]); } } chunk = (DWORD *)NextChunk((BYTE *)chunk); @@ -3365,6 +3395,14 @@ int DLevelScript::DoSpawn (int type, fixed_t x, fixed_t y, fixed_t z, int tid, i if (info != NULL) { + info = info->GetReplacement (); + + if ((GetDefaultByType (info)->flags3 & MF3_ISMONSTER) && + ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) + { + return 0; + } + actor = Spawn (info, x, y, z, ALLOW_REPLACE); if (actor != NULL) { @@ -3404,12 +3442,12 @@ int DLevelScript::DoSpawnSpot (int type, int spot, int tid, int angle, bool forc while ( (aspot = iterator.Next ()) ) { - spawned += DoSpawn (type, aspot->x, aspot->y, aspot->z, tid, angle, force); + spawned += DoSpawn (type, aspot->X(), aspot->Y(), aspot->Z(), tid, angle, force); } } else if (activator != NULL) { - spawned += DoSpawn (type, activator->x, activator->y, activator->z, tid, angle, force); + spawned += DoSpawn (type, activator->X(), activator->Y(), activator->Z(), tid, angle, force); } return spawned; } @@ -3425,12 +3463,12 @@ int DLevelScript::DoSpawnSpotFacing (int type, int spot, int tid, bool force) while ( (aspot = iterator.Next ()) ) { - spawned += DoSpawn (type, aspot->x, aspot->y, aspot->z, tid, aspot->angle >> 24, force); + spawned += DoSpawn (type, aspot->X(), aspot->Y(), aspot->Z(), tid, aspot->angle >> 24, force); } } else if (activator != NULL) { - spawned += DoSpawn (type, activator->x, activator->y, activator->z, tid, activator->angle >> 24, force); + spawned += DoSpawn (type, activator->X(), activator->Y(), activator->Z(), tid, activator->angle >> 24, force); } return spawned; } @@ -3596,7 +3634,7 @@ int DoGetMasterTID (AActor *self) else return 0; } -static AActor *SingleActorFromTID (int tid, AActor *defactor) +AActor *SingleActorFromTID (int tid, AActor *defactor) { if (tid == 0) { @@ -4108,7 +4146,7 @@ bool DLevelScript::DoCheckActorTexture(int tid, AActor *activator, int string, b F3DFloor *ff = sec->e->XFloor.ffloors[i]; if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID) && - actor->z >= ff->top.plane->ZatPoint(actor->x, actor->y)) + actor->Z() >= ff->top.plane->ZatPoint(actor)) { // This floor is beneath our feet. secpic = *ff->top.texture; break; @@ -4121,14 +4159,14 @@ bool DLevelScript::DoCheckActorTexture(int tid, AActor *activator, int string, b } else { - fixed_t z = actor->z + actor->height; + fixed_t z = actor->Top(); // Looking through planes from bottom to top for (i = numff-1; i >= 0; --i) { F3DFloor *ff = sec->e->XFloor.ffloors[i]; if ((ff->flags & (FF_EXISTS | FF_SOLID)) == (FF_EXISTS | FF_SOLID) && - z <= ff->bottom.plane->ZatPoint(actor->x, actor->y)) + z <= ff->bottom.plane->ZatPoint(actor)) { // This floor is above our eyes. secpic = *ff->bottom.texture; break; @@ -4423,6 +4461,11 @@ enum EACSFunctions ACSF_ChangeActorRoll, ACSF_GetActorRoll, ACSF_QuakeEx, + ACSF_Warp, // 92 + ACSF_GetMaxInventory, + ACSF_SetSectorDamage, + ACSF_SetSectorTerrain, + /* Zandronum's - these must be skipped when we reach 99! -100:ResetMap(0), -101 : PlayerIsSpectator(1), @@ -4686,8 +4729,8 @@ static bool DoSpawnDecal(AActor *actor, const FDecalTemplate *tpl, int flags, an { angle += actor->angle; } - return NULL != ShootDecal(tpl, actor, actor->Sector, actor->x, actor->y, - actor->z + (actor->height>>1) - actor->floorclip + actor->GetBobOffset() + zofs, + return NULL != ShootDecal(tpl, actor, actor->Sector, actor->X(), actor->Y(), + actor->Z() + (actor->height>>1) - actor->floorclip + actor->GetBobOffset() + zofs, angle, distance, !!(flags & SDF_PERMANENT)); } @@ -5295,6 +5338,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const ClipRectWidth = argCount > 2 ? args[2] : 0; ClipRectHeight = argCount > 3 ? args[3] : 0; WrapWidth = argCount > 4 ? args[4] : 0; + HandleAspect = argCount > 5 ? !!args[5] : true; break; case ACSF_SetHUDWrapWidth: @@ -5360,6 +5404,7 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const FName damagetype = argCount > 5 && args[5]? FName(FBehavior::StaticLookupString(args[5])) : NAME_None; fixed_t range = argCount > 6 && args[6]? args[6] : MISSILERANGE; int flags = argCount > 7 && args[7]? args[7] : 0; + int pufftid = argCount > 8 && args[8]? args[8] : 0; int fhflags = 0; if (flags & FHF_NORANDOMPUFFZ) fhflags |= LAF_NORANDOMPUFFZ; @@ -5367,7 +5412,12 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const if (args[0] == 0) { - P_LineAttack(activator, angle, range, pitch, damage, damagetype, pufftype, fhflags); + AActor *puff = P_LineAttack(activator, angle, range, pitch, damage, damagetype, pufftype, fhflags); + if (puff != NULL && pufftid != 0) + { + puff->tid = pufftid; + puff->AddToHash(); + } } else { @@ -5376,7 +5426,12 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args, const while ((source = it.Next()) != NULL) { - P_LineAttack(source, angle, range, pitch, damage, damagetype, pufftype, fhflags); + AActor *puff = P_LineAttack(source, angle, range, pitch, damage, damagetype, pufftype, fhflags); + if (puff != NULL && pufftid != 0) + { + puff->tid = pufftid; + puff->AddToHash(); + } } } } @@ -5759,9 +5814,9 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return 0; } - DWORD actorMask = MF_SHOOTABLE; + ActorFlags actorMask = MF_SHOOTABLE; if (argCount >= 6) { - actorMask = args[5]; + actorMask = ActorFlags::FromInt(args[5]); } DWORD wallMask = ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN; @@ -5769,11 +5824,10 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) wallMask = args[6]; } - bool forceTID = 0; + int flags = 0; if (argCount >= 8) { - if (args[7] != 0) - forceTID = 1; + flags = args[7]; } AActor* pickedActor = P_LinePickActor(actor, args[1] << 16, args[3], args[2] << 16, actorMask, wallMask); @@ -5781,15 +5835,19 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return 0; } - if (!(forceTID) && (args[4] == 0) && (pickedActor->tid == 0)) + if (!(flags & PICKAF_FORCETID) && (args[4] == 0) && (pickedActor->tid == 0)) return 0; - if ((pickedActor->tid == 0) || (forceTID)) + if ((pickedActor->tid == 0) || (flags & PICKAF_FORCETID)) { pickedActor->RemoveFromHash(); pickedActor->tid = args[4]; pickedActor->AddToHash(); } + if (flags & PICKAF_RETURNTID) + { + return pickedActor->tid; + } return 1; } break; @@ -5849,11 +5907,94 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) case ACSF_GetActorRoll: actor = SingleActorFromTID(args[0], activator); return actor != NULL? actor->roll >> 16 : 0; + + // [ZK] A_Warp in ACS + case ACSF_Warp: + { + int tid_dest = args[0]; + fixed_t xofs = args[1]; + fixed_t yofs = args[2]; + fixed_t zofs = args[3]; + angle_t angle = args[4]; + int flags = args[5]; + const char *statename = argCount > 6 ? FBehavior::StaticLookupString(args[6]) : ""; + bool exact = argCount > 7 ? !!args[7] : false; + fixed_t heightoffset = argCount > 8 ? args[8] : 0; + fixed_t radiusoffset = argCount > 9 ? args[9] : 0; + fixed_t pitch = argCount > 10 ? args[10] : 0; + FState *state = argCount > 6 ? activator->GetClass()->ActorInfo->FindStateByString(statename, exact) : 0; + + AActor *reference; + if((flags & WARPF_USEPTR) && tid_dest != AAPTR_DEFAULT) + { + reference = COPY_AAPTR(activator, tid_dest); + } + else + { + reference = SingleActorFromTID(tid_dest, activator); + } + + // If there is no actor to warp to, fail. + if (!reference) + return false; + + if (P_Thing_Warp(activator, reference, xofs, yofs, zofs, angle, flags, heightoffset, radiusoffset, pitch)) + { + if (state && argCount > 6) + { + activator->SetState(state); + } + return true; + } + return false; + } + case ACSF_GetMaxInventory: + actor = SingleActorFromTID(args[0], activator); + if (actor != NULL) + { + return CheckInventory(actor, FBehavior::StaticLookupString(args[1]), true); + } + break; + + case ACSF_SetSectorDamage: + if (argCount >= 2) + { + FSectorTagIterator it(args[0]); + int s; + while ((s = it.Next()) >= 0) + { + sector_t *sec = §ors[s]; + + sec->damageamount = args[1]; + sec->damagetype = argCount >= 3 ? FName(FBehavior::StaticLookupString(args[2])) : FName(NAME_None); + sec->damageinterval = argCount >= 4 ? clamp(args[3], 1, INT_MAX) : 32; + sec->leakydamage = argCount >= 5 ? args[4] : 0; + } + } + break; + + case ACSF_SetSectorTerrain: + if (argCount >= 3) + { + if (args[1] == sector_t::floor || args[1] == sector_t::ceiling) + { + int terrain = P_FindTerrain(FBehavior::StaticLookupString(args[2])); + FSectorTagIterator it(args[0]); + int s; + while ((s = it.Next()) >= 0) + { + sectors[s].terrainnum[args[1]] = terrain; + } + } + } + break; + default: break; } + return 0; } @@ -5991,6 +6132,7 @@ int DLevelScript::RunScript () int sp = 0; int *pc = this->pc; ACSFormat fmt = activeBehavior->GetFormat(); + FBehavior* const savedActiveBehavior = activeBehavior; unsigned int runaway = 0; // used to prevent infinite loops int pcd; FString work; @@ -6024,6 +6166,7 @@ int DLevelScript::RunScript () { default: Printf ("Unknown P-Code %d in %s\n", pcd, ScriptPresentation(script).GetChars()); + activeBehavior = savedActiveBehavior; // fall through case PCD_TERMINATE: DPrintf ("%s finished\n", ScriptPresentation(script).GetChars()); @@ -7230,9 +7373,9 @@ int DLevelScript::RunScript () sp--; break; - case PCD_DROP: case PCD_SETRESULTVALUE: resultValue = STACK(1); + case PCD_DROP: //fall through. sp--; break; @@ -7787,7 +7930,7 @@ scriptwait: } break; } - msg->SetClipRect(ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight); + msg->SetClipRect(ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight, HandleAspect); if (WrapWidth != 0) { msg->SetWrapWidth(WrapWidth); @@ -8273,17 +8416,17 @@ scriptwait: break; case PCD_CHECKINVENTORY: - STACK(1) = CheckInventory (activator, FBehavior::StaticLookupString (STACK(1))); + STACK(1) = CheckInventory (activator, FBehavior::StaticLookupString (STACK(1)), false); break; case PCD_CHECKACTORINVENTORY: STACK(2) = CheckInventory (SingleActorFromTID(STACK(2), NULL), - FBehavior::StaticLookupString (STACK(1))); + FBehavior::StaticLookupString (STACK(1)), false); sp--; break; case PCD_CHECKINVENTORYDIRECT: - PushToStack (CheckInventory (activator, FBehavior::StaticLookupString (TAGSTR(uallong(pc[0]))))); + PushToStack (CheckInventory (activator, FBehavior::StaticLookupString (TAGSTR(uallong(pc[0]))), false)); pc += 1; break; @@ -8459,11 +8602,11 @@ scriptwait: } else if (pcd == PCD_GETACTORZ) { - STACK(1) = actor->z + actor->GetBobOffset(); + STACK(1) = actor->Z() + actor->GetBobOffset(); } else { - STACK(1) = (&actor->x)[pcd - PCD_GETACTORX]; + STACK(1) = pcd == PCD_GETACTORX ? actor->X() : pcd == PCD_GETACTORY ? actor->Y() : actor->Z(); } } break; @@ -8842,7 +8985,22 @@ scriptwait: break; case PCD_STRLEN: - STACK(1) = SDWORD(strlen(FBehavior::StaticLookupString (STACK(1)))); + { + const char *str = FBehavior::StaticLookupString(STACK(1)); + if (str != NULL) + { + STACK(1) = SDWORD(strlen(str)); + break; + } + + static bool StrlenInvalidPrintedAlready = false; + if (!StrlenInvalidPrintedAlready) + { + Printf(PRINT_BOLD, "Warning: ACS function strlen called with invalid string argument.\n"); + StrlenInvalidPrintedAlready = true; + } + STACK(1) = 0; + } break; case PCD_GETCVAR: @@ -9302,6 +9460,10 @@ scriptwait: } break; + case PCD_CONSOLECOMMAND: + Printf (TEXTCOLOR_RED GAMENAME " doesn't support execution of console commands from scripts\n"); + sp -= 3; + break; } } @@ -9383,6 +9545,7 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr activefont = SmallFont; hudwidth = hudheight = 0; ClipRectLeft = ClipRectTop = ClipRectWidth = ClipRectHeight = WrapWidth = 0; + HandleAspect = true; state = SCRIPT_Running; // Hexen waited one second before executing any open scripts. I didn't realize diff --git a/src/p_acs.h b/src/p_acs.h index d5971e3497..3188e46aaf 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -891,6 +891,7 @@ protected: int hudwidth, hudheight; int ClipRectLeft, ClipRectTop, ClipRectWidth, ClipRectHeight; int WrapWidth; + bool HandleAspect; FBehavior *activeBehavior; int InModuleScriptNumber; diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index 49e834764e..ae0674572d 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -444,6 +444,7 @@ static void LoadSectors (sectortype *bsec) sec->movefactor = ORIG_FRICTION_FACTOR; sec->ColorMap = map; sec->ZoneNumber = 0xFFFF; + sec->terrainnum[sector_t::ceiling] = sec->terrainnum[sector_t::floor] = -1; if (bsec->floorstat & 4) { @@ -713,6 +714,7 @@ static int LoadSprites (spritetype *sprites, Xsprite *xsprites, int numsprites, mapthings[count].RenderStyle = STYLE_Count; mapthings[count].alpha = -1; mapthings[count].health = -1; + mapthings[count].FloatbobPhase = -1; if (xsprites != NULL && sprites[i].lotag == 710) { // Blood ambient sound diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 7efef80a53..3203cf7779 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -90,6 +90,7 @@ void DCeiling::Serialize (FArchive &arc) void DCeiling::PlayCeilingSound () { + if (m_Sector->Flags & SECF_SILENTMOVE) return; if (m_Sector->seqType >= 0) { SN_StartSequence (m_Sector, CHAN_CEILING, m_Sector->seqType, SEQ_PLATFORM, 0, false); @@ -142,7 +143,7 @@ void DCeiling::Tick () // movers with texture change, change the texture then get removed case genCeilingChgT: case genCeilingChg0: - m_Sector->special = m_NewSpecial; + m_Sector->SetSpecial(&m_NewSpecial); // fall through case genCeilingChg: m_Sector->SetTexture(sector_t::ceiling, m_Texture); @@ -175,7 +176,7 @@ void DCeiling::Tick () // then remove the active ceiling case genCeilingChgT: case genCeilingChg0: - m_Sector->special = m_NewSpecial; + m_Sector->SetSpecial(&m_NewSpecial); // fall through case genCeilingChg: m_Sector->SetTexture(sector_t::ceiling, m_Texture); @@ -435,11 +436,11 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, switch (change & 3) { case 1: // type is zeroed - ceiling->m_NewSpecial = 0; + ceiling->m_NewSpecial.Clear(); ceiling->m_Type = genCeilingChg0; break; case 2: // type is copied - ceiling->m_NewSpecial = sec->special; + sec->GetSpecial(&ceiling->m_NewSpecial); ceiling->m_Type = genCeilingChgT; break; case 3: // type is left alone @@ -454,11 +455,11 @@ DCeiling *DCeiling::Create(sector_t *sec, DCeiling::ECeiling type, line_t *line, switch (change & 3) { case 1: // type is zeroed - ceiling->m_NewSpecial = 0; + ceiling->m_NewSpecial.Clear(); ceiling->m_Type = genCeilingChg0; break; case 2: // type is copied - ceiling->m_NewSpecial = line->frontsector->special; + line->frontsector->GetSpecial(&ceiling->m_NewSpecial); ceiling->m_Type = genCeilingChgT; break; case 3: // type is left alone diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 069fca6e12..a74b9bd18e 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -1117,7 +1117,7 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang if (facetalker) { A_FaceTarget (npc); - pc->angle = R_PointToAngle2 (pc->x, pc->y, npc->x, npc->y); + pc->angle = pc->AngleTo(npc); } if ((npc->flags & MF_FRIENDLY) || (npc->flags4 & MF4_NOHATEPLAYERS)) { @@ -1345,9 +1345,12 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply if (reply->NextNode != 0) { int rootnode = npc->ConversationRoot; - if (reply->NextNode < 0) + unsigned next = (unsigned)(rootnode + (reply->NextNode < 0 ? -1 : 1) * reply->NextNode - 1); + + if (next < StrifeDialogues.Size()) { - npc->Conversation = StrifeDialogues[rootnode - reply->NextNode - 1]; + npc->Conversation = StrifeDialogues[next]; + if (gameaction != ga_slideshow) { P_StartConversation (npc, player->mo, player->ConversationFaceTalker, false); @@ -1358,10 +1361,6 @@ static void HandleReply(player_t *player, bool isconsole, int nodenum, int reply S_StopSound (npc, CHAN_VOICE); } } - else - { - npc->Conversation = StrifeDialogues[rootnode + reply->NextNode - 1]; - } } npc->angle = player->ConversationNPCAngle; diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 49ed20f460..bce3249024 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -250,6 +250,8 @@ void DDoor::DoorSound(bool raise, DSeqNode *curseq) const choice = !raise; + if (m_Sector->Flags & SECF_SILENTMOVE) return; + if (m_Speed >= FRACUNIT*8) { choice += 2; @@ -511,8 +513,6 @@ void P_SpawnDoorCloseIn30 (sector_t *sec) fixed_t height; DDoor *door = new DDoor (sec); - sec->special = 0; - door->m_Sector = sec; door->m_Direction = 0; door->m_Type = DDoor::doorRaise; @@ -533,7 +533,6 @@ void P_SpawnDoorCloseIn30 (sector_t *sec) void P_SpawnDoorRaiseIn5Mins (sector_t *sec) { - sec->special = 0; new DDoor (sec, DDoor::doorRaiseIn5Mins, 2*FRACUNIT, TICRATE*30/7, 0); } diff --git a/src/p_effect.cpp b/src/p_effect.cpp index 1b7ce0bc8c..0cd6d5c518 100644 --- a/src/p_effect.cpp +++ b/src/p_effect.cpp @@ -356,9 +356,10 @@ static void MakeFountain (AActor *actor, int color1, int color2) angle_t an = M_Random()<<(24-ANGLETOFINESHIFT); fixed_t out = FixedMul (actor->radius, M_Random()<<8); - particle->x = actor->x + FixedMul (out, finecosine[an]); - particle->y = actor->y + FixedMul (out, finesine[an]); - particle->z = actor->z + actor->height + FRACUNIT; + fixedvec3 pos = actor->Vec3Offset(FixedMul(out, finecosine[an]), FixedMul(out, finesine[an]), actor->height + FRACUNIT); + particle->x = pos.x; + particle->y = pos.y; + particle->z = pos.z; if (out < actor->radius/8) particle->velz += FRACUNIT*10/3; else @@ -395,9 +396,10 @@ void P_RunEffect (AActor *actor, int effects) { // Rocket trail - fixed_t backx = actor->x - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2); - fixed_t backy = actor->y - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2); - fixed_t backz = actor->z - (actor->height>>3) * (actor->velz>>16) + (2*actor->height)/3; + + fixed_t backx = - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2); + fixed_t backy = - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2); + fixed_t backz = - (actor->height>>3) * (actor->velz>>16) + (2*actor->height)/3; angle_t an = (moveangle + ANG90) >> ANGLETOFINESHIFT; int speed; @@ -405,9 +407,13 @@ void P_RunEffect (AActor *actor, int effects) particle = JitterParticle (3 + (M_Random() & 31)); if (particle) { fixed_t pathdist = M_Random()<<8; - particle->x = backx - FixedMul(actor->velx, pathdist); - particle->y = backy - FixedMul(actor->vely, pathdist); - particle->z = backz - FixedMul(actor->velz, pathdist); + fixedvec3 pos = actor->Vec3Offset( + backx - FixedMul(actor->velx, pathdist), + backy - FixedMul(actor->vely, pathdist), + backz - FixedMul(actor->velz, pathdist)); + particle->x = pos.x; + particle->y = pos.y; + particle->z = pos.z; speed = (M_Random () - 128) * (FRACUNIT/200); particle->velx += FixedMul (speed, finecosine[an]); particle->vely += FixedMul (speed, finesine[an]); @@ -420,9 +426,13 @@ void P_RunEffect (AActor *actor, int effects) particle_t *particle = JitterParticle (3 + (M_Random() & 31)); if (particle) { fixed_t pathdist = M_Random()<<8; - particle->x = backx - FixedMul(actor->velx, pathdist); - particle->y = backy - FixedMul(actor->vely, pathdist); - particle->z = backz - FixedMul(actor->velz, pathdist) + (M_Random() << 10); + fixedvec3 pos = actor->Vec3Offset( + backx - FixedMul(actor->velx, pathdist), + backy - FixedMul(actor->vely, pathdist), + backz - FixedMul(actor->velz, pathdist) + (M_Random() << 10)); + particle->x = pos.x; + particle->y = pos.y; + particle->z = pos.z; speed = (M_Random () - 128) * (FRACUNIT/200); particle->velx += FixedMul (speed, finecosine[an]); particle->vely += FixedMul (speed, finesine[an]); @@ -441,10 +451,12 @@ void P_RunEffect (AActor *actor, int effects) { // Grenade trail - P_DrawSplash2 (6, - actor->x - FixedMul (finecosine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2), - actor->y - FixedMul (finesine[(moveangle)>>ANGLETOFINESHIFT], actor->radius*2), - actor->z - (actor->height>>3) * (actor->velz>>16) + (2*actor->height)/3, + fixedvec3 pos = actor->Vec3Offset( + -FixedMul(finecosine[(moveangle) >> ANGLETOFINESHIFT], actor->radius * 2), + -FixedMul(finesine[(moveangle) >> ANGLETOFINESHIFT], actor->radius * 2), + -(actor->height >> 3) * (actor->velz >> 16) + (2 * actor->height) / 3); + + P_DrawSplash2 (6, pos.x, pos.y, pos.z, moveangle + ANG180, 2, 2); } if (effects & FX_FOUNTAINMASK) @@ -476,10 +488,11 @@ void P_RunEffect (AActor *actor, int effects) if (particle != NULL) { angle_t ang = M_Random () << (32-ANGLETOFINESHIFT-8); - particle->x = actor->x + FixedMul (actor->radius, finecosine[ang]); - particle->y = actor->y + FixedMul (actor->radius, finesine[ang]); + fixedvec3 pos = actor->Vec3Offset(FixedMul (actor->radius, finecosine[ang]), FixedMul (actor->radius, finesine[ang]), 0); + particle->x = pos.x; + particle->y = pos.y; + particle->z = pos.z; particle->color = *protectColors[M_Random() & 1]; - particle->z = actor->z; particle->velz = FRACUNIT; particle->accz = M_Random () << 7; particle->size = 1; @@ -586,12 +599,12 @@ void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, i } } -void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, float maxdiff, int flags, const PClass *spawnclass, angle_t angle, int duration, float sparsity, float drift, int SpiralOffset) +void P_DrawRailTrail(AActor *source, const TVector3 &start, const TVector3 &end, int color1, int color2, double maxdiff, int flags, const PClass *spawnclass, angle_t angle, int duration, double sparsity, double drift, int SpiralOffset) { double length, lengthsquared; int steps, i; - FAngle deg; - FVector3 step, dir, pos, extend; + TAngle deg; + TVector3 step, dir, pos, extend; bool fullbright; dir = end - start; @@ -615,12 +628,12 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end // The railgun's sound is special. It gets played from the // point on the slug's trail that is closest to the hearing player. AActor *mo = players[consoleplayer].camera; - FVector3 point; + TVector3 point; double r; - float dirz; + double dirz; - if (abs(mo->x - FLOAT2FIXED(start.X)) < 20 * FRACUNIT - && (mo->y - FLOAT2FIXED(start.Y)) < 20 * FRACUNIT) + if (abs(mo->X() - FLOAT2FIXED(start.X)) < 20 * FRACUNIT + && (mo->Y() - FLOAT2FIXED(start.Y)) < 20 * FRACUNIT) { // This player (probably) fired the railgun S_Sound (mo, CHAN_WEAPON, sound, 1, ATTN_NORM); } @@ -630,7 +643,7 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end // Only consider sound in 2D (for now, anyway) // [BB] You have to divide by lengthsquared here, not multiply with it. - r = ((start.Y - FIXED2FLOAT(mo->y)) * (-dir.Y) - (start.X - FIXED2FLOAT(mo->x)) * (dir.X)) / lengthsquared; + r = ((start.Y - FIXED2DBL(mo->Y())) * (-dir.Y) - (start.X - FIXED2DBL(mo->X())) * (dir.X)) / lengthsquared; r = clamp(r, 0., 1.); dirz = dir.Z; @@ -662,7 +675,7 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end minelem = fabs(dir[i]); } } - FVector3 tempvec(0,0,0); + TVector3 tempvec(0, 0, 0); tempvec[epos] = 1; extend = tempvec - (dir | tempvec) * dir; // @@ -673,16 +686,16 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end // Create the outer spiral. if (color1 != -1 && (!r_rail_smartspiral || color2 == -1) && r_rail_spiralsparsity > 0 && (spawnclass == NULL)) { - FVector3 spiral_step = step * r_rail_spiralsparsity * sparsity; + TVector3 spiral_step = step * r_rail_spiralsparsity * sparsity; int spiral_steps = (int)(steps * r_rail_spiralsparsity / sparsity); color1 = color1 == 0 ? -1 : ParticleColor(color1); pos = start; - deg = FAngle(SpiralOffset); + deg = TAngle(SpiralOffset); for (i = spiral_steps; i; i--) { particle_t *p = NewParticle (); - FVector3 tempvec; + TVector3 tempvec; if (!p) return; @@ -695,7 +708,7 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end p->size = 3; p->bright = fullbright; - tempvec = FMatrix3x3(dir, deg) * extend; + tempvec = TMatrix3x3(dir, deg) * extend; p->velx = FLOAT2FIXED(tempvec.X * drift)>>4; p->vely = FLOAT2FIXED(tempvec.Y * drift)>>4; p->velz = FLOAT2FIXED(tempvec.Z * drift)>>4; @@ -704,7 +717,7 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end p->y = FLOAT2FIXED(tempvec.Y); p->z = FLOAT2FIXED(tempvec.Z); pos += spiral_step; - deg += FAngle(r_rail_spiralsparsity * 14); + deg += TAngle(r_rail_spiralsparsity * 14); if (color1 == -1) { @@ -729,18 +742,18 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end // Create the inner trail. if (color2 != -1 && r_rail_trailsparsity > 0 && spawnclass == NULL) { - FVector3 trail_step = step * r_rail_trailsparsity * sparsity; + TVector3 trail_step = step * r_rail_trailsparsity * sparsity; int trail_steps = xs_FloorToInt(steps * r_rail_trailsparsity / sparsity); color2 = color2 == 0 ? -1 : ParticleColor(color2); - FVector3 diff(0, 0, 0); + TVector3 diff(0, 0, 0); pos = start; for (i = trail_steps; i; i--) { // [XA] inner trail uses a different default duration (33). int innerduration = (duration == 0) ? 33 : duration; - particle_t *p = JitterParticle (innerduration, drift); + particle_t *p = JitterParticle (innerduration, (float)drift); if (!p) return; @@ -749,14 +762,14 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end { int rnd = M_Random (); if (rnd & 1) - diff.X = clamp (diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); + diff.X = clamp(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 2) - diff.Y = clamp (diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); + diff.Y = clamp(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 4) - diff.Z = clamp (diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); + diff.Z = clamp(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); } - FVector3 postmp = pos + diff; + TVector3 postmp = pos + diff; p->size = 2; p->x = FLOAT2FIXED(postmp.X); @@ -791,9 +804,9 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end if (sparsity < 1) sparsity = 32; - FVector3 trail_step = (step / 3) * sparsity; + TVector3 trail_step = (step / 3) * sparsity; int trail_steps = (int)((steps * 3) / sparsity); - FVector3 diff(0, 0, 0); + TVector3 diff(0, 0, 0); pos = start; for (i = trail_steps; i; i--) @@ -802,13 +815,13 @@ void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end { int rnd = pr_railtrail(); if (rnd & 1) - diff.X = clamp (diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); + diff.X = clamp(diff.X + ((rnd & 8) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 2) - diff.Y = clamp (diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); + diff.Y = clamp(diff.Y + ((rnd & 16) ? 1 : -1), -maxdiff, maxdiff); if (rnd & 4) - diff.Z = clamp (diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); + diff.Z = clamp(diff.Z + ((rnd & 32) ? 1 : -1), -maxdiff, maxdiff); } - FVector3 postmp = pos + diff; + TVector3 postmp = pos + diff; AActor *thing = Spawn (spawnclass, FLOAT2FIXED(postmp.X), FLOAT2FIXED(postmp.Y), FLOAT2FIXED(postmp.Z), ALLOW_REPLACE); if (thing) @@ -832,9 +845,13 @@ void P_DisconnectEffect (AActor *actor) if (!p) break; - p->x = actor->x + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS); - p->y = actor->y + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS); - p->z = actor->z + (M_Random()<<8) * (actor->height>>FRACBITS); + fixedvec3 pos = actor->Vec3Offset( + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS), + ((M_Random()-128)<<9) * (actor->radius>>FRACBITS), + (M_Random()<<8) * (actor->height>>FRACBITS)); + p->x = pos.x; + p->y = pos.y; + p->z = pos.z; p->accz -= FRACUNIT/4096; p->color = M_Random() < 128 ? maroon1 : maroon2; p->size = 4; diff --git a/src/p_effect.h b/src/p_effect.h index 6ca9dfea9e..11116e8a3f 100644 --- a/src/p_effect.h +++ b/src/p_effect.h @@ -88,7 +88,7 @@ void P_RunEffects (void); void P_RunEffect (AActor *actor, int effects); -void P_DrawRailTrail (AActor *source, const FVector3 &start, const FVector3 &end, int color1, int color2, float maxdiff = 0, int flags = 0, const PClass *spawnclass = NULL, angle_t angle = 0, int duration = 35, float sparsity = 1.0, float drift = 1.0, int SpiralOffset = 270); +void P_DrawRailTrail(AActor *source, const TVector3 &start, const TVector3 &end, int color1, int color2, double maxdiff = 0, int flags = 0, const PClass *spawnclass = NULL, angle_t angle = 0, int duration = 35, double sparsity = 1.0, double drift = 1.0, int SpiralOffset = 270); void P_DrawSplash (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int kind); void P_DrawSplash2 (int count, fixed_t x, fixed_t y, fixed_t z, angle_t angle, int updown, int kind); void P_DisconnectEffect (AActor *actor); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 3e17a85f35..d8230a0315 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -138,7 +138,7 @@ void P_RecursiveSound (sector_t *sec, AActor *soundtarget, bool splash, int soun for (actor = sec->thinglist; actor != NULL; actor = actor->snext) { if (actor != soundtarget && (!splash || !(actor->flags4 & MF4_NOSPLASHALERT)) && - (!maxdist || (P_AproxDistance(actor->x - emitter->x, actor->y - emitter->y) <= maxdist))) + (!maxdist || (actor->AproxDistance(emitter) <= maxdist))) { actor->LastHeard = soundtarget; } @@ -231,7 +231,7 @@ bool AActor::CheckMeleeRange () if (!pl) return false; - dist = P_AproxDistance (pl->x - x, pl->y - y); + dist = AproxDistance (pl); if (dist >= meleerange + pl->radius) return false; @@ -243,9 +243,9 @@ bool AActor::CheckMeleeRange () // [RH] Don't melee things too far above or below actor. if (!(flags5 & MF5_NOVERTICALMELEERANGE)) { - if (pl->z > z + height) + if (pl->Z() > Top()) return false; - if (pl->z + pl->height < z) + if (pl->Top() < Z()) return false; } @@ -275,16 +275,16 @@ bool P_CheckMeleeRange2 (AActor *actor) return false; } mo = actor->target; - dist = P_AproxDistance (mo->x-actor->x, mo->y-actor->y); + dist = mo->AproxDistance (actor); if (dist >= MELEERANGE*2 || dist < MELEERANGE-20*FRACUNIT + mo->radius) { return false; } - if (mo->z > actor->z+actor->height) + if (mo->Z() > actor->Top()) { // Target is higher than the attacker return false; } - else if (actor->z > mo->z+mo->height) + else if (actor->Z() > mo->Top()) { // Attacker is higher return false; } @@ -348,8 +348,7 @@ bool P_CheckMissileRange (AActor *actor) // OPTIMIZE: get this from a global checksight // [RH] What? - dist = P_AproxDistance (actor->x-actor->target->x, - actor->y-actor->target->y) - 64*FRACUNIT; + dist = actor->AproxDistance (actor->target) - 64*FRACUNIT; if (actor->MeleeState == NULL) dist -= 128*FRACUNIT; // no melee attack, so fire more @@ -393,8 +392,8 @@ bool P_HitFriend(AActor * self) if (self->flags&MF_FRIENDLY && self->target != NULL) { - angle_t angle = R_PointToAngle2 (self->x, self->y, self->target->x, self->target->y); - fixed_t dist = P_AproxDistance (self->x - self->target->x, self->y - self->target->y); + angle_t angle = self->AngleTo(self->target); + fixed_t dist = self->AproxDistance (self->target); P_AimLineAttack (self, angle, dist, &linetarget, 0, true); if (linetarget != NULL && linetarget != self->target) { @@ -435,7 +434,7 @@ bool P_Move (AActor *actor) // it difficult to thrust them vertically in a reasonable manner. // [GZ] Let jumping actors jump. if (!((actor->flags & MF_NOGRAVITY) || (actor->flags6 & MF6_CANJUMP)) - && actor->z > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) + && actor->Z() > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) { return false; } @@ -450,7 +449,7 @@ bool P_Move (AActor *actor) if ((actor->flags6 & MF6_JUMPDOWN) && target && !(target->IsFriend(actor)) && - P_AproxDistance(actor->x - target->x, actor->y - target->y) < FRACUNIT*144 && + actor->AproxDistance(target) < FRACUNIT*144 && pr_dropoff() < 235) { dropoff = 2; @@ -474,8 +473,8 @@ bool P_Move (AActor *actor) } } - tryx = (origx = actor->x) + (deltax = FixedMul (speed, xspeed[actor->movedir])); - tryy = (origy = actor->y) + (deltay = FixedMul (speed, yspeed[actor->movedir])); + tryx = (origx = actor->X()) + (deltax = FixedMul (speed, xspeed[actor->movedir])); + tryy = (origy = actor->Y()) + (deltay = FixedMul (speed, yspeed[actor->movedir])); // Like P_XYMovement this should do multiple moves if the step size is too large @@ -520,15 +519,14 @@ bool P_Move (AActor *actor) // so make it switchable if (nomonsterinterpolation) { - actor->PrevX = actor->x; - actor->PrevY = actor->y; - actor->PrevZ = actor->z; + actor->PrevX = actor->X(); + actor->PrevY = actor->Y(); + actor->PrevZ = actor->Z(); } if (try_ok && friction > ORIG_FRICTION) { - actor->x = origx; - actor->y = origy; + actor->SetOrigin(origx, origy, actor->Z(), false); movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; actor->velx += FixedMul (deltax, movefactor); actor->vely += FixedMul (deltay, movefactor); @@ -539,22 +537,22 @@ bool P_Move (AActor *actor) // actually walking down a step. if (try_ok && !((actor->flags & MF_NOGRAVITY) || (actor->flags6 & MF6_CANJUMP)) - && actor->z > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) + && actor->Z() > actor->floorz && !(actor->flags2 & MF2_ONMOBJ)) { - if (actor->z <= actor->floorz + actor->MaxStepHeight) + if (actor->Y() <= actor->floorz + actor->MaxStepHeight) { - fixed_t savedz = actor->z; - actor->z = actor->floorz; + fixed_t savedz = actor->Z(); + actor->SetZ(actor->floorz); // Make sure that there isn't some other actor between us and // the floor we could get stuck in. The old code did not do this. if (!P_TestMobjZ(actor)) { - actor->z = savedz; + actor->SetZ(savedz); } else { // The monster just hit the floor, so trigger any actions. if (actor->floorsector->SecActTarget != NULL && - actor->floorz == actor->floorsector->floorplane.ZatPoint(actor->x, actor->y)) + actor->floorz == actor->floorsector->floorplane.ZatPoint(actor)) { actor->floorsector->SecActTarget->TriggerAction(actor, SECSPAC_HitFloor); } @@ -567,12 +565,12 @@ bool P_Move (AActor *actor) { if (((actor->flags6 & MF6_CANJUMP)||(actor->flags & MF_FLOAT)) && tm.floatok) { // must adjust height - fixed_t savedz = actor->z; + fixed_t savedz = actor->Z(); - if (actor->z < tm.floorz) - actor->z += actor->FloatSpeed; + if (actor->Z() < tm.floorz) + actor->SetZ(actor->Z() + actor->FloatSpeed); else - actor->z -= actor->FloatSpeed; + actor->SetZ(actor->Z() - actor->FloatSpeed); // [RH] Check to make sure there's nothing in the way of the float @@ -581,7 +579,7 @@ bool P_Move (AActor *actor) actor->flags |= MF_INFLOAT; return true; } - actor->z = savedz; + actor->SetZ(savedz); } if (!spechit.Size ()) @@ -813,28 +811,25 @@ void P_DoNewChaseDir (AActor *actor, fixed_t deltax, fixed_t deltay) void P_NewChaseDir(AActor * actor) { - fixed_t deltax; - fixed_t deltay; + fixedvec2 delta; actor->strafecount = 0; if ((actor->flags5&MF5_CHASEGOAL || actor->goal == actor->target) && actor->goal!=NULL) { - deltax = actor->goal->x - actor->x; - deltay = actor->goal->y - actor->y; + delta = actor->Vec2To(actor->goal); } else if (actor->target != NULL) { - deltax = actor->target->x - actor->x; - deltay = actor->target->y - actor->y; + delta = actor->Vec2To(actor->target); if (!(actor->flags6 & MF6_NOFEAR)) { if ((actor->target->player != NULL && (actor->target->player->cheats & CF_FRIGHTENING)) || (actor->flags4 & MF4_FRIGHTENED)) { - deltax = -deltax; - deltay = -deltay; + delta.x = -delta.x; + delta.y = -delta.y; } } } @@ -848,11 +843,11 @@ void P_NewChaseDir(AActor * actor) // Try to move away from a dropoff if (actor->floorz - actor->dropoffz > actor->MaxDropOffHeight && - actor->z <= actor->floorz && !(actor->flags & MF_DROPOFF) && + actor->Z() <= actor->floorz && !(actor->flags & MF_DROPOFF) && !(actor->flags2 & MF2_ONMOBJ) && !(actor->flags & MF_FLOAT) && !(i_compatflags & COMPATF_DROPOFF)) { - FBoundingBox box(actor->x, actor->y, actor->radius); + FBoundingBox box(actor->X(), actor->Y(), actor->radius); FBlockLinesIterator it(box); line_t *line; @@ -867,18 +862,18 @@ void P_NewChaseDir(AActor * actor) box.Bottom() < line->bbox[BOXTOP] && box.BoxOnLineSide(line) == -1) { - fixed_t front = line->frontsector->floorplane.ZatPoint(actor->x,actor->y); - fixed_t back = line->backsector->floorplane.ZatPoint(actor->x,actor->y); + fixed_t front = line->frontsector->floorplane.ZatPoint(actor); + fixed_t back = line->backsector->floorplane.ZatPoint(actor); angle_t angle; // The monster must contact one of the two floors, // and the other must be a tall dropoff. - if (back == actor->z && front < actor->z - actor->MaxDropOffHeight) + if (back == actor->Z() && front < actor->Z() - actor->MaxDropOffHeight) { angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff } - else if (front == actor->z && back < actor->z - actor->MaxDropOffHeight) + else if (front == actor->Z() && back < actor->Z() - actor->MaxDropOffHeight) { angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff } @@ -933,7 +928,7 @@ void P_NewChaseDir(AActor * actor) if (actor->flags3 & MF3_AVOIDMELEE) { bool ismeleeattacker = false; - fixed_t dist = P_AproxDistance(actor->x-target->x, actor->y-target->y); + fixed_t dist = actor->AproxDistance(target); if (target->player == NULL) { ismeleeattacker = (target->MissileState == NULL && dist < (target->meleerange + target->radius)*2); @@ -947,12 +942,12 @@ void P_NewChaseDir(AActor * actor) if (ismeleeattacker) { actor->strafecount = pr_enemystrafe() & 15; - deltax = -deltax, deltay = -deltay; + delta.x = -delta.x, delta.y = -delta.y; } } } - P_DoNewChaseDir(actor, deltax, deltay); + P_DoNewChaseDir(actor, delta.x, delta.y); // If strafing, set movecount to strafecount so that old Doom // logic still works the same, except in the strafing part @@ -984,7 +979,7 @@ void P_RandomChaseDir (AActor *actor) if (actor->flags & MF_FRIENDLY) { AActor *player; - fixed_t deltax, deltay; + fixedvec2 delta; dirtype_t d[3]; if (actor->FriendPlayer != 0) @@ -1006,19 +1001,18 @@ void P_RandomChaseDir (AActor *actor) { if (pr_newchasedir() & 1 || !P_CheckSight (actor, player)) { - deltax = player->x - actor->x; - deltay = player->y - actor->y; + delta = actor->Vec2To(player); - if (deltax>128*FRACUNIT) + if (delta.x>128*FRACUNIT) d[1]= DI_EAST; - else if (deltax<-128*FRACUNIT) + else if (delta.x<-128*FRACUNIT) d[1]= DI_WEST; else d[1]=DI_NODIR; - if (deltay<-128*FRACUNIT) + if (delta.y<-128*FRACUNIT) d[2]= DI_SOUTH; - else if (deltay>128*FRACUNIT) + else if (delta.y>128*FRACUNIT) d[2]= DI_NORTH; else d[2]=DI_NODIR; @@ -1026,13 +1020,13 @@ void P_RandomChaseDir (AActor *actor) // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { - actor->movedir = diags[((deltay<0)<<1) + (deltax>0)]; + actor->movedir = diags[((delta.y<0)<<1) + (delta.x>0)]; if (actor->movedir != turnaround && P_TryWalk(actor)) return; } // try other directions - if (pr_newchasedir() > 200 || abs(deltay) > abs(deltax)) + if (pr_newchasedir() > 200 || abs(delta.y) > abs(delta.x)) { swapvalues (d[1], d[2]); } @@ -1151,7 +1145,7 @@ bool P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams fov = allaround ? 0 : ANGLE_180; } - fixed_t dist = P_AproxDistance (other->x - lookee->x, other->y - lookee->y); + fixed_t dist = lookee->AproxDistance (other); if (maxdist && dist > maxdist) return false; // [KS] too far @@ -1161,7 +1155,7 @@ bool P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams if (fov && fov < ANGLE_MAX) { - angle_t an = R_PointToAngle2 (lookee->x, lookee->y, other->x, other->y) - lookee->angle; + angle_t an = lookee->AngleTo(other) - lookee->angle; if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) { @@ -1202,8 +1196,7 @@ bool P_LookForMonsters (AActor *actor) { // Not a valid monster continue; } - if (P_AproxDistance (actor->x-mo->x, actor->y-mo->y) - > MONS_LOOK_RANGE) + if (mo->AproxDistance (actor) > MONS_LOOK_RANGE) { // Out of range continue; } @@ -1703,10 +1696,8 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) if ((player->mo->flags & MF_SHADOW && !(i_compatflags & COMPATF_INVISIBILITY)) || player->mo->flags3 & MF3_GHOST) { - if ((P_AproxDistance (player->mo->x - actor->x, - player->mo->y - actor->y) > 2*MELEERANGE) - && P_AproxDistance (player->mo->velx, player->mo->vely) - < 5*FRACUNIT) + if ((player->mo->AproxDistance (actor) > 2*MELEERANGE) + && P_AproxDistance (player->mo->velx, player->mo->vely) < 5*FRACUNIT) { // Player is sneaking - can't detect continue; } @@ -1903,8 +1894,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LookEx) } else { - dist = P_AproxDistance (targ->x - self->x, - targ->y - self->y); + dist = self->AproxDistance (targ); // [KS] If the target is too far away, don't respond to the sound. if (maxheardist && dist > maxheardist) @@ -1975,8 +1965,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LookEx) { if (self->flags & MF_AMBUSH) { - dist = P_AproxDistance (self->target->x - self->x, - self->target->y - self->y); + dist = self->AproxDistance (self->target); if (P_CheckSight (self, self->target, SF_SEEPASTBLOCKEVERYTHING) && (!minseedist || dist > minseedist) && (!maxseedist || dist < maxseedist)) @@ -2390,12 +2379,12 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi actor->FastChaseStrafeCount = 0; actor->velx = 0; actor->vely = 0; - fixed_t dist = P_AproxDistance (actor->x - actor->target->x, actor->y - actor->target->y); + fixed_t dist = actor->AproxDistance (actor->target); if (dist < CLASS_BOSS_STRAFE_RANGE) { if (pr_chase() < 100) { - angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y); + angle_t ang = actor->AngleTo(actor->target); if (pr_chase() < 128) ang += ANGLE_90; else ang -= ANGLE_90; actor->velx = 13 * finecosine[ang>>ANGLETOFINESHIFT]; @@ -2480,8 +2469,8 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi if ((!fastchase || !actor->FastChaseStrafeCount) && !dontmove) { // CANTLEAVEFLOORPIC handling was completely missing in the non-serpent functions. - fixed_t oldX = actor->x; - fixed_t oldY = actor->y; + fixed_t oldX = actor->X(); + fixed_t oldY = actor->Y(); FTextureID oldFloor = actor->floorpic; // chase towards player @@ -2532,11 +2521,12 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) if (self->movedir != DI_NODIR) { const fixed_t absSpeed = abs (self->Speed); - fixed_t viletryx = self->x + FixedMul (absSpeed, xspeed[self->movedir]); - fixed_t viletryy = self->y + FixedMul (absSpeed, yspeed[self->movedir]); + fixedvec2 viletry = self->Vec2Offset( + FixedMul (absSpeed, xspeed[self->movedir]), + FixedMul (absSpeed, yspeed[self->movedir]), true); AActor *corpsehit; - FBlockThingsIterator it(FBoundingBox(viletryx, viletryy, 32*FRACUNIT)); + FBlockThingsIterator it(FBoundingBox(viletry.x, viletry.y, 32*FRACUNIT)); while ((corpsehit = it.Next())) { FState *raisestate = corpsehit->GetRaiseState(); @@ -2545,10 +2535,9 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) // use the current actor's radius instead of the Arch Vile's default. fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius; - if (abs(corpsehit->x - viletryx) > maxdist || - abs(corpsehit->y - viletryy) > maxdist) + if (abs(corpsehit->X() - viletry.x) > maxdist || + abs(corpsehit->Y() - viletry.y) > maxdist) continue; // not actually touching -#ifdef _3DFLOORS // Let's check if there are floors in between the archvile and its target sector_t *vilesec = self->Sector; sector_t *corpsec = corpsehit->Sector; @@ -2558,15 +2547,14 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) if (testsec) { fixed_t zdist1, zdist2; - if (P_Find3DFloor(testsec, corpsehit->x, corpsehit->y, corpsehit->z, false, true, zdist1) - != P_Find3DFloor(testsec, self->x, self->y, self->z, false, true, zdist2)) + if (P_Find3DFloor(testsec, corpsehit->X(), corpsehit->Y(), corpsehit->Z(), false, true, zdist1) + != P_Find3DFloor(testsec, self->X(), self->Y(), self->Z(), false, true, zdist2)) { // Not on same floor - if (vilesec == corpsec || abs(zdist1 - self->z) > self->height) + if (vilesec == corpsec || abs(zdist1 - self->Z()) > self->height) continue; } } -#endif corpsehit->velx = corpsehit->vely = 0; // [RH] Check against real height and radius @@ -2577,7 +2565,7 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) corpsehit->flags |= MF_SOLID; corpsehit->height = corpsehit->GetDefault()->height; - bool check = P_CheckPosition(corpsehit, corpsehit->x, corpsehit->y); + bool check = P_CheckPosition(corpsehit, corpsehit->X(), corpsehit->Y()); corpsehit->flags = oldflags; corpsehit->radius = oldradius; corpsehit->height = oldheight; @@ -2751,7 +2739,7 @@ void A_Face (AActor *self, AActor *other, angle_t max_turn, angle_t max_pitch, a self->flags &= ~MF_AMBUSH; - angle_t other_angle = R_PointToAngle2 (self->x, self->y, other->x, other->y); + angle_t other_angle = self->AngleTo(other); // 0 means no limit. Also, if we turn in a single step anyways, no need to go through the algorithms. // It also means that there is no need to check for going past the other. @@ -2791,34 +2779,33 @@ void A_Face (AActor *self, AActor *other, angle_t max_turn, angle_t max_pitch, a { // [DH] Don't need to do proper fixed->double conversion, since the // result is only used in a ratio. - double dist_x = other->x - self->x; - double dist_y = other->y - self->y; + fixedvec2 dist = self->Vec2To(other); // Positioning ala missile spawning, 32 units above foot level - fixed_t source_z = self->z + 32*FRACUNIT + self->GetBobOffset(); - fixed_t target_z = other->z + 32*FRACUNIT + other->GetBobOffset(); + fixed_t source_z = self->Z() + 32*FRACUNIT + self->GetBobOffset(); + fixed_t target_z = other->Z() + 32*FRACUNIT + other->GetBobOffset(); // If the target z is above the target's head, reposition to the middle of // its body. - if (target_z >= other->z + other->height) + if (target_z >= other->Top()) { - target_z = other->z + (other->height / 2); + target_z = other->Z() + (other->height / 2); } //Note there is no +32*FRACUNIT on purpose. This is for customization sake. //If one doesn't want this behavior, just don't use FAF_BOTTOM. if (flags & FAF_BOTTOM) - target_z = other->z + other->GetBobOffset(); + target_z = other->Z() + other->GetBobOffset(); if (flags & FAF_MIDDLE) - target_z = other->z + (other->height / 2) + other->GetBobOffset(); + target_z = other->Z() + (other->height / 2) + other->GetBobOffset(); if (flags & FAF_TOP) - target_z = other->z + (other->height) + other->GetBobOffset(); + target_z = other->Z() + (other->height) + other->GetBobOffset(); if (!(flags & FAF_NODISTFACTOR)) target_z += pitch_offset; double dist_z = target_z - source_z; - double dist = sqrt(dist_x*dist_x + dist_y*dist_y + dist_z*dist_z); - int other_pitch = (int)rad2bam(asin(dist_z / dist)); + double ddist = sqrt(dist.x*dist.x + dist.y*dist.y + dist_z*dist_z); + int other_pitch = (int)rad2bam(asin(dist_z / ddist)); if (max_pitch != 0) { @@ -2924,26 +2911,19 @@ DEFINE_ACTION_FUNCTION(AActor, A_MonsterRail) self->flags &= ~MF_AMBUSH; - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x, - self->target->y); + self->angle = self->AngleTo(self->target); self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, self->target); if (linetarget == NULL) { // We probably won't hit the target, but aim at it anyway so we don't look stupid. - FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); - double zdiff = (self->target->z + (self->target->height>>1)) - - (self->z + (self->height>>1) - self->floorclip); + TVector2 xydiff = self->Vec2To(self->target); + double zdiff = FIXED2DBL((self->target->Z() + (self->target->height>>1)) - (self->Z() + (self->height>>1) - self->floorclip)); self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); } // Let the aim trail behind the player - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); + self->angle = self->AngleTo(self->target, -self->target->velx * 3, -self->target->vely * 3); if (self->target->flags & MF_SHADOW && !(self->flags6 & MF6_SEEINVISIBLE)) { @@ -3086,7 +3066,7 @@ AInventory *P_DropItem (AActor *source, const PClass *type, int dropamount, int AActor *mo; fixed_t spawnz; - spawnz = source->z; + spawnz = source->Z(); if (!(i_compatflags & COMPATF_NOTOSSDROPS)) { int style = sv_dropstyle; @@ -3101,7 +3081,7 @@ AInventory *P_DropItem (AActor *source, const PClass *type, int dropamount, int spawnz += source->height / 2; } } - mo = Spawn (type, source->x, source->y, spawnz, ALLOW_REPLACE); + mo = Spawn (type, source->X(), source->Y(), spawnz, ALLOW_REPLACE); if (mo != NULL) { mo->flags |= MF_DROPPED; diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 97e23571b7..e3763e677a 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -55,6 +55,8 @@ inline FArchive &operator<< (FArchive &arc, DFloor::EFloor &type) static void StartFloorSound (sector_t *sec) { + if (sec->Flags & SECF_SILENTMOVE) return; + if (sec->seqType >= 0) { SN_StartSequence (sec, CHAN_FLOOR, sec->seqType, SEQ_PLATFORM, 0); @@ -159,7 +161,7 @@ void DFloor::Tick () case donutRaise: case genFloorChgT: case genFloorChg0: - m_Sector->special = (m_Sector->special & SECRET_MASK) | m_NewSpecial; + m_Sector->SetSpecial(&m_NewSpecial); //fall thru case genFloorChg: m_Sector->SetTexture(sector_t::floor, m_Texture); @@ -175,7 +177,7 @@ void DFloor::Tick () case floorLowerAndChange: case genFloorChgT: case genFloorChg0: - m_Sector->special = (m_Sector->special & SECRET_MASK) | m_NewSpecial; + m_Sector->SetSpecial(&m_NewSpecial); //fall thru case genFloorChg: m_Sector->SetTexture(sector_t::floor, m_Texture); @@ -233,14 +235,14 @@ void DFloor::SetFloorChangeType (sector_t *sec, int change) switch (change & 3) { case 1: - m_NewSpecial = 0; + m_NewSpecial.Clear(); m_Type = DFloor::genFloorChg0; break; case 2: m_Type = DFloor::genFloorChg; break; case 3: - m_NewSpecial = sec->special & ~SECRET_MASK; + sec->GetSpecial(&m_NewSpecial); m_Type = DFloor::genFloorChgT; break; } @@ -438,11 +440,11 @@ bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, { FTextureID oldpic = sec->GetTexture(sector_t::floor); sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor)); - sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK); + sec->TransferSpecial(line->frontsector); } else { - sec->special &= SECRET_MASK; + sec->ClearSpecial(); } break; @@ -453,8 +455,7 @@ bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, floor->m_Texture = sec->GetTexture(sector_t::floor); // jff 1/24/98 make sure floor->m_NewSpecial gets initialized // in case no surrounding sector is at floordestheight - // --> should not affect compatibility <-- - floor->m_NewSpecial = sec->special & ~SECRET_MASK; + sec->GetSpecial(&floor->m_NewSpecial); //jff 5/23/98 use model subroutine to unify fixes and handling sector_t *modelsec; @@ -462,7 +463,7 @@ bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, if (modelsec != NULL) { floor->m_Texture = modelsec->GetTexture(sector_t::floor); - floor->m_NewSpecial = modelsec->special & ~SECRET_MASK; + modelsec->GetSpecial(&floor->m_NewSpecial); } break; @@ -635,7 +636,7 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, { // [RH] Find the next sector by scanning for Stairs_Special? tsec = sec->NextSpecialSector ( - (sec->special & 0xff) == Stairs_Special1 ? + sec->special == Stairs_Special1 ? Stairs_Special2 : Stairs_Special1, prev); if ( (ok = (tsec != NULL)) ) @@ -727,6 +728,7 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, floor->m_Type = DFloor::buildStair; //jff 3/31/98 do not leave uninited //jff 2/27/98 fix uninitialized crush field floor->m_Crush = (!usespecials && speed == 4*FRACUNIT) ? 10 : -1; + floor->m_Hexencrush = false; floor->m_ResetCount = reset; // [RH] Tics until reset (0 if never) floor->m_OrgDist = sec->floorplane.d; // [RH] Height to reset to } @@ -791,7 +793,7 @@ bool EV_DoDonut (int tag, line_t *line, fixed_t pillarspeed, fixed_t slimespeed) floor->m_Sector = s2; floor->m_Speed = slimespeed; floor->m_Texture = s3->GetTexture(sector_t::floor); - floor->m_NewSpecial = 0; + floor->m_NewSpecial.Clear(); height = s3->FindHighestFloorPoint (&spot); floor->m_FloorDestDist = s2->floorplane.PointToDist (spot, height); floor->StartFloorSound (); @@ -1090,7 +1092,7 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag) if (line) { // [RH] if no line, no change sec->SetTexture(sector_t::floor, line->frontsector->GetTexture(sector_t::floor)); - sec->special = (sec->special & SECRET_MASK) | (line->frontsector->special & ~SECRET_MASK); + sec->TransferSpecial(line->frontsector); } break; case numChangeOnly: @@ -1098,7 +1100,7 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag) if (secm) { // if no model, no change sec->SetTexture(sector_t::floor, secm->GetTexture(sector_t::floor)); - sec->special = secm->special; + sec->TransferSpecial(secm); } break; default: diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index cbbd879781..0c61b2d7df 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -74,7 +74,6 @@ EXTERN_CVAR (Bool, show_obituaries) FName MeansOfDeath; -bool FriendlyFire; // // GET STUFF @@ -85,7 +84,7 @@ bool FriendlyFire; // void P_TouchSpecialThing (AActor *special, AActor *toucher) { - fixed_t delta = special->z - toucher->z; + fixed_t delta = special->Z() - toucher->Z(); // The pickup is at or above the toucher's feet OR // The pickup is below the toucher. @@ -185,7 +184,6 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf const char *message; const char *messagename; char gendermessage[1024]; - bool friendly; int gender; // No obituaries for non-players, voodoo dolls or when not wanted @@ -198,10 +196,6 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf if (inflictor && inflictor->player && inflictor->player->mo != inflictor) MeansOfDeath = NAME_None; - if (multiplayer && !deathmatch) - FriendlyFire = true; - - friendly = FriendlyFire; mod = MeansOfDeath; message = NULL; messagename = NULL; @@ -269,10 +263,8 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf if (message == NULL && attacker != NULL && attacker->player != NULL) { - if (friendly) + if (self->player != attacker->player && self->IsTeammate(attacker)) { - attacker->player->fragcount -= 2; - attacker->player->frags[attacker->player - players]++; self = attacker; gender = self->player->userinfo.GetGender(); mysnprintf (gendermessage, countof(gendermessage), "OB_FRIENDLY%c", '1' + (pr_obituary() & 3)); @@ -470,12 +462,21 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags) if ((dmflags2 & DF2_YES_LOSEFRAG) && deathmatch) player->fragcount--; - ++source->player->fragcount; - ++source->player->spreecount; + if (this->IsTeammate(source)) + { + source->player->fragcount--; + } + else + { + ++source->player->fragcount; + ++source->player->spreecount; + } + if (source->player->morphTics) { // Make a super chicken source->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); } + if (deathmatch && cl_showsprees) { const char *spreemsg; @@ -969,7 +970,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } if (target->health <= 0) { - if (inflictor && mod == NAME_Ice) + if (inflictor && mod == NAME_Ice && !(inflictor->flags7 & MF7_ICESHATTER)) { return -1; } @@ -1024,7 +1025,6 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } MeansOfDeath = mod; - FriendlyFire = false; // [RH] Andy Baker's Stealth monsters if (target->flags & MF_STEALTH) { @@ -1045,7 +1045,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, return -1; } - if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with NOTELEFRAGPIERCE or it may not guarantee its effect. + if ((rawdamage < TELEFRAG_DAMAGE) || (target->flags7 & MF7_LAXTELEFRAGDMG)) // TELEFRAG_DAMAGE may only be reduced with LAXTELEFRAGDMG or it may not guarantee its effect. { if (player && damage > 1) { @@ -1158,13 +1158,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // If the origin and target are in exactly the same spot, choose a random direction. // (Most likely cause is from telefragging somebody during spawning because they // haven't moved from their spawn spot at all.) - if (origin->x == target->x && origin->y == target->y) + if (origin->X() == target->X() && origin->Y() == target->Y()) { ang = pr_kickbackdir.GenRand32(); } else { - ang = R_PointToAngle2 (origin->x, origin->y, target->x, target->y); + ang = origin->AngleTo(target); } // Calculate this as float to avoid overflows so that the @@ -1184,7 +1184,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, // make fall forwards sometimes if ((damage < 40) && (damage > target->health) - && (target->z - origin->z > 64*FRACUNIT) + && (target->Z() - origin->Z() > 64*FRACUNIT) && (pr_damagemobj()&1) // [RH] But only if not too fast and not flying && thrust < 10*FRACUNIT @@ -1221,9 +1221,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, ((player && player != source->player) || (!player && target != source)) && target->IsTeammate (source)) { - if (player) - FriendlyFire = true; - if (rawdamage < TELEFRAG_DAMAGE) //Use the original damage to check for telefrag amount. Don't let the now-amplified damagetypes do it. + //Use the original damage to check for telefrag amount. Don't let the now-amplified damagetypes do it. + if (rawdamage < TELEFRAG_DAMAGE || (target->flags7 & MF7_LAXTELEFRAGDMG)) { // Still allow telefragging :-( damage = (int)((float)damage * level.teamdamage); if (damage < 0) @@ -1257,8 +1256,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, } // end of game hell hack - if ((target->Sector->special & 255) == dDamage_End - && damage >= target->health) + if ((target->Sector->Flags & SECF_ENDLEVEL) && damage >= target->health) { damage = target->health - 1; } @@ -1641,10 +1639,8 @@ bool AActor::OkayToSwitchTarget (AActor *other) int infight; if (flags5 & MF5_NOINFIGHTING) infight=-1; - else if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight=1; - else if (level.flags2 & LEVEL2_NOINFIGHTING) infight=-1; - else infight = infighting; - + else infight = G_SkillProperty(SKILLP_Infight); + if (infight < 0 && other->player == NULL && !IsHostile (other)) { return false; // infighting off: Non-friendlies don't target other non-friendlies @@ -1841,3 +1837,22 @@ CCMD (kill) } C_HideConsole (); } + +CCMD(remove) +{ + if (argv.argc() == 2) + { + if (CheckCheatmode()) + return; + + Net_WriteByte(DEM_REMOVE); + Net_WriteString(argv[1]); + C_HideConsole(); + } + else + { + Printf("Usage: remove \n"); + return; + } + +} \ No newline at end of file diff --git a/src/p_lights.cpp b/src/p_lights.cpp index 49ef130e8a..656b861124 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -786,12 +786,12 @@ int DPhased::PhaseHelper (sector_t *sector, int index, int light, sector_t *prev l = new DPhased (sector, baselevel); int numsteps = PhaseHelper (sector->NextSpecialSector ( - (sector->special & 0x00ff) == LightSequenceSpecial1 ? + sector->special == LightSequenceSpecial1 ? LightSequenceSpecial2 : LightSequenceSpecial1, prev), index + 1, l->m_BaseLevel, sector); l->m_Phase = ((numsteps - index - 1) * 64) / numsteps; - sector->special &= 0xff00; + sector->special = 0; return numsteps; } @@ -820,7 +820,6 @@ DPhased::DPhased (sector_t *sector, int baselevel, int phase) { m_BaseLevel = baselevel; m_Phase = phase; - sector->special &= 0xff00; } //============================================================================ diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 37d6d60581..06e2f5f215 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -56,6 +56,7 @@ #include "p_3dmidtex.h" #include "d_net.h" #include "d_event.h" +#include "gstrings.h" #include "r_data/colormaps.h" #define FUNC(a) static int a (line_t *ln, AActor *it, bool backSide, \ @@ -147,7 +148,7 @@ FUNC(LS_Polyobj_MoveToSpot) FActorIterator iterator (arg2); AActor *spot = iterator.Next(); if (spot == NULL) return false; - return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->x, spot->y, false); + return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->X(), spot->Y(), false); } FUNC(LS_Polyobj_DoorSwing) @@ -198,7 +199,7 @@ FUNC(LS_Polyobj_OR_MoveToSpot) FActorIterator iterator (arg2); AActor *spot = iterator.Next(); if (spot == NULL) return false; - return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->x, spot->y, true); + return EV_MovePolyTo (ln, arg0, SPEED(arg1), spot->X(), spot->Y(), true); } FUNC(LS_Polyobj_Stop) @@ -1599,6 +1600,11 @@ FUNC(LS_Thing_Move) // [BC] return P_Thing_Move (arg0, it, arg1, arg2 ? false : true); } +enum +{ + TRANSLATION_ICE = 0x100007 +}; + FUNC(LS_Thing_SetTranslation) // Thing_SetTranslation (tid, range) { @@ -1615,6 +1621,10 @@ FUNC(LS_Thing_SetTranslation) { range = TRANSLATION(TRANSLATION_LevelScripted, (arg1-1)); } + else if (arg1 == TRANSLATION_ICE) + { + range = TRANSLATION(TRANSLATION_Standard, 7); + } else { range = 0; @@ -1935,6 +1945,9 @@ FUNC(LS_Sector_ChangeFlags) rtn = false; FSectorTagIterator itr(arg0); + // exclude protected flags + arg1 &= ~SECF_NOMODIFY; + arg2 &= ~SECF_NOMODIFY; while ((secNum = itr.Next()) >= 0) { sectors[secNum].Flags = (sectors[secNum].Flags | arg1) & ~arg2; @@ -2236,7 +2249,7 @@ FUNC(LS_PointPush_SetForce) } FUNC(LS_Sector_SetDamage) -// Sector_SetDamage (tag, amount, mod) +// Sector_SetDamage (tag, amount, mod, interval, leaky) { // The sector still stores the mod in its old format because // adding an FName to the sector_t structure might cause @@ -2247,8 +2260,28 @@ FUNC(LS_Sector_SetDamage) int secnum; while ((secnum = itr.Next()) >= 0) { - sectors[secnum].damage = arg1; - sectors[secnum].mod = arg2; + if (arg3 <= 0) // emulate old and hacky method to handle leakiness. + { + if (arg1 < 20) + { + arg4 = 0; + arg3 = 32; + } + else if (arg1 < 50) + { + arg4 = 5; + arg3 = 32; + } + else + { + arg4 = 256; + arg3 = 1; + } + } + sectors[secnum].damageamount = (short)arg1; + sectors[secnum].damagetype = MODtoDamageType(arg2); + sectors[secnum].damageinterval = (short)arg3; + sectors[secnum].leakydamage = (short)arg4; } return true; } @@ -2577,6 +2610,7 @@ FUNC(LS_Line_SetBlocking) ML_BLOCKUSE, ML_BLOCKSIGHT, ML_BLOCKHITSCAN, + ML_SOUNDBLOCK, -1 }; @@ -2985,13 +3019,14 @@ FUNC(LS_SendToCommunicator) { S_StopSound (CHAN_VOICE); S_Sound (CHAN_VOICE, name, 1, ATTN_NORM); - if (arg2 == 0) + + // Get the message from the LANGUAGE lump. + FString msg; + msg.Format("TXT_COMM%d", arg2); + const char *str = GStrings[msg]; + if (str != NULL) { - Printf (PRINT_CHAT, "Incoming Message\n"); - } - else if (arg2 == 1) - { - Printf (PRINT_CHAT, "Incoming Message from BlackBird\n"); + Printf (PRINT_CHAT, "%s\n", str); } } return true; @@ -3072,7 +3107,7 @@ FUNC(LS_GlassBreak) { glass = Spawn("GlassJunk", x, y, ONFLOORZ, ALLOW_REPLACE); - glass->z += 24 * FRACUNIT; + glass->SetZ(glass->Z() + 24 * FRACUNIT); glass->SetState (glass->SpawnState + (pr_glass() % glass->health)); an = pr_glass() << (32-8); glass->angle = an; diff --git a/src/p_local.h b/src/p_local.h index b62836c172..7e00276bdf 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -1,612 +1,660 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Play functions, animation, global header. -// -//----------------------------------------------------------------------------- - - -#ifndef __P_LOCAL__ -#define __P_LOCAL__ - -#include "doomtype.h" -#include "doomdef.h" -#include "tables.h" -#include "r_state.h" -#include "r_utility.h" -#include "d_player.h" - -#include "a_morph.h" - -#include - -#define STEEPSLOPE 46342 // [RH] Minimum floorplane.c value for walking - -#define BONUSADD 6 - -// mapblocks are used to check movement -// against lines and things -#define MAPBLOCKUNITS 128 -#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) -#define MAPBLOCKSHIFT (FRACBITS+7) -#define MAPBMASK (MAPBLOCKSIZE-1) -#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) - -// Inspired by Maes -extern int bmapnegx; -extern int bmapnegy; - -inline int GetSafeBlockX(int blockx) -{ - blockx >>= MAPBLOCKSHIFT; - return (blockx <= bmapnegx) ? blockx & 0x1FF : blockx; -} -inline int GetSafeBlockX(long long blockx) -{ - blockx >>= MAPBLOCKSHIFT; - return int((blockx <= bmapnegx) ? blockx & 0x1FF : blockx); -} - -inline int GetSafeBlockY(int blocky) -{ - blocky >>= MAPBLOCKSHIFT; - return (blocky <= bmapnegy) ? blocky & 0x1FF: blocky; -} -inline int GetSafeBlockY(long long blocky) -{ - blocky >>= MAPBLOCKSHIFT; - return int((blocky <= bmapnegy) ? blocky & 0x1FF: blocky); -} - -// MAXRADIUS is for precalculated sector block boxes -// the spider demon is larger, -// but we do not have any moving sectors nearby -#define MAXRADIUS 0/*32*FRACUNIT*/ - -//#define GRAVITY FRACUNIT -#define MAXMOVE (30*FRACUNIT) - -#define TALKRANGE (128*FRACUNIT) -#define USERANGE (64*FRACUNIT) -#define MELEERANGE (64*FRACUNIT) -#define MISSILERANGE (32*64*FRACUNIT) -#define PLAYERMISSILERANGE (8192*FRACUNIT) // [RH] New MISSILERANGE for players - -// follow a player exlusively for 3 seconds -#define BASETHRESHOLD 100 - - -// -// P_PSPR -// -void P_SetupPsprites (player_t* curplayer, bool startweaponup); -void P_MovePsprites (player_t* curplayer); -void P_DropWeapon (player_t* player); - - -// -// P_USER -// -void P_FallingDamage (AActor *ent); -void P_PlayerThink (player_t *player); -void P_PredictPlayer (player_t *player); -void P_UnPredictPlayer (); -void P_PredictionLerpReset(); - -// -// P_MOBJ -// - -#define ONFLOORZ FIXED_MIN -#define ONCEILINGZ FIXED_MAX -#define FLOATRANDZ (FIXED_MAX-1) - -#define SPF_TEMPPLAYER 1 // spawning a short-lived dummy player -#define SPF_WEAPONFULLYUP 2 // spawn with weapon already raised - -APlayerPawn *P_SpawnPlayer (struct FPlayerStart *mthing, int playernum, int flags=0); - -void P_ThrustMobj (AActor *mo, angle_t angle, fixed_t move); -int P_FaceMobj (AActor *source, AActor *target, angle_t *delta); -bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax, bool precise = false, bool usecurspeed=false); - -enum EPuffFlags -{ - PF_HITTHING = 1, - PF_MELEERANGE = 2, - PF_TEMPORARY = 4, - PF_HITTHINGBLEED = 8, - PF_NORANDOMZ = 16 -}; - -AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL); -void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator); -void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator); -void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator); -void P_RipperBlood (AActor *mo, AActor *bleeder); -int P_GetThingFloorType (AActor *thing); -void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target); - -AActor *P_SpawnMissile (AActor* source, AActor* dest, const PClass *type, AActor* owner = NULL); -AActor *P_SpawnMissileZ (AActor* source, fixed_t z, AActor* dest, const PClass *type); -AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, AActor *source, AActor *dest, const PClass *type, bool checkspawn = true, AActor *owner = NULL); -AActor *P_SpawnMissileAngle (AActor *source, const PClass *type, angle_t angle, fixed_t velz); -AActor *P_SpawnMissileAngleSpeed (AActor *source, const PClass *type, angle_t angle, fixed_t velz, fixed_t speed); -AActor *P_SpawnMissileAngleZ (AActor *source, fixed_t z, const PClass *type, angle_t angle, fixed_t velz); -AActor *P_SpawnMissileAngleZSpeed (AActor *source, fixed_t z, const PClass *type, angle_t angle, fixed_t velz, fixed_t speed, AActor *owner=NULL, bool checkspawn = true); -AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PClass *type); - -AActor *P_SpawnPlayerMissile (AActor* source, const PClass *type); -AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle); -AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, const PClass *type, angle_t angle, - AActor **pLineTarget = NULL, AActor **MissileActor = NULL, bool nofreeaim = false); - -void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheight=false); - -// -// [RH] P_THINGS -// -extern FClassMap SpawnableThings; -extern FClassMap StrifeTypes; - -bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, int newtid); -bool P_Thing_Projectile (int tid, AActor *source, int type, const char * type_name, angle_t angle, - fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, - bool leadTarget); -bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog); -bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); -int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); -void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); -void P_RemoveThing(AActor * actor); -bool P_Thing_Raise(AActor *thing, AActor *raiser); -bool P_Thing_CanRaise(AActor *thing); -const PClass *P_GetSpawnableType(int spawnnum); -void InitSpawnablesFromMapinfo(); - -// -// P_MAPUTL -// -struct divline_t -{ - fixed_t x; - fixed_t y; - fixed_t dx; - fixed_t dy; - -}; - -struct intercept_t -{ - fixed_t frac; // along trace line - bool isaline; - bool done; - union { - AActor *thing; - line_t *line; - } d; -}; - -typedef bool (*traverser_t) (intercept_t *in); - -fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); - -//========================================================================== -// -// P_PointOnLineSide -// -// Returns 0 (front/on) or 1 (back) -// [RH] inlined, stripped down, and made more precise -// -//========================================================================== - -inline int P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line) -{ - return DMulScale32 (y-line->v1->y, line->dx, line->v1->x-x, line->dy) > 0; -} - -//========================================================================== -// -// P_PointOnDivlineSide -// -// Same as P_PointOnLineSide except it uses divlines -// [RH] inlined, stripped down, and made more precise -// -//========================================================================== - -inline int P_PointOnDivlineSide (fixed_t x, fixed_t y, const divline_t *line) -{ - return DMulScale32 (y-line->y, line->dx, line->x-x, line->dy) > 0; -} - -//========================================================================== -// -// P_MakeDivline -// -//========================================================================== - -inline void P_MakeDivline (const line_t *li, divline_t *dl) -{ - dl->x = li->v1->x; - dl->y = li->v1->y; - dl->dx = li->dx; - dl->dy = li->dy; -} - -fixed_t P_InterceptVector (const divline_t *v2, const divline_t *v1); - -struct FLineOpening -{ - fixed_t top; - fixed_t bottom; - fixed_t range; - fixed_t lowfloor; - sector_t *bottomsec; - sector_t *topsec; - FTextureID ceilingpic; - FTextureID floorpic; - bool touchmidtex; - bool abovemidtex; -}; - -void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fixed_t x, fixed_t y, fixed_t refx=FIXED_MIN, fixed_t refy=0, int flags=0); - -class FBoundingBox; -struct polyblock_t; - -class FBlockLinesIterator -{ - int minx, maxx; - int miny, maxy; - - int curx, cury; - polyblock_t *polyLink; - int polyIndex; - int *list; - - void StartBlock(int x, int y); - -public: - FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false); - FBlockLinesIterator(const FBoundingBox &box); - line_t *Next(); - void Reset() { StartBlock(minx, miny); } -}; - -class FBlockThingsIterator -{ - int minx, maxx; - int miny, maxy; - - int curx, cury; - - FBlockNode *block; - - int Buckets[32]; - - struct HashEntry - { - AActor *Actor; - int Next; - }; - HashEntry FixedHash[10]; - int NumFixedHash; - TArray DynHash; - - HashEntry *GetHashEntry(int i) { return i < (int)countof(FixedHash) ? &FixedHash[i] : &DynHash[i - countof(FixedHash)]; } - - void StartBlock(int x, int y); - void SwitchBlock(int x, int y); - void ClearHash(); - - // The following is only for use in the path traverser - // and therefore declared private. - FBlockThingsIterator(); - - friend class FPathTraverse; - -public: - FBlockThingsIterator(int minx, int miny, int maxx, int maxy); - FBlockThingsIterator(const FBoundingBox &box); - AActor *Next(bool centeronly = false); - void Reset() { StartBlock(minx, miny); } -}; - -class FPathTraverse -{ - static TArray intercepts; - - divline_t trace; - unsigned int intercept_index; - unsigned int intercept_count; - fixed_t maxfrac; - unsigned int count; - - void AddLineIntercepts(int bx, int by); - void AddThingIntercepts(int bx, int by, FBlockThingsIterator &it, bool compatible); -public: - - intercept_t *Next(); - - FPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags); - ~FPathTraverse(); - const divline_t &Trace() const { return trace; } -}; - - -#define PT_ADDLINES 1 -#define PT_ADDTHINGS 2 -#define PT_COMPATIBLE 4 -#define PT_DELTA 8 // x2,y2 is passed as a delta, not as an endpoint - -AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL); -AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false); - -// -// P_MAP -// - -struct FCheckPosition -{ - // in - AActor *thing; - fixed_t x; - fixed_t y; - fixed_t z; - - // out - sector_t *sector; - fixed_t floorz; - fixed_t ceilingz; - fixed_t dropoffz; - FTextureID floorpic; - sector_t *floorsector; - FTextureID ceilingpic; - sector_t *ceilingsector; - bool touchmidtex; - bool abovemidtex; - bool floatok; - bool FromPMove; - line_t *ceilingline; - AActor *stepthing; - // [RH] These are used by PIT_CheckThing and P_XYMovement to apply - // ripping damage once per tic instead of once per move. - bool DoRipping; - AActor *LastRipped; - int PushTime; - - FCheckPosition(bool rip=false) - { - DoRipping = rip; - LastRipped = NULL; - PushTime = 0; - FromPMove = false; - } -}; - - - -// If "floatok" true, move would be ok -// if within "tmfloorz - tmceilingz". -extern msecnode_t *sector_list; // phares 3/16/98 - -extern TArray spechit; - - -bool P_TestMobjLocation (AActor *mobj); -bool P_TestMobjZ (AActor *mobj, bool quick=true, AActor **pOnmobj = NULL); -bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bool actorsonly=false); -bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, bool actorsonly=false); -AActor *P_CheckOnmobj (AActor *thing); -void P_FakeZMovement (AActor *mo); -bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor, FCheckPosition &tm, bool missileCheck = false); -bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor = NULL); -bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y); -void P_ApplyTorque(AActor *mo); -bool P_TeleportMove (AActor* thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag); // [RH] Added z and telefrag parameters -void P_PlayerStartStomp (AActor *actor); // [RH] Stomp on things for a newly spawned player -void P_SlideMove (AActor* mo, fixed_t tryx, fixed_t tryy, int numsteps); -bool P_BounceWall (AActor *mo); -bool P_BounceActor (AActor *mo, AActor *BlockingMobj, bool ontop); -bool P_CheckSight (const AActor *t1, const AActor *t2, int flags=0); - -enum ESightFlags -{ - SF_IGNOREVISIBILITY=1, - SF_SEEPASTSHOOTABLELINES=2, - SF_SEEPASTBLOCKEVERYTHING=4, - SF_IGNOREWATERBOUNDARY=8 -}; - -void P_ResetSightCounters (bool full); -bool P_TalkFacing (AActor *player); -void P_UseLines (player_t* player); -bool P_UsePuzzleItem (AActor *actor, int itemType); - -enum -{ - FFCF_ONLYSPAWNPOS = 1, - FFCF_SAMESECTOR = 2, - FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes - FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z -}; -void P_FindFloorCeiling (AActor *actor, int flags=0); - -bool P_ChangeSector (sector_t* sector, int crunch, int amt, int floorOrCeil, bool isreset); - -fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget = NULL, fixed_t vrange=0, int flags = 0, AActor *target=NULL, AActor *friender=NULL); - -enum // P_AimLineAttack flags -{ - ALF_FORCENOSMART = 1, - ALF_CHECK3D = 2, - ALF_CHECKNONSHOOTABLE = 4, - ALF_CHECKCONVERSATION = 8, - ALF_NOFRIENDS = 16, -}; - -enum // P_LineAttack flags -{ - LAF_ISMELEEATTACK = 1, - LAF_NORANDOMPUFFZ = 2, - LAF_NOIMPACTDECAL = 4 -}; - -AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); -AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); -AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, DWORD actorMask, DWORD wallMask); -void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); -void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); -void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version -void P_TraceBleed (int damage, AActor *target); // random direction version -bool P_HitFloor (AActor *thing); -bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true); -void P_CheckSplash(AActor *self, fixed_t distance); -void P_RailAttack (AActor *source, int damage, int offset_xy, fixed_t offset_z = 0, int color1 = 0, int color2 = 0, float maxdiff = 0, int flags = 0, const PClass *puff = NULL, angle_t angleoffset = 0, angle_t pitchoffset = 0, fixed_t distance = 8192*FRACUNIT, int duration = 0, float sparsity = 1.0, float drift = 1.0, const PClass *spawnclass = NULL, int SpiralOffset = 270); // [RH] Shoot a railgun - -enum // P_RailAttack / A_RailAttack / A_CustomRailgun / P_DrawRailTrail flags -{ - RAF_SILENT = 1, - RAF_NOPIERCE = 2, - RAF_EXPLICITANGLE = 4, - RAF_FULLBRIGHT = 8, - RAF_CENTERZ = 16, -}; - - -bool P_CheckMissileSpawn (AActor *missile, fixed_t maxdist); -void P_PlaySpawnSound(AActor *missile, AActor *spawner); - -// [RH] Position the chasecam -void P_AimCamera (AActor *t1, fixed_t &x, fixed_t &y, fixed_t &z, sector_t *&sec); - -// [RH] Means of death -enum -{ - RADF_HURTSOURCE = 1, - RADF_NOIMPACTDAMAGE = 2, - RADF_SOURCEISSPOT = 4, - RADF_NODAMAGE = 8, -}; -void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, - FName damageType, int flags, int fulldamagedistance=0); - -void P_DelSector_List(); -void P_DelSeclist(msecnode_t *); // phares 3/16/98 -msecnode_t* P_DelSecnode(msecnode_t *); -void P_CreateSecNodeList(AActor*,fixed_t,fixed_t); // phares 3/14/98 -int P_GetMoveFactor(const AActor *mo, int *frictionp); // phares 3/6/98 -int P_GetFriction(const AActor *mo, int *frictionfactor); -bool Check_Sides(AActor *, int, int); // phares - -// [RH] -const secplane_t * P_CheckSlopeWalk (AActor *actor, fixed_t &xmove, fixed_t &ymove); - -//---------------------------------------------------------------------------------- -// -// Added so that in the source there's a clear distinction between -// game engine and renderer specific calls. -// (For ZDoom itself this doesn't make any difference here but for GZDoom it does.) -// -//---------------------------------------------------------------------------------- -subsector_t *P_PointInSubsector (fixed_t x, fixed_t y); -inline sector_t *P_PointInSector(fixed_t x, fixed_t y) -{ - return P_PointInSubsector(x,y)->sector; -} - -// -// P_SETUP -// -extern BYTE* rejectmatrix; // for fast sight rejection -extern int* blockmaplump; // offsets in blockmap are from here - -extern int* blockmap; -extern int bmapwidth; -extern int bmapheight; // in mapblocks -extern fixed_t bmaporgx; -extern fixed_t bmaporgy; // origin of block map -extern FBlockNode** blocklinks; // for thing chains - - - -// -// P_INTER -// -void P_TouchSpecialThing (AActor *special, AActor *toucher); -int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags=0); -void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int duration, int period, FName type); -bool P_GiveBody (AActor *actor, int num, int max=0); -bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison); -void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound); - -enum EDmgFlags -{ - DMG_NO_ARMOR = 1, - DMG_INFLICTOR_IS_PUFF = 2, - DMG_THRUSTLESS = 4, - DMG_FORCED = 8, - DMG_NO_FACTOR = 16, - DMG_PLAYERATTACK = 32, - DMG_FOILINVUL = 64, - DMG_FOILBUDDHA = 128, - DMG_NO_PROTECT = 256, -}; - - -// ===== PO_MAN ===== - -typedef enum -{ - PODOOR_NONE, - PODOOR_SLIDE, - PODOOR_SWING, -} podoortype_t; - -bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide); -bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle, fixed_t dist, bool overRide); -bool EV_MovePolyTo (line_t *line, int polyNum, int speed, fixed_t x, fixed_t y, bool overRide); -bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, int delay, int distance, podoortype_t type); -bool EV_StopPoly (int polyNum); - - -// [RH] Data structure for P_SpawnMapThing() to keep track -// of polyobject-related things. -struct polyspawns_t -{ - polyspawns_t *next; - fixed_t x; - fixed_t y; - short angle; - short type; -}; - -extern int po_NumPolyobjs; -extern polyspawns_t *polyspawns; // [RH] list of polyobject things to spawn - - -void PO_Init (); -bool PO_Busy (int polyobj); -FPolyObj *PO_GetPolyobj(int polyNum); - -// -// P_SPEC -// -#include "p_spec.h" - -bool P_AlignFlat (int linenum, int side, int fc); - -#endif // __P_LOCAL__ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// Play functions, animation, global header. +// +//----------------------------------------------------------------------------- + + +#ifndef __P_LOCAL__ +#define __P_LOCAL__ + +#include "doomtype.h" +#include "doomdef.h" +#include "tables.h" +#include "r_state.h" +#include "r_utility.h" +#include "d_player.h" + +#include "a_morph.h" + +#include + +#define STEEPSLOPE 46342 // [RH] Minimum floorplane.c value for walking + +#define BONUSADD 6 + +// mapblocks are used to check movement +// against lines and things +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + +// Inspired by Maes +extern int bmapnegx; +extern int bmapnegy; + +inline int GetSafeBlockX(int blockx) +{ + blockx >>= MAPBLOCKSHIFT; + return (blockx <= bmapnegx) ? blockx & 0x1FF : blockx; +} +inline int GetSafeBlockX(long long blockx) +{ + blockx >>= MAPBLOCKSHIFT; + return int((blockx <= bmapnegx) ? blockx & 0x1FF : blockx); +} + +inline int GetSafeBlockY(int blocky) +{ + blocky >>= MAPBLOCKSHIFT; + return (blocky <= bmapnegy) ? blocky & 0x1FF: blocky; +} +inline int GetSafeBlockY(long long blocky) +{ + blocky >>= MAPBLOCKSHIFT; + return int((blocky <= bmapnegy) ? blocky & 0x1FF: blocky); +} + +// MAXRADIUS is for precalculated sector block boxes +// the spider demon is larger, +// but we do not have any moving sectors nearby +#define MAXRADIUS 0/*32*FRACUNIT*/ + +//#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define TALKRANGE (128*FRACUNIT) +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) +#define PLAYERMISSILERANGE (8192*FRACUNIT) // [RH] New MISSILERANGE for players + +// follow a player exlusively for 3 seconds +#define BASETHRESHOLD 100 + + +// +// P_PSPR +// +void P_SetupPsprites (player_t* curplayer, bool startweaponup); +void P_MovePsprites (player_t* curplayer); +void P_DropWeapon (player_t* player); + + +// +// P_USER +// +void P_FallingDamage (AActor *ent); +void P_PlayerThink (player_t *player); +void P_PredictPlayer (player_t *player); +void P_UnPredictPlayer (); +void P_PredictionLerpReset(); + +// +// P_MOBJ +// + +#define ONFLOORZ FIXED_MIN +#define ONCEILINGZ FIXED_MAX +#define FLOATRANDZ (FIXED_MAX-1) + +#define SPF_TEMPPLAYER 1 // spawning a short-lived dummy player +#define SPF_WEAPONFULLYUP 2 // spawn with weapon already raised + +APlayerPawn *P_SpawnPlayer (struct FPlayerStart *mthing, int playernum, int flags=0); + +void P_ThrustMobj (AActor *mo, angle_t angle, fixed_t move); +int P_FaceMobj (AActor *source, AActor *target, angle_t *delta); +bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax, bool precise = false, bool usecurspeed=false); + +enum EPuffFlags +{ + PF_HITTHING = 1, + PF_MELEERANGE = 2, + PF_TEMPORARY = 4, + PF_HITTHINGBLEED = 8, + PF_NORANDOMZ = 16 +}; + +AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL); +void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, angle_t dir, int damage, AActor *originator); +void P_BloodSplatter (fixed_t x, fixed_t y, fixed_t z, AActor *originator); +void P_BloodSplatter2 (fixed_t x, fixed_t y, fixed_t z, AActor *originator); +void P_RipperBlood (AActor *mo, AActor *bleeder); +int P_GetThingFloorType (AActor *thing); +void P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target); + +AActor *P_SpawnMissile (AActor* source, AActor* dest, const PClass *type, AActor* owner = NULL); +AActor *P_SpawnMissileZ (AActor* source, fixed_t z, AActor* dest, const PClass *type); +AActor *P_SpawnMissileXYZ (fixed_t x, fixed_t y, fixed_t z, AActor *source, AActor *dest, const PClass *type, bool checkspawn = true, AActor *owner = NULL); +AActor *P_SpawnMissileAngle (AActor *source, const PClass *type, angle_t angle, fixed_t velz); +AActor *P_SpawnMissileAngleSpeed (AActor *source, const PClass *type, angle_t angle, fixed_t velz, fixed_t speed); +AActor *P_SpawnMissileAngleZ (AActor *source, fixed_t z, const PClass *type, angle_t angle, fixed_t velz); +AActor *P_SpawnMissileAngleZSpeed (AActor *source, fixed_t z, const PClass *type, angle_t angle, fixed_t velz, fixed_t speed, AActor *owner=NULL, bool checkspawn = true); +AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PClass *type); + +AActor *P_SpawnPlayerMissile (AActor* source, const PClass *type); +AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle); +AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, const PClass *type, angle_t angle, + AActor **pLineTarget = NULL, AActor **MissileActor = NULL, bool nofreeaim = false, bool noautoaim = false); + +void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheight=false); + +// +// [RH] P_THINGS +// +extern FClassMap SpawnableThings; +extern FClassMap StrifeTypes; + +bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, int newtid); +bool P_Thing_Projectile (int tid, AActor *source, int type, const char * type_name, angle_t angle, + fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, + bool leadTarget); +bool P_MoveThing(AActor *source, fixed_t x, fixed_t y, fixed_t z, bool fog); +bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); +int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); +void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); +void P_RemoveThing(AActor * actor); +bool P_Thing_Raise(AActor *thing, AActor *raiser); +bool P_Thing_CanRaise(AActor *thing); +const PClass *P_GetSpawnableType(int spawnnum); +void InitSpawnablesFromMapinfo(); +int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset, fixed_t radiusoffset, angle_t pitch); + +enum WARPF +{ + WARPF_ABSOLUTEOFFSET = 0x1, + WARPF_ABSOLUTEANGLE = 0x2, + WARPF_USECALLERANGLE = 0x4, + + WARPF_NOCHECKPOSITION = 0x8, + + WARPF_INTERPOLATE = 0x10, + WARPF_WARPINTERPOLATION = 0x20, + WARPF_COPYINTERPOLATION = 0x40, + + WARPF_STOP = 0x80, + WARPF_TOFLOOR = 0x100, + WARPF_TESTONLY = 0x200, + WARPF_ABSOLUTEPOSITION = 0x400, + WARPF_BOB = 0x800, + WARPF_MOVEPTR = 0x1000, + WARPF_USEPTR = 0x2000, + WARPF_USETID = 0x2000, + WARPF_COPYVELOCITY = 0x4000, + WARPF_COPYPITCH = 0x8000, +}; + + + +// +// P_MAPUTL +// +struct divline_t +{ + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; + +}; + +struct intercept_t +{ + fixed_t frac; // along trace line + bool isaline; + bool done; + union { + AActor *thing; + line_t *line; + } d; +}; + +typedef bool (*traverser_t) (intercept_t *in); + +fixed_t P_AproxDistance (fixed_t dx, fixed_t dy); + +//========================================================================== +// +// P_PointOnLineSide +// +// Returns 0 (front/on) or 1 (back) +// [RH] inlined, stripped down, and made more precise +// +//========================================================================== + +inline int P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line) +{ + extern int P_VanillaPointOnLineSide(fixed_t x, fixed_t y, const line_t* line); + + return i_compatflags2 & COMPATF2_POINTONLINE + ? P_VanillaPointOnLineSide(x, y, line) + : DMulScale32 (y-line->v1->y, line->dx, line->v1->x-x, line->dy) > 0; +} + +inline int P_PointOnLineSidePrecise (fixed_t x, fixed_t y, const line_t *line) +{ + return DMulScale32 (y-line->v1->y, line->dx, line->v1->x-x, line->dy) > 0; +} + + +//========================================================================== +// +// P_PointOnDivlineSide +// +// Same as P_PointOnLineSide except it uses divlines +// [RH] inlined, stripped down, and made more precise +// +//========================================================================== + +inline int P_PointOnDivlineSide (fixed_t x, fixed_t y, const divline_t *line) +{ + extern int P_VanillaPointOnDivlineSide(fixed_t x, fixed_t y, const divline_t* line); + + return (i_compatflags2 & COMPATF2_POINTONLINE) + ? P_VanillaPointOnDivlineSide(x, y, line) + : (DMulScale32 (y-line->y, line->dx, line->x-x, line->dy) > 0); +} + +inline int P_PointOnDivlineSidePrecise (fixed_t x, fixed_t y, const divline_t *line) +{ + return DMulScale32 (y-line->y, line->dx, line->x-x, line->dy) > 0; +} + + +//========================================================================== +// +// P_MakeDivline +// +//========================================================================== + +inline void P_MakeDivline (const line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +fixed_t P_InterceptVector (const divline_t *v2, const divline_t *v1); + +struct FLineOpening +{ + fixed_t top; + fixed_t bottom; + fixed_t range; + fixed_t lowfloor; + sector_t *bottomsec; + sector_t *topsec; + FTextureID ceilingpic; + FTextureID floorpic; + int floorterrain; + bool touchmidtex; + bool abovemidtex; +}; + +void P_LineOpening (FLineOpening &open, AActor *thing, const line_t *linedef, fixed_t x, fixed_t y, fixed_t refx=FIXED_MIN, fixed_t refy=0, int flags=0); + +class FBoundingBox; +struct polyblock_t; + +class FBlockLinesIterator +{ + int minx, maxx; + int miny, maxy; + + int curx, cury; + polyblock_t *polyLink; + int polyIndex; + int *list; + + void StartBlock(int x, int y); + +public: + FBlockLinesIterator(int minx, int miny, int maxx, int maxy, bool keepvalidcount = false); + FBlockLinesIterator(const FBoundingBox &box); + line_t *Next(); + void Reset() { StartBlock(minx, miny); } +}; + +class FBlockThingsIterator +{ + int minx, maxx; + int miny, maxy; + + int curx, cury; + + FBlockNode *block; + + int Buckets[32]; + + struct HashEntry + { + AActor *Actor; + int Next; + }; + HashEntry FixedHash[10]; + int NumFixedHash; + TArray DynHash; + + HashEntry *GetHashEntry(int i) { return i < (int)countof(FixedHash) ? &FixedHash[i] : &DynHash[i - countof(FixedHash)]; } + + void StartBlock(int x, int y); + void SwitchBlock(int x, int y); + void ClearHash(); + + // The following is only for use in the path traverser + // and therefore declared private. + FBlockThingsIterator(); + + friend class FPathTraverse; + +public: + FBlockThingsIterator(int minx, int miny, int maxx, int maxy); + FBlockThingsIterator(const FBoundingBox &box); + AActor *Next(bool centeronly = false); + void Reset() { StartBlock(minx, miny); } +}; + +class FPathTraverse +{ + static TArray intercepts; + + divline_t trace; + unsigned int intercept_index; + unsigned int intercept_count; + unsigned int count; + + void AddLineIntercepts(int bx, int by); + void AddThingIntercepts(int bx, int by, FBlockThingsIterator &it, bool compatible); +public: + + intercept_t *Next(); + + FPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags); + ~FPathTraverse(); + const divline_t &Trace() const { return trace; } +}; + + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_COMPATIBLE 4 +#define PT_DELTA 8 // x2,y2 is passed as a delta, not as an endpoint + +AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL); +AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false); + +// +// P_MAP +// + +struct FCheckPosition +{ + // in + AActor *thing; + fixed_t x; + fixed_t y; + fixed_t z; + + // out + sector_t *sector; + fixed_t floorz; + fixed_t ceilingz; + fixed_t dropoffz; + FTextureID floorpic; + int floorterrain; + sector_t *floorsector; + FTextureID ceilingpic; + sector_t *ceilingsector; + bool touchmidtex; + bool abovemidtex; + bool floatok; + bool FromPMove; + line_t *ceilingline; + AActor *stepthing; + // [RH] These are used by PIT_CheckThing and P_XYMovement to apply + // ripping damage once per tic instead of once per move. + bool DoRipping; + AActor *LastRipped; + int PushTime; + + FCheckPosition(bool rip=false) + { + DoRipping = rip; + LastRipped = NULL; + PushTime = 0; + FromPMove = false; + } +}; + + + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". +extern msecnode_t *sector_list; // phares 3/16/98 + +extern TArray spechit; + + +bool P_TestMobjLocation (AActor *mobj); +bool P_TestMobjZ (AActor *mobj, bool quick=true, AActor **pOnmobj = NULL); +bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bool actorsonly=false); +bool P_CheckPosition (AActor *thing, fixed_t x, fixed_t y, bool actorsonly=false); +AActor *P_CheckOnmobj (AActor *thing); +void P_FakeZMovement (AActor *mo); +bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor, FCheckPosition &tm, bool missileCheck = false); +bool P_TryMove (AActor* thing, fixed_t x, fixed_t y, int dropoff, const secplane_t * onfloor = NULL); +bool P_CheckMove(AActor *thing, fixed_t x, fixed_t y); +void P_ApplyTorque(AActor *mo); +bool P_TeleportMove (AActor* thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag, bool modifyactor = true); // [RH] Added z and telefrag parameters +void P_PlayerStartStomp (AActor *actor); // [RH] Stomp on things for a newly spawned player +void P_SlideMove (AActor* mo, fixed_t tryx, fixed_t tryy, int numsteps); +bool P_BounceWall (AActor *mo); +bool P_BounceActor (AActor *mo, AActor *BlockingMobj, bool ontop); +bool P_CheckSight (const AActor *t1, const AActor *t2, int flags=0); + +enum ESightFlags +{ + SF_IGNOREVISIBILITY=1, + SF_SEEPASTSHOOTABLELINES=2, + SF_SEEPASTBLOCKEVERYTHING=4, + SF_IGNOREWATERBOUNDARY=8 +}; + +void P_ResetSightCounters (bool full); +bool P_TalkFacing (AActor *player); +void P_UseLines (player_t* player); +bool P_UsePuzzleItem (AActor *actor, int itemType); + +enum +{ + FFCF_ONLYSPAWNPOS = 1, + FFCF_SAMESECTOR = 2, + FFCF_ONLY3DFLOORS = 4, // includes 3D midtexes + FFCF_3DRESTRICT = 8, // ignore 3D midtexes and floors whose floorz are above thing's z +}; +void P_FindFloorCeiling (AActor *actor, int flags=0); + +bool P_ChangeSector (sector_t* sector, int crunch, int amt, int floorOrCeil, bool isreset); + +fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, AActor **pLineTarget = NULL, fixed_t vrange=0, int flags = 0, AActor *target=NULL, AActor *friender=NULL); + +enum // P_AimLineAttack flags +{ + ALF_FORCENOSMART = 1, + ALF_CHECK3D = 2, + ALF_CHECKNONSHOOTABLE = 4, + ALF_CHECKCONVERSATION = 8, + ALF_NOFRIENDS = 16, +}; + +enum // P_LineAttack flags +{ + LAF_ISMELEEATTACK = 1, + LAF_NORANDOMPUFFZ = 2, + LAF_NOIMPACTDECAL = 4 +}; + +AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, const PClass *pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); +AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, int pitch, int damage, FName damageType, FName pufftype, int flags = 0, AActor **victim = NULL, int *actualdamage = NULL); +AActor *P_LinePickActor (AActor *t1, angle_t angle, fixed_t distance, int pitch, ActorFlags actorMask, DWORD wallMask); +void P_TraceBleed (int damage, fixed_t x, fixed_t y, fixed_t z, AActor *target, angle_t angle, int pitch); +void P_TraceBleed (int damage, AActor *target, angle_t angle, int pitch); +void P_TraceBleed (int damage, AActor *target, AActor *missile); // missile version +void P_TraceBleed (int damage, AActor *target); // random direction version +bool P_HitFloor (AActor *thing); +bool P_HitWater (AActor *thing, sector_t *sec, fixed_t splashx = FIXED_MIN, fixed_t splashy = FIXED_MIN, fixed_t splashz=FIXED_MIN, bool checkabove = false, bool alert = true, bool force = false); +void P_CheckSplash(AActor *self, fixed_t distance); +void P_RailAttack (AActor *source, int damage, int offset_xy, fixed_t offset_z = 0, int color1 = 0, int color2 = 0, double maxdiff = 0, int flags = 0, const PClass *puff = NULL, angle_t angleoffset = 0, angle_t pitchoffset = 0, fixed_t distance = 8192*FRACUNIT, int duration = 0, double sparsity = 1.0, double drift = 1.0, const PClass *spawnclass = NULL, int SpiralOffset = 270); // [RH] Shoot a railgun + +enum // P_RailAttack / A_RailAttack / A_CustomRailgun / P_DrawRailTrail flags +{ + RAF_SILENT = 1, + RAF_NOPIERCE = 2, + RAF_EXPLICITANGLE = 4, + RAF_FULLBRIGHT = 8, + RAF_CENTERZ = 16, +}; + + +bool P_CheckMissileSpawn (AActor *missile, fixed_t maxdist); +void P_PlaySpawnSound(AActor *missile, AActor *spawner); + +// [RH] Position the chasecam +void P_AimCamera (AActor *t1, fixed_t &x, fixed_t &y, fixed_t &z, sector_t *&sec); + +// [RH] Means of death +enum +{ + RADF_HURTSOURCE = 1, + RADF_NOIMPACTDAMAGE = 2, + RADF_SOURCEISSPOT = 4, + RADF_NODAMAGE = 8, +}; +void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, + FName damageType, int flags, int fulldamagedistance=0); + +void P_DelSector_List(); +void P_DelSeclist(msecnode_t *); // phares 3/16/98 +msecnode_t* P_DelSecnode(msecnode_t *); +void P_CreateSecNodeList(AActor*,fixed_t,fixed_t); // phares 3/14/98 +int P_GetMoveFactor(const AActor *mo, int *frictionp); // phares 3/6/98 +int P_GetFriction(const AActor *mo, int *frictionfactor); +bool Check_Sides(AActor *, int, int); // phares + +// [RH] +const secplane_t * P_CheckSlopeWalk (AActor *actor, fixed_t &xmove, fixed_t &ymove); + +//---------------------------------------------------------------------------------- +// +// Added so that in the source there's a clear distinction between +// game engine and renderer specific calls. +// (For ZDoom itself this doesn't make any difference here but for GZDoom it does.) +// +//---------------------------------------------------------------------------------- +subsector_t *P_PointInSubsector (fixed_t x, fixed_t y); +inline sector_t *P_PointInSector(fixed_t x, fixed_t y) +{ + return P_PointInSubsector(x,y)->sector; +} + +// +// P_SETUP +// +extern BYTE* rejectmatrix; // for fast sight rejection +extern int* blockmaplump; // offsets in blockmap are from here + +extern int* blockmap; +extern int bmapwidth; +extern int bmapheight; // in mapblocks +extern fixed_t bmaporgx; +extern fixed_t bmaporgy; // origin of block map +extern FBlockNode** blocklinks; // for thing chains + + + +// +// P_INTER +// +void P_TouchSpecialThing (AActor *special, AActor *toucher); +int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags=0); +void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int duration, int period, FName type); +bool P_GiveBody (AActor *actor, int num, int max=0); +bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison); +void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound); + +enum EDmgFlags +{ + DMG_NO_ARMOR = 1, + DMG_INFLICTOR_IS_PUFF = 2, + DMG_THRUSTLESS = 4, + DMG_FORCED = 8, + DMG_NO_FACTOR = 16, + DMG_PLAYERATTACK = 32, + DMG_FOILINVUL = 64, + DMG_FOILBUDDHA = 128, + DMG_NO_PROTECT = 256, +}; + + +// ===== PO_MAN ===== + +typedef enum +{ + PODOOR_NONE, + PODOOR_SLIDE, + PODOOR_SWING, +} podoortype_t; + +bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide); +bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle, fixed_t dist, bool overRide); +bool EV_MovePolyTo (line_t *line, int polyNum, int speed, fixed_t x, fixed_t y, bool overRide); +bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, int delay, int distance, podoortype_t type); +bool EV_StopPoly (int polyNum); + + +// [RH] Data structure for P_SpawnMapThing() to keep track +// of polyobject-related things. +struct polyspawns_t +{ + polyspawns_t *next; + fixed_t x; + fixed_t y; + short angle; + short type; +}; + +extern int po_NumPolyobjs; +extern polyspawns_t *polyspawns; // [RH] list of polyobject things to spawn + + +void PO_Init (); +bool PO_Busy (int polyobj); +FPolyObj *PO_GetPolyobj(int polyNum); + +// +// P_SPEC +// +#include "p_spec.h" + +bool P_AlignFlat (int linenum, int side, int fc); + +#endif // __P_LOCAL__ diff --git a/src/p_map.cpp b/src/p_map.cpp index b76919a7de..69f8b6c5be 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -52,6 +52,7 @@ #include "p_conversation.h" #include "r_data/r_translate.h" #include "g_level.h" +#include "r_sky.h" CVAR(Bool, cl_bloodsplats, true, CVAR_ARCHIVE) CVAR(Int, sv_smartaim, 0, CVAR_ARCHIVE | CVAR_SERVERINFO) @@ -189,7 +190,7 @@ static bool PIT_FindFloorCeiling(line_t *ld, const FBoundingBox &box, FCheckPosi } else if (r >= (1 << 24)) { - P_LineOpening(open, tmf.thing, ld, sx = ld->v2->x, sy = ld->v2->y, tmf.thing->x, tmf.thing->y, flags); + P_LineOpening(open, tmf.thing, ld, sx = ld->v2->x, sy = ld->v2->y, tmf.thing->X(), tmf.thing->Y(), flags); } else { @@ -242,6 +243,7 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) tmf.floorz = tmf.dropoffz = sec->floorplane.ZatPoint(tmf.x, tmf.y); tmf.ceilingz = sec->ceilingplane.ZatPoint(tmf.x, tmf.y); tmf.floorpic = sec->GetTexture(sector_t::floor); + tmf.floorterrain = sec->GetTerrain(sector_t::floor); tmf.ceilingpic = sec->GetTexture(sector_t::ceiling); } else @@ -249,7 +251,6 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) sec = tmf.thing->Sector; } -#ifdef _3DFLOORS for (unsigned int i = 0; ie->XFloor.ffloors.Size(); i++) { F3DFloor* rover = sec->e->XFloor.ffloors[i]; @@ -265,6 +266,7 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) { tmf.dropoffz = tmf.floorz = ff_top; tmf.floorpic = *rover->top.texture; + tmf.floorterrain = rover->model->GetTerrain(rover->top.isceiling); } } if (ff_bottom <= tmf.ceilingz && ff_bottom > tmf.z + tmf.thing->height) @@ -273,7 +275,6 @@ void P_GetFloorCeilingZ(FCheckPosition &tmf, int flags) tmf.ceilingpic = *rover->bottom.texture; } } -#endif } //========================================================================== @@ -287,9 +288,9 @@ void P_FindFloorCeiling(AActor *actor, int flags) FCheckPosition tmf; tmf.thing = actor; - tmf.x = actor->x; - tmf.y = actor->y; - tmf.z = actor->z; + tmf.x = actor->X(); + tmf.y = actor->Y(); + tmf.z = actor->Z(); if (flags & FFCF_ONLYSPAWNPOS) { @@ -306,6 +307,7 @@ void P_FindFloorCeiling(AActor *actor, int flags) tmf.floorz = tmf.dropoffz = actor->floorz; tmf.ceilingz = actor->ceilingz; tmf.floorpic = actor->floorpic; + tmf.floorterrain = actor->floorterrain; tmf.ceilingpic = actor->ceilingpic; P_GetFloorCeilingZ(tmf, flags); } @@ -313,6 +315,7 @@ void P_FindFloorCeiling(AActor *actor, int flags) actor->dropoffz = tmf.dropoffz; actor->ceilingz = tmf.ceilingz; actor->floorpic = tmf.floorpic; + actor->floorterrain = tmf.floorterrain; actor->floorsector = tmf.floorsector; actor->ceilingpic = tmf.ceilingpic; actor->ceilingsector = tmf.ceilingsector; @@ -333,12 +336,13 @@ void P_FindFloorCeiling(AActor *actor, int flags) if (tmf.touchmidtex) tmf.dropoffz = tmf.floorz; - if (!(flags & FFCF_ONLYSPAWNPOS) || (tmf.abovemidtex && (tmf.floorz <= actor->z))) + if (!(flags & FFCF_ONLYSPAWNPOS) || (tmf.abovemidtex && (tmf.floorz <= actor->Z()))) { actor->floorz = tmf.floorz; actor->dropoffz = tmf.dropoffz; actor->ceilingz = tmf.ceilingz; actor->floorpic = tmf.floorpic; + actor->floorterrain = tmf.floorterrain; actor->floorsector = tmf.floorsector; actor->ceilingpic = tmf.ceilingpic; actor->ceilingsector = tmf.ceilingsector; @@ -350,6 +354,7 @@ void P_FindFloorCeiling(AActor *actor, int flags) if (actor->Sector != NULL) { actor->floorpic = actor->Sector->GetTexture(sector_t::floor); + actor->floorterrain = actor->Sector->GetTerrain(sector_t::floor); actor->ceilingpic = actor->Sector->GetTexture(sector_t::ceiling); } } @@ -372,7 +377,7 @@ void P_FindFloorCeiling(AActor *actor, int flags) // //========================================================================== -bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag) +bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefrag, bool modifyactor) { FCheckPosition tmf; sector_t *oldsec = thing->Sector; @@ -399,13 +404,13 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra line_t *ld; // P_LineOpening requires the thing's z to be the destination z in order to work. - fixed_t savedz = thing->z; - thing->z = z; + fixed_t savedz = thing->Z(); + thing->SetZ(z); while ((ld = it.Next())) { PIT_FindFloorCeiling(ld, box, tmf, 0); } - thing->z = savedz; + thing->SetZ(savedz); if (tmf.touchmidtex) tmf.dropoffz = tmf.floorz; @@ -422,7 +427,7 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra continue; fixed_t blockdist = th->radius + tmf.thing->radius; - if (abs(th->x - tmf.x) >= blockdist || abs(th->y - tmf.y) >= blockdist) + if (abs(th->X() - tmf.x) >= blockdist || abs(th->Y() - tmf.y) >= blockdist) continue; // [RH] Z-Check @@ -432,8 +437,8 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra { if (!(th->flags3 & thing->flags3 & MF3_DONTOVERLAP)) { - if (z > th->z + th->height || // overhead - z + thing->height < th->z) // underneath + if (z > th->Top() || // overhead + z + thing->height < th->Z()) // underneath continue; } } @@ -451,36 +456,40 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra return false; } - // the move is ok, so link the thing into its new position - thing->SetOrigin(x, y, z); - thing->floorz = tmf.floorz; - thing->ceilingz = tmf.ceilingz; - thing->floorsector = tmf.floorsector; - thing->floorpic = tmf.floorpic; - thing->ceilingsector = tmf.ceilingsector; - thing->ceilingpic = tmf.ceilingpic; - thing->dropoffz = tmf.dropoffz; // killough 11/98 - thing->BlockingLine = NULL; - - if (thing->flags2 & MF2_FLOORCLIP) + if (modifyactor) { - thing->AdjustFloorClip(); - } + // the move is ok, so link the thing into its new position + thing->SetOrigin(x, y, z, false); + thing->floorz = tmf.floorz; + thing->ceilingz = tmf.ceilingz; + thing->floorsector = tmf.floorsector; + thing->floorpic = tmf.floorpic; + thing->floorterrain = tmf.floorterrain; + thing->ceilingsector = tmf.ceilingsector; + thing->ceilingpic = tmf.ceilingpic; + thing->dropoffz = tmf.dropoffz; // killough 11/98 + thing->BlockingLine = NULL; - if (thing == players[consoleplayer].camera) - { - R_ResetViewInterpolation(); - } + if (thing->flags2 & MF2_FLOORCLIP) + { + thing->AdjustFloorClip(); + } - thing->PrevX = x; - thing->PrevY = y; - thing->PrevZ = z; + if (thing == players[consoleplayer].camera) + { + R_ResetViewInterpolation(); + } - // If this teleport was caused by a move, P_TryMove() will handle the - // sector transition messages better than we can here. - if (!(thing->flags6 & MF6_INTRYMOVE)) - { - thing->CheckSectorTransition(oldsec); + thing->PrevX = x; + thing->PrevY = y; + thing->PrevZ = z; + + // If this teleport was caused by a move, P_TryMove() will handle the + // sector transition messages better than we can here. + if (!(thing->flags6 & MF6_INTRYMOVE)) + { + thing->CheckSectorTransition(oldsec); + } } return true; @@ -498,7 +507,7 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra void P_PlayerStartStomp(AActor *actor) { AActor *th; - FBlockThingsIterator it(FBoundingBox(actor->x, actor->y, actor->radius)); + FBlockThingsIterator it(FBoundingBox(actor->X(), actor->Y(), actor->radius)); while ((th = it.Next())) { @@ -516,9 +525,9 @@ void P_PlayerStartStomp(AActor *actor) if (th->player == NULL && !(th->flags3 & MF3_ISMONSTER)) continue; - if (actor->z > th->z + th->height) + if (actor->Z() > th->Top()) continue; // overhead - if (actor->z + actor->height < th->z) + if (actor->Top() < th->Z()) continue; // underneath P_DamageMobj(th, actor, actor, TELEFRAG_DAMAGE, NAME_Telefrag); @@ -531,16 +540,18 @@ void P_PlayerStartStomp(AActor *actor) // //========================================================================== -inline fixed_t secfriction(const sector_t *sec) +inline fixed_t secfriction(const sector_t *sec, int plane = sector_t::floor) { - fixed_t friction = Terrains[TerrainTypes[sec->GetTexture(sector_t::floor)]].Friction; - return friction != 0 ? friction : sec->friction; + if (sec->Flags & SECF_FRICTION) return sec->friction; + fixed_t friction = Terrains[sec->GetTerrain(plane)].Friction; + return friction != 0 ? friction : ORIG_FRICTION; } -inline fixed_t secmovefac(const sector_t *sec) +inline fixed_t secmovefac(const sector_t *sec, int plane = sector_t::floor) { - fixed_t movefactor = Terrains[TerrainTypes[sec->GetTexture(sector_t::floor)]].MoveFactor; - return movefactor != 0 ? movefactor : sec->movefactor; + if (sec->Flags & SECF_FRICTION) return sec->movefactor; + fixed_t movefactor = Terrains[sec->GetTerrain(plane)].MoveFactor; + return movefactor != 0 ? movefactor : ORIG_FRICTION_FACTOR; } //========================================================================== @@ -574,6 +585,25 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) { friction = secfriction(mo->Sector); movefactor = secmovefac(mo->Sector) >> 1; + + // Check 3D floors -- might be the source of the waterlevel + for (unsigned i = 0; i < mo->Sector->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *rover = mo->Sector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (!(rover->flags & FF_SWIMMABLE)) continue; + + if (mo->z > rover->top.plane->ZatPoint(mo) || + mo->z < rover->bottom.plane->ZatPoint(mo)) + continue; + + newfriction = secfriction(rover->model, rover->top.isceiling); + if (newfriction < friction || friction == ORIG_FRICTION) + { + friction = newfriction; + movefactor = secmovefac(rover->model, rover->top.isceiling) >> 1; + } + } } else if (var_friction && !(mo->flags & (MF_NOCLIP | MF_NOGRAVITY))) { // When the object is straddling sectors with the same @@ -584,35 +614,45 @@ int P_GetFriction(const AActor *mo, int *frictionfactor) { sec = m->m_sector; -#ifdef _3DFLOORS // 3D floors must be checked, too for (unsigned 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_SOLID)) continue; - // Player must be on top of the floor to be affected... - if (mo->z != rover->top.plane->ZatPoint(mo->x, mo->y)) continue; - newfriction = secfriction(rover->model); + if (rover->flags & FF_SOLID) + { + // Must be standing on a solid floor + if (mo->z != rover->top.plane->ZatPoint(mo)) continue; + } + else if (rover->flags & FF_SWIMMABLE) + { + // Or on or inside a swimmable floor (e.g. in shallow water) + if (mo->z > rover->top.plane->ZatPoint(mo) || + (mo->z + mo->height) < rover->bottom.plane->ZatPoint(mo)) + continue; + } + else + continue; + + newfriction = secfriction(rover->model, rover->top.isceiling); if (newfriction < friction || friction == ORIG_FRICTION) { friction = newfriction; - movefactor = secmovefac(rover->model); + movefactor = secmovefac(rover->model, rover->top.isceiling); } } -#endif - if (!(sec->special & FRICTION_MASK) && - Terrains[TerrainTypes[sec->GetTexture(sector_t::floor)]].Friction == 0) + if (!(sec->Flags & SECF_FRICTION) && + Terrains[sec->GetTerrain(sector_t::floor)].Friction == 0) { continue; } newfriction = secfriction(sec); if ((newfriction < friction || friction == ORIG_FRICTION) && - (mo->z <= sec->floorplane.ZatPoint(mo->x, mo->y) || + (mo->z <= sec->floorplane.ZatPoint(mo) || (sec->GetHeightSec() != NULL && - mo->z <= sec->heightsec->floorplane.ZatPoint(mo->x, mo->y)))) + mo->z <= sec->heightsec->floorplane.ZatPoint(mo)))) { friction = newfriction; movefactor = secmovefac(sec); @@ -756,14 +796,9 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) !(tm.thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) { secplane_t frontplane, backplane; -#ifdef _3DFLOORS // Check 3D floors as well frontplane = P_FindFloorPlane(ld->frontsector, tm.thing->x, tm.thing->y, tm.thing->floorz); backplane = P_FindFloorPlane(ld->backsector, tm.thing->x, tm.thing->y, tm.thing->floorz); -#else - frontplane = ld->frontsector->floorplane; - backplane = ld->backsector->floorplane; -#endif if (frontplane.c < STEEPSLOPE || backplane.c < STEEPSLOPE) { const msecnode_t *node = tm.thing->touching_sectorlist; @@ -867,6 +902,7 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) tm.floorz = open.bottom; tm.floorsector = open.bottomsec; tm.floorpic = open.floorpic; + tm.floorterrain = open.floorterrain; tm.touchmidtex = open.touchmidtex; tm.abovemidtex = open.abovemidtex; tm.thing->BlockingLine = ld; @@ -916,10 +952,7 @@ static bool CanAttackHurt(AActor *victim, AActor *shooter) // to harm / be harmed by anything. if (!victim->player && !shooter->player) { - int infight; - if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight = 1; - else if (level.flags2 & LEVEL2_NOINFIGHTING) infight = -1; - else infight = infighting; + int infight = G_SkillProperty(SKILLP_Infight); if (infight < 0) { @@ -991,6 +1024,10 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) bool solid; int damage; + // don't clip against self + if (thing == tm.thing) + return true; + if (!((thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)) || thing->flags6 & MF6_TOUCHY)) return true; // can't hit thing @@ -998,10 +1035,6 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) if (abs(thing->x - tm.x) >= blockdist || abs(thing->y - tm.y) >= blockdist) return true; - // don't clip against self - if (thing == tm.thing) - return true; - if ((thing->flags2 | tm.thing->flags2) & MF2_THRUACTORS) return true; @@ -1042,12 +1075,12 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { unblocking = true; } - else if (abs(thing->x - tm.thing->x) < (thing->radius+tm.thing->radius)/2 && - abs(thing->y - tm.thing->y) < (thing->radius+tm.thing->radius)/2) + else if (abs(thing->x - tm.thing->x) < (thing->radius+tm.thing->radius) && + abs(thing->y - tm.thing->y) < (thing->radius+tm.thing->radius)) { - fixed_t newdist = P_AproxDistance(thing->x - tm.x, thing->y - tm.y); - fixed_t olddist = P_AproxDistance(thing->x - tm.thing->x, thing->y - tm.thing->y); + fixed_t newdist = thing->AproxDistance(tm.x, tm.y, tm.thing); + fixed_t olddist = thing->AproxDistance(tm.thing); if (newdist > olddist) { @@ -1415,6 +1448,7 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo tm.floorz = tm.dropoffz = newsec->floorplane.ZatPoint(x, y); tm.ceilingz = newsec->ceilingplane.ZatPoint(x, y); tm.floorpic = newsec->GetTexture(sector_t::floor); + tm.floorterrain = newsec->GetTerrain(sector_t::floor); tm.floorsector = newsec; tm.ceilingpic = newsec->GetTexture(sector_t::ceiling); tm.ceilingsector = newsec; @@ -1424,7 +1458,6 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo //Added by MC: Fill the tmsector. tm.sector = newsec; -#ifdef _3DFLOORS //Check 3D floors if (!thing->IsNoClip2() && newsec->e->XFloor.ffloors.Size()) { @@ -1448,6 +1481,7 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo { tm.floorz = tm.dropoffz = ff_top; tm.floorpic = *rover->top.texture; + tm.floorterrain = rover->model->GetTerrain(rover->top.isceiling); } if (ff_bottom < tm.ceilingz && abs(delta1) >= abs(delta2)) { @@ -1456,7 +1490,6 @@ bool P_CheckPosition(AActor *thing, fixed_t x, fixed_t y, FCheckPosition &tm, bo } } } -#endif validcount++; spechit.Clear(); @@ -1725,7 +1758,7 @@ void P_FakeZMovement(AActor *mo) { // float down towards target if too close if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT)) { - fixed_t dist = P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y); + fixed_t dist = mo->AproxDistance(mo->target); fixed_t delta = (mo->target->z + (mo->height >> 1)) - mo->z; if (delta < 0 && dist < -(delta * 3)) mo->z -= mo->FloatSpeed; @@ -1765,30 +1798,28 @@ static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, bool windo if (windowcheck && !(ib_compatflags & BCOMPATF_NOWINDOWCHECK) && line->backsector != NULL) { // Make sure this line actually blocks us and is not a window // or similar construct we are standing inside of. - fixed_t fzt = line->frontsector->ceilingplane.ZatPoint(mobj->x, mobj->y); - fixed_t fzb = line->frontsector->floorplane.ZatPoint(mobj->x, mobj->y); - fixed_t bzt = line->backsector->ceilingplane.ZatPoint(mobj->x, mobj->y); - fixed_t bzb = line->backsector->floorplane.ZatPoint(mobj->x, mobj->y); + fixed_t fzt = line->frontsector->ceilingplane.ZatPoint(mobj); + fixed_t fzb = line->frontsector->floorplane.ZatPoint(mobj); + fixed_t bzt = line->backsector->ceilingplane.ZatPoint(mobj); + fixed_t bzb = line->backsector->floorplane.ZatPoint(mobj); if (fzt >= mobj->z + mobj->height && bzt >= mobj->z + mobj->height && fzb <= mobj->z && bzb <= mobj->z) { // we must also check if some 3D floor in the backsector may be blocking -#ifdef _3DFLOORS for (unsigned int i = 0; ibacksector->e->XFloor.ffloors.Size(); i++) { F3DFloor* rover = line->backsector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - fixed_t ff_bottom = rover->bottom.plane->ZatPoint(mobj->x, mobj->y); - fixed_t ff_top = rover->top.plane->ZatPoint(mobj->x, mobj->y); + fixed_t ff_bottom = rover->bottom.plane->ZatPoint(mobj); + fixed_t ff_top = rover->top.plane->ZatPoint(mobj); if (ff_bottom < mobj->z + mobj->height && ff_top > mobj->z) { goto isblocking; } } -#endif return; } } @@ -2052,8 +2083,8 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, { fixed_t eyez = oldz + viewheight; - oldAboveFakeFloor = eyez > oldsec->heightsec->floorplane.ZatPoint(thing->x, thing->y); - oldAboveFakeCeiling = eyez > oldsec->heightsec->ceilingplane.ZatPoint(thing->x, thing->y); + oldAboveFakeFloor = eyez > oldsec->heightsec->floorplane.ZatPoint(thing); + oldAboveFakeCeiling = eyez > oldsec->heightsec->ceilingplane.ZatPoint(thing); } // Borrowed from MBF: @@ -2074,6 +2105,7 @@ bool P_TryMove(AActor *thing, fixed_t x, fixed_t y, thing->ceilingz = tm.ceilingz; thing->dropoffz = tm.dropoffz; // killough 11/98: keep track of dropoffs thing->floorpic = tm.floorpic; + thing->floorterrain = tm.floorterrain; thing->floorsector = tm.floorsector; thing->ceilingpic = tm.ceilingpic; thing->ceilingsector = tm.ceilingsector; @@ -2711,15 +2743,14 @@ const secplane_t * P_CheckSlopeWalk(AActor *actor, fixed_t &xmove, fixed_t &ymov } const secplane_t *plane = &actor->floorsector->floorplane; - fixed_t planezhere = plane->ZatPoint(actor->x, actor->y); + fixed_t planezhere = plane->ZatPoint(actor); -#ifdef _3DFLOORS for (unsigned int i = 0; ifloorsector->e->XFloor.ffloors.Size(); i++) { F3DFloor * rover = actor->floorsector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - fixed_t thisplanez = rover->top.plane->ZatPoint(actor->x, actor->y); + fixed_t thisplanez = rover->top.plane->ZatPoint(actor); if (thisplanez>planezhere && thisplanez <= actor->z + actor->MaxStepHeight) { @@ -2737,7 +2768,7 @@ const secplane_t * P_CheckSlopeWalk(AActor *actor, fixed_t &xmove, fixed_t &ymov F3DFloor * rover = actor->Sector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; - fixed_t thisplanez = rover->top.plane->ZatPoint(actor->x, actor->y); + fixed_t thisplanez = rover->top.plane->ZatPoint(actor); if (thisplanez>planezhere && thisplanez <= actor->z + actor->MaxStepHeight) { @@ -2748,7 +2779,6 @@ const secplane_t * P_CheckSlopeWalk(AActor *actor, fixed_t &xmove, fixed_t &ymov } } } -#endif if (actor->floorsector != actor->Sector) { @@ -3040,7 +3070,7 @@ bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) if (!ontop) { fixed_t speed; - angle_t angle = R_PointToAngle2(BlockingMobj->x,BlockingMobj->y, mo->x, mo->y) + ANGLE_1*((pr_bounce() % 16) - 8); + angle_t angle = BlockingMobj->AngleTo(mo) + ANGLE_1*((pr_bounce() % 16) - 8); speed = P_AproxDistance(mo->velx, mo->vely); speed = FixedMul(speed, mo->wallbouncefactor); // [GZ] was 0.75, using wallbouncefactor seems more consistent mo->angle = angle; @@ -3126,7 +3156,6 @@ struct aim_t AActor * thing_friend, *thing_other; angle_t pitch_friend, pitch_other; int flags; -#ifdef _3DFLOORS sector_t * lastsector; secplane_t * lastfloorplane; secplane_t * lastceilingplane; @@ -3134,13 +3163,11 @@ struct aim_t bool crossedffloors; bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in); -#endif void AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy, AActor *target = NULL); }; -#ifdef _3DFLOORS //============================================================================ // // AimTraverse3DFloors @@ -3242,7 +3269,6 @@ bool aim_t::AimTraverse3DFloors(const divline_t &trace, intercept_t * in) lastfloorplane = nextbottomplane; return true; } -#endif //============================================================================ // @@ -3295,9 +3321,7 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en if (toppitch >= bottompitch) return; // stop -#ifdef _3DFLOORS if (!AimTraverse3DFloors(it.Trace(), in)) return; -#endif continue; // shot continues } @@ -3336,7 +3360,6 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en continue; } -#ifdef _3DFLOORS // we must do one last check whether the trace has crossed a 3D floor if (lastsector == th->Sector && th->Sector->e->XFloor.ffloors.Size()) { @@ -3361,7 +3384,6 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en } } } -#endif // check angles to see if the thing can be aimed at @@ -3375,7 +3397,6 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en if (thingbottompitch < toppitch) continue; // shot under the thing -#ifdef _3DFLOORS if (crossedffloors) { // if 3D floors were in the way do an extra visibility check for safety @@ -3394,7 +3415,6 @@ void aim_t::AimTraverse(fixed_t startx, fixed_t starty, fixed_t endx, fixed_t en else return; } } -#endif // this thing can be hit! if (thingtoppitch < toppitch) @@ -3531,7 +3551,6 @@ fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, AActor **pL // Information for tracking crossed 3D floors aim.aimpitch = t1->pitch; -#ifdef _3DFLOORS aim.crossedffloors = t1->Sector->e->XFloor.ffloors.Size() != 0; aim.lastsector = t1->Sector; aim.lastfloorplane = aim.lastceilingplane = NULL; @@ -3547,7 +3566,6 @@ fixed_t P_AimLineAttack(AActor *t1, angle_t angle, fixed_t distance, AActor **pL bottomz = rover->top.plane->ZatPoint(t1->x, t1->y); if (bottomz <= t1->z) aim.lastfloorplane = rover->top.plane; } -#endif aim.AimTraverse(t1->x, t1->y, x2, y2, target); @@ -3908,7 +3926,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance, //========================================================================== AActor *P_LinePickActor(AActor *t1, angle_t angle, fixed_t distance, int pitch, - DWORD actorMask, DWORD wallMask) + ActorFlags actorMask, DWORD wallMask) { fixed_t vx, vy, vz, shootz; @@ -4070,7 +4088,7 @@ void P_TraceBleed(int damage, AActor *target, AActor *missile) pitch = 0; } P_TraceBleed(damage, target->x, target->y, target->z + target->height / 2, - target, R_PointToAngle2(missile->x, missile->y, target->x, target->y), + target, missile->AngleTo(target), pitch); } @@ -4146,12 +4164,12 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) // // //========================================================================== -void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, int color1, int color2, float maxdiff, int railflags, const PClass *puffclass, angle_t angleoffset, angle_t pitchoffset, fixed_t distance, int duration, float sparsity, float drift, const PClass *spawnclass, int SpiralOffset) +void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, int color1, int color2, double maxdiff, int railflags, const PClass *puffclass, angle_t angleoffset, angle_t pitchoffset, fixed_t distance, int duration, double sparsity, double drift, const PClass *spawnclass, int SpiralOffset) { fixed_t vx, vy, vz; angle_t angle, pitch; fixed_t x1, y1; - FVector3 start, end; + TVector3 start, end; FTraceResults trace; fixed_t shootz; @@ -4271,13 +4289,34 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i // Spawn a decal or puff at the point where the trace ended. if (trace.HitType == TRACE_HitWall) { - SpawnShootDecal(source, trace); + AActor* puff = NULL; + if (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) { - P_SpawnPuff(source, puffclass, trace.X, trace.Y, trace.Z, (source->angle + angleoffset) - ANG90, 1, 0); + puff = P_SpawnPuff(source, puffclass, trace.X, trace.Y, trace.Z, (source->angle + angleoffset) - ANG90, 1, 0); + if (puff && (trace.Line != NULL) && (trace.Line->special == Line_Horizon) && !(puff->flags3 & MF3_SKYEXPLODE)) + puff->Destroy(); } + if (puff != NULL && puffDefaults->flags7 & MF7_FORCEDECAL && puff->DecalGenerator) + SpawnShootDecal(puff, trace); + else + SpawnShootDecal(source, trace); } + if (trace.HitType == TRACE_HitFloor || trace.HitType == TRACE_HitCeiling) + { + AActor* puff = NULL; + if (puffclass != NULL && puffDefaults->flags3 & MF3_ALWAYSPUFF) + { + puff = P_SpawnPuff(source, puffclass, trace.X, trace.Y, trace.Z, (source->angle + angleoffset) - ANG90, 1, 0); + if (puff && !(puff->flags3 & MF3_SKYEXPLODE) && + (((trace.HitType == TRACE_HitFloor) && (puff->floorpic == skyflatnum)) || + ((trace.HitType == TRACE_HitCeiling) && (puff->ceilingpic == skyflatnum)))) + { + puff->Destroy(); + } + } + } if (thepuff != NULL) { if (trace.HitType == TRACE_HitFloor && @@ -4295,9 +4334,9 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i } // Draw the slug's trail. - end.X = FIXED2FLOAT(trace.X); - end.Y = FIXED2FLOAT(trace.Y); - end.Z = FIXED2FLOAT(trace.Z); + end.X = FIXED2DBL(trace.X); + end.Y = FIXED2DBL(trace.Y); + end.Z = FIXED2DBL(trace.Z); P_DrawRailTrail(source, start, end, color1, color2, maxdiff, railflags, spawnclass, source->angle + angleoffset, duration, sparsity, drift, SpiralOffset); } @@ -4834,7 +4873,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo { velz *= 0.8f; } - angle_t ang = R_PointToAngle2(bombspot->x, bombspot->y, thing->x, thing->y) >> ANGLETOFINESHIFT; + angle_t ang = bombspot->AngleTo(thing) >> ANGLETOFINESHIFT; thing->velx += fixed_t(finecosine[ang] * thrust); thing->vely += fixed_t(finesine[ang] * thrust); if (!(flags & RADF_NODAMAGE)) @@ -4943,6 +4982,7 @@ bool P_AdjustFloorCeil(AActor *thing, FChangePosition *cpos) thing->ceilingz = tm.ceilingz; thing->dropoffz = tm.dropoffz; // killough 11/98: remember dropoffs thing->floorpic = tm.floorpic; + thing->floorterrain = tm.floorterrain; thing->floorsector = tm.floorsector; thing->ceilingpic = tm.ceilingpic; thing->ceilingsector = tm.ceilingsector; @@ -5423,7 +5463,6 @@ bool P_ChangeSector(sector_t *sector, int crunch, int amt, int floorOrCeil, bool cpos.movemidtex = false; cpos.sector = sector; -#ifdef _3DFLOORS // Also process all sectors that have 3D floors transferred from the // changed sector. if (sector->e->XFloor.attached.Size()) @@ -5471,8 +5510,6 @@ bool P_ChangeSector(sector_t *sector, int crunch, int amt, int floorOrCeil, bool } } P_Recalculate3DFloors(sector); // Must recalculate the 3d floor and light lists -#endif - // [RH] Use different functions for the four different types of sector // movement. diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index 3a4a4c6a04..1c7a205a19 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -194,6 +194,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.bottom = ff; open.bottomsec = front; open.floorpic = front->GetTexture(sector_t::floor); + open.floorterrain = front->GetTerrain(sector_t::floor); open.lowfloor = bf; } else @@ -201,6 +202,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.bottom = bf; open.bottomsec = back; open.floorpic = back->GetTexture(sector_t::floor); + open.floorterrain = back->GetTerrain(sector_t::floor); open.lowfloor = ff; } } @@ -211,6 +213,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, open.top = FIXED_MAX; open.bottomsec = NULL; open.floorpic.SetInvalid(); + open.floorterrain = -1; open.bottom = FIXED_MIN; open.lowfloor = FIXED_MAX; } @@ -582,7 +585,7 @@ sector_t *AActor::LinkToWorldForMapThing () return ssec->sector; } -void AActor::SetOrigin (fixed_t ix, fixed_t iy, fixed_t iz) +void AActor::SetOrigin (fixed_t ix, fixed_t iy, fixed_t iz, bool moving) { UnlinkFromWorld (); x = ix; @@ -982,7 +985,7 @@ void FPathTraverse::AddLineIntercepts(int bx, int by) P_MakeDivline (ld, &dl); frac = P_InterceptVector (&trace, &dl); - if (frac < 0) continue; // behind source + if (frac < 0 || frac > FRACUNIT) continue; // behind source or beyond end point intercept_t newintercept; @@ -1053,13 +1056,13 @@ void FPathTraverse::AddThingIntercepts (int bx, int by, FBlockThingsIterator &it break; } // Check if this side is facing the trace origin - if (P_PointOnDivlineSide (trace.x, trace.y, &line) == 0) + if (P_PointOnDivlineSidePrecise (trace.x, trace.y, &line) == 0) { numfronts++; // If it is, see if the trace crosses it - if (P_PointOnDivlineSide (line.x, line.y, &trace) != - P_PointOnDivlineSide (line.x + line.dx, line.y + line.dy, &trace)) + if (P_PointOnDivlineSidePrecise (line.x, line.y, &trace) != + P_PointOnDivlineSidePrecise (line.x + line.dx, line.y + line.dy, &trace)) { // It's a hit fixed_t frac = P_InterceptVector (&trace, &line); @@ -1167,7 +1170,7 @@ intercept_t *FPathTraverse::Next() } } - if (dist > maxfrac || in == NULL) return NULL; // checked everything in range + if (dist > FRACUNIT || in == NULL) return NULL; // checked everything in range in->done = true; return in; } @@ -1385,7 +1388,6 @@ FPathTraverse::FPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, in break; } } - maxfrac = FRACUNIT; } FPathTraverse::~FPathTraverse() @@ -1523,3 +1525,92 @@ static AActor *RoughBlockCheck (AActor *mo, int index, void *param) } return NULL; } + +//=========================================================================== +// +// P_VanillaPointOnLineSide +// P_PointOnLineSide() from the initial Doom source code release +// +//=========================================================================== + +int P_VanillaPointOnLineSide(fixed_t x, fixed_t y, const line_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->v1->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->v1->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->v1->x); + dy = (y - line->v1->y); + + left = FixedMul ( line->dy>>FRACBITS , dx ); + right = FixedMul ( dy , line->dx>>FRACBITS ); + + if (right < left) + return 0; // front side + return 1; // back side +} + +//=========================================================================== +// +// P_VanillaPointOnDivlineSide +// P_PointOnDivlineSide() from the initial Doom source code release +// +//=========================================================================== + +int P_VanillaPointOnDivlineSide(fixed_t x, fixed_t y, const divline_t* line) +{ + fixed_t dx; + fixed_t dy; + fixed_t left; + fixed_t right; + + if (!line->dx) + { + if (x <= line->x) + return line->dy > 0; + + return line->dy < 0; + } + if (!line->dy) + { + if (y <= line->y) + return line->dx < 0; + + return line->dx > 0; + } + + dx = (x - line->x); + dy = (y - line->y); + + // try to quickly decide by looking at sign bits + if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) + { + if ( (line->dy ^ dx) & 0x80000000 ) + return 1; // (left is negative) + return 0; + } + + left = FixedMul ( line->dy>>8, dx>>8 ); + right = FixedMul ( dy>>8 , line->dx>>8 ); + + if (right < left) + return 0; // front side + return 1; // back side +} + diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index b0fc0d08ed..1c5105235c 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -191,6 +191,10 @@ void AActor::Serialize (FArchive &arc) << tics << state << Damage; + if (SaveVersion >= 4530) + { + P_SerializeTerrain(arc, floorterrain); + } if (SaveVersion >= 3227) { arc << projectileKickback; @@ -1280,7 +1284,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (line != NULL && cl_missiledecals) { - int side = P_PointOnLineSide (mo->x, mo->y, line); + int side = P_PointOnLineSidePrecise (mo->x, mo->y, line); if (line->sidedef[side] == NULL) side ^= 1; if (line->sidedef[side] != NULL) @@ -1316,7 +1320,6 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) z = mo->z; F3DFloor * ffloor=NULL; -#ifdef _3DFLOORS if (line->sidedef[side^1] != NULL) { sector_t * backsector = line->sidedef[side^1]->sector; @@ -1336,7 +1339,6 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) } } } -#endif DImpactDecal::StaticCreate (base->GetDecal (), x, y, z, line->sidedef[side], ffloor); @@ -1554,7 +1556,7 @@ int P_FaceMobj (AActor *source, AActor *target, angle_t *delta) angle_t angle2; angle1 = source->angle; - angle2 = R_PointToAngle2 (source->x, source->y, target->x, target->y); + angle2 = source->AngleTo(target); if (angle2 > angle1) { diff = angle2 - angle1; @@ -1667,8 +1669,7 @@ bool P_SeekerMissile (AActor *actor, angle_t thresh, angle_t turnMax, bool preci if (actor->z + actor->height < target->z || target->z + target->height < actor->z) { // Need to seek vertically - dist = P_AproxDistance (target->x - actor->x, target->y - actor->y); - dist = dist / speed; + dist = actor->AproxDistance (target) / speed; if (dist < 1) { dist = 1; @@ -2018,7 +2019,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) // Don't change the angle if there's THRUREFLECT on the monster. if (!(BlockingMobj->flags7 & MF7_THRUREFLECT)) { - angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); + angle = BlockingMobj->AngleTo(mo); bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle)); // Change angle for deflection/reflection @@ -2076,7 +2077,7 @@ explode: if (tm.ceilingline && tm.ceilingline->backsector && tm.ceilingline->backsector->GetTexture(sector_t::ceiling) == skyflatnum && - mo->z >= tm.ceilingline->backsector->ceilingplane.ZatPoint (mo->x, mo->y)) + mo->z >= tm.ceilingline->backsector->ceilingplane.ZatPoint(mo)) { // Hack to prevent missiles exploding against the sky. // Does not handle sky floors. @@ -2160,11 +2161,10 @@ explode: { // Don't stop sliding if halfway off a step with some velocity if (mo->velx > FRACUNIT/4 || mo->velx < -FRACUNIT/4 || mo->vely > FRACUNIT/4 || mo->vely < -FRACUNIT/4) { - if (mo->floorz > mo->Sector->floorplane.ZatPoint (mo->x, mo->y)) + if (mo->floorz > mo->Sector->floorplane.ZatPoint(mo)) { if (mo->dropoffz != mo->floorz) // 3DMidtex or other special cases that must be excluded { -#ifdef _3DFLOORS unsigned i; for(i=0;iSector->e->XFloor.ffloors.Size();i++) { @@ -2175,7 +2175,6 @@ explode: if (rover->flags&FF_SOLID && rover->top.plane->ZatPoint(mo->x,mo->y)==mo->floorz) break; } if (i==mo->Sector->e->XFloor.ffloors.Size()) -#endif return oldfloorz; } } @@ -2380,7 +2379,7 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) { // float down towards target if too close if (!(mo->flags & (MF_SKULLFLY | MF_INFLOAT))) { - dist = P_AproxDistance (mo->x - mo->target->x, mo->y - mo->target->y); + dist = mo->AproxDistance (mo->target); delta = (mo->target->z + (mo->height>>1)) - mo->z; if (delta < 0 && dist < -(delta*3)) mo->z -= mo->FloatSpeed; @@ -2408,7 +2407,7 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) { // Hit the floor if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) && mo->Sector->SecActTarget != NULL && - mo->Sector->floorplane.ZatPoint (mo->x, mo->y) == mo->floorz) + mo->Sector->floorplane.ZatPoint(mo) == mo->floorz) { // [RH] Let the sector do something to the actor mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitFloor); } @@ -2510,7 +2509,7 @@ void P_ZMovement (AActor *mo, fixed_t oldfloorz) { // hit the ceiling if ((!mo->player || !(mo->player->cheats & CF_PREDICTING)) && mo->Sector->SecActTarget != NULL && - mo->Sector->ceilingplane.ZatPoint (mo->x, mo->y) == mo->ceilingz) + mo->Sector->ceilingplane.ZatPoint(mo) == mo->ceilingz) { // [RH] Let the sector do something to the actor mo->Sector->SecActTarget->TriggerAction (mo, SECSPAC_HitCeiling); } @@ -2565,7 +2564,7 @@ void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheigh if (sec->heightsec != NULL && sec->SecActTarget != NULL) { sector_t *hs = sec->heightsec; - fixed_t waterz = hs->floorplane.ZatPoint (mo->x, mo->y); + fixed_t waterz = hs->floorplane.ZatPoint(mo); fixed_t newz; fixed_t viewheight; @@ -2600,7 +2599,7 @@ void P_CheckFakeFloorTriggers (AActor *mo, fixed_t oldz, bool oldz_has_viewheigh if (!(hs->MoreFlags & SECF_FAKEFLOORONLY)) { - waterz = hs->ceilingplane.ZatPoint (mo->x, mo->y); + waterz = hs->ceilingplane.ZatPoint(mo); if (oldz <= waterz && newz > waterz) { // View went above fake ceiling sec->SecActTarget->TriggerAction (mo, SECSPAC_EyesAboveC); @@ -3089,8 +3088,7 @@ bool AActor::IsOkayToAttack (AActor *link) // to only allow the check to succeed if the enemy was in a ~84� FOV of the player if (flags3 & MF3_SCREENSEEKER) { - angle_t angle = R_PointToAngle2(Friend->x, - Friend->y, link->x, link->y) - Friend->angle; + angle_t angle = Friend->AngleTo(link) - Friend->angle; angle >>= 24; if (angle>226 || angle<30) { @@ -3392,7 +3390,7 @@ void AActor::Tick () if (health > 0 && !players[i].Bot->enemy && player ? !IsTeammate (players[i].mo) : true - && P_AproxDistance (players[i].mo->x-x, players[i].mo->y-y) < MAX_MONSTER_TARGET_DIST + && AproxDistance (players[i].mo) < MAX_MONSTER_TARGET_DIST && P_CheckSight (players[i].mo, this, SF_SEEPASTBLOCKEVERYTHING)) { //Probably a monster, so go kill it. players[i].Bot->enemy = this; @@ -3456,7 +3454,7 @@ void AActor::Tick () if (player != NULL) { - int scrolltype = sec->special & 0xff; + int scrolltype = sec->special; if (scrolltype >= Scroll_North_Slow && scrolltype <= Scroll_SouthWest_Fast) @@ -3567,12 +3565,8 @@ void AActor::Tick () { secplane_t floorplane; -#ifdef _3DFLOORS // Check 3D floors as well floorplane = P_FindFloorPlane(floorsector, x, y, floorz); -#else - floorplane = floorsector->floorplane; -#endif if (floorplane.c < STEEPSLOPE && floorplane.ZatPoint (x, y) <= floorz) @@ -3889,7 +3883,6 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) reset = true; } } -#ifdef _3DFLOORS else { // Check 3D floors as well! @@ -3923,7 +3916,6 @@ bool AActor::UpdateWaterLevel (fixed_t oldz, bool dosplash) break; } } -#endif } // some additional checks to make deep sectors like Boom's splash without setting @@ -4045,6 +4037,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t { actor->floorsector = actor->Sector; actor->floorpic = actor->floorsector->GetTexture(sector_t::floor); + actor->floorterrain = actor->floorsector->GetTerrain(sector_t::floor); actor->ceilingsector = actor->Sector; actor->ceilingpic = actor->ceilingsector->GetTexture(sector_t::ceiling); } @@ -4056,6 +4049,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t else { actor->floorpic = actor->Sector->GetTexture(sector_t::floor); + actor->floorterrain = actor->Sector->GetTerrain(sector_t::floor); actor->floorsector = actor->Sector; actor->ceilingpic = actor->Sector->GetTexture(sector_t::ceiling); actor->ceilingsector = actor->Sector; @@ -4349,7 +4343,7 @@ void AActor::AdjustFloorClip () sector_t *hsec = m->m_sector->GetHeightSec(); if (hsec == NULL && m->m_sector->floorplane.ZatPoint (x, y) == z) { - fixed_t clip = Terrains[TerrainTypes[m->m_sector->GetTexture(sector_t::floor)]].FootClip; + fixed_t clip = Terrains[m->m_sector->GetTerrain(sector_t::floor)].FootClip; if (clip < shallowestclip) { shallowestclip = clip; @@ -4434,7 +4428,7 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) ( gameaction != ga_worlddone ) && ( p->mo != NULL ) && ( !(p->mo->Sector->Flags & SECF_NORESPAWN) ) && - ( (p->mo->Sector->special & 255) != Damage_InstantDeath )) + ( p->mo->Sector->damageamount < TELEFRAG_DAMAGE )) // this really should be a bit smarter... { spawn_x = p->mo->x; spawn_y = p->mo->y; @@ -4684,10 +4678,10 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) } // copy args to mapthing so that we have them in one place for the rest of this function - if (mentry->ArgsDefined) + if (mentry->ArgsDefined > 0) { if (mentry->Type!= NULL) mthing->special = mentry->Special; - memcpy(mthing->args, mentry->Args, sizeof(mthing->args)); + memcpy(mthing->args, mentry->Args, sizeof(mthing->args[0]) * mentry->ArgsDefined); } int pnum = -1; @@ -4927,6 +4921,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) mobj->SpawnPoint[2] = mthing->z; mobj->SpawnAngle = mthing->angle; mobj->SpawnFlags = mthing->flags; + if (mthing->FloatbobPhase >= 0 && mthing->FloatbobPhase < 64) mobj->FloatBobPhase = mthing->FloatbobPhase; if (mthing->gravity < 0) mobj->gravity = -mthing->gravity; else if (mthing->gravity > 0) mobj->gravity = FixedMul(mobj->gravity, mthing->gravity); else mobj->flags &= ~MF_NOGRAVITY; @@ -5039,7 +5034,7 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t puff->target = source; - if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y); + if (source != NULL) puff->angle = puff->AngleTo(source); // If a puff has a crash state and an actor was not hit, // it will enter the crash state. This is used by the StrifeSpark @@ -5312,13 +5307,13 @@ void P_RipperBlood (AActor *mo, AActor *bleeder) int P_GetThingFloorType (AActor *thing) { - if (thing->floorpic.isValid()) + if (thing->floorterrain >= 0) { - return TerrainTypes[thing->floorpic]; + return thing->floorterrain; } else { - return TerrainTypes[thing->Sector->GetTexture(sector_t::floor)]; + return thing->Sector->GetTerrain(sector_t::floor); } } @@ -5329,7 +5324,7 @@ int P_GetThingFloorType (AActor *thing) // Returns true if hit liquid and splashed, false if not. //--------------------------------------------------------------------------- -bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert) +bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool checkabove, bool alert, bool force) { if (thing->flags3 & MF3_DONTSPLASH) return false; @@ -5371,36 +5366,36 @@ bool P_HitWater (AActor * thing, sector_t * sec, fixed_t x, fixed_t y, fixed_t z } #endif -#ifdef _3DFLOORS - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) - { - F3DFloor * rover = sec->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_EXISTS)) continue; - fixed_t planez = rover->top.plane->ZatPoint(x, y); - if (z > planez - FRACUNIT/2 && z < planez + FRACUNIT/2) // allow minor imprecisions - { - if (rover->flags & (FF_SOLID|FF_SWIMMABLE) ) - { - terrainnum = TerrainTypes[*rover->top.texture]; - goto foundone; - } - } - planez = rover->bottom.plane->ZatPoint(x, y); - if (planez < z && !(planez < thing->floorz)) return false; - } -#endif - hsec = sec->GetHeightSec(); - if (hsec == NULL || !(hsec->MoreFlags & SECF_CLIPFAKEPLANES)) + // 'force' means, we want this sector's terrain, no matter what. + if (!force) { - terrainnum = TerrainTypes[sec->GetTexture(sector_t::floor)]; + for (unsigned int i = 0; ie->XFloor.ffloors.Size(); i++) + { + F3DFloor * rover = sec->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + fixed_t planez = rover->top.plane->ZatPoint(x, y); + if (z > planez - FRACUNIT / 2 && z < planez + FRACUNIT / 2) // allow minor imprecisions + { + if (rover->flags & (FF_SOLID | FF_SWIMMABLE)) + { + terrainnum = rover->model->GetTerrain(rover->top.isceiling); + goto foundone; + } + } + planez = rover->bottom.plane->ZatPoint(x, y); + if (planez < z && !(planez < thing->floorz)) return false; + } + } + hsec = sec->GetHeightSec(); + if (force || hsec == NULL || !(hsec->MoreFlags & SECF_CLIPFAKEPLANES)) + { + terrainnum = sec->GetTerrain(sector_t::floor); } else { - terrainnum = TerrainTypes[hsec->GetTexture(sector_t::floor)]; + terrainnum = hsec->GetTerrain(sector_t::floor); } -#ifdef _3DFLOORS foundone: -#endif int splashnum = Terrains[terrainnum].Splash; bool smallsplash = false; @@ -5416,7 +5411,7 @@ foundone: // Don't splash for living things with small vertical velocities. // There are levels where the constant splashing from the monsters gets extremely annoying - if ((thing->flags3&MF3_ISMONSTER || thing->player) && thing->velz >= -6*FRACUNIT) + if (((thing->flags3&MF3_ISMONSTER || thing->player) && thing->velz >= -6*FRACUNIT) && !force) return Terrains[terrainnum].IsLiquid; splash = &Splashes[splashnum]; @@ -5498,12 +5493,11 @@ bool P_HitFloor (AActor *thing) // don't splash if landing on the edge above water/lava/etc.... for (m = thing->touching_sectorlist; m; m = m->m_tnext) { - if (thing->z == m->m_sector->floorplane.ZatPoint (thing->x, thing->y)) + if (thing->z == m->m_sector->floorplane.ZatPoint(thing)) { break; } -#ifdef _3DFLOORS // Check 3D floors for(unsigned int i=0;im_sector->e->XFloor.ffloors.Size();i++) { @@ -5511,13 +5505,12 @@ bool P_HitFloor (AActor *thing) if (!(rover->flags & FF_EXISTS)) continue; if (rover->flags & (FF_SOLID|FF_SWIMMABLE)) { - if (rover->top.plane->ZatPoint(thing->x, thing->y) == thing->z) + if (rover->top.plane->ZatPoint(thing) == thing->z) { return P_HitWater (thing, m->m_sector); } } } -#endif } if (m == NULL || m->m_sector->GetHeightSec() != NULL) { @@ -5788,12 +5781,12 @@ AActor * P_OldSpawnMissile(AActor * source, AActor * owner, AActor * dest, const P_PlaySpawnSound(th, source); th->target = owner; // record missile's originator - th->angle = an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); + th->angle = an = source->AngleTo(dest); an >>= ANGLETOFINESHIFT; th->velx = FixedMul (th->Speed, finecosine[an]); th->vely = FixedMul (th->Speed, finesine[an]); - dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); + dist = source->AproxDistance (dest); if (th->Speed) dist = dist / th->Speed; if (dist < 1) @@ -5854,7 +5847,7 @@ AActor *P_SpawnMissileZAimed (AActor *source, fixed_t z, AActor *dest, const PCl { an += pr_spawnmissile.Random2() << 20; } - dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); + dist = source->AproxDistance (dest); speed = GetDefaultSpeed (type); dist /= speed; velz = dist != 0 ? (dest->z - source->z)/dist : speed; @@ -5939,7 +5932,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, const PClass *type, angle_t angle) AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, const PClass *type, angle_t angle, AActor **pLineTarget, AActor **pMissileActor, - bool nofreeaim) + bool nofreeaim, bool noautoaim) { static const int angdiff[3] = { -1<<26, 1<<26, 0 }; angle_t an = angle; @@ -5952,7 +5945,7 @@ AActor *P_SpawnPlayerMissile (AActor *source, fixed_t x, fixed_t y, fixed_t z, { return NULL; } - if (source->player && source->player->ReadyWeapon && (source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM)) + if (source->player && source->player->ReadyWeapon && ((source->player->ReadyWeapon->WeaponFlags & WIF_NOAUTOAIM) || noautoaim)) { // Keep exactly the same angle and pitch as the player's own aim an = angle; @@ -6483,25 +6476,25 @@ void PrintMiscActorInfo(AActor *query) Printf("%s @ %p has the following flags:\n flags: %x", query->GetTag(), query, query->flags.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags & 1<flags & ActorFlags::FromInt(1<flags2.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags2 & 1<flags2 & ActorFlags2::FromInt(1<flags3.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags3 & 1<flags3 & ActorFlags3::FromInt(1<flags4.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags4 & 1<flags4 & ActorFlags4::FromInt(1<flags5.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags5 & 1<flags5 & ActorFlags5::FromInt(1<flags6.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags6 & 1<flags6 & ActorFlags6::FromInt(1<flags7.GetValue()); for (flagi = 0; flagi <= 31; flagi++) - if (query->flags7 & 1<flags7 & ActorFlags7::FromInt(1<BounceFlags.GetValue(), FIXED2FLOAT(query->bouncefactor), FIXED2FLOAT(query->wallbouncefactor)); diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index 884218bee7..b1cae3af9b 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -198,17 +198,20 @@ DPillar::DPillar (sector_t *sector, EPillar type, fixed_t speed, m_FloorSpeed = Scale (speed, floordist, ceilingdist); } - if (sector->seqType >= 0) + if (!(m_Sector->Flags & SECF_SILENTMOVE)) { - SN_StartSequence (sector, CHAN_FLOOR, sector->seqType, SEQ_PLATFORM, 0); - } - else if (sector->SeqName != NAME_None) - { - SN_StartSequence (sector, CHAN_FLOOR, sector->SeqName, 0); - } - else - { - SN_StartSequence (sector, CHAN_FLOOR, "Floor", 0); + if (sector->seqType >= 0) + { + SN_StartSequence(sector, CHAN_FLOOR, sector->seqType, SEQ_PLATFORM, 0); + } + else if (sector->SeqName != NAME_None) + { + SN_StartSequence(sector, CHAN_FLOOR, sector->SeqName, 0); + } + else + { + SN_StartSequence(sector, CHAN_FLOOR, "Floor", 0); + } } } diff --git a/src/p_plats.cpp b/src/p_plats.cpp index 7600637b2f..b0697fc34b 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -72,6 +72,8 @@ void DPlat::Serialize (FArchive &arc) void DPlat::PlayPlatSound (const char *sound) { + if (m_Sector->Flags & SECF_SILENTMOVE) return; + if (m_Sector->seqType >= 0) { SN_StartSequence (m_Sector, CHAN_FLOOR, m_Sector->seqType, SEQ_PLATFORM, 0); @@ -280,8 +282,7 @@ bool EV_DoPlat (int tag, line_t *line, DPlat::EPlatType type, int height, { if (line) sec->SetTexture(sector_t::floor, line->sidedef[0]->sector->GetTexture(sector_t::floor)); - if (change == 1) - sec->special &= SECRET_MASK; // Stop damage and other stuff, if any + if (change == 1) sec->ClearSpecial(); } switch (type) @@ -293,7 +294,7 @@ bool EV_DoPlat (int tag, line_t *line, DPlat::EPlatType type, int height, plat->m_Low = sec->floorplane.d; plat->m_Status = DPlat::up; plat->PlayPlatSound ("Floor"); - sec->special &= SECRET_MASK; // NO MORE DAMAGE, IF APPLICABLE + sec->ClearSpecial(); break; case DPlat::platUpByValue: diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 63f3bc6486..7cdef059cb 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -38,6 +38,30 @@ // TYPES ------------------------------------------------------------------- +struct FGenericButtons +{ + int ReadyFlag; // Flag passed to A_WeaponReady + int StateFlag; // Flag set in WeaponState + int ButtonFlag; // Button to press + ENamedName StateName; // Name of the button/state +}; + +enum EWRF_Options +{ + WRF_NoBob = 1, + WRF_NoSwitch = 1 << 1, + WRF_NoPrimary = 1 << 2, + WRF_NoSecondary = 1 << 3, + WRF_NoFire = WRF_NoPrimary | WRF_NoSecondary, + WRF_AllowReload = 1 << 4, + WRF_AllowZoom = 1 << 5, + WRF_DisableSwitch = 1 << 6, + WRF_AllowUser1 = 1 << 7, + WRF_AllowUser2 = 1 << 8, + WRF_AllowUser3 = 1 << 9, + WRF_AllowUser4 = 1 << 10, +}; + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -57,6 +81,16 @@ CVAR(Int, sv_fastweapons, false, CVAR_SERVERINFO); static FRandom pr_wpnreadysnd ("WpnReadySnd"); static FRandom pr_gunshot ("GunShot"); +static const FGenericButtons ButtonChecks[] = +{ + { WRF_AllowZoom, WF_WEAPONZOOMOK, BT_ZOOM, NAME_Zoom }, + { WRF_AllowReload, WF_WEAPONRELOADOK, BT_RELOAD, NAME_Reload }, + { WRF_AllowUser1, WF_USER1OK, BT_USER1, NAME_User1 }, + { WRF_AllowUser2, WF_USER2OK, BT_USER2, NAME_User2 }, + { WRF_AllowUser3, WF_USER3OK, BT_USER3, NAME_User3 }, + { WRF_AllowUser4, WF_USER4OK, BT_USER4, NAME_User4 }, +}; + // CODE -------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -95,7 +129,8 @@ void P_SetPsprite (player_t *player, int position, FState *state, bool nofunctio if (position == ps_weapon && !nofunction) { // A_WeaponReady will re-set these as needed - player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK); + player->WeaponState &= ~(WF_WEAPONREADY | WF_WEAPONREADYALT | WF_WEAPONBOBBING | WF_WEAPONSWITCHOK | WF_WEAPONRELOADOK | WF_WEAPONZOOMOK | + WF_USER1OK | WF_USER2OK | WF_USER3OK | WF_USER4OK); } psp = &player->psprites[position]; @@ -289,66 +324,6 @@ void P_FireWeaponAlt (player_t *player, FState *state) } } -//--------------------------------------------------------------------------- -// -// PROC P_ReloadWeapon -// -//--------------------------------------------------------------------------- - -void P_ReloadWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetRelState(); - } - // [XA] don't change state if still null, so if the modder sets - // WRF_RELOAD to true but forgets to define the Reload state, the weapon - // won't disappear. ;) - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - -//--------------------------------------------------------------------------- -// -// PROC P_ZoomWeapon -// -//--------------------------------------------------------------------------- - -void P_ZoomWeapon (player_t *player, FState *state) -{ - AWeapon *weapon; - if (player->Bot == NULL && bot_observer) - { - return; - } - - weapon = player->ReadyWeapon; - if (weapon == NULL) - { - return; - } - - if (state == NULL) - { - state = weapon->GetZoomState(); - } - // [XA] don't change state if still null. Same reasons as above. - if (state != NULL) - P_SetPsprite (player, ps_weapon, state); -} - //--------------------------------------------------------------------------- // // PROC P_DropWeapon @@ -562,22 +537,21 @@ void DoReadyWeaponToBob (AActor *self) } } -void DoReadyWeaponToReload (AActor *self) +void DoReadyWeaponToGeneric(AActor *self, int paramflags) { - // Prepare for reload action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONRELOADOK; - return; -} + int flags = 0; -void DoReadyWeaponToZoom (AActor *self) -{ - // Prepare for reload action. - player_t *player; - if (self && (player = self->player)) - player->WeaponState |= WF_WEAPONZOOMOK; - return; + for (size_t i = 0; i < countof(ButtonChecks); ++i) + { + if (paramflags & ButtonChecks[i].ReadyFlag) + { + flags |= ButtonChecks[i].StateFlag; + } + } + if (self != NULL && self->player != NULL) + { + self->player->WeaponState |= flags; + } } // This function replaces calls to A_WeaponReady in other codepointers. @@ -586,22 +560,9 @@ void DoReadyWeapon(AActor *self) DoReadyWeaponToBob(self); DoReadyWeaponToFire(self); DoReadyWeaponToSwitch(self); - DoReadyWeaponToReload(self); - DoReadyWeaponToZoom(self); + DoReadyWeaponToGeneric(self, ~0); } -enum EWRF_Options -{ - WRF_NoBob = 1, - WRF_NoSwitch = 2, - WRF_NoPrimary = 4, - WRF_NoSecondary = 8, - WRF_NoFire = WRF_NoPrimary + WRF_NoSecondary, - WRF_AllowReload = 16, - WRF_AllowZoom = 32, - WRF_DisableSwitch = 64, -}; - DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) { ACTION_PARAM_START(1); @@ -610,10 +571,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AInventory, A_WeaponReady) DoReadyWeaponToSwitch(self, !(paramflags & WRF_NoSwitch)); if ((paramflags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(self, !(paramflags & WRF_NoPrimary), !(paramflags & WRF_NoSecondary)); if (!(paramflags & WRF_NoBob)) DoReadyWeaponToBob(self); - if ((paramflags & WRF_AllowReload)) DoReadyWeaponToReload(self); - if ((paramflags & WRF_AllowZoom)) DoReadyWeaponToZoom(self); - - DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); + DoReadyWeaponToGeneric(self, paramflags); + DoReadyWeaponDisableSwitch(self, paramflags & WRF_DisableSwitch); } //--------------------------------------------------------------------------- @@ -690,45 +649,40 @@ void P_CheckWeaponSwitch (player_t *player) //--------------------------------------------------------------------------- // -// PROC P_CheckWeaponReload +// PROC P_CheckWeaponButtons // -// The player can reload the weapon. +// Check extra button presses for weapons. // //--------------------------------------------------------------------------- -void P_CheckWeaponReload (player_t *player) +static void P_CheckWeaponButtons (player_t *player) { - AWeapon *weapon = player->ReadyWeapon; - - if (weapon == NULL) - return; - - // Check for reload. - if ((player->WeaponState & WF_WEAPONRELOADOK) && (player->cmd.ucmd.buttons & BT_RELOAD)) + if (player->Bot == NULL && bot_observer) { - P_ReloadWeapon (player, NULL); + return; } -} - -//--------------------------------------------------------------------------- -// -// PROC P_CheckWeaponZoom -// -// The player can use the weapon's zoom function. -// -//--------------------------------------------------------------------------- - -void P_CheckWeaponZoom (player_t *player) -{ AWeapon *weapon = player->ReadyWeapon; - if (weapon == NULL) - return; - - // Check for zoom. - if ((player->WeaponState & WF_WEAPONZOOMOK) && (player->cmd.ucmd.buttons & BT_ZOOM)) { - P_ZoomWeapon (player, NULL); + return; + } + // The button checks are ordered by precedence. The first one to match a + // button press and affect a state change wins. + for (size_t i = 0; i < countof(ButtonChecks); ++i) + { + if ((player->WeaponState & ButtonChecks[i].StateFlag) && + (player->cmd.ucmd.buttons & ButtonChecks[i].ButtonFlag)) + { + FState *state = weapon->GetStateForButtonName(ButtonChecks[i].StateName); + // [XA] don't change state if still null, so if the modder + // sets WRF_xxx to true but forgets to define the corresponding + // state, the weapon won't disappear. ;) + if (state != NULL) + { + P_SetPsprite(player, ps_weapon, state); + return; + } + } } } @@ -1095,14 +1049,9 @@ void P_MovePsprites (player_t *player) { P_CheckWeaponFire (player); } - if (player->WeaponState & WF_WEAPONRELOADOK) - { - P_CheckWeaponReload (player); - } - if (player->WeaponState & WF_WEAPONZOOMOK) - { - P_CheckWeaponZoom (player); - } + + // Check custom buttons + P_CheckWeaponButtons(player); } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 4804943203..7623104d7d 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -52,6 +52,7 @@ #include "farchive.h" #include "p_lnspec.h" #include "p_acs.h" +#include "p_terrain.h" static void CopyPlayer (player_t *dst, player_t *src, const char *name); static void ReadOnePlayer (FArchive &arc, bool skipload); @@ -354,7 +355,7 @@ void P_SerializeWorld (FArchive &arc) short tag; arc << tag; } - arc << sec->soundtraversed + arc << sec->soundtraversed << sec->seqType << sec->friction << sec->movefactor @@ -368,18 +369,66 @@ void P_SerializeWorld (FArchive &arc) << sec->planes[sector_t::ceiling] << sec->heightsec << sec->bottommap << sec->midmap << sec->topmap - << sec->gravity - << sec->damage - << sec->mod - << sec->SoundTarget + << sec->gravity; + if (SaveVersion >= 4530) + { + P_SerializeTerrain(arc, sec->terrainnum[0]); + P_SerializeTerrain(arc, sec->terrainnum[1]); + } + if (SaveVersion >= 4529) + { + arc << sec->damageamount; + } + else + { + short dmg; + arc << dmg; + sec->damageamount = dmg; + } + if (SaveVersion >= 4528) + { + arc << sec->damageinterval + << sec->leakydamage + << sec->damagetype; + } + else + { + short damagemod; + arc << damagemod; + sec->damagetype = MODtoDamageType(damagemod); + if (sec->damageamount < 20) + { + sec->leakydamage = 0; + sec->damageinterval = 32; + } + else if (sec->damageamount < 50) + { + sec->leakydamage = 5; + sec->damageinterval = 32; + } + else + { + sec->leakydamage = 256; + sec->damageinterval = 1; + } + } + + arc << sec->SoundTarget << sec->SecActTarget << sec->sky << sec->MoreFlags << sec->Flags - << sec->FloorSkyBox << sec->CeilingSkyBox - << sec->ZoneNumber - << sec->secretsector - << sec->interpolations[0] + << sec->SkyBoxes[sector_t::floor] << sec->SkyBoxes[sector_t::ceiling] + << sec->ZoneNumber; + if (SaveVersion < 4529) + { + short secretsector; + arc << secretsector; + if (secretsector) sec->Flags |= SECF_WASSECRET; + sec->special &= ~(SECRET_MASK|FRICTION_MASK|PUSH_MASK); + P_InitSectorSpecial(sec, sec->special, true); + } + arc << sec->interpolations[0] << sec->interpolations[1] << sec->interpolations[2] << sec->interpolations[3] diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 904c76c1b3..83adc438f0 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -26,9 +26,11 @@ #include "doomstat.h" #include "g_level.h" #include "nodebuild.h" +#include "p_terrain.h" #include "po_man.h" #include "farchive.h" #include "r_utility.h" +#include "a_sharedglobal.h" #include "r_data/colormaps.h" @@ -48,7 +50,7 @@ sector_t *sector_t::NextSpecialSector (int type, sector_t *nogood) const if (NULL != (tsec = getNextSector (ln, this)) && tsec != nogood && - (tsec->special & 0x00ff) == type) + tsec->special == type) { return tsec; } @@ -800,6 +802,15 @@ int sector_t::GetCeilingLight () const } } + +ASkyViewpoint *sector_t::GetSkyBox(int which) +{ + if (SkyBoxes[which] != NULL) return SkyBoxes[which]; + if (MoreFlags & (SECF_NOFLOORSKYBOX << which)) return NULL; + return level.DefaultSkybox; +} + + sector_t *sector_t::GetHeightSec() const { if (heightsec == NULL) @@ -825,6 +836,65 @@ sector_t *sector_t::GetHeightSec() const } +void sector_t::GetSpecial(secspecial_t *spec) +{ + spec->special = special; + spec->damageamount = damageamount; + spec->damagetype = damagetype; + spec->damageinterval = damageinterval; + spec->leakydamage = leakydamage; + spec->Flags = Flags & SECF_SPECIALFLAGS; +} + +void sector_t::SetSpecial(const secspecial_t *spec) +{ + special = spec->special; + damageamount = spec->damageamount; + damagetype = spec->damagetype; + damageinterval = spec->damageinterval; + leakydamage = spec->leakydamage; + Flags = (Flags & ~SECF_SPECIALFLAGS) | (spec->Flags & SECF_SPECIALFLAGS); +} + +void sector_t::TransferSpecial(sector_t *model) +{ + special = model->special; + damageamount = model->damageamount; + damagetype = model->damagetype; + damageinterval = model->damageinterval; + leakydamage = model->leakydamage; + Flags = (Flags&~SECF_SPECIALFLAGS) | (model->Flags & SECF_SPECIALFLAGS); +} + +int sector_t::GetTerrain(int pos) const +{ + return terrainnum[pos] >= 0 ? terrainnum[pos] : TerrainTypes[GetTexture(pos)]; +} + +FArchive &operator<< (FArchive &arc, secspecial_t &p) +{ + if (SaveVersion < 4529) + { + int special; + arc << special; + sector_t sec; + P_InitSectorSpecial(&sec, special, true); + sec.GetSpecial(&p); + } + else + { + arc << p.special + << p.damageamount + << p.damagetype + << p.damageinterval + << p.leakydamage + << p.Flags; + } + return arc; +} + + + bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const { bool copy = false; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index fc31db4112..f92e1b9d22 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1513,7 +1513,6 @@ void P_LoadSectors (MapData *map, FMissingTextureTracker &missingtex) ss->special = LittleShort(ms->special); else // [RH] Translate to new sector special ss->special = P_TranslateSectorSpecial (LittleShort(ms->special)); - ss->secretsector = !!(ss->special&SECRET_MASK); tagManager.AddSectorTag(i, LittleShort(ms->tag)); ss->thinglist = NULL; ss->touching_thinglist = NULL; // phares 3/14/98 @@ -1534,6 +1533,7 @@ void P_LoadSectors (MapData *map, FMissingTextureTracker &missingtex) ss->gravity = 1.f; // [RH] Default sector gravity of 1.0 ss->ZoneNumber = 0xFFFF; + ss->terrainnum[sector_t::ceiling] = ss->terrainnum[sector_t::floor] = -1; // [RH] Sectors default to white light with the default fade. // If they are outside (have a sky ceiling), they use the outside fog. @@ -1759,6 +1759,7 @@ void P_LoadThings (MapData * map) mti[i].RenderStyle = STYLE_Count; mti[i].alpha = -1; mti[i].health = 1; + mti[i].FloatbobPhase = -1; flags &= ~MTF_SKILLMASK; mti[i].flags = (short)((flags & 0xf) | 0x7e0); if (gameinfo.gametype == GAME_Strife) @@ -1843,6 +1844,7 @@ void P_LoadThings2 (MapData * map) mti[i].RenderStyle = STYLE_Count; mti[i].alpha = -1; mti[i].health = 1; + mti[i].FloatbobPhase = -1; } delete[] mtp; } @@ -2364,7 +2366,16 @@ static void P_LoopSidedefs (bool firstloop) // instead of as part of another loop if (line->frontsector == line->backsector) { - right = DWORD(line->sidedef[!sidetemp[i].b.lineside] - sides); + const side_t* const rightside = line->sidedef[!sidetemp[i].b.lineside]; + + if (NULL == rightside) + { + // There is no right side! + if (firstloop) Printf ("Line %d's right edge is unconnected\n", linemap[unsigned(line-lines)]); + continue; + } + + right = DWORD(rightside - sides); } else { @@ -2530,7 +2541,6 @@ void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, intmaps } break; -#ifdef _3DFLOORS case Sector_Set3DFloor: if (msd->toptexture[0]=='#') { @@ -2545,7 +2555,6 @@ void P_ProcessSideTextures(bool checktranmap, side_t *sd, sector_t *sec, intmaps SetTexture(sd, side_t::mid, msd->midtexture, missingtex); SetTexture(sd, side_t::bottom, msd->bottomtexture, missingtex); break; -#endif case TranslucentLine: // killough 4/11/98: apply translucency to 2s normal texture if (checktranmap) @@ -4051,7 +4060,7 @@ void P_SetupLevel (const char *lumpname, int position) { if (mo->flags & MF_COUNTKILL) { - if (mo->Sector->special == dDamage_End) + if (mo->Sector->damageamount > 0 && (mo->Sector->Flags & (SECF_ENDGODMODE|SECF_ENDLEVEL)) == (SECF_ENDGODMODE|SECF_ENDLEVEL)) { mo->ClearCounters(); } diff --git a/src/p_sight.cpp b/src/p_sight.cpp index ef4dfacd0e..2d465aab54 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -127,13 +127,12 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) if (topslope <= bottomslope) return false; // stop -#ifdef _3DFLOORS // now handle 3D-floors if(li->frontsector->e->XFloor.ffloors.Size() || li->backsector->e->XFloor.ffloors.Size()) { int frontflag; - frontflag = P_PointOnLineSide(sightthing->x, sightthing->y, li); + frontflag = P_PointOnLineSidePrecise(sightthing->x, sightthing->y, li); //Check 3D FLOORS! for(int i=1;i<=2;i++) @@ -219,7 +218,6 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) lastztop= FixedMul (topslope, in->frac) + sightzstart; lastzbottom= FixedMul (bottomslope, in->frac) + sightzstart; -#endif return true; // keep going } @@ -243,14 +241,14 @@ bool SightCheck::P_SightCheckLine (line_t *ld) return true; } ld->validcount = validcount; - if (P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace) == - P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace)) + if (P_PointOnDivlineSidePrecise (ld->v1->x, ld->v1->y, &trace) == + P_PointOnDivlineSidePrecise (ld->v2->x, ld->v2->y, &trace)) { return true; // line isn't crossed } P_MakeDivline (ld, &dl); - if (P_PointOnDivlineSide (trace.x, trace.y, &dl) == - P_PointOnDivlineSide (trace.x+trace.dx, trace.y+trace.dy, &dl)) + if (P_PointOnDivlineSidePrecise (trace.x, trace.y, &dl) == + P_PointOnDivlineSidePrecise (trace.x+trace.dx, trace.y+trace.dy, &dl)) { return true; // line isn't crossed } @@ -401,7 +399,6 @@ bool SightCheck::P_SightTraverseIntercepts () } } -#ifdef _3DFLOORS if (lastsector==seeingthing->Sector && lastsector->e->XFloor.ffloors.Size()) { // we must do one last check whether the trace has crossed a 3D floor in the last sector @@ -424,7 +421,6 @@ bool SightCheck::P_SightTraverseIntercepts () } } -#endif return true; // everything was traversed } @@ -453,7 +449,6 @@ bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_ validcount++; intercepts.Clear (); -#ifdef _3DFLOORS // for FF_SEETHROUGH the following rule applies: // If the viewer is in an area without FF_SEETHROUGH he can only see into areas without this flag // If the viewer is in an area with FF_SEETHROUGH he can only see into areas with this flag @@ -472,7 +467,6 @@ bool SightCheck::P_SightPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_ break; } } -#endif if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index c0b9b5ef0e..a3db265261 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -54,7 +54,7 @@ static void P_SlopeLineToPoint (int lineid, fixed_t x, fixed_t y, fixed_t z, boo sector_t *sec; secplane_t *plane; - if (P_PointOnLineSide (x, y, line) == 0) + if (P_PointOnLineSidePrecise (x, y, line) == 0) { sec = line->frontsector; } @@ -363,7 +363,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, z3 = h3? *h3 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt3.Z = FIXED2FLOAT(z3); - if (P_PointOnLineSide(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0) + if (P_PointOnLineSidePrecise(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0) { vec1 = vt2 - vt3; vec2 = vt1 - vt3; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 42a7b9ca46..baec6d9e1b 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -62,6 +62,7 @@ #include "farchive.h" #include "a_keys.h" #include "c_dispatch.h" +#include "r_sky.h" // State. #include "r_state.h" @@ -71,7 +72,6 @@ #include "r_data/r_interpolate.h" static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); -void P_SetupPortals(); EXTERN_CVAR(Bool, cl_predict_specials) @@ -288,16 +288,16 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) } } + if (activationType == SPAC_Use && (lineActivation & SPAC_MUse) && !mo->player && mo->flags4 & MF4_CANUSEWALLS) + { + return true; + } + if (activationType == SPAC_Push && (lineActivation & SPAC_MPush) && !mo->player && mo->flags2 & MF2_PUSHWALL) + { + return true; + } if ((lineActivation & activationType) == 0) { - if (activationType == SPAC_Use && (lineActivation & SPAC_MUse) && !mo->player && mo->flags4 & MF4_CANUSEWALLS) - { - return true; - } - if (activationType == SPAC_Push && (lineActivation & SPAC_MPush) && !mo->player && mo->flags2 & MF2_PUSHWALL) - { - return true; - } if (activationType != SPAC_MCross || lineActivation != SPAC_Cross) { return false; @@ -430,166 +430,62 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) { // Falling, not all the way down yet? sector = player->mo->Sector; - if (player->mo->z != sector->floorplane.ZatPoint (player->mo->x, player->mo->y) + if (player->mo->z != sector->floorplane.ZatPoint(player->mo) && !player->mo->waterlevel) { return; } } - int special = sector->special & ~SECRET_MASK; - // Has hit ground. AInventory *ironfeet; - // Allow subclasses. Better would be to implement it as armor and let that reduce - // the damage as part of the normal damage procedure. Unfortunately, I don't have - // different damage types yet, so that's not happening for now. - for (ironfeet = player->mo->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory) - { - if (ironfeet->IsKindOf (RUNTIME_CLASS(APowerIronFeet))) - break; - } - - // [RH] Normal DOOM special or BOOM specialized? - if (special >= dLight_Flicker && special <= 255) - { - switch (special) - { - case Sector_Heal: - // CoD's healing sector - if (!(level.time & 0x1f)) - P_GiveBody (player->mo, 1); - break; - - case Damage_InstantDeath: - // Strife's instant death sector - P_DamageMobj (player->mo, NULL, NULL, 999, NAME_InstantDeath); - break; - - case dDamage_Hellslime: - // HELLSLIME DAMAGE - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 10, NAME_Slime); - break; - - case dDamage_Nukage: - // NUKAGE DAMAGE - case sLight_Strobe_Hurt: - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 5, NAME_Slime); - break; - - case hDamage_Sludge: - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 4, NAME_Slime); - break; - - case dDamage_SuperHellslime: - // SUPER HELLSLIME DAMAGE - case dLight_Strobe_Hurt: - // STROBE HURT - if (ironfeet == NULL || pr_playerinspecialsector() < 5) - { - if (!(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 20, NAME_Slime); - } - break; - - case sDamage_Hellslime: - if (ironfeet == NULL) - player->hazardcount += 2; - break; - - case sDamage_SuperHellslime: - if (ironfeet == NULL) - player->hazardcount += 4; - break; - - case dDamage_End: - // EXIT SUPER DAMAGE! (for E1M8 finale) - player->cheats &= ~CF_GODMODE; - - if (!(level.time & 0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 20, NAME_None); - - if (player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT))) - G_ExitLevel(0, false); - break; - - case dDamage_LavaWimpy: - case dScroll_EastLavaDamage: - if (!(level.time & 15)) - { - P_DamageMobj(player->mo, NULL, NULL, 5, NAME_Fire); - P_HitFloor(player->mo); - } - break; - - case dDamage_LavaHefty: - if(!(level.time & 15)) - { - P_DamageMobj(player->mo, NULL, NULL, 8, NAME_Fire); - P_HitFloor(player->mo); - } - break; - - default: - // [RH] Ignore unknown specials - break; - } - } - else - { - //jff 3/14/98 handle extended sector types for secrets and damage - switch (special & DAMAGE_MASK) - { - case 0x000: // no damage - break; - case 0x100: // 2/5 damage per 31 ticks - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 5, NAME_Fire); - break; - case 0x200: // 5/10 damage per 31 ticks - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 10, NAME_Slime); - break; - case 0x300: // 10/20 damage per 31 ticks - if (ironfeet == NULL - || pr_playerinspecialsector() < 5) // take damage even with suit - { - if (!(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, 20, NAME_Slime); - } - break; - } - } - // [RH] Apply any customizable damage - if (sector->damage) + if (sector->damageamount > 0) { - if (sector->damage < 20) + // Allow subclasses. Better would be to implement it as armor and let that reduce + // the damage as part of the normal damage procedure. Unfortunately, I don't have + // different damage types yet, so that's not happening for now. + for (ironfeet = player->mo->Inventory; ironfeet != NULL; ironfeet = ironfeet->Inventory) { - if (ironfeet == NULL && !(level.time&0x1f)) - P_DamageMobj (player->mo, NULL, NULL, sector->damage, MODtoDamageType (sector->mod)); + if (ironfeet->IsKindOf (RUNTIME_CLASS(APowerIronFeet))) + break; } - else if (sector->damage < 50) + + if (sector->Flags & SECF_ENDGODMODE) player->cheats &= ~CF_GODMODE; + if ((ironfeet == NULL || pr_playerinspecialsector() < sector->leakydamage)) { - if ((ironfeet == NULL || (pr_playerinspecialsector()<5)) - && !(level.time&0x1f)) + if (sector->Flags & SECF_HAZARD) { - P_DamageMobj (player->mo, NULL, NULL, sector->damage, MODtoDamageType (sector->mod)); + player->hazardcount += sector->damageamount; + player->hazardtype = sector->damagetype; + player->hazardinterval = sector->damageinterval; + } + else if (level.time % sector->damageinterval == 0) + { + P_DamageMobj(player->mo, NULL, NULL, sector->damageamount, sector->damagetype); + if ((sector->Flags & SECF_ENDLEVEL) && player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT))) + { + G_ExitLevel(0, false); + } + if (sector->Flags & SECF_DMGTERRAINFX) + { + P_HitWater(player->mo, sector, INT_MIN, INT_MIN, INT_MIN, false, true, true); + } } } - else + } + else if (sector->damageamount < 0) + { + if (level.time % sector->damageinterval == 0) { - P_DamageMobj (player->mo, NULL, NULL, sector->damage, MODtoDamageType (sector->mod)); + P_GiveBody(player->mo, -sector->damageamount, 100); } } - if (sector->special & SECRET_MASK) + if (sector->isSecret()) { - sector->special &= ~SECRET_MASK; + sector->ClearSecret(); P_GiveSecret(player->mo, true, true, int(sector - sectors)); } } @@ -611,7 +507,7 @@ static void DoSectorDamage(AActor *actor, sector_t *sec, int amount, FName type, if (!(flags & DAMAGE_PLAYERS) && actor->player != NULL) return; - if (!(flags & DAMAGE_IN_AIR) && actor->z != sec->floorplane.ZatPoint(actor->x, actor->y) && !actor->waterlevel) + if (!(flags & DAMAGE_IN_AIR) && actor->z != sec->floorplane.ZatPoint(actor) && !actor->waterlevel) return; if (protectClass != NULL) @@ -648,8 +544,8 @@ void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass, { next = actor->snext; // Only affect actors touching the 3D floor - fixed_t z1 = sec->floorplane.ZatPoint(actor->x, actor->y); - fixed_t z2 = sec->ceilingplane.ZatPoint(actor->x, actor->y); + fixed_t z1 = sec->floorplane.ZatPoint(actor); + fixed_t z2 = sec->ceilingplane.ZatPoint(actor); if (z2 < z1) { // Account for Vavoom-style 3D floors @@ -718,12 +614,6 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornu void P_PlayerOnSpecialFlat (player_t *player, int floorType) { - if (player->mo->z > player->mo->Sector->floorplane.ZatPoint ( - player->mo->x, player->mo->y) && - !player->mo->waterlevel) - { // Player is not touching the floor - return; - } if (Terrains[floorType].DamageAmount && !(level.time & Terrains[floorType].DamageTimeMask)) { @@ -738,12 +628,13 @@ void P_PlayerOnSpecialFlat (player_t *player, int floorType) } } + int damage = 0; if (ironfeet == NULL) { - P_DamageMobj (player->mo, NULL, NULL, Terrains[floorType].DamageAmount, + damage = P_DamageMobj (player->mo, NULL, NULL, Terrains[floorType].DamageAmount, Terrains[floorType].DamageMOD); } - if (Terrains[floorType].Splash != -1) + if (damage > 0 && Terrains[floorType].Splash != -1) { S_Sound (player->mo, CHAN_AUTO, Splashes[Terrains[floorType].Splash].NormalSplashSound, 1, @@ -1018,10 +909,10 @@ static void SetupFloorPortal (AStackPoint *point) { NActorIterator it (NAME_LowerStackLookOnly, point->tid); sector_t *Sector = point->Sector; - Sector->FloorSkyBox = static_cast(it.Next()); - if (Sector->FloorSkyBox != NULL && Sector->FloorSkyBox->bAlways) + Sector->SkyBoxes[sector_t::floor] = static_cast(it.Next()); + if (Sector->SkyBoxes[sector_t::floor] != NULL && Sector->SkyBoxes[sector_t::floor]->bAlways) { - Sector->FloorSkyBox->Mate = point; + Sector->SkyBoxes[sector_t::floor]->Mate = point; if (Sector->GetAlpha(sector_t::floor) == OPAQUE) Sector->SetAlpha(sector_t::floor, Scale (point->args[0], OPAQUE, 255)); } @@ -1031,48 +922,15 @@ static void SetupCeilingPortal (AStackPoint *point) { NActorIterator it (NAME_UpperStackLookOnly, point->tid); sector_t *Sector = point->Sector; - Sector->CeilingSkyBox = static_cast(it.Next()); - if (Sector->CeilingSkyBox != NULL && Sector->CeilingSkyBox->bAlways) + Sector->SkyBoxes[sector_t::ceiling] = static_cast(it.Next()); + if (Sector->SkyBoxes[sector_t::ceiling] != NULL && Sector->SkyBoxes[sector_t::ceiling]->bAlways) { - Sector->CeilingSkyBox->Mate = point; + Sector->SkyBoxes[sector_t::ceiling]->Mate = point; if (Sector->GetAlpha(sector_t::ceiling) == OPAQUE) Sector->SetAlpha(sector_t::ceiling, Scale (point->args[0], OPAQUE, 255)); } } -static bool SpreadCeilingPortal(AStackPoint *pt, fixed_t alpha, sector_t *sector) -{ - bool fail = false; - sector->validcount = validcount; - for(int i=0; ilinecount; i++) - { - line_t *line = sector->lines[i]; - sector_t *backsector = sector == line->frontsector? line->backsector : line->frontsector; - if (line->backsector == line->frontsector) continue; - if (backsector == NULL) { fail = true; continue; } - if (backsector->validcount == validcount) continue; - if (backsector->CeilingSkyBox == pt) continue; - - // Check if the backside would map to the same visplane - if (backsector->CeilingSkyBox != NULL) { fail = true; continue; } - if (backsector->ceilingplane != sector->ceilingplane) { fail = true; continue; } - if (backsector->lightlevel != sector->lightlevel) { fail = true; continue; } - if (backsector->GetTexture(sector_t::ceiling) != sector->GetTexture(sector_t::ceiling)) { fail = true; continue; } - if (backsector->GetXOffset(sector_t::ceiling) != sector->GetXOffset(sector_t::ceiling)) { fail = true; continue; } - if (backsector->GetYOffset(sector_t::ceiling) != sector->GetYOffset(sector_t::ceiling)) { fail = true; continue; } - if (backsector->GetXScale(sector_t::ceiling) != sector->GetXScale(sector_t::ceiling)) { fail = true; continue; } - if (backsector->GetYScale(sector_t::ceiling) != sector->GetYScale(sector_t::ceiling)) { fail = true; continue; } - if (backsector->GetAngle(sector_t::ceiling) != sector->GetAngle(sector_t::ceiling)) { fail = true; continue; } - if (SpreadCeilingPortal(pt, alpha, backsector)) { fail = true; continue; } - } - if (!fail) - { - sector->CeilingSkyBox = pt; - sector->SetAlpha(sector_t::ceiling, alpha); - } - return fail; -} - void P_SetupPortals() { TThinkerIterator it; @@ -1095,26 +953,64 @@ void P_SetupPortals() } } -inline void SetPortal(sector_t *sector, int plane, AStackPoint *portal, fixed_t alpha) +static void SetPortal(sector_t *sector, int plane, ASkyViewpoint *portal, fixed_t alpha) { // plane: 0=floor, 1=ceiling, 2=both if (plane > 0) { - if (sector->CeilingSkyBox == NULL || !sector->CeilingSkyBox->bAlways) + if (sector->SkyBoxes[sector_t::ceiling] == NULL || !sector->SkyBoxes[sector_t::ceiling]->bAlways) { - sector->CeilingSkyBox = portal; + sector->SkyBoxes[sector_t::ceiling] = portal; if (sector->GetAlpha(sector_t::ceiling) == OPAQUE) sector->SetAlpha(sector_t::ceiling, alpha); + + if (!portal->bAlways) sector->SetTexture(sector_t::ceiling, skyflatnum); } } if (plane == 2 || plane == 0) { - if (sector->FloorSkyBox == NULL || !sector->FloorSkyBox->bAlways) + if (sector->SkyBoxes[sector_t::floor] == NULL || !sector->SkyBoxes[sector_t::floor]->bAlways) { - sector->FloorSkyBox = portal; + sector->SkyBoxes[sector_t::floor] = portal; } if (sector->GetAlpha(sector_t::floor) == OPAQUE) sector->SetAlpha(sector_t::floor, alpha); + + if (!portal->bAlways) sector->SetTexture(sector_t::floor, skyflatnum); + } +} + +static void CopyPortal(int sectortag, int plane, ASkyViewpoint *origin, fixed_t alpha, bool tolines) +{ + int s; + FSectorTagIterator itr(sectortag); + while ((s = itr.Next()) >= 0) + { + SetPortal(§ors[s], plane, origin, alpha); + } + + for (int j=0;j= 0) + { + SetPortal(§ors[s], plane, origin, alpha); + } + } + } } } @@ -1130,10 +1026,11 @@ void P_SpawnPortal(line_t *line, int sectortag, int plane, int alpha) lines[i].args[2] == plane && lines[i].args[3] == 1) { - fixed_t x1 = (line->v1->x + line->v2->x) >> 1; - fixed_t y1 = (line->v1->y + line->v2->y) >> 1; - fixed_t x2 = (lines[i].v1->x + lines[i].v2->x) >> 1; - fixed_t y2 = (lines[i].v1->y + lines[i].v2->y) >> 1; + // beware of overflows. + fixed_t x1 = fixed_t((SQWORD(line->v1->x) + SQWORD(line->v2->x)) >> 1); + fixed_t y1 = fixed_t((SQWORD(line->v1->y) + SQWORD(line->v2->y)) >> 1); + fixed_t x2 = fixed_t((SQWORD(lines[i].v1->x) + SQWORD(lines[i].v2->x)) >> 1); + fixed_t y2 = fixed_t((SQWORD(lines[i].v1->y) + SQWORD(lines[i].v2->y)) >> 1); fixed_t alpha = Scale (lines[i].args[4], OPAQUE, 255); AStackPoint *anchor = Spawn(x1, y1, 0, NO_REPLACE); @@ -1147,42 +1044,261 @@ void P_SpawnPortal(line_t *line, int sectortag, int plane, int alpha) reference->flags |= MF_JUSTATTACKED; anchor->flags |= MF_JUSTATTACKED; - int s; - FSectorTagIterator itr(sectortag); - while ((s = itr.Next()) >= 0) - { - SetPortal(§ors[s], plane, reference, alpha); - } - - for (int j=0;j= 0) - { - SetPortal(§ors[s], plane, reference, alpha); - } - } - } - } - + CopyPortal(sectortag, plane, reference, alpha, false); return; } } } +// This searches the viewpoint's sector +// for a skybox line special, gets its tag and transfers the skybox to all tagged sectors. +void P_SpawnSkybox(ASkyViewpoint *origin) +{ + sector_t *Sector = origin->Sector; + if (Sector == NULL) + { + Printf("Sector not initialized for SkyCamCompat\n"); + origin->Sector = Sector = P_PointInSector(origin->x, origin->y); + } + if (Sector) + { + line_t * refline = NULL; + for (short i = 0; i < Sector->linecount; i++) + { + refline = Sector->lines[i]; + if (refline->special == Sector_SetPortal && refline->args[1] == 2) + { + // We found the setup linedef for this skybox, so let's use it for our init. + CopyPortal(refline->args[0], refline->args[2], origin, 0, true); + return; + } + } + } +} + + + +// +// P_SetSectorDamage +// +// Sets damage properties for one sector. Allows combination of original specials with explicit use of the damage properties +// + +static void P_SetupSectorDamage(sector_t *sector, int damage, int interval, int leakchance, FName type, int flags) +{ + // Only set if damage is not yet initialized. This ensures that UDMF takes precedence over sector specials. + if (sector->damageamount == 0) + { + sector->damageamount = damage; + sector->damageinterval = MAX(1, interval); + sector->leakydamage = leakchance; + sector->damagetype = type; + sector->Flags = (sector->Flags & ~SECF_DAMAGEFLAGS) | (flags & SECF_DAMAGEFLAGS); + } +} + +// +// P_InitSectorSpecial +// +// Sets up everything derived from 'sector->special' for one sector +// ('fromload' is necessary to allow conversion upon savegame load.) +// + +void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers) +{ + // [RH] All secret sectors are marked with a BOOM-ish bitfield + if (sector->special & SECRET_MASK) + { + sector->Flags |= SECF_SECRET | SECF_WASSECRET; + level.total_secrets++; + } + if (sector->special & FRICTION_MASK) + { + sector->Flags |= SECF_FRICTION; + } + if (sector->special & PUSH_MASK) + { + sector->Flags |= SECF_PUSH; + } + if ((sector->special & DAMAGE_MASK) == 0x100) + { + P_SetupSectorDamage(sector, 5, 32, 0, NAME_Fire, 0); + } + else if ((sector->special & DAMAGE_MASK) == 0x200) + { + P_SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); + } + else if ((sector->special & DAMAGE_MASK) == 0x300) + { + P_SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + } + sector->special &= 0xff; + + // [RH] Normal DOOM special or BOOM specialized? + bool keepspecial = false; + switch (sector->special) + { + case Light_Phased: + if (!nothinkers) new DPhased (sector, 48, 63 - (sector->lightlevel & 63)); + break; + + // [RH] Hexen-like phased lighting + case LightSequenceStart: + if (!nothinkers) new DPhased (sector); + break; + + case dLight_Flicker: + if (!nothinkers) new DLightFlash (sector); + break; + + case dLight_StrobeFast: + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); + break; + + case dLight_StrobeSlow: + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, SLOWDARK, false); + break; + + case dLight_Strobe_Hurt: + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); + P_SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + break; + + case dDamage_Hellslime: + P_SetupSectorDamage(sector, 10, 32, 0, NAME_Slime, 0); + break; + + case dDamage_Nukage: + P_SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); + break; + + case dLight_Glow: + if (!nothinkers) new DGlow (sector); + break; + + case dSector_DoorCloseIn30: + P_SpawnDoorCloseIn30 (sector); + break; + + case dDamage_End: + P_SetupSectorDamage(sector, 20, 32, 256, NAME_None, SECF_ENDGODMODE|SECF_ENDLEVEL); + break; + + case dLight_StrobeSlowSync: + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, SLOWDARK, true); + break; + + case dLight_StrobeFastSync: + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, FASTDARK, true); + break; + + case dSector_DoorRaiseIn5Mins: + P_SpawnDoorRaiseIn5Mins (sector); + break; + + case dFriction_Low: + sector->friction = FRICTION_LOW; + sector->movefactor = 0x269; + sector->Flags |= SECF_FRICTION; + break; + + case dDamage_SuperHellslime: + P_SetupSectorDamage(sector, 20, 32, 5, NAME_Slime, 0); + break; + + case dLight_FireFlicker: + if (!nothinkers) new DFireFlicker (sector); + break; + + case dDamage_LavaWimpy: + P_SetupSectorDamage(sector, 5, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + break; + + case dDamage_LavaHefty: + P_SetupSectorDamage(sector, 8, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + break; + + case dScroll_EastLavaDamage: + P_SetupSectorDamage(sector, 5, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + if (!nothinkers) + { + new DStrobe(sector, STROBEBRIGHT, FASTDARK, false); + new DScroller(DScroller::sc_floor, (-FRACUNIT / 2) << 3, + 0, -1, int(sector - sectors), 0); + } + keepspecial = true; + break; + + case hDamage_Sludge: + P_SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, 0); + break; + + case sLight_Strobe_Hurt: + P_SetupSectorDamage(sector, 5, 32, 0, NAME_Slime, 0); + if (!nothinkers) new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); + break; + + case sDamage_Hellslime: + P_SetupSectorDamage(sector, 2, 32, 0, NAME_Slime, SECF_HAZARD); + break; + + case Damage_InstantDeath: + // Strife's instant death sector + P_SetupSectorDamage(sector, TELEFRAG_DAMAGE, 1, 256, NAME_InstantDeath, 0); + break; + + case sDamage_SuperHellslime: + P_SetupSectorDamage(sector, 4, 32, 0, NAME_Slime, SECF_HAZARD); + break; + + case Sector_Hidden: + sector->MoreFlags |= SECF_HIDDEN; + break; + + case Sector_Heal: + // CoD's healing sector + P_SetupSectorDamage(sector, -1, 32, 0, NAME_None, 0); + break; + + case Sky2: + sector->sky = PL_SKYFLAT; + break; + + default: + if (sector->special >= Scroll_North_Slow && + sector->special <= Scroll_SouthWest_Fast) + { // Hexen scroll special + static const char hexenScrollies[24][2] = + { + { 0, 1 }, { 0, 2 }, { 0, 4 }, + { -1, 0 }, { -2, 0 }, { -4, 0 }, + { 0, -1 }, { 0, -2 }, { 0, -4 }, + { 1, 0 }, { 2, 0 }, { 4, 0 }, + { 1, 1 }, { 2, 2 }, { 4, 4 }, + { -1, 1 }, { -2, 2 }, { -4, 4 }, + { -1, -1 }, { -2, -2 }, { -4, -4 }, + { 1, -1 }, { 2, -2 }, { 4, -4 } + }; + + + int i = sector->special - Scroll_North_Slow; + fixed_t dx = hexenScrollies[i][0] * (FRACUNIT/2); + fixed_t dy = hexenScrollies[i][1] * (FRACUNIT/2); + if (!nothinkers) new DScroller (DScroller::sc_floor, dx, dy, -1, int(sector-sectors), 0); + } + else if (sector->special >= Carry_East5 && + sector->special <= Carry_East35) + { // Heretic scroll special + // Only east scrollers also scroll the texture + if (!nothinkers) new DScroller (DScroller::sc_floor, + (-FRACUNIT/2)<<(sector->special - Carry_East5), + 0, -1, int(sector-sectors), 0); + } + keepspecial = true; + break; + } + if (!keepspecial) sector->special = 0; +} // // P_SpawnSpecials @@ -1204,135 +1320,7 @@ void P_SpawnSpecials (void) if (sector->special == 0) continue; - // [RH] All secret sectors are marked with a BOOM-ish bitfield - if (sector->special & SECRET_MASK) - level.total_secrets++; - - switch (sector->special & 0xff) - { - // [RH] Normal DOOM/Hexen specials. We clear off the special for lights - // here instead of inside the spawners. - - case dLight_Flicker: - // FLICKERING LIGHTS - new DLightFlash (sector); - sector->special &= 0xff00; - break; - - case dLight_StrobeFast: - // STROBE FAST - new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); - sector->special &= 0xff00; - break; - - case dLight_StrobeSlow: - // STROBE SLOW - new DStrobe (sector, STROBEBRIGHT, SLOWDARK, false); - sector->special &= 0xff00; - break; - - case dLight_Strobe_Hurt: - case sLight_Strobe_Hurt: - // STROBE FAST/DEATH SLIME - new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); - break; - - case dLight_Glow: - // GLOWING LIGHT - new DGlow (sector); - sector->special &= 0xff00; - break; - - case dSector_DoorCloseIn30: - // DOOR CLOSE IN 30 SECONDS - P_SpawnDoorCloseIn30 (sector); - break; - - case dLight_StrobeSlowSync: - // SYNC STROBE SLOW - new DStrobe (sector, STROBEBRIGHT, SLOWDARK, true); - sector->special &= 0xff00; - break; - - case dLight_StrobeFastSync: - // SYNC STROBE FAST - new DStrobe (sector, STROBEBRIGHT, FASTDARK, true); - sector->special &= 0xff00; - break; - - case dSector_DoorRaiseIn5Mins: - // DOOR RAISE IN 5 MINUTES - P_SpawnDoorRaiseIn5Mins (sector); - break; - - case dLight_FireFlicker: - // fire flickering - new DFireFlicker (sector); - sector->special &= 0xff00; - break; - - case dFriction_Low: - sector->friction = FRICTION_LOW; - sector->movefactor = 0x269; - sector->special &= 0xff00; - sector->special |= FRICTION_MASK; - break; - - // [RH] Hexen-like phased lighting - case LightSequenceStart: - new DPhased (sector); - break; - - case Light_Phased: - new DPhased (sector, 48, 63 - (sector->lightlevel & 63)); - break; - - case Sky2: - sector->sky = PL_SKYFLAT; - break; - - case dScroll_EastLavaDamage: - new DStrobe (sector, STROBEBRIGHT, FASTDARK, false); - new DScroller (DScroller::sc_floor, (-FRACUNIT/2)<<3, - 0, -1, int(sector-sectors), 0); - break; - - case Sector_Hidden: - sector->MoreFlags |= SECF_HIDDEN; - sector->special &= 0xff00; - break; - - default: - if ((sector->special & 0xff) >= Scroll_North_Slow && - (sector->special & 0xff) <= Scroll_SouthWest_Fast) - { // Hexen scroll special - static const char hexenScrollies[24][2] = - { - { 0, 1 }, { 0, 2 }, { 0, 4 }, - { -1, 0 }, { -2, 0 }, { -4, 0 }, - { 0, -1 }, { 0, -2 }, { 0, -4 }, - { 1, 0 }, { 2, 0 }, { 4, 0 }, - { 1, 1 }, { 2, 2 }, { 4, 4 }, - { -1, 1 }, { -2, 2 }, { -4, 4 }, - { -1, -1 }, { -2, -2 }, { -4, -4 }, - { 1, -1 }, { 2, -2 }, { 4, -4 } - }; - - int i = (sector->special & 0xff) - Scroll_North_Slow; - fixed_t dx = hexenScrollies[i][0] * (FRACUNIT/2); - fixed_t dy = hexenScrollies[i][1] * (FRACUNIT/2); - new DScroller (DScroller::sc_floor, dx, dy, -1, int(sector-sectors), 0); - } - else if ((sector->special & 0xff) >= Carry_East5 && - (sector->special & 0xff) <= Carry_East35) - { // Heretic scroll special - // Only east scrollers also scroll the texture - new DScroller (DScroller::sc_floor, - (-FRACUNIT/2)<<((sector->special & 0xff) - Carry_East5), - 0, -1, int(sector-sectors), 0); - } - break; - } + P_InitSectorSpecial(sector, sector->special, false); } // Init other misc stuff @@ -1341,6 +1329,13 @@ void P_SpawnSpecials (void) P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + TThinkerIterator it2; + ASkyCamCompat *pt2; + while ((pt2 = it2.Next())) + { + P_SpawnSkybox(pt2); + } + for (i = 0; i < numlines; i++) { switch (lines[i].special) @@ -1454,8 +1449,24 @@ void P_SpawnSpecials (void) FSectorTagIterator itr(lines[i].args[0]); while ((s = itr.Next()) >= 0) { - sectors[s].damage = damage; - sectors[s].mod = 0;//MOD_UNKNOWN; + sector_t *sec = §ors[s]; + sec->damageamount = damage; + sec->damagetype = NAME_None; + if (sec->damageamount < 20) + { + sec->leakydamage = 0; + sec->damageinterval = 32; + } + else if (sec->damageamount < 50) + { + sec->leakydamage = 5; + sec->damageinterval = 32; + } + else + { + sec->leakydamage = 256; + sec->damageinterval = 1; + } } } break; @@ -2057,11 +2068,11 @@ void P_SetSectorFriction (int tag, int amount, bool alterFlag) // can be enabled and disabled at will. if (friction == ORIG_FRICTION) { - sectors[s].special &= ~FRICTION_MASK; + sectors[s].Flags &= ~SECF_FRICTION; } else { - sectors[s].special |= FRICTION_MASK; + sectors[s].Flags |= SECF_FRICTION; } } } @@ -2177,7 +2188,7 @@ void DPusher::Tick () // Be sure the special sector type is still turned on. If so, proceed. // Else, bail out; the sector type has been changed on us. - if (!(sec->special & PUSH_MASK)) + if (!(sec->Flags & SECF_PUSH)) return; // For constant pushers (wind/current) there are 3 situations: @@ -2223,7 +2234,7 @@ void DPusher::Tick () { int sx = m_X; int sy = m_Y; - int dist = P_AproxDistance (thing->x - sx,thing->y - sy); + int dist = thing->AproxDistance (sx, sy); int speed = (m_Magnitude - ((dist>>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); // If speed <= 0, you're outside the effective radius. You also have @@ -2231,7 +2242,7 @@ void DPusher::Tick () if ((speed > 0) && (P_CheckSight (thing, m_Source, SF_IGNOREVISIBILITY))) { - angle_t pushangle = R_PointToAngle2 (thing->x, thing->y, sx, sy); + angle_t pushangle = thing->AngleTo(sx, sy); if (m_Source->GetClass()->TypeName == NAME_PointPusher) pushangle += ANG180; // away pushangle >>= ANGLETOFINESHIFT; @@ -2270,7 +2281,7 @@ void DPusher::Tick () } else // special water sector { - ht = hsec->floorplane.ZatPoint (thing->x, thing->y); + ht = hsec->floorplane.ZatPoint(thing); if (thing->z > ht) // above ground { xspeed = m_Xmag; // full force @@ -2299,7 +2310,7 @@ void DPusher::Tick () { // special water sector floor = &hsec->floorplane; } - if (thing->z > floor->ZatPoint (thing->x, thing->y)) + if (thing->z > floor->ZatPoint(thing)) { // above ground xspeed = yspeed = 0; // no force } diff --git a/src/p_spec.h b/src/p_spec.h index 0c3ee87450..a95e07ce8b 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -158,6 +158,7 @@ bool PIT_PushThing (AActor *thing); bool CheckIfExitIsGood (AActor *self, level_info_t *info); // at map load +void P_InitSectorSpecial(sector_t *sector, int special, bool nothinkers); void P_SpawnSpecials (void); // every tic @@ -673,7 +674,7 @@ protected: // [RH] Need these for BOOM-ish transferring ceilings FTextureID m_Texture; - int m_NewSpecial; + secspecial_t m_NewSpecial; // ID int m_Tag; @@ -760,7 +761,7 @@ protected: int m_Crush; bool m_Hexencrush; int m_Direction; - int m_NewSpecial; + secspecial_t m_NewSpecial; FTextureID m_Texture; fixed_t m_FloorDestDist; fixed_t m_Speed; diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 5aeff7b469..984794c8af 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -177,11 +177,48 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) if ((TexMan.FindSwitch(side->GetTexture(side_t::top))) != NULL) { - return (user->z + user->height >= open.top); + + // Check 3D floors on back side + { + sector_t * back = line->sidedef[1 - sideno]->sector; + for (unsigned i = 0; i < back->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *rover = back->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (!(rover->flags & FF_UPPERTEXTURE)) continue; + + if (user->z > rover->top.plane->ZatPoint(checkx, checky) || + user->z + user->height < rover->bottom.plane->ZatPoint(checkx, checky)) + continue; + + // This 3D floor depicts a switch texture in front of the player's eyes + return true; + } + } + + return (user->z + user->height > open.top); } else if ((TexMan.FindSwitch(side->GetTexture(side_t::bottom))) != NULL) { - return (user->z <= open.bottom); + // Check 3D floors on back side + { + sector_t * back = line->sidedef[1 - sideno]->sector; + for (unsigned i = 0; i < back->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *rover = back->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + if (!(rover->flags & FF_LOWERTEXTURE)) continue; + + if (user->z > rover->top.plane->ZatPoint(checkx, checky) || + user->z + user->height < rover->bottom.plane->ZatPoint(checkx, checky)) + continue; + + // This 3D floor depicts a switch texture in front of the player's eyes + return true; + } + } + + return (user->z < open.bottom); } else if ((flags & ML_3DMIDTEX) || (TexMan.FindSwitch(side->GetTexture(side_t::mid))) != NULL) { @@ -194,7 +231,7 @@ bool P_CheckSwitchRange(AActor *user, line_t *line, int sideno) else { // no switch found. Check whether the player can touch either top or bottom texture - return (user->z + user->height >= open.top) || (user->z <= open.bottom); + return (user->z + user->height > open.top) || (user->z < open.bottom); } } diff --git a/src/p_tags.cpp b/src/p_tags.cpp index c08caec8fb..15528378b0 100644 --- a/src/p_tags.cpp +++ b/src/p_tags.cpp @@ -98,7 +98,7 @@ void FTagManager::AddSectorTag(int sector, int tag) void FTagManager::RemoveSectorTags(int sect) { - if (startForSector.Size() > 0) + if (startForSector.Size() > (unsigned int)sect) { int start = startForSector[sect]; if (start >= 0) diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 82359f4e04..380f5572d1 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -211,7 +211,7 @@ bool P_Teleport (AActor *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, if (thing->player && (useFog || !keepOrientation) && bHaltVelocity) { // Freeze player for about .5 sec - if (thing->Inventory == NULL || thing->Inventory->GetSpeedFactor() <= FRACUNIT) + if (thing->Inventory == NULL || !thing->Inventory->GetNoTeleportFreeze()) thing->reactiontime = 18; } if (thing->flags & MF_MISSILE) @@ -530,7 +530,7 @@ bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBO int fudge = FUDGEFACTOR; // Make sure we are on correct side of exit linedef. - while (P_PointOnLineSide(x, y, l) != side && --fudge >= 0) + while (P_PointOnLineSidePrecise(x, y, l) != side && --fudge >= 0) { if (abs(l->dx) > abs(l->dy)) y -= (l->dx < 0) != side ? -1 : 1; diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index a1a9ea25b4..af8de5cab2 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -46,6 +46,7 @@ #include "s_sound.h" #include "p_local.h" #include "templates.h" +#include "farchive.h" // MACROS ------------------------------------------------------------------ @@ -121,7 +122,6 @@ static void ParseSplash (FScanner &sc); static void ParseTerrain (FScanner &sc); static void ParseFloor (FScanner &sc); static int FindSplash (FName name); -static int FindTerrain (FName name); static void GenericParse (FScanner &sc, FGenericParse *parser, const char **keywords, void *fields, const char *type, FName name); static void ParseDamage (FScanner &sc, int keyword, void *fields); @@ -427,7 +427,7 @@ void ParseTerrain (FScanner &sc) sc.MustGetString (); name = sc.String; - terrainnum = (int)FindTerrain (name); + terrainnum = (int)P_FindTerrain (name); if (terrainnum < 0) { FTerrainDef def; @@ -637,7 +637,7 @@ static void ParseFloor (FScanner &sc) return; } sc.MustGetString (); - terrain = FindTerrain (sc.String); + terrain = P_FindTerrain (sc.String); if (terrain == -1) { Printf ("Unknown terrain %s\n", sc.String); @@ -657,7 +657,7 @@ static void ParseDefault (FScanner &sc) int terrain; sc.MustGetString (); - terrain = FindTerrain (sc.String); + terrain = P_FindTerrain (sc.String); if (terrain == -1) { Printf ("Unknown terrain %s\n", sc.String); @@ -692,7 +692,7 @@ int FindSplash (FName name) // //========================================================================== -int FindTerrain (FName name) +int P_FindTerrain (FName name) { unsigned int i; @@ -705,3 +705,26 @@ int FindTerrain (FName name) } return -1; } + +void P_SerializeTerrain(FArchive &arc, int &terrainnum) +{ + FName val; + if (arc.IsStoring()) + { + if (terrainnum < 0 || terrainnum >= (int)Terrains.Size()) + { + val = NAME_Null; + } + else + { + val = Terrains[terrainnum].Name; + } + arc << val; + } + else + { + arc << val; + terrainnum = P_FindTerrain(val); + + } +} diff --git a/src/p_terrain.h b/src/p_terrain.h index 482c66b00e..dab4904b66 100644 --- a/src/p_terrain.h +++ b/src/p_terrain.h @@ -122,4 +122,8 @@ struct FTerrainDef extern TArray Splashes; extern TArray Terrains; +class FArchive; +int P_FindTerrain(FName name); +void P_SerializeTerrain(FArchive &arc, int &terrainnum); + #endif //__P_TERRAIN_H__ diff --git a/src/p_things.cpp b/src/p_things.cpp index 632f53bc38..e8f9d60114 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -82,7 +82,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, } while (spot != NULL) { - mobj = Spawn (kind, spot->x, spot->y, spot->z, ALLOW_REPLACE); + mobj = Spawn (kind, spot->X(), spot->Y(), spot->Z(), ALLOW_REPLACE); if (mobj != NULL) { @@ -94,7 +94,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, mobj->angle = (angle != ANGLE_MAX ? angle : spot->angle); if (fog) { - P_SpawnTeleportFog(mobj, spot->x, spot->y, spot->z + TELEFOGHEIGHT, false, true); + P_SpawnTeleportFog(mobj, spot->X(), spot->Y(), spot->Z() + TELEFOGHEIGHT, false, true); } if (mobj->flags & MF_SPECIAL) mobj->flags |= MF_DROPPED; // Don't respawn @@ -165,7 +165,7 @@ bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog) if (source != NULL && target != NULL) { - return P_MoveThing(source, target->x, target->y, target->z, fog); + return P_MoveThing(source, target->X(), target->Y(), target->Z(), fog); } return false; } @@ -304,7 +304,8 @@ bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_nam } else { -nolead: mobj->angle = R_PointToAngle2 (mobj->x, mobj->y, targ->x, targ->y); +nolead: + mobj->angle = mobj->AngleTo(targ); aim.Resize (fspeed); mobj->velx = fixed_t(aim[0]); mobj->vely = fixed_t(aim[1]); @@ -678,3 +679,132 @@ void InitSpawnablesFromMapinfo() InitClassMap(SpawnableThings, SpawnablesFromMapinfo); InitClassMap(StrifeTypes, ConversationIDsFromMapinfo); } + + +int P_Thing_Warp(AActor *caller, AActor *reference, fixed_t xofs, fixed_t yofs, fixed_t zofs, angle_t angle, int flags, fixed_t heightoffset, fixed_t radiusoffset, angle_t pitch) +{ + if (flags & WARPF_MOVEPTR) + { + AActor *temp = reference; + reference = caller; + caller = temp; + } + + fixed_t oldx = caller->x; + fixed_t oldy = caller->y; + fixed_t oldz = caller->z; + zofs += FixedMul(reference->height, heightoffset); + + + if (!(flags & WARPF_ABSOLUTEANGLE)) + { + angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; + } + + const fixed_t rad = FixedMul(radiusoffset, reference->radius); + const angle_t fineangle = angle >> ANGLETOFINESHIFT; + + if (!(flags & WARPF_ABSOLUTEPOSITION)) + { + if (!(flags & WARPF_ABSOLUTEOFFSET)) + { + fixed_t xofs1 = xofs; + + // (borrowed from A_SpawnItemEx, assumed workable) + // in relative mode negative y values mean 'left' and positive ones mean 'right' + // This is the inverse orientation of the absolute mode! + + xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); + yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); + } + + if (flags & WARPF_TOFLOOR) + { + // set correct xy + // now the caller's floorz should be appropriate for the assigned xy-position + // assigning position again with. + // extra unlink, link and environment calculation + caller->SetOrigin( + reference->x + xofs + FixedMul(rad, finecosine[fineangle]), + reference->y + yofs + FixedMul(rad, finesine[fineangle]), + reference->z); + caller->z = caller->floorz + zofs; + } + else + { + caller->SetOrigin( + reference->x + xofs + FixedMul(rad, finecosine[fineangle]), + reference->y + yofs + FixedMul(rad, finesine[fineangle]), + reference->z + zofs); + } + } + else // [MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. + { + if (flags & WARPF_TOFLOOR) + { + caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs); + caller->z = caller->floorz + zofs; + } + else + { + caller->SetOrigin(xofs + FixedMul(rad, finecosine[fineangle]), yofs + FixedMul(rad, finesine[fineangle]), zofs); + } + } + + if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) + { + if (flags & WARPF_TESTONLY) + { + caller->SetOrigin(oldx, oldy, oldz); + } + else + { + caller->angle = angle; + + if (flags & WARPF_COPYPITCH) + caller->SetPitch(reference->pitch, false); + + if (pitch) + caller->SetPitch(caller->pitch + pitch, false); + + if (flags & WARPF_COPYVELOCITY) + { + caller->velx = reference->velx; + caller->vely = reference->vely; + caller->velz = reference->velz; + } + if (flags & WARPF_STOP) + { + caller->velx = 0; + caller->vely = 0; + caller->velz = 0; + } + + if (flags & WARPF_WARPINTERPOLATION) + { + caller->PrevX += caller->x - oldx; + caller->PrevY += caller->y - oldy; + caller->PrevZ += caller->z - oldz; + } + else if (flags & WARPF_COPYINTERPOLATION) + { + caller->PrevX = caller->x + reference->PrevX - reference->x; + caller->PrevY = caller->y + reference->PrevY - reference->y; + caller->PrevZ = caller->z + reference->PrevZ - reference->z; + } + else if (!(flags & WARPF_INTERPOLATE)) + { + caller->PrevX = caller->x; + caller->PrevY = caller->y; + caller->PrevZ = caller->z; + } + if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) + { + caller->z += reference->GetBobOffset(); + } + } + return true; + } + caller->SetOrigin(oldx, oldy, oldz); + return false; +} \ No newline at end of file diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 26852269ab..4b4683be50 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -42,7 +42,8 @@ struct FTraceInfo { fixed_t StartX, StartY, StartZ; fixed_t Vx, Vy, Vz; - DWORD ActorMask, WallMask; + ActorFlags ActorMask; + DWORD WallMask; AActor *IgnoreThis; FTraceResults *Results; sector_t *CurSector; @@ -70,7 +71,7 @@ static bool EditTraceResult (DWORD flags, FTraceResults &res); bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, - DWORD actorMask, DWORD wallMask, AActor *ignore, + ActorFlags actorMask, DWORD wallMask, AActor *ignore, FTraceResults &res, DWORD flags, ETraceStatus (*callback)(FTraceResults &res, void *), void *callbackdata) { @@ -104,7 +105,6 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, res.Crossed3DWater = NULL; */ -#ifdef _3DFLOORS // Do a 3D floor check in the starting sector TDeletingArray &ff = sector->e->XFloor.ffloors; @@ -174,7 +174,6 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, } } } -#endif // check for overflows and clip if necessary SQWORD xd = (SQWORD)x + ( ( SQWORD(vx) * SQWORD(maxDist) )>>16); @@ -257,7 +256,6 @@ bool FTraceInfo::TraceTraverse (int ptflags) fixed_t dist; // Deal with splashes in 3D floors -#ifdef _3DFLOORS if (CurSector->e->XFloor.ffloors.Size()) { for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) @@ -274,7 +272,6 @@ bool FTraceInfo::TraceTraverse (int ptflags) } } } -#endif if (in->isaline) { @@ -381,7 +378,6 @@ bool FTraceInfo::TraceTraverse (int ptflags) } else { // made it past the wall -#ifdef _3DFLOORS // check for 3D floors first if (entersector->e->XFloor.ffloors.Size()) { @@ -435,9 +431,6 @@ bool FTraceInfo::TraceTraverse (int ptflags) } } } -#endif - - Results->HitType = TRACE_HitNone; if (TraceFlags & TRACE_PCross) @@ -450,9 +443,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); } } -#ifdef _3DFLOORS cont: -#endif if (Results->HitType != TRACE_HitNone) { diff --git a/src/p_trace.h b/src/p_trace.h index 403b1f9cf7..ecf37e01e1 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -35,6 +35,7 @@ #define __P_TRACE_H__ #include +#include "actor.h" #include "textures/textures.h" struct sector_t; @@ -96,7 +97,7 @@ enum ETraceStatus bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, - DWORD ActorMask, DWORD WallMask, AActor *ignore, + ActorFlags ActorMask, DWORD WallMask, AActor *ignore, FTraceResults &res, DWORD traceFlags=0, ETraceStatus (*callback)(FTraceResults &res, void *)=NULL, void *callbackdata=NULL); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index e10cc33f47..5b6c0b60fa 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -48,6 +48,7 @@ #include "w_wad.h" #include "p_tags.h" #include "portal.h" +#include "p_terrain.h" //=========================================================================== // @@ -475,6 +476,7 @@ public: th->RenderStyle = STYLE_Count; th->alpha = -1; th->health = 1; + th->FloatbobPhase = -1; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { @@ -632,6 +634,11 @@ public: Flag(th->flags, MTF_SECRET, key); break; + case NAME_Floatbobphase: + CHECK_N(Zd | Zdt) + th->FloatbobPhase = CheckInt(key); + break; + case NAME_Renderstyle: { FName style = CheckString(key); @@ -780,7 +787,7 @@ public: bool strifetrans = false; bool strifetrans2 = false; FString arg0str, arg1str; - int lineid; // forZDoomTranslated namespace + int lineid = -1; // forZDoomTranslated namespace FString tagstring; memset(ld, 0, sizeof(*ld)); @@ -1077,7 +1084,7 @@ public: maplinedef_t mld; memset(&mld, 0, sizeof(mld)); mld.special = ld->special; - mld.tag = lineid; + mld.tag = ld->args[0]; P_TranslateLineDef(ld, &mld); ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY)); } @@ -1294,6 +1301,8 @@ public: sec->prevsec = -1; // stair retriggering until build completes sec->heightsec = NULL; // sector used to get floor and ceiling height sec->sectornum = index; + sec->damageinterval = 32; + sec->terrainnum[sector_t::ceiling] = sec->terrainnum[sector_t::floor] = -1; if (floordrop) sec->Flags = SECF_FLOORDROP; // killough 3/7/98: end changes @@ -1519,6 +1528,39 @@ public: cp[3] = CheckFloat(key); break; + case NAME_damageamount: + sec->damageamount = CheckInt(key); + break; + + case NAME_damagetype: + sec->damagetype = CheckString(key); + break; + + case NAME_damageinterval: + sec->damageinterval = CheckInt(key); + if (sec->damageinterval < 1) sec->damageinterval = 1; + break; + + case NAME_leakiness: + sec->leakydamage = CheckInt(key); + break; + + case NAME_damageterraineffect: + Flag(sec->Flags, SECF_DMGTERRAINFX, key); + break; + + case NAME_damagehazard: + Flag(sec->Flags, SECF_HAZARD, key); + break; + + case NAME_floorterrain: + sec->terrainnum[sector_t::floor] = P_FindTerrain(CheckString(key)); + break; + + case NAME_ceilingterrain: + sec->terrainnum[sector_t::ceiling] = P_FindTerrain(CheckString(key)); + break; + case NAME_MoreIds: // delay parsing of the tag string until parsing of the sector is complete // This ensures that the ID is always the first tag in the list. @@ -1545,7 +1587,15 @@ public: } } - sec->secretsector = !!(sec->special&SECRET_MASK); + if (sec->damageamount == 0) + { + // If no damage is set, clear all other related properties so that they do not interfere + // with other means of setting them. + sec->damagetype = NAME_None; + sec->damageinterval = 0; + sec->leakydamage = 0; + sec->Flags &= ~SECF_DAMAGEFLAGS; + } // Reset the planes to their defaults if not all of the plane equation's parameters were found. if (fplaneflags != 15) @@ -1589,10 +1639,10 @@ public: { // [RH] Sectors default to white light with the default fade. // If they are outside (have a sky ceiling), they use the outside fog. - if (level.outsidefog != 0xff000000 && (sec->GetTexture(sector_t::ceiling) == skyflatnum || (sec->special&0xff) == Sector_Outside)) + if (level.outsidefog != 0xff000000 && (sec->GetTexture(sector_t::ceiling) == skyflatnum || (sec->special & 0xff) == Sector_Outside)) { if (fogMap == NULL) - fogMap = GetSpecialLights (PalEntry (255,255,255), level.outsidefog, 0); + fogMap = GetSpecialLights(PalEntry(255, 255, 255), level.outsidefog, 0); sec->ColorMap = fogMap; } else @@ -1605,9 +1655,9 @@ public: else { if (lightcolor == -1) lightcolor = PalEntry(255,255,255); - if (fadecolor == -1) + if (fadecolor == -1) { - if (level.outsidefog != 0xff000000 && (sec->GetTexture(sector_t::ceiling) == skyflatnum || (sec->special&0xff) == Sector_Outside)) + if (level.outsidefog != 0xff000000 && (sec->GetTexture(sector_t::ceiling) == skyflatnum || (sec->special & 0xff) == Sector_Outside)) fadecolor = level.outsidefog; else fadecolor = level.fadeto; diff --git a/src/p_user.cpp b/src/p_user.cpp index 5f9a69c3a0..ba987d3930 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -362,6 +362,8 @@ player_t &player_t::operator=(const player_t &p) damagecount = p.damagecount; bonuscount = p.bonuscount; hazardcount = p.hazardcount; + hazardtype = p.hazardtype; + hazardinterval = p.hazardinterval; poisoncount = p.poisoncount; poisontype = p.poisontype; poisonpaintype = p.poisonpaintype; @@ -566,6 +568,10 @@ void APlayerPawn::Serialize (FArchive &arc) { arc << AirCapacity; } + if (SaveVersion >= 4526) + { + arc << ViewHeight; + } } //=========================================================================== @@ -1562,12 +1568,15 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SkullPop) if (player != NULL) { player->mo = mo; - if (player->camera == self) - { - player->camera = mo; - } player->damagecount = 32; } + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].camera == self) + { + players[i].camera = mo; + } + } } //---------------------------------------------------------------------------- @@ -2531,11 +2540,15 @@ void P_PlayerThink (player_t *player) if (!(player->cheats & CF_PREDICTING)) { P_PlayerOnSpecial3DFloor (player); - if (player->mo->Sector->special || player->mo->Sector->damage) + P_PlayerInSpecialSector (player); + + if (player->mo->z <= player->mo->Sector->floorplane.ZatPoint( + player->mo->x, player->mo->y) || + player->mo->waterlevel) { - P_PlayerInSpecialSector (player); + // Player must be touching the floor + P_PlayerOnSpecialFlat(player, P_GetThingFloorType(player->mo)); } - P_PlayerOnSpecialFlat (player, P_GetThingFloorType (player->mo)); if (player->mo->velz <= -player->mo->FallingScreamMinSpeed && player->mo->velz >= -player->mo->FallingScreamMaxSpeed && !player->morphTics && player->mo->waterlevel == 0) @@ -2587,8 +2600,8 @@ void P_PlayerThink (player_t *player) if (player->hazardcount) { player->hazardcount--; - if (!(level.time & 31) && player->hazardcount > 16*TICRATE) - P_DamageMobj (player->mo, NULL, NULL, 5, NAME_Slime); + if (!(level.time % player->hazardinterval) && player->hazardcount > 16*TICRATE) + P_DamageMobj (player->mo, NULL, NULL, 5, player->hazardtype); } if (player->poisoncount && !(level.time & 15)) @@ -3001,7 +3014,12 @@ void player_t::Serialize (FArchive &arc) << air_finished << turnticks << oldbuttons; - bool IsBot; + if (SaveVersion >= 4929) + { + arc << hazardtype + << hazardinterval; + } + bool IsBot = false; if (SaveVersion >= 4514) { arc << Bot; @@ -3030,6 +3048,12 @@ void player_t::Serialize (FArchive &arc) WeaponState = ((cheats >> 14) & 1) | ((cheats & (0x37 << 24)) >> (24 - 1)); cheats &= ~((1 << 14) | (0x37 << 24)); } + if (SaveVersion < 4527) + { + BYTE oldWeaponState; + arc << oldWeaponState; + WeaponState = oldWeaponState; + } else { arc << WeaponState; diff --git a/src/pathexpander.cpp b/src/pathexpander.cpp new file mode 100644 index 0000000000..bb89a0555d --- /dev/null +++ b/src/pathexpander.cpp @@ -0,0 +1,134 @@ +/* +** pathexpander.cpp +** Utility class for expanding a given path with a range of directories +** +**--------------------------------------------------------------------------- +** Copyright 2015 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "pathexpander.h" +#include "cmdlib.h" +#include "w_wad.h" + +//============================================================================ +// +// +// +//============================================================================ + +static FString BuildPath(const FString &base, const char *name) +{ + FString current; + if (base.IsNotEmpty()) + { + current = base; + if (current[current.Len() - 1] != '/') current += '/'; + } + current += name; + return current; +} + +//============================================================================ +// +// This is meant to find and open files for reading. +// +//============================================================================ + +FileReader *PathExpander::openFileReader(const char *name, int *plumpnum) +{ + FileReader *fp; + FString current_filename; + + if (!name || !(*name)) + { + return 0; + } + + /* First try the given name */ + current_filename = name; + FixPathSeperator(current_filename); + + + if (openmode != OM_FILE) + { + int lumpnum = Wads.CheckNumForFullName(current_filename); + if (lumpnum >= 0) + { + fp = Wads.ReopenLumpNum(lumpnum); + if (plumpnum) *plumpnum = lumpnum; + return fp; + } + if (openmode == OM_LUMP) // search the path list when not loading the main config + { + for (unsigned int plp = PathList.Size(); plp-- != 0; ) + { /* Try along the path then */ + current_filename = BuildPath(PathList[plp], name); + lumpnum = Wads.CheckNumForFullName(current_filename); + if (lumpnum >= 0) + { + fp = Wads.ReopenLumpNum(lumpnum); + if (plumpnum) *plumpnum = lumpnum; + return fp; + } + } + return NULL; + } + } + if (plumpnum) *plumpnum = -1; + + + fp = new FileReader; + if (fp->Open(current_filename)) return fp; + + if (name[0] != '/') + { + for (unsigned int plp = PathList.Size(); plp-- != 0; ) + { /* Try along the path then */ + current_filename = BuildPath(PathList[plp], name); + if (fp->Open(current_filename)) return fp; + } + } + delete fp; + + /* Nothing could be opened. */ + return NULL; +} + +/* This adds a directory to the path list */ +void PathExpander::addToPathlist(const char *s) +{ + FString copy = s; + FixPathSeperator(copy); + PathList.Push(copy); +} + +void PathExpander::clearPathlist() +{ + PathList.Clear(); +} diff --git a/src/pathexpander.h b/src/pathexpander.h new file mode 100644 index 0000000000..556598071e --- /dev/null +++ b/src/pathexpander.h @@ -0,0 +1,31 @@ +#ifndef __PATHEXPANDER_H +#define __PATHEXPANDER_H + +#include "tarray.h" +#include "zstring.h" +#include "files.h" + +class PathExpander +{ + TArray PathList; + +public: + int openmode; + + enum + { + OM_FILEORLUMP = 0, + OM_LUMP, + OM_FILE + }; + + PathExpander(int om = OM_FILEORLUMP) + { + openmode = om; + } + void addToPathlist(const char *s); + void clearPathlist(); + FileReader *openFileReader(const char *name, int *plumpnum); +}; + +#endif diff --git a/src/po_man.cpp b/src/po_man.cpp index 60627e4f3c..fb1d12519d 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -1231,15 +1231,15 @@ bool FPolyObj::CheckMobjBlocking (side_t *sd) // Best use the one facing the player and ignore the back side. if (ld->sidedef[1] != NULL) { - int side = P_PointOnLineSide(mobj->x, mobj->y, ld); + int side = P_PointOnLineSidePrecise(mobj->x, mobj->y, ld); if (ld->sidedef[side] != sd) { continue; } // [BL] See if we hit below the floor/ceiling of the poly. else if(!performBlockingThrust && ( - mobj->z < ld->sidedef[!side]->sector->GetSecPlane(sector_t::floor).ZatPoint(mobj->x, mobj->y) || - mobj->z + mobj->height > ld->sidedef[!side]->sector->GetSecPlane(sector_t::ceiling).ZatPoint(mobj->x, mobj->y) + mobj->z < ld->sidedef[!side]->sector->GetSecPlane(sector_t::floor).ZatPoint(mobj) || + mobj->z + mobj->height > ld->sidedef[!side]->sector->GetSecPlane(sector_t::ceiling).ZatPoint(mobj) )) { performBlockingThrust = true; @@ -1533,11 +1533,15 @@ static void IterFindPolySides (FPolyObj *po, side_t *side) // //========================================================================== +static int STACK_ARGS posicmp(const void *a, const void *b) +{ + return (*(const side_t **)a)->linedef->args[1] - (*(const side_t **)b)->linedef->args[1]; +} + static void SpawnPolyobj (int index, int tag, int type) { unsigned int ii; int i; - int j; FPolyObj *po = &polyobjs[index]; for (ii = 0; ii < KnownPolySides.Size(); ++ii) @@ -1577,59 +1581,24 @@ static void SpawnPolyobj (int index, int tag, int type) // didn't find a polyobj through PO_LINE_START TArray polySideList; unsigned int psIndexOld; - for (j = 1; j < PO_MAXPOLYSEGS; j++) - { - psIndexOld = po->Sidedefs.Size(); - for (ii = 0; ii < KnownPolySides.Size(); ++ii) - { - i = KnownPolySides[ii]; + psIndexOld = po->Sidedefs.Size(); - if (i >= 0 && - sides[i].linedef->special == Polyobj_ExplicitLine && - sides[i].linedef->args[0] == tag) - { - if (!sides[i].linedef->args[1]) - { - I_Error ("SpawnPolyobj: Explicit line missing order number (probably %d) in poly %d.\n", - j+1, tag); - } - if (sides[i].linedef->args[1] == j) - { - po->Sidedefs.Push (&sides[i]); - } - } - } - // Clear out any specials for these segs...we cannot clear them out - // in the above loop, since we aren't guaranteed one seg per linedef. - for (ii = 0; ii < KnownPolySides.Size(); ++ii) + for (ii = 0; ii < KnownPolySides.Size(); ++ii) + { + i = KnownPolySides[ii]; + + if (i >= 0 && + sides[i].linedef->special == Polyobj_ExplicitLine && + sides[i].linedef->args[0] == tag) { - i = KnownPolySides[ii]; - if (i >= 0 && - sides[i].linedef->special == Polyobj_ExplicitLine && - sides[i].linedef->args[0] == tag && sides[i].linedef->args[1] == j) + if (!sides[i].linedef->args[1]) { - sides[i].linedef->special = 0; - sides[i].linedef->args[0] = 0; - KnownPolySides[ii] = -1; - } - } - if (po->Sidedefs.Size() == psIndexOld) - { // Check if an explicit line order has been skipped. - // A line has been skipped if there are any more explicit - // lines with the current tag value. [RH] Can this actually happen? - for (ii = 0; ii < KnownPolySides.Size(); ++ii) - { - i = KnownPolySides[ii]; - if (i >= 0 && - sides[i].linedef->special == Polyobj_ExplicitLine && - sides[i].linedef->args[0] == tag) - { - I_Error ("SpawnPolyobj: Missing explicit line %d for poly %d\n", - j, tag); - } + I_Error("SpawnPolyobj: Explicit line missing order number in poly %d, linedef %d.\n", tag, int(sides[i].linedef - lines)); } + po->Sidedefs.Push (&sides[i]); } } + qsort(&po->Sidedefs[0], po->Sidedefs.Size(), sizeof(po->Sidedefs[0]), posicmp); if (po->Sidedefs.Size() > 0) { po->crush = (type != SMT_PolySpawn) ? 3 : 0; diff --git a/src/posix/cocoa/i_common.h b/src/posix/cocoa/i_common.h index 545540b2fd..c0986b90af 100644 --- a/src/posix/cocoa/i_common.h +++ b/src/posix/cocoa/i_common.h @@ -31,6 +31,9 @@ ** */ +#ifndef COCOA_I_COMMON_INCLUDED +#define COCOA_I_COMMON_INCLUDED + #import @@ -50,12 +53,23 @@ struct RenderBufferOptions extern RenderBufferOptions rbOpts; +// Version of AppKit framework we are interested in +// The following values are needed to build with earlier SDKs + +#define AppKit10_4 824 +#define AppKit10_5 949 +#define AppKit10_6 1038 +#define AppKit10_7 1138 + + +@interface NSWindow(ExitAppOnClose) +- (void)exitAppOnClose; +@end + + inline bool I_IsHiDPISupported() { - // The following value shoud be equal to NSAppKitVersionNumber10_7 - // and it's hard-coded in order to build on earlier SDKs - - return NSAppKitVersionNumber >= 1138; + return NSAppKitVersionNumber >= AppKit10_7; } void I_ProcessEvent(NSEvent* event); @@ -79,6 +93,7 @@ typedef float CGFloat; // From HIToolbox/Events.h enum { + kVK_ANSI_F = 0x03, kVK_Return = 0x24, kVK_Tab = 0x30, kVK_Space = 0x31, @@ -168,3 +183,5 @@ typedef NSInteger NSApplicationActivationPolicy; @end #endif // prior to 10.7 + +#endif // COCOA_I_COMMON_INCLUDED diff --git a/src/posix/cocoa/i_input.mm b/src/posix/cocoa/i_input.mm index 5e0c5f1c85..3bbf42a9ad 100644 --- a/src/posix/cocoa/i_input.mm +++ b/src/posix/cocoa/i_input.mm @@ -57,6 +57,8 @@ CVAR(Bool, use_mouse, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, m_noprescale, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, k_allowfullscreentoggle, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) { if (self < 0) @@ -542,6 +544,16 @@ void ProcessKeyboardEvent(NSEvent* theEvent) return; } + if (k_allowfullscreentoggle + && (kVK_ANSI_F == keyCode) + && (NSCommandKeyMask & [theEvent modifierFlags]) + && (NSKeyDown == [theEvent type]) + && ![theEvent isARepeat]) + { + ToggleFullscreen = !ToggleFullscreen; + return; + } + if (GUICapture) { ProcessKeyboardEventInMenu(theEvent); diff --git a/src/posix/cocoa/i_main.mm b/src/posix/cocoa/i_main.mm index 05a8081f6c..2f65254a98 100644 --- a/src/posix/cocoa/i_main.mm +++ b/src/posix/cocoa/i_main.mm @@ -47,6 +47,7 @@ #include "i_system.h" #include "m_argv.h" #include "s_sound.h" +#include "st_console.h" #include "version.h" #undef Class @@ -125,18 +126,7 @@ void Mac_I_FatalError(const char* const message) { I_SetMainWindowVisible(false); - const CFStringRef errorString = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, - message, kCFStringEncodingASCII, kCFAllocatorNull); - - if (NULL != errorString) - { - CFOptionFlags dummy; - - CFUserNotificationDisplayAlert( 0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, - CFSTR("Fatal Error"), errorString, CFSTR("Exit"), NULL, NULL, &dummy); - - CFRelease(errorString); - } + FConsoleWindow::GetInstance().ShowFatalError(message); } @@ -315,6 +305,9 @@ ApplicationController* appCtrl; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; + FConsoleWindow::CreateInstance(); + atterm(FConsoleWindow::DeleteInstance); + exit(OriginalMain(s_argc, s_argv)); } diff --git a/src/posix/cocoa/i_system.mm b/src/posix/cocoa/i_system.mm new file mode 100644 index 0000000000..498498d946 --- /dev/null +++ b/src/posix/cocoa/i_system.mm @@ -0,0 +1,378 @@ +/* + ** i_system.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2012-2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include "i_common.h" + +#include +#include +#include + +#include "d_ticcmd.h" +#include "doomdef.h" +#include "doomerrors.h" +#include "doomstat.h" +#include "g_game.h" +#include "gameconfigfile.h" +#include "i_sound.h" +#include "i_system.h" +#include "st_console.h" +#include "v_text.h" +#include "x86.h" + + +EXTERN_CVAR(String, language) + +DWORD LanguageIDs[4]; + + +int (*I_GetTime)(bool saveMS); +int (*I_WaitForTic)(int); +void (*I_FreezeTime)(bool frozen); + + +void I_Tactile(int /*on*/, int /*off*/, int /*total*/) +{ +} + + +ticcmd_t* I_BaseTiccmd() +{ + static ticcmd_t emptycmd; + return &emptycmd; +} + + +void I_WaitVBL(const int count) +{ + // I_WaitVBL is never used to actually synchronize to the + // vertical blank. Instead, it's used for delay purposes. + usleep(1000000 * count / 70); +} + + +// +// SetLanguageIDs +// +void SetLanguageIDs() +{ + size_t langlen = strlen(language); + + DWORD lang = (langlen < 2 || langlen > 3) + ? MAKE_ID('e', 'n', 'u', '\0') + : MAKE_ID(language[0], language[1], language[2], '\0'); + + LanguageIDs[3] = LanguageIDs[2] = LanguageIDs[1] = LanguageIDs[0] = lang; +} + + +void I_InitTimer(); +void I_ShutdownTimer(); + +void I_Init(void) +{ + CheckCPUID(&CPU); + DumpCPUInfo(&CPU); + + atterm(I_ShutdownSound); + I_InitSound(); + I_InitTimer(); +} + +static int has_exited; + +void I_Quit() +{ + has_exited = 1; // Prevent infinitely recursive exits -- killough + + if (demorecording) + { + G_CheckDemoStatus(); + } + + C_DeinitConsole(); + + I_ShutdownTimer(); +} + + +extern FILE* Logfile; +bool gameisdead; + +void STACK_ARGS I_FatalError(const char* const error, ...) +{ + static bool alreadyThrown = false; + gameisdead = true; + + if (!alreadyThrown) // ignore all but the first message -- killough + { + alreadyThrown = true; + + char errortext[MAX_ERRORTEXT]; + int index; + va_list argptr; + va_start(argptr, error); + index = vsnprintf(errortext, MAX_ERRORTEXT, error, argptr); + va_end(argptr); + + extern void Mac_I_FatalError(const char*); + Mac_I_FatalError(errortext); + + // Record error to log (if logging) + if (Logfile) + { + fprintf(Logfile, "\n**** DIED WITH FATAL ERROR:\n%s\n", errortext); + fflush(Logfile); + } + + fprintf(stderr, "%s\n", errortext); + exit(-1); + } + + if (!has_exited) // If it hasn't exited yet, exit now -- killough + { + has_exited = 1; // Prevent infinitely recursive exits -- killough + exit(-1); + } +} + +void STACK_ARGS I_Error(const char* const error, ...) +{ + va_list argptr; + char errortext[MAX_ERRORTEXT]; + + va_start(argptr, error); + vsnprintf(errortext, MAX_ERRORTEXT, error, argptr); + va_end(argptr); + + throw CRecoverableError(errortext); +} + + +void I_SetIWADInfo() +{ +} + + +void I_PrintStr(const char* const message) +{ + FConsoleWindow::GetInstance().AddText(message); + + // Strip out any color escape sequences before writing to output + char* const copy = new char[strlen(message) + 1]; + const char* srcp = message; + char* dstp = copy; + + while ('\0' != *srcp) + { + if (TEXTCOLOR_ESCAPE == *srcp) + { + if ('\0' != srcp[1]) + { + srcp += 2; + } + else + { + break; + } + } + else if (0x1d == *srcp || 0x1f == *srcp) // Opening and closing bar character + { + *dstp++ = '-'; + *srcp++; + } + else if (0x1e == *srcp) // Middle bar character + { + *dstp++ = '='; + *srcp++; + } + else + { + *dstp++ = *srcp++; + } + } + + *dstp = '\0'; + + fputs(copy, stdout); + delete[] copy; + fflush(stdout); +} + + +int I_PickIWad(WadStuff* const wads, const int numwads, const bool showwin, const int defaultiwad) +{ + if (!showwin) + { + return defaultiwad; + } + + I_SetMainWindowVisible(false); + + extern int I_PickIWad_Cocoa(WadStuff*, int, bool, int); + const int result = I_PickIWad_Cocoa(wads, numwads, showwin, defaultiwad); + + I_SetMainWindowVisible(true); + + return result; +} + + +bool I_WriteIniFailed() +{ + printf("The config file %s could not be saved:\n%s\n", GameConfig->GetPathName(), strerror(errno)); + return false; // return true to retry +} + + +static const char *pattern; + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1080 +static int matchfile(struct dirent *ent) +#else +static int matchfile(const struct dirent *ent) +#endif +{ + return fnmatch(pattern, ent->d_name, FNM_NOESCAPE) == 0; +} + +void* I_FindFirst(const char* const filespec, findstate_t* const fileinfo) +{ + FString dir; + + const char* const slash = strrchr(filespec, '/'); + + if (slash) + { + pattern = slash+1; + dir = FString(filespec, slash - filespec + 1); + } + else + { + pattern = filespec; + dir = "."; + } + + fileinfo->current = 0; + fileinfo->count = scandir(dir.GetChars(), &fileinfo->namelist, matchfile, alphasort); + + if (fileinfo->count > 0) + { + return fileinfo; + } + + return (void*)-1; +} + +int I_FindNext(void* const handle, findstate_t* const fileinfo) +{ + findstate_t* const state = static_cast(handle); + + if (state->current < fileinfo->count) + { + return ++state->current < fileinfo->count ? 0 : -1; + } + + return -1; +} + +int I_FindClose(void* const handle) +{ + findstate_t* const state = static_cast(handle); + + if (handle != (void*)-1 && state->count > 0) + { + for (int i = 0; i < state->count; ++i) + { + free(state->namelist[i]); + } + + free(state->namelist); + state->namelist = NULL; + state->count = 0; + } + + return 0; +} + +int I_FindAttr(findstate_t* const fileinfo) +{ + dirent* const ent = fileinfo->namelist[fileinfo->current]; + struct stat buf; + + if (stat(ent->d_name, &buf) == 0) + { + return S_ISDIR(buf.st_mode) ? FA_DIREC : 0; + } + + return 0; +} + + +void I_PutInClipboard(const char* const string) +{ + NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard]; + NSString* const stringType = NSStringPboardType; + NSArray* const types = [NSArray arrayWithObjects:stringType, nil]; + NSString* const content = [NSString stringWithUTF8String:string]; + + [pasteBoard declareTypes:types + owner:nil]; + [pasteBoard setString:content + forType:stringType]; +} + +FString I_GetFromClipboard(bool returnNothing) +{ + if (returnNothing) + { + return FString(); + } + + NSPasteboard* const pasteBoard = [NSPasteboard generalPasteboard]; + NSString* const value = [pasteBoard stringForType:NSStringPboardType]; + + return FString([value UTF8String]); +} + + +unsigned int I_MakeRNGSeed() +{ + return static_cast(arc4random()); +} + + +TArray I_GetGogPaths() +{ + // GOG's Doom games are Windows only at the moment + return TArray(); +} diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 29e6bf4d4b..1a535c0880 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -47,6 +47,7 @@ #include "m_argv.h" #include "r_renderer.h" #include "r_swrenderer.h" +#include "st_console.h" #include "stats.h" #include "textures.h" #include "v_palette.h" @@ -58,6 +59,18 @@ #undef Class +@implementation NSWindow(ExitAppOnClose) + +- (void)exitAppOnClose +{ + NSButton* closeButton = [self standardWindowButton:NSWindowCloseButton]; + [closeButton setAction:@selector(terminate:)]; + [closeButton setTarget:NSApp]; +} + +@end + + EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_vsync) EXTERN_CVAR(Bool, vid_hidpi) @@ -339,19 +352,22 @@ VideoModes[] = { 1600, 900 }, // 16:9 { 1600, 1000 }, // 16:10 { 1600, 1200 }, + { 1680, 1050 }, // 16:10 { 1920, 1080 }, { 1920, 1200 }, + { 2048, 1152 }, // 16:9, iMac Retina 4K 21.5", HiDPI off { 2048, 1536 }, - { 2304, 1440 }, + { 2304, 1440 }, // 16:10, MacBook Retina 12" { 2560, 1440 }, { 2560, 1600 }, { 2560, 2048 }, - { 2880, 1800 }, + { 2880, 1800 }, // 16:10, MacBook Pro Retina 15" { 3200, 1800 }, { 3840, 2160 }, { 3840, 2400 }, { 4096, 2160 }, - { 5120, 2880 } + { 4096, 2304 }, // 16:9, iMac Retina 4K 21.5" + { 5120, 2880 } // 16:9, iMac Retina 5K 27" }; @@ -404,7 +420,7 @@ CocoaVideo::CocoaVideo(const int multisample) attributes[i++] = NSOpenGLPFAStencilSize; attributes[i++] = NSOpenGLPixelFormatAttribute(8); - if (!vid_autoswitch) + if (NSAppKitVersionNumber >= AppKit10_5 && !vid_autoswitch) { attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; } @@ -430,6 +446,8 @@ CocoaVideo::CocoaVideo(const int multisample) [[glView openGLContext] makeCurrentContext]; [m_window setContentView:glView]; + + FConsoleWindow::GetInstance().Show(false); } void CocoaVideo::StartModeIterator(const int bits, const bool fullscreen) @@ -552,16 +570,15 @@ void CocoaVideo::SetWindowVisible(bool visible) { [video->m_window orderOut:nil]; } + + I_SetNativeMouse(!visible); } } static bool HasModernFullscreenAPI() { - // The following value shoud be equal to NSAppKitVersionNumber10_6 - // and it's hard-coded in order to build on earlier SDKs - - return NSAppKitVersionNumber >= 1038; + return NSAppKitVersionNumber >= AppKit10_6; } void CocoaVideo::SetStyleMask(const NSUInteger styleMask) @@ -622,7 +639,7 @@ void CocoaVideo::SetFullscreenMode(const int width, const int height) [m_window setHidesOnDeactivate:YES]; } - [m_window setFrame:displayRect display:YES]; + [m_window setFrame:screenFrame display:YES]; [m_window setFrameOrigin:NSMakePoint(0.0f, 0.0f)]; } @@ -659,10 +676,7 @@ void CocoaVideo::SetWindowedMode(const int width, const int height) [m_window setContentSize:windowSize]; [m_window center]; - - NSButton* closeButton = [m_window standardWindowButton:NSWindowCloseButton]; - [closeButton setAction:@selector(terminate:)]; - [closeButton setTarget:NSApp]; + [m_window exitAppOnClose]; } void CocoaVideo::SetMode(const int width, const int height, const bool fullscreen, const bool hiDPI) @@ -1208,7 +1222,7 @@ bool I_SetCursor(FTexture* cursorpic) // Create image from representation and set it as cursor NSData* imageData = [bitmapImageRep representationUsingType:NSPNGFileType - properties:nil]; + properties:[NSDictionary dictionary]]; NSImage* cursorImage = [[NSImage alloc] initWithData:imageData]; cursor = [[NSCursor alloc] initWithImage:cursorImage @@ -1228,10 +1242,7 @@ NSSize I_GetContentViewSize(const NSWindow* const window) const NSView* const view = [window contentView]; const NSSize frameSize = [view frame].size; - // TODO: figure out why [NSView frame] returns different values in "fullscreen" and in window - // In "fullscreen" the result is multiplied by [NSScreen backingScaleFactor], but not in window - - return (vid_hidpi && !fullscreen) + return (vid_hidpi) ? [view convertSizeToBacking:frameSize] : frameSize; } @@ -1239,5 +1250,4 @@ NSSize I_GetContentViewSize(const NSWindow* const window) void I_SetMainWindowVisible(bool visible) { CocoaVideo::SetWindowVisible(visible); - I_SetNativeMouse(!visible); } diff --git a/src/posix/cocoa/st_console.h b/src/posix/cocoa/st_console.h new file mode 100644 index 0000000000..6b6f018207 --- /dev/null +++ b/src/posix/cocoa/st_console.h @@ -0,0 +1,94 @@ +/* + ** st_console.h + ** + **--------------------------------------------------------------------------- + ** Copyright 2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#ifndef COCOA_ST_CONSOLE_INCLUDED +#define COCOA_ST_CONSOLE_INCLUDED + +@class NSButton; +@class NSProgressIndicator; +@class NSScrollView; +@class NSTextField; +@class NSTextView; +@class NSView; +@class NSWindow; + +struct PalEntry; + + +class FConsoleWindow +{ +public: + static FConsoleWindow& GetInstance(); + + static void CreateInstance(); + static void DeleteInstance(); + + void Show(bool visible); + void ShowFatalError(const char* message); + + void AddText(const char* message); + + void SetTitleText(); + void SetProgressBar(bool visible); + + // FStartupScreen functionality + void Progress(int current, int maximum); + void NetInit(const char* message, int playerCount); + void NetProgress(int count); + void NetDone(); + +private: + NSWindow* m_window; + NSTextView* m_textView; + NSScrollView* m_scrollView; + NSProgressIndicator* m_progressBar; + + NSView* m_netView; + NSTextField* m_netMessageText; + NSTextField* m_netCountText; + NSProgressIndicator* m_netProgressBar; + NSButton* m_netAbortButton; + + unsigned int m_characterCount; + + int m_netCurPos; + int m_netMaxPos; + + FConsoleWindow(); + + void ExpandTextView(float height); + + void AddText(const PalEntry& color, const char* message); +}; + +#endif // COCOA_ST_CONSOLE_INCLUDED diff --git a/src/posix/cocoa/st_console.mm b/src/posix/cocoa/st_console.mm new file mode 100644 index 0000000000..e952d22d6b --- /dev/null +++ b/src/posix/cocoa/st_console.mm @@ -0,0 +1,499 @@ +/* + ** st_console.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include "i_common.h" + +#include "d_main.h" +#include "i_system.h" +#include "st_console.h" +#include "v_text.h" +#include "version.h" + + +static NSColor* RGB(const BYTE red, const BYTE green, const BYTE blue) +{ + return [NSColor colorWithCalibratedRed:red / 255.0f + green:green / 255.0f + blue:blue / 255.0f + alpha:1.0f]; +} + +static NSColor* RGB(const PalEntry& color) +{ + return RGB(color.r, color.g, color.b); +} + +static NSColor* RGB(const DWORD color) +{ + return RGB(PalEntry(color)); +} + + +static const CGFloat PROGRESS_BAR_HEIGHT = 18.0f; +static const CGFloat NET_VIEW_HEIGHT = 88.0f; + + +FConsoleWindow::FConsoleWindow() +: m_window([NSWindow alloc]) +, m_textView([NSTextView alloc]) +, m_scrollView([NSScrollView alloc]) +, m_progressBar(nil) +, m_netView(nil) +, m_netMessageText(nil) +, m_netCountText(nil) +, m_netProgressBar(nil) +, m_netAbortButton(nil) +, m_characterCount(0) +, m_netCurPos(0) +, m_netMaxPos(0) +{ + const CGFloat initialWidth = 512.0f; + const CGFloat initialHeight = 384.0f; + const NSRect initialRect = NSMakeRect(0.0f, 0.0f, initialWidth, initialHeight); + + [m_textView initWithFrame:initialRect]; + [m_textView setEditable:NO]; + [m_textView setBackgroundColor:RGB(70, 70, 70)]; + [m_textView setMinSize:NSMakeSize(0.0f, initialHeight)]; + [m_textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; + [m_textView setVerticallyResizable:YES]; + [m_textView setHorizontallyResizable:NO]; + [m_textView setAutoresizingMask:NSViewWidthSizable]; + + NSTextContainer* const textContainer = [m_textView textContainer]; + [textContainer setContainerSize:NSMakeSize(initialWidth, FLT_MAX)]; + [textContainer setWidthTracksTextView:YES]; + + [m_scrollView initWithFrame:NSMakeRect(0.0f, 0.0f, initialWidth, initialHeight)]; + [m_scrollView setBorderType:NSNoBorder]; + [m_scrollView setHasVerticalScroller:YES]; + [m_scrollView setHasHorizontalScroller:NO]; + [m_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [m_scrollView setDocumentView:m_textView]; + + NSString* const title = [NSString stringWithFormat:@"%s %s - Console", GAMESIG, GetVersionString()]; + + [m_window initWithContentRect:initialRect + styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + [m_window setMinSize:[m_window frame].size]; + [m_window setShowsResizeIndicator:NO]; + [m_window setTitle:title]; + [m_window center]; + [m_window exitAppOnClose]; + + [[m_window contentView] addSubview:m_scrollView]; + + [m_window makeKeyAndOrderFront:nil]; +} + + +static FConsoleWindow* s_instance; + + +void FConsoleWindow::CreateInstance() +{ + assert(NULL == s_instance); + s_instance = new FConsoleWindow; +} + +void FConsoleWindow::DeleteInstance() +{ + assert(NULL != s_instance); + delete s_instance; + s_instance = NULL; +} + +FConsoleWindow& FConsoleWindow::GetInstance() +{ + assert(NULL != s_instance); + return *s_instance; +} + + +void FConsoleWindow::Show(const bool visible) +{ + if (visible) + { + [m_window orderFront:nil]; + } + else + { + [m_window orderOut:nil]; + } +} + +void FConsoleWindow::ShowFatalError(const char* const message) +{ + SetProgressBar(false); + NetDone(); + + const CGFloat textViewWidth = [m_scrollView frame].size.width; + + ExpandTextView(-32.0f); + + NSButton* quitButton = [[NSButton alloc] initWithFrame:NSMakeRect(textViewWidth - 76.0f, 0.0f, 72.0f, 30.0f)]; + [quitButton setAutoresizingMask:NSViewMinXMargin]; + [quitButton setBezelStyle:NSRoundedBezelStyle]; + [quitButton setTitle:@"Quit"]; + [quitButton setKeyEquivalent:@"\r"]; + [quitButton setTarget:NSApp]; + [quitButton setAction:@selector(terminate:)]; + + NSView* quitPanel = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, textViewWidth, 32.0f)]; + [quitPanel setAutoresizingMask:NSViewWidthSizable]; + [quitPanel addSubview:quitButton]; + + [[m_window contentView] addSubview:quitPanel]; + [m_window orderFront:nil]; + + AddText(PalEntry(255, 0, 0), "\nExecution could not continue.\n"); + AddText(PalEntry(255, 255, 170), message); + AddText("\n"); + + [NSApp runModalForWindow:m_window]; +} + + +void FConsoleWindow::AddText(const char* message) +{ + PalEntry color(223, 223, 223); + + char buffer[1024] = {}; + size_t pos = 0; + bool reset = false; + + while (*message != '\0') + { + if ((TEXTCOLOR_ESCAPE == *message && 0 != pos) + || (pos == sizeof buffer - 1) + || reset) + { + buffer[pos] = '\0'; + pos = 0; + reset = false; + + AddText(color, buffer); + } + +#define CHECK_BUFFER_SPACE \ + if (pos >= sizeof buffer - 3) { reset = true; continue; } + + if (TEXTCOLOR_ESCAPE == *message) + { + const BYTE* colorID = reinterpret_cast(message) + 1; + if ('\0' == colorID) + { + break; + } + + const EColorRange range = V_ParseFontColor(colorID, CR_UNTRANSLATED, CR_YELLOW); + + if (range != CR_UNDEFINED) + { + color = V_LogColorFromColorRange(range); + } + + message += 2; + } + else if (0x1d == *message) // Opening bar character + { + CHECK_BUFFER_SPACE; + + // Insert BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT + buffer[pos++] = '\xe2'; + buffer[pos++] = '\x95'; + buffer[pos++] = '\xbc'; + ++message; + } + else if (0x1e == *message) // Middle bar character + { + CHECK_BUFFER_SPACE; + + // Insert BOX DRAWINGS HEAVY HORIZONTAL + buffer[pos++] = '\xe2'; + buffer[pos++] = '\x94'; + buffer[pos++] = '\x81'; + ++message; + } + else if (0x1f == *message) // Closing bar character + { + CHECK_BUFFER_SPACE; + + // Insert BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT + buffer[pos++] = '\xe2'; + buffer[pos++] = '\x95'; + buffer[pos++] = '\xbe'; + ++message; + } + else + { + buffer[pos++] = *message++; + } + +#undef CHECK_BUFFER_SPACE + } + + if (0 != pos) + { + buffer[pos] = '\0'; + + AddText(color, buffer); + } + + if ([m_window isVisible]) + { + [m_textView scrollRangeToVisible:NSMakeRange(m_characterCount, 0)]; + + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; + } +} + +void FConsoleWindow::AddText(const PalEntry& color, const char* const message) +{ + NSString* const text = [NSString stringWithUTF8String:message]; + + NSDictionary* const attributes = [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont systemFontOfSize:14.0f], NSFontAttributeName, + RGB(color), NSForegroundColorAttributeName, + nil]; + + NSAttributedString* const formattedText = + [[NSAttributedString alloc] initWithString:text + attributes:attributes]; + [[m_textView textStorage] appendAttributedString:formattedText]; + + m_characterCount += [text length]; +} + + +void FConsoleWindow::SetTitleText() +{ + static const CGFloat TITLE_TEXT_HEIGHT = 32.0f; + + NSRect textViewFrame = [m_scrollView frame]; + textViewFrame.size.height -= TITLE_TEXT_HEIGHT; + [m_scrollView setFrame:textViewFrame]; + + const NSRect titleTextRect = NSMakeRect( + 0.0f, + textViewFrame.origin.y + textViewFrame.size.height, + textViewFrame.size.width, + TITLE_TEXT_HEIGHT); + + NSTextField* titleText = [[NSTextField alloc] initWithFrame:titleTextRect]; + [titleText setStringValue:[NSString stringWithUTF8String:DoomStartupInfo.Name]]; + [titleText setAlignment:NSCenterTextAlignment]; + [titleText setTextColor:RGB(DoomStartupInfo.FgColor)]; + [titleText setBackgroundColor:RGB(DoomStartupInfo.BkColor)]; + [titleText setFont:[NSFont fontWithName:@"Trebuchet MS Bold" size:18.0f]]; + [titleText setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; + [titleText setSelectable:NO]; + [titleText setBordered:NO]; + + [[m_window contentView] addSubview:titleText]; +} + +void FConsoleWindow::SetProgressBar(const bool visible) +{ + if ( (!visible && nil == m_progressBar) + || (visible && nil != m_progressBar)) + { + return; + } + + if (visible) + { + ExpandTextView(-PROGRESS_BAR_HEIGHT); + + m_progressBar = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(2.0f, 0.0f, 508.0f, 16.0f)]; + [m_progressBar setIndeterminate:NO]; + [m_progressBar setAutoresizingMask:NSViewWidthSizable]; + + [[m_window contentView] addSubview:m_progressBar]; + } + else + { + ExpandTextView(PROGRESS_BAR_HEIGHT); + + [m_progressBar removeFromSuperview]; + [m_progressBar release]; + m_progressBar = nil; + } +} + + +void FConsoleWindow::ExpandTextView(const float height) +{ + NSRect textFrame = [m_scrollView frame]; + textFrame.origin.y -= height; + textFrame.size.height += height; + [m_scrollView setFrame:textFrame]; +} + + +void FConsoleWindow::Progress(const int current, const int maximum) +{ + if (nil == m_progressBar) + { + return; + } + + static unsigned int previousTime = I_MSTime(); + unsigned int currentTime = I_MSTime(); + + if (currentTime - previousTime > 33) // approx. 30 FPS + { + previousTime = currentTime; + + [m_progressBar setMaxValue:maximum]; + [m_progressBar setDoubleValue:current]; + + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; + } +} + + +void FConsoleWindow::NetInit(const char* const message, const int playerCount) +{ + if (nil == m_netView) + { + SetProgressBar(false); + ExpandTextView(-NET_VIEW_HEIGHT); + + // Message like 'Waiting for players' or 'Contacting host' + m_netMessageText = [[NSTextField alloc] initWithFrame:NSMakeRect(12.0f, 64.0f, 400.0f, 16.0f)]; + [m_netMessageText setAutoresizingMask:NSViewWidthSizable]; + [m_netMessageText setDrawsBackground:NO]; + [m_netMessageText setSelectable:NO]; + [m_netMessageText setBordered:NO]; + + // Text with connected/total players count + m_netCountText = [[NSTextField alloc] initWithFrame:NSMakeRect(428.0f, 64.0f, 72.0f, 16.0f)]; + [m_netCountText setAutoresizingMask:NSViewMinXMargin]; + [m_netCountText setAlignment:NSRightTextAlignment]; + [m_netCountText setDrawsBackground:NO]; + [m_netCountText setSelectable:NO]; + [m_netCountText setBordered:NO]; + + // Connection progress + m_netProgressBar = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(12.0f, 40.0f, 488.0f, 16.0f)]; + [m_netProgressBar setAutoresizingMask:NSViewWidthSizable]; + [m_netProgressBar setMaxValue:playerCount]; + + if (0 == playerCount) + { + // Joining game + [m_netProgressBar setIndeterminate:YES]; + [m_netProgressBar startAnimation:nil]; + } + else + { + // Hosting game + [m_netProgressBar setIndeterminate:NO]; + } + + // Cancel network game button + m_netAbortButton = [[NSButton alloc] initWithFrame:NSMakeRect(432.0f, 8.0f, 72.0f, 28.0f)]; + [m_netAbortButton setAutoresizingMask:NSViewMinXMargin]; + [m_netAbortButton setBezelStyle:NSRoundedBezelStyle]; + [m_netAbortButton setTitle:@"Cancel"]; + [m_netAbortButton setKeyEquivalent:@"\r"]; + [m_netAbortButton setTarget:NSApp]; + [m_netAbortButton setAction:@selector(terminate:)]; + + // Panel for controls above + m_netView = [[NSView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, 512.0f, NET_VIEW_HEIGHT)]; + [m_netView setAutoresizingMask:NSViewWidthSizable]; + [m_netView addSubview:m_netMessageText]; + [m_netView addSubview:m_netCountText]; + [m_netView addSubview:m_netProgressBar]; + [m_netView addSubview:m_netAbortButton]; + + NSRect windowRect = [m_window frame]; + windowRect.origin.y -= NET_VIEW_HEIGHT; + windowRect.size.height += NET_VIEW_HEIGHT; + + [m_window setFrame:windowRect display:YES]; + [[m_window contentView] addSubview:m_netView]; + } + + [m_netMessageText setStringValue:[NSString stringWithUTF8String:message]]; + + m_netCurPos = 0; + m_netMaxPos = playerCount; + + NetProgress(1); // You always know about yourself +} + +void FConsoleWindow::NetProgress(const int count) +{ + if (0 == count) + { + ++m_netCurPos; + } + else + { + m_netCurPos = count; + } + + if (nil == m_netView) + { + return; + } + + if (m_netMaxPos > 1) + { + [m_netCountText setStringValue:[NSString stringWithFormat:@"%d / %d", m_netCurPos, m_netMaxPos]]; + [m_netProgressBar setDoubleValue:MIN(m_netCurPos, m_netMaxPos)]; + } +} + +void FConsoleWindow::NetDone() +{ + if (nil != m_netView) + { + ExpandTextView(NET_VIEW_HEIGHT); + + [m_netView removeFromSuperview]; + [m_netView release]; + m_netView = nil; + + // Released by m_netView + m_netMessageText = nil; + m_netCountText = nil; + m_netProgressBar = nil; + m_netAbortButton = nil; + } +} diff --git a/src/posix/cocoa/st_start.mm b/src/posix/cocoa/st_start.mm new file mode 100644 index 0000000000..36582c64b7 --- /dev/null +++ b/src/posix/cocoa/st_start.mm @@ -0,0 +1,192 @@ +/* + ** st_start.mm + ** + **--------------------------------------------------------------------------- + ** Copyright 2015 Alexey Lysiuk + ** All rights reserved. + ** + ** Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions + ** are met: + ** + ** 1. Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** 2. Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in the + ** documentation and/or other materials provided with the distribution. + ** 3. The name of the author may not be used to endorse or promote products + ** derived from this software without specific prior written permission. + ** + ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **--------------------------------------------------------------------------- + ** + */ + +#include + +#import + +#include "c_cvars.h" +#include "st_console.h" +#include "st_start.h" +#include "v_text.h" + + +FStartupScreen *StartScreen; + + +CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0) + { + self = 0; + } + else if (self > 2) + { + self = 2; + } +} + + +// --------------------------------------------------------------------------- + + +class FBasicStartupScreen : public FStartupScreen +{ +public: + FBasicStartupScreen(int maxProgress, bool showBar); + ~FBasicStartupScreen(); + + virtual void Progress(); + + virtual void NetInit(const char* message, int playerCount); + virtual void NetProgress(int count); + virtual void NetMessage(const char *format, ...); + virtual void NetDone(); + virtual bool NetLoop(bool (*timerCallback)(void*), void* userData); +}; + + +FBasicStartupScreen::FBasicStartupScreen(int maxProgress, bool showBar) +: FStartupScreen(maxProgress) +{ + FConsoleWindow& consoleWindow = FConsoleWindow::GetInstance(); + consoleWindow.SetProgressBar(true); + consoleWindow.SetTitleText(); + +#if 0 + // Testing code, please do not remove + consoleWindow.AddText("----------------------------------------------------------------\n"); + consoleWindow.AddText("1234567890 !@#$%^&*() ,<.>/?;:'\" [{]}\\| `~-_=+ " + "This is very very very long message needed to trigger word wrapping...\n\n"); + consoleWindow.AddText("Multiline...\n\tmessage...\n\t\twith...\n\t\t\ttabs.\n\n"); + + consoleWindow.AddText(TEXTCOLOR_BRICK "TEXTCOLOR_BRICK\n" TEXTCOLOR_TAN "TEXTCOLOR_TAN\n"); + consoleWindow.AddText(TEXTCOLOR_GRAY "TEXTCOLOR_GRAY & TEXTCOLOR_GREY\n"); + consoleWindow.AddText(TEXTCOLOR_GREEN "TEXTCOLOR_GREEN\n" TEXTCOLOR_BROWN "TEXTCOLOR_BROWN\n"); + consoleWindow.AddText(TEXTCOLOR_GOLD "TEXTCOLOR_GOLD\n" TEXTCOLOR_RED "TEXTCOLOR_RED\n"); + consoleWindow.AddText(TEXTCOLOR_BLUE "TEXTCOLOR_BLUE\n" TEXTCOLOR_ORANGE "TEXTCOLOR_ORANGE\n"); + consoleWindow.AddText(TEXTCOLOR_WHITE "TEXTCOLOR_WHITE\n" TEXTCOLOR_YELLOW "TEXTCOLOR_YELLOW\n"); + consoleWindow.AddText(TEXTCOLOR_UNTRANSLATED "TEXTCOLOR_UNTRANSLATED\n"); + consoleWindow.AddText(TEXTCOLOR_BLACK "TEXTCOLOR_BLACK\n" TEXTCOLOR_LIGHTBLUE "TEXTCOLOR_LIGHTBLUE\n"); + consoleWindow.AddText(TEXTCOLOR_CREAM "TEXTCOLOR_CREAM\n" TEXTCOLOR_OLIVE "TEXTCOLOR_OLIVE\n"); + consoleWindow.AddText(TEXTCOLOR_DARKGREEN "TEXTCOLOR_DARKGREEN\n" TEXTCOLOR_DARKRED "TEXTCOLOR_DARKRED\n"); + consoleWindow.AddText(TEXTCOLOR_DARKBROWN "TEXTCOLOR_DARKBROWN\n" TEXTCOLOR_PURPLE "TEXTCOLOR_PURPLE\n"); + consoleWindow.AddText(TEXTCOLOR_DARKGRAY "TEXTCOLOR_DARKGRAY\n" TEXTCOLOR_CYAN "TEXTCOLOR_CYAN\n"); + consoleWindow.AddText(TEXTCOLOR_NORMAL "TEXTCOLOR_NORMAL\n" TEXTCOLOR_BOLD "TEXTCOLOR_BOLD\n"); + consoleWindow.AddText(TEXTCOLOR_CHAT "TEXTCOLOR_CHAT\n" TEXTCOLOR_TEAMCHAT "TEXTCOLOR_TEAMCHAT\n"); + consoleWindow.AddText("----------------------------------------------------------------\n"); +#endif // _DEBUG +} + +FBasicStartupScreen::~FBasicStartupScreen() +{ + FConsoleWindow::GetInstance().SetProgressBar(false); +} + + +void FBasicStartupScreen::Progress() +{ + if (CurPos < MaxPos) + { + ++CurPos; + } + + FConsoleWindow::GetInstance().Progress(CurPos, MaxPos); +} + + +void FBasicStartupScreen::NetInit(const char* const message, const int playerCount) +{ + FConsoleWindow::GetInstance().NetInit(message, playerCount); +} + +void FBasicStartupScreen::NetProgress(const int count) +{ + FConsoleWindow::GetInstance().NetProgress(count); +} + +void FBasicStartupScreen::NetMessage(const char* const format, ...) +{ + va_list args; + va_start(args, format); + + FString message; + message.VFormat(format, args); + va_end(args); + + Printf("%s\n", message.GetChars()); +} + +void FBasicStartupScreen::NetDone() +{ + FConsoleWindow::GetInstance().NetDone(); +} + +bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData) +{ + while (true) + { + if (timerCallback(userData)) + { + break; + } + + [[NSRunLoop currentRunLoop] limitDateForMode:NSDefaultRunLoopMode]; + + // Do not poll to often + usleep(50000); + } + + return true; +} + + +// --------------------------------------------------------------------------- + + +FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress) +{ + return new FBasicStartupScreen(maxProgress, true); +} + + +// --------------------------------------------------------------------------- + + +void ST_Endoom() +{ + extern void I_ShutdownJoysticks(); + I_ShutdownJoysticks(); + + exit(0); +} diff --git a/src/posix/i_system.h b/src/posix/i_system.h index abda490c4f..3915036028 100644 --- a/src/posix/i_system.h +++ b/src/posix/i_system.h @@ -124,6 +124,8 @@ int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); // directories for IWADs if the user purchased any through Steam. TArray I_GetSteamPath(); +TArray I_GetGogPaths(); + // The ini could not be saved at exit bool I_WriteIniFailed (); diff --git a/src/posix/osx/iwadpicker_cocoa.mm b/src/posix/osx/iwadpicker_cocoa.mm index d2bc1ff0cc..5f2ff2ae2a 100644 --- a/src/posix/osx/iwadpicker_cocoa.mm +++ b/src/posix/osx/iwadpicker_cocoa.mm @@ -462,20 +462,14 @@ static void RestartWithParameters(const char* iwadPath, NSString* parameters) [pool release]; } -void I_SetMainWindowVisible(bool visible); - // Simple wrapper so we can call this from outside. int I_PickIWad_Cocoa (WadStuff *wads, int numwads, bool showwin, int defaultiwad) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - I_SetMainWindowVisible(false); - IWADPicker *picker = [IWADPicker alloc]; int ret = [picker pickIWad:wads num:numwads showWindow:showwin defaultWad:defaultiwad]; - I_SetMainWindowVisible(true); - NSString* parametersToAppend = [picker commandLineParameters]; osx_additional_parameters = [parametersToAppend UTF8String]; diff --git a/src/posix/sdl/crashcatcher.c b/src/posix/sdl/crashcatcher.c index f85713e312..70c9e7b0cb 100644 --- a/src/posix/sdl/crashcatcher.c +++ b/src/posix/sdl/crashcatcher.c @@ -15,7 +15,7 @@ #ifndef PR_SET_PTRACER #define PR_SET_PTRACER 0x59616d61 #endif -#elif defined (__APPLE__) +#elif defined (__APPLE__) || defined (__FreeBSD__) #include #endif diff --git a/src/posix/sdl/i_gui.cpp b/src/posix/sdl/i_gui.cpp index f9e6714b47..37a3b1062f 100644 --- a/src/posix/sdl/i_gui.cpp +++ b/src/posix/sdl/i_gui.cpp @@ -54,8 +54,3 @@ bool I_SetCursor(FTexture *cursorpic) } return true; } - -void I_SetMainWindowVisible(bool visible) -{ - -} diff --git a/src/posix/i_system.cpp b/src/posix/sdl/i_system.cpp similarity index 99% rename from src/posix/i_system.cpp rename to src/posix/sdl/i_system.cpp index fd6bb4c0c9..84b9537177 100644 --- a/src/posix/i_system.cpp +++ b/src/posix/sdl/i_system.cpp @@ -758,3 +758,9 @@ unsigned int I_MakeRNGSeed() } return seed; } + +TArray I_GetGogPaths() +{ + // GOG's Doom games are Windows only at the moment + return TArray(); +} diff --git a/src/posix/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp index 76191e1a92..9da52c2dd3 100644 --- a/src/posix/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -178,6 +178,7 @@ static MiniModeInfo WinModes[] = { 1600, 900 }, // 16:9 { 1600, 1000 }, // 16:10 { 1600, 1200 }, + { 1680, 1050 }, // 16:10 { 1920, 1080 }, { 1920, 1200 }, { 2048, 1536 }, diff --git a/src/posix/st_start.cpp b/src/posix/sdl/st_start.cpp similarity index 100% rename from src/posix/st_start.cpp rename to src/posix/sdl/st_start.cpp diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 8a7e5c5603..525543ec12 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -1093,7 +1093,8 @@ void R_Subsector (subsector_t *sub) basecolormap = frontsector->ColorMap; } - skybox = frontsector->CeilingSkyBox != NULL ? frontsector->CeilingSkyBox : level.DefaultSkybox; + skybox = frontsector->GetSkyBox(sector_t::ceiling); + ceilingplane = frontsector->ceilingplane.PointOnSide(viewx, viewy, viewz) > 0 || frontsector->GetTexture(sector_t::ceiling) == skyflatnum || (skybox != NULL && skybox->bAlways) || @@ -1133,7 +1134,7 @@ void R_Subsector (subsector_t *sub) // killough 3/7/98: Add (x,y) offsets to flats, add deep water check // killough 3/16/98: add floorlightlevel // killough 10/98: add support for skies transferred from sidedefs - skybox = frontsector->FloorSkyBox != NULL ? frontsector->FloorSkyBox : level.DefaultSkybox; + skybox = frontsector->GetSkyBox(sector_t::floor); floorplane = frontsector->floorplane.PointOnSide(viewx, viewy, viewz) > 0 || // killough 3/7/98 frontsector->GetTexture(sector_t::floor) == skyflatnum || (skybox != NULL && skybox->bAlways) || diff --git a/src/r_defs.h b/src/r_defs.h index bbc7a9198c..eb691f4f54 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -194,14 +194,19 @@ class ASectorAction : public AActor { DECLARE_CLASS (ASectorAction, AActor) public: + ASectorAction (bool activatedByUse = false); void Destroy (); void BeginPlay (); void Activate (AActor *source); void Deactivate (AActor *source); bool TriggerAction(AActor *triggerer, int activationType); + bool CanTrigger (AActor *triggerer) const; + bool IsActivatedByUse() const; protected: virtual bool DoTriggerAction(AActor *triggerer, int activationType); bool CheckTrigger(AActor *triggerer) const; +private: + bool ActivatedByUse; }; class ASkyViewpoint; @@ -243,6 +248,11 @@ struct secplane_t return FixedMul (ic, -d - DMulScale16 (a, v->x, b, v->y)); } + fixed_t ZatPoint (const AActor *ac) const + { + return FixedMul (ic, -d - DMulScale16 (a, ac->X(), b, ac->Y())); + } + // Returns the value of z at (x,y) if d is equal to dist fixed_t ZatPointDist (fixed_t x, fixed_t y, fixed_t dist) const { @@ -340,6 +350,8 @@ enum SECF_UNDERWATERMASK = 32+64, SECF_DRAWN = 128, // sector has been drawn at least once SECF_HIDDEN = 256, // Do not draw on textured automap + SECF_NOFLOORSKYBOX = 512, // force use of regular sky + SECF_NOCEILINGSKYBOX = 1024, // force use of regular sky (do not separate from NOFLOORSKYBOX!!!) }; enum @@ -348,6 +360,20 @@ enum SECF_NOFALLINGDAMAGE= 2, // No falling damage in this sector SECF_FLOORDROP = 4, // all actors standing on this floor will remain on it when it lowers very fast. SECF_NORESPAWN = 8, // players can not respawn in this sector + SECF_FRICTION = 16, // sector has friction enabled + SECF_PUSH = 32, // pushers enabled + SECF_SILENTMOVE = 64, // Sector movement makes mo sound (Eternity got this so this may be useful for an extended cross-port standard.) + SECF_DMGTERRAINFX = 128, // spawns terrain splash when inflicting damage + SECF_ENDGODMODE = 256, // getting damaged by this sector ends god mode + SECF_ENDLEVEL = 512, // ends level when health goes below 10 + SECF_HAZARD = 1024, // Change to Strife's delayed damage handling. + + SECF_WASSECRET = 1 << 30, // a secret that was discovered + SECF_SECRET = 1 << 31, // a secret sector + + SECF_DAMAGEFLAGS = SECF_ENDGODMODE|SECF_ENDLEVEL|SECF_DMGTERRAINFX|SECF_HAZARD, + SECF_NOMODIFY = SECF_SECRET|SECF_WASSECRET, // not modifiable by Sector_ChangeFlags + SECF_SPECIALFLAGS = SECF_DAMAGEFLAGS|SECF_FRICTION|SECF_PUSH, // these flags originate from 'special and must be transferrable by floor thinkers }; enum @@ -420,6 +446,28 @@ struct FTransform fixed_t base_angle, base_yoffs; }; +struct secspecial_t +{ + FNameNoInit damagetype; // [RH] Means-of-death for applied damage + int damageamount; // [RH] Damage to do while standing on floor + short special; + short damageinterval; // Interval for damage application + short leakydamage; // chance of leaking through radiation suit + int Flags; + + secspecial_t() + { + Clear(); + } + + void Clear() + { + memset(this, 0, sizeof(*this)); + } +}; + +FArchive &operator<< (FArchive &arc, secspecial_t &p); + struct sector_t { // Member functions @@ -451,6 +499,8 @@ struct sector_t DInterpolation *SetInterpolation(int position, bool attach); void StopInterpolation(int position); + ASkyViewpoint *GetSkyBox(int which); + enum { floor, @@ -633,7 +683,37 @@ struct sector_t return pos == floor? floorplane:ceilingplane; } + bool isSecret() const + { + return !!(Flags & SECF_SECRET); + } + bool wasSecret() const + { + return !!(Flags & SECF_WASSECRET); + } + + void ClearSecret() + { + Flags &= ~SECF_SECRET; + } + + void ClearSpecial() + { + // clears all variables that originate from 'special'. Used for sector type transferring thinkers + special = 0; + damageamount = 0; + damageinterval = 0; + damagetype = NAME_None; + leakydamage = 0; + Flags &= ~SECF_SPECIALFLAGS; + } + + int GetTerrain(int pos) const; + + void TransferSpecial(sector_t *model); + void GetSpecial(secspecial_t *spec); + void SetSpecial(const secspecial_t *spec); bool PlaneMoving(int pos); @@ -666,6 +746,8 @@ struct sector_t // when processed as mobj properties. Fix is to make them sector properties. fixed_t friction, movefactor; + int terrainnum[2]; + // thinker_t for reversable actions TObjPtr floordata; // jff 2/22/98 make thinkers on TObjPtr ceilingdata; // floors, ceilings, lighting, @@ -700,9 +782,11 @@ struct sector_t // thinglist is a subset of touching_thinglist struct msecnode_t *touching_thinglist; // phares 3/14/98 - float gravity; // [RH] Sector gravity (1.0 is normal) - short damage; // [RH] Damage to do while standing on floor - short mod; // [RH] Means-of-death for applied damage + float gravity; // [RH] Sector gravity (1.0 is normal) + FNameNoInit damagetype; // [RH] Means-of-death for applied damage + int damageamount; // [RH] Damage to do while standing on floor + short damageinterval; // Interval for damage application + short leakydamage; // chance of leaking through radiation suit WORD ZoneNumber; // [RH] Zone this sector belongs to WORD MoreFlags; // [RH] Internal sector flags @@ -716,9 +800,8 @@ struct sector_t // [RH] The sky box to render for this sector. NULL means use a // regular sky. - TObjPtr FloorSkyBox, CeilingSkyBox; + TObjPtr SkyBoxes[2]; - short secretsector; //jff 2/16/98 remembers if sector WAS secret (automap) int sectornum; // for comparing sector copies extsector_t * e; // This stores data that requires construction/destruction. Such data must not be copied by R_FakeFlat. diff --git a/src/r_main.cpp b/src/r_main.cpp index 9af2e00b43..02ecc43ec0 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -255,7 +255,7 @@ void R_InitTextureMapping () void R_SetVisibility (float vis) { // Allow negative visibilities, just for novelty's sake - //vis = clamp (vis, -204.7f, 204.7f); + vis = clamp (vis, -204.7f, 204.7f); // (205 and larger do not work in 5:4 aspect ratio) CurrentVisibility = vis; diff --git a/src/r_plane.cpp b/src/r_plane.cpp index ed013b8215..8fd4f53c20 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -1296,6 +1296,7 @@ void R_DrawSkyBoxes () ds_p->maskedtexturecol = ds_p->swall = -1; ds_p->bFogBoundary = false; ds_p->curline = NULL; + ds_p->fake = 0; memcpy (openings + ds_p->sprbottomclip, floorclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); diff --git a/src/r_segs.cpp b/src/r_segs.cpp index d4eb657c3d..bc9d81564b 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -558,8 +558,25 @@ void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) MaskedSWall = (fixed_t *)(openings + ds->swall) - ds->x1; // find positioning - xscale = FixedMul(rw_pic->xScale, sidedef->GetTextureXScale(side_t::mid)); - yscale = FixedMul(rw_pic->yScale, sidedef->GetTextureYScale(side_t::mid)); + side_t *scaledside; + side_t::ETexpart scaledpart; + if (rover->flags & FF_UPPERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::top; + } + else if (rover->flags & FF_LOWERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::bottom; + } + else + { + scaledside = rover->master->sidedef[0]; + scaledpart = side_t::mid; + } + xscale = FixedMul(rw_pic->xScale, scaledside->GetTextureXScale(scaledpart)); + yscale = FixedMul(rw_pic->yScale, scaledside->GetTextureYScale(scaledpart)); // encapsulate the lifetime of rowoffset fixed_t rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); dc_texturemid = rover->model->GetPlaneTexZ(sector_t::ceiling); diff --git a/src/resourcefiles/file_7z.cpp b/src/resourcefiles/file_7z.cpp index 1fb553932d..65676fcf4f 100644 --- a/src/resourcefiles/file_7z.cpp +++ b/src/resourcefiles/file_7z.cpp @@ -252,25 +252,26 @@ bool F7ZFile::Open(bool quiet) } return false; } - NumLumps = Archive->DB.db.NumFiles; + CSzArEx* const archPtr = &Archive->DB; + + NumLumps = archPtr->NumFiles; Lumps = new F7ZLump[NumLumps]; F7ZLump *lump_p = Lumps; TArray nameUTF16; TArray nameASCII; + for (DWORD i = 0; i < NumLumps; ++i) { - CSzFileItem *file = &Archive->DB.db.Files[i]; - // skip Directories - if (file->IsDir) + if (SzArEx_IsDir(archPtr, i)) { skipped++; continue; } - const size_t nameLength = SzArEx_GetFileNameUtf16(&Archive->DB, i, NULL); + const size_t nameLength = SzArEx_GetFileNameUtf16(archPtr, i, NULL); if (0 == nameLength) { @@ -280,7 +281,7 @@ bool F7ZFile::Open(bool quiet) nameUTF16.Resize((unsigned)nameLength); nameASCII.Resize((unsigned)nameLength); - SzArEx_GetFileNameUtf16(&Archive->DB, i, &nameUTF16[0]); + SzArEx_GetFileNameUtf16(archPtr, i, &nameUTF16[0]); for (size_t c = 0; c < nameLength; ++c) { nameASCII[c] = static_cast(nameUTF16[c]); @@ -291,7 +292,7 @@ bool F7ZFile::Open(bool quiet) name.ToLower(); lump_p->LumpNameSetup(name); - lump_p->LumpSize = int(file->Size); + lump_p->LumpSize = static_cast(SzArEx_GetFileSize(archPtr, i)); lump_p->Owner = this; lump_p->Flags = LUMPF_ZIPFILE; lump_p->Position = i; diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c72005813e..8170c1a30e 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1364,15 +1364,36 @@ static void S_AddSNDINFO (int lump) case SI_MidiDevice: { sc.MustGetString(); FName nm = sc.String; + FScanner::SavedPos save = sc.SavePos(); + + sc.SetCMode(true); sc.MustGetString(); - if (sc.Compare("timidity")) MidiDevices[nm] = MDEV_TIMIDITY; - else if (sc.Compare("fmod") || sc.Compare("sndsys")) MidiDevices[nm] = MDEV_SNDSYS; - else if (sc.Compare("standard")) MidiDevices[nm] = MDEV_MMAPI; - else if (sc.Compare("opl")) MidiDevices[nm] = MDEV_OPL; - else if (sc.Compare("default")) MidiDevices[nm] = MDEV_DEFAULT; - else if (sc.Compare("fluidsynth")) MidiDevices[nm] = MDEV_FLUIDSYNTH; - else if (sc.Compare("gus")) MidiDevices[nm] = MDEV_GUS; + MidiDeviceSetting devset; + if (sc.Compare("timidity")) devset.device = MDEV_TIMIDITY; + else if (sc.Compare("fmod") || sc.Compare("sndsys")) devset.device = MDEV_SNDSYS; + else if (sc.Compare("standard")) devset.device = MDEV_MMAPI; + else if (sc.Compare("opl")) devset.device = MDEV_OPL; + else if (sc.Compare("default")) devset.device = MDEV_DEFAULT; + else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH; + else if (sc.Compare("gus")) devset.device = MDEV_GUS; + else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI; else sc.ScriptError("Unknown MIDI device %s\n", sc.String); + + if (sc.CheckString(",")) + { + sc.SetCMode(false); + sc.MustGetString(); + devset.args = sc.String; + } + else + { + // This does not really do what one might expect, because the next token has already been parsed and can be a '$'. + // So in order to continue parsing without C-Mode, we need to reset and parse the last token again. + sc.SetCMode(false); + sc.RestorePos(save); + sc.MustGetString(); + } + MidiDevices[nm] = devset; } break; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index bacd5aa946..f388246a9e 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2375,6 +2375,16 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } } + FName *aliasp = MusicAliases.CheckKey(musicname); + if (aliasp != NULL) + { + if (*aliasp == NAME_None) + { + return true; // flagged to be ignored + } + musicname = aliasp->GetChars(); + } + if (!mus_playing.name.IsEmpty() && mus_playing.handle != NULL && stricmp (mus_playing.name, musicname) == 0 && @@ -2411,19 +2421,8 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { int lumpnum = -1; int length = 0; - int device = MDEV_DEFAULT; MusInfo *handle = NULL; - FName musicasname = musicname; - - FName *aliasp = MusicAliases.CheckKey(musicasname); - if (aliasp != NULL) - { - musicname = (musicasname = *aliasp).GetChars(); - if (musicasname == NAME_None) return true; - } - - int *devp = MidiDevices.CheckKey(musicasname); - if (devp != NULL) device = *devp; + MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname); // Strip off any leading file:// component. if (strncmp(musicname, "file://", 7) == 0) @@ -2493,7 +2492,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) } else { - mus_playing.handle = I_RegisterSong (reader, device); + mus_playing.handle = I_RegisterSong (reader, devp); } } diff --git a/src/s_sound.h b/src/s_sound.h index 49fb81fb35..a72f3be0ce 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -394,10 +394,22 @@ enum EMidiDevice MDEV_TIMIDITY = 3, MDEV_FLUIDSYNTH = 4, MDEV_GUS = 5, + MDEV_WILDMIDI = 6, +}; + +struct MidiDeviceSetting +{ + int device; + FString args; + + MidiDeviceSetting() + { + device = MDEV_DEFAULT; + } }; typedef TMap MusicAliasMap; -typedef TMap MidiDeviceMap; +typedef TMap MidiDeviceMap; extern MusicAliasMap MusicAliases; extern MidiDeviceMap MidiDevices; diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 8433b301b0..e7a617f5a7 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -44,7 +44,7 @@ extern HWND Window; #define FALSE 0 #define TRUE 1 #endif -#ifdef __APPLE__ +#if defined(__FreeBSD__) || defined(__APPLE__) #include #elif __sun #include @@ -462,14 +462,12 @@ public: Stream->release(); Channel = NULL; Stream = NULL; - Owner->Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); // Open the stream asynchronously, so we don't hang the game while trying to reconnect. // (It would be nice to do the initial open asynchronously as well, but I'd need to rethink // the music system design to pull that off.) result = Owner->Sys->createSound(URL, (Loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM | FMOD_NONBLOCKING, NULL, &Stream); JustStarted = true; - Owner->Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); return result != FMOD_OK; } if (JustStarted && openstate == FMOD_OPENSTATE_PLAYING) @@ -771,7 +769,7 @@ bool FMODSoundRenderer::Init() } #endif -#ifndef _WIN32 +#if !defined _WIN32 && !defined __APPLE__ // Try to load SDL output plugin result = Sys->setPluginPath(progdir); // Should we really look for it in the program directory? result = Sys->loadPlugin("liboutput_sdl.so", &OutputPlugin); @@ -1170,6 +1168,9 @@ bool FMODSoundRenderer::Init() } Sys->set3DSettings(0.5f, 96.f, 1.f); Sys->set3DRolloffCallback(RolloffCallback); + // The default is 16k, which periodically starves later FMOD versions + // when streaming FLAC files. + Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); snd_sfxvolume.Callback (); return true; } @@ -1733,10 +1734,6 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) exinfo.dlsname = patches; } - // Use a larger buffer for URLs so that it's less likely to be effected - // by hiccups in the data rate from the remote server. - Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); - result = Sys->createSound(url, mode, &exinfo, &stream); if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) { @@ -1748,9 +1745,6 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) } } - // Restore standard buffer size. - Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); - if(result != FMOD_OK) return NULL; diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index 3f8fac0da6..5723a18dc3 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -162,7 +162,7 @@ void I_InitMusic (void) if (!setatterm) { setatterm = true; - atterm (I_ShutdownMusic); + atterm (I_ShutdownMusicExit); #ifndef _WIN32 signal (SIGCHLD, ChildSigHandler); @@ -178,7 +178,7 @@ void I_InitMusic (void) // //========================================================================== -void I_ShutdownMusic(void) +void I_ShutdownMusic(bool onexit) { if (MusicDown) return; @@ -189,11 +189,17 @@ void I_ShutdownMusic(void) assert (currSong == NULL); } Timidity::FreeAll(); + if (onexit) WildMidi_Shutdown(); #ifdef _WIN32 I_ShutdownMusicWin32(); #endif // _WIN32 } +void I_ShutdownMusicExit() +{ + I_ShutdownMusic(true); +} + //========================================================================== // @@ -280,6 +286,10 @@ void MusInfo::FluidSettingStr(const char *, const char *) { } +void MusInfo::WildMidiSetOption(int opt, int set) +{ +} + FString MusInfo::GetStats() { return "No stats available for this song"; @@ -301,21 +311,21 @@ MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate) // //========================================================================== -static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype) +static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype, const char *args) { switch (miditype) { case MIDI_MUS: - return new MUSSong2(reader, devtype); + return new MUSSong2(reader, devtype, args); case MIDI_MIDI: - return new MIDISong2(reader, devtype); + return new MIDISong2(reader, devtype, args); case MIDI_HMI: - return new HMISong(reader, devtype); + return new HMISong(reader, devtype, args); case MIDI_XMI: - return new XMISong(reader, devtype); + return new XMISong(reader, devtype, args); default: return NULL; @@ -377,7 +387,7 @@ static EMIDIType IdentifyMIDIType(DWORD *id, int size) // //========================================================================== -MusInfo *I_RegisterSong (FileReader *reader, int device) +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device) { MusInfo *info = NULL; const char *fmt; @@ -395,12 +405,6 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) return 0; } -#ifndef _WIN32 - // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS - if (device == MDEV_MMAPI) - device = MDEV_SNDSYS; -#endif - // Check for gzip compression. Some formats are expected to have players // that can handle it, so it simplifies things if we make all songs // gzippable. @@ -437,10 +441,15 @@ MusInfo *I_RegisterSong (FileReader *reader, int device) EMIDIType miditype = IdentifyMIDIType(id, sizeof(id)); if (miditype != MIDI_NOTMIDI) { - EMidiDevice devtype = (EMidiDevice)device; + EMidiDevice devtype = device == NULL? MDEV_DEFAULT : (EMidiDevice)device->device; +#ifndef _WIN32 + // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS + if (devtype == MDEV_MMAPI) + devtype = MDEV_SNDSYS; +#endif retry_as_sndsys: - info = CreateMIDIStreamer(*reader, devtype, miditype); + info = CreateMIDIStreamer(*reader, devtype, miditype, device != NULL? device->args.GetChars() : ""); if (info != NULL && !info->IsValid()) { delete info; @@ -454,7 +463,7 @@ retry_as_sndsys: #ifdef _WIN32 if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0) { - info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype); + info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype, ""); } #endif } @@ -465,7 +474,7 @@ retry_as_sndsys: (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF { - info = new OPLMUSSong (*reader); + info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : ""); } // Check for game music else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0') diff --git a/src/sound/i_music.h b/src/sound/i_music.h index f6a6dbadd8..514400e5db 100644 --- a/src/sound/i_music.h +++ b/src/sound/i_music.h @@ -43,7 +43,8 @@ struct FOptionValues; // MUSIC I/O // void I_InitMusic (); -void I_ShutdownMusic (); +void I_ShutdownMusic (bool onexit = false); +void I_ShutdownMusicExit (); void I_BuildMIDIMenuList (FOptionValues *); void I_UpdateMusic (); @@ -52,7 +53,8 @@ void I_SetMusicVolume (float volume); // Registers a song handle to song data. class MusInfo; -MusInfo *I_RegisterSong (FileReader *reader, int device); +struct MidiDeviceSetting; +MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device); MusInfo *I_RegisterCDSong (int track, int cdid = 0); MusInfo *I_RegisterURLSong (const char *url); @@ -81,6 +83,7 @@ public: virtual void FluidSettingInt(const char *setting, int value); // FluidSynth settings virtual void FluidSettingNum(const char *setting, double value); // " virtual void FluidSettingStr(const char *setting, const char *value); // " + virtual void WildMidiSetOption(int opt, int set); void Start(bool loop, float rel_vol = -1.f, int subsong = 0); diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 77aa313122..52364ab587 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -24,6 +24,7 @@ #include "i_music.h" #include "s_sound.h" #include "files.h" +#include "wildmidi/wildmidi_lib.h" void I_InitMusicWin32 (); void I_ShutdownMusicWin32 (); @@ -101,6 +102,7 @@ public: virtual void FluidSettingInt(const char *setting, int value); virtual void FluidSettingNum(const char *setting, double value); virtual void FluidSettingStr(const char *setting, const char *value); + virtual void WildMidiSetOption(int opt, int set); virtual bool Preprocess(MIDIStreamer *song, bool looping); virtual FString GetStats(); }; @@ -183,7 +185,7 @@ public: class TimidityPPMIDIDevice : public PseudoMIDIDevice { public: - TimidityPPMIDIDevice(); + TimidityPPMIDIDevice(const char *args); ~TimidityPPMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -218,7 +220,6 @@ protected: #endif }; - // Base class for software synthesizer MIDI output devices ------------------ class SoftSynthMIDIDevice : public MIDIDevice @@ -269,7 +270,7 @@ protected: class OPLMIDIDevice : public SoftSynthMIDIDevice, protected OPLmusicBlock { public: - OPLMIDIDevice(); + OPLMIDIDevice(const char *args); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); void Close(); int GetTechnology() const; @@ -302,7 +303,7 @@ namespace Timidity { struct Renderer; } class TimidityMIDIDevice : public SoftSynthMIDIDevice { public: - TimidityMIDIDevice(); + TimidityMIDIDevice(const char *args); ~TimidityMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -331,6 +332,27 @@ protected: FILE *File; }; +// WildMidi implementation of a MIDI device --------------------------------- + +class WildMIDIDevice : public SoftSynthMIDIDevice +{ +public: + WildMIDIDevice(const char *args); + ~WildMIDIDevice(); + + int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); + void PrecacheInstruments(const WORD *instruments, int count); + FString GetStats(); + +protected: + WildMidi_Renderer *Renderer; + + void HandleEvent(int status, int parm1, int parm2); + void HandleLongEvent(const BYTE *data, int len); + void ComputeOutput(float *buffer, int len); + void WildMidiSetOption(int opt, int set); +}; + // FluidSynth implementation of a MIDI device ------------------------------- #ifdef HAVE_FLUIDSYNTH @@ -344,7 +366,7 @@ struct fluid_synth_t; class FluidSynthMIDIDevice : public SoftSynthMIDIDevice { public: - FluidSynthMIDIDevice(); + FluidSynthMIDIDevice(const char *args); ~FluidSynthMIDIDevice(); int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata); @@ -409,7 +431,7 @@ protected: class MIDIStreamer : public MusInfo { public: - MIDIStreamer(EMidiDevice type); + MIDIStreamer(EMidiDevice type, const char *args); ~MIDIStreamer(); void MusicVolumeChanged(); @@ -427,6 +449,7 @@ public: void FluidSettingInt(const char *setting, int value); void FluidSettingNum(const char *setting, double value); void FluidSettingStr(const char *setting, const char *value); + void WildMidiSetOption(int opt, int set); void CreateSMF(TArray &file, int looplimit=0); protected: @@ -494,6 +517,7 @@ protected: bool CallbackIsThreaded; int LoopLimit; FString DumpFilename; + FString Args; }; // MUS file played with a MIDI stream --------------------------------------- @@ -501,7 +525,7 @@ protected: class MUSSong2 : public MIDIStreamer { public: - MUSSong2(FileReader &reader, EMidiDevice type); + MUSSong2(FileReader &reader, EMidiDevice type, const char *args); ~MUSSong2(); MusInfo *GetOPLDumper(const char *filename); @@ -527,7 +551,7 @@ protected: class MIDISong2 : public MIDIStreamer { public: - MIDISong2(FileReader &reader, EMidiDevice type); + MIDISong2(FileReader &reader, EMidiDevice type, const char *args); ~MIDISong2(); MusInfo *GetOPLDumper(const char *filename); @@ -546,7 +570,7 @@ protected: struct TrackInfo; void ProcessInitialMetaEvents (); - DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay); + DWORD *SendCommand (DWORD *event, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom); TrackInfo *FindNextDue (); BYTE *MusHeader; @@ -584,7 +608,7 @@ protected: class HMISong : public MIDIStreamer { public: - HMISong(FileReader &reader, EMidiDevice type); + HMISong(FileReader &reader, EMidiDevice type, const char *args); ~HMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -627,7 +651,7 @@ protected: class XMISong : public MIDIStreamer { public: - XMISong(FileReader &reader, EMidiDevice type); + XMISong(FileReader &reader, EMidiDevice type, const char *args); ~XMISong(); MusInfo *GetOPLDumper(const char *filename); @@ -690,7 +714,7 @@ protected: class OPLMUSSong : public StreamSong { public: - OPLMUSSong (FileReader &reader); + OPLMUSSong (FileReader &reader, const char *args); ~OPLMUSSong (); void Play (bool looping, int subsong); bool IsPlaying (); diff --git a/src/sound/music_fluidsynth_mididevice.cpp b/src/sound/music_fluidsynth_mididevice.cpp index af8fe6667f..3be4de56bd 100644 --- a/src/sound/music_fluidsynth_mididevice.cpp +++ b/src/sound/music_fluidsynth_mididevice.cpp @@ -255,7 +255,7 @@ CUSTOM_CVAR(Int, fluid_chorus_type, FLUID_CHORUS_DEFAULT_TYPE, CVAR_ARCHIVE|CVAR // //========================================================================== -FluidSynthMIDIDevice::FluidSynthMIDIDevice() +FluidSynthMIDIDevice::FluidSynthMIDIDevice(const char *args) { FluidSynth = NULL; FluidSettings = NULL; @@ -293,7 +293,15 @@ FluidSynthMIDIDevice::FluidSynthMIDIDevice() fluid_reverb_width, fluid_reverb_level); fluid_synth_set_chorus(FluidSynth, fluid_chorus_voices, fluid_chorus_level, fluid_chorus_speed, fluid_chorus_depth, fluid_chorus_type); - if (0 == LoadPatchSets(fluid_patchset)) + + // try loading a patch set that got specified with $mididevice. + int res = 0; + if (args != NULL && *args != 0) + { + res = LoadPatchSets(args); + } + + if (res == 0 && 0 == LoadPatchSets(fluid_patchset)) { #ifdef __unix__ // This is the standard location on Ubuntu. diff --git a/src/sound/music_hmi_midiout.cpp b/src/sound/music_hmi_midiout.cpp index 5fef706dd8..ca3ae09054 100644 --- a/src/sound/music_hmi_midiout.cpp +++ b/src/sound/music_hmi_midiout.cpp @@ -128,8 +128,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -HMISong::HMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +HMISong::HMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 649f9002ec..260f635b26 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -11,9 +11,9 @@ static DWORD nummididevices; static bool nummididevicesset; #ifdef HAVE_FLUIDSYNTH -#define NUM_DEF_DEVICES 5 +#define NUM_DEF_DEVICES 6 #else -#define NUM_DEF_DEVICES 4 +#define NUM_DEF_DEVICES 5 #endif static void AddDefaultMidiDevices(FOptionValues *opt) @@ -33,8 +33,10 @@ static void AddDefaultMidiDevices(FOptionValues *opt) pair[p+1].Value = -3.0; pair[p+2].Text = "TiMidity++"; pair[p+2].Value = -2.0; - pair[p+3].Text = "Sound System"; - pair[p+3].Value = -1.0; + pair[p+3].Text = "WildMidi"; + pair[p+3].Value = -6.0; + pair[p+4].Text = "Sound System"; + pair[p+4].Value = -1.0; } @@ -70,7 +72,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) if (!nummididevicesset) return; - if ((self >= (signed)nummididevices) || (self < -5)) + if ((self >= (signed)nummididevices) || (self < -6)) { Printf ("ID out of range. Using default device.\n"); self = 0; @@ -166,6 +168,7 @@ CCMD (snd_listmididevices) MIDIOUTCAPS caps; MMRESULT res; + PrintMidiDevice (-6, "WildMidi", MOD_SWSYNTH, 0); #ifdef HAVE_FLUIDSYNTH PrintMidiDevice (-5, "FluidSynth", MOD_SWSYNTH, 0); #endif @@ -196,8 +199,8 @@ CCMD (snd_listmididevices) CUSTOM_CVAR(Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { - if (self < -5) - self = -5; + if (self < -6) + self = -6; else if (self > -1) self = -1; else @@ -211,6 +214,7 @@ void I_BuildMIDIMenuList (FOptionValues *opt) CCMD (snd_listmididevices) { + Printf("%s-6. WildMidi\n", -6 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #ifdef HAVE_FLUIDSYNTH Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : ""); #endif diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index ec2843c790..667de88421 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -58,7 +58,7 @@ CUSTOM_CVAR (Int, timidity_pipe, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Int, timidity_frequency, 44100, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { // Clamp frequency to Timidity's limits if (self < 4000) self = 4000; @@ -72,7 +72,7 @@ CUSTOM_CVAR (Int, timidity_frequency, 22050, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -TimidityPPMIDIDevice::TimidityPPMIDIDevice() +TimidityPPMIDIDevice::TimidityPPMIDIDevice(const char *args) : DiskName("zmid"), #ifdef _WIN32 ReadWavePipe(INVALID_HANDLE_VALUE), WriteWavePipe(INVALID_HANDLE_VALUE), @@ -85,7 +85,13 @@ TimidityPPMIDIDevice::TimidityPPMIDIDevice() #ifndef _WIN32 WavePipe[0] = WavePipe[1] = -1; #endif - + + if (args == NULL || *args == 0) args = timidity_exe; + + CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", + args, *timidity_extargs, + *timidity_chorus, *timidity_reverb, *timidity_frequency); + if (DiskName == NULL) { Printf(PRINT_BOLD, "Could not create temp music file\n"); @@ -187,10 +193,6 @@ int TimidityPPMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWO Validated = true; #endif // WIN32 - CommandLine.Format("%s %s -EFchorus=%s -EFreverb=%s -s%d ", - *timidity_exe, *timidity_extargs, - *timidity_chorus, *timidity_reverb, *timidity_frequency); - pipeSize = (timidity_pipe * timidity_frequency / 1000) << (timidity_stereo + !timidity_8bit); @@ -713,11 +715,11 @@ BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode) if ( hRT ) { - // Must wait process to terminate to guarantee that it has exited... - WaitForSingleObject(hProcess, INFINITE); - + // Must wait for process to terminate to guarantee that it has exited... + DWORD res = WaitForSingleObject(hProcess, 1000); CloseHandle(hRT); - bSuccess = TRUE; + bSuccess = (res == WAIT_OBJECT_0); + dwErr = WAIT_TIMEOUT; } if ( !bSuccess ) diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 6c85c06af8..579fd7851b 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -89,12 +89,12 @@ static const BYTE StaticMIDIhead[] = // //========================================================================== -MIDIStreamer::MIDIStreamer(EMidiDevice type) +MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args) : #ifdef _WIN32 PlayerThread(0), ExitEvent(0), BufferDoneEvent(0), #endif - MIDI(0), Division(0), InitialTempo(500000), DeviceType(type) + MIDI(0), Division(0), InitialTempo(500000), DeviceType(type), Args(args) { #ifdef _WIN32 BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); @@ -240,6 +240,7 @@ EMidiDevice MIDIStreamer::SelectMIDIDevice(EMidiDevice device) #ifdef HAVE_FLUIDSYNTH case -5: return MDEV_FLUIDSYNTH; #endif + case -6: return MDEV_WILDMIDI; default: #ifdef _WIN32 return MDEV_MMAPI; @@ -268,19 +269,19 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const #ifdef HAVE_FLUIDSYNTH case MDEV_FLUIDSYNTH: - return new FluidSynthMIDIDevice; + return new FluidSynthMIDIDevice(Args); #endif case MDEV_SNDSYS: return new SndSysMIDIDevice; case MDEV_GUS: - return new TimidityMIDIDevice; + return new TimidityMIDIDevice(Args); case MDEV_OPL: try { - return new OPLMIDIDevice; + return new OPLMIDIDevice(Args); } catch (CRecoverableError &err) { @@ -290,7 +291,10 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype) const } case MDEV_TIMIDITY: - return new TimidityPPMIDIDevice; + return new TimidityPPMIDIDevice(Args); + + case MDEV_WILDMIDI: + return new WildMIDIDevice(Args); default: return NULL; @@ -622,6 +626,21 @@ void MIDIStreamer::FluidSettingStr(const char *setting, const char *value) } +//========================================================================== +// +// MIDIDeviceStreamer :: WildMidiSetOption +// +//========================================================================== + +void MIDIStreamer::WildMidiSetOption(int opt, int set) +{ + if (MIDI != NULL) + { + MIDI->WildMidiSetOption(opt, set); + } +} + + //========================================================================== // // MIDIStreamer :: OutputVolume @@ -1192,9 +1211,15 @@ void MIDIStreamer::CreateSMF(TArray &file, int looplimit) len--; file.Push(MIDI_SYSEX); WriteVarLen(file, len); - memcpy(&file[file.Reserve(len - 1)], bytes, len); - running_status = 255; + memcpy(&file[file.Reserve(len)], bytes + 1, len); } + else + { + file.Push(MIDI_SYSEXEND); + WriteVarLen(file, len); + memcpy(&file[file.Reserve(len)], bytes, len); + } + running_status = 255; } else if (MEVT_EVENTTYPE(event[2]) == 0) { @@ -1512,6 +1537,16 @@ void MIDIDevice::FluidSettingStr(const char *setting, const char *value) { } +//========================================================================== +// +// MIDIDevice :: WildMidiSetOption +// +//========================================================================== + +void MIDIDevice::WildMidiSetOption(int opt, int set) +{ +} + //========================================================================== // // MIDIDevice :: GetStats diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 5f7d2d5031..bc810b4a8e 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -43,6 +43,7 @@ #include "doomdef.h" #include "m_swap.h" #include "files.h" +#include "s_sound.h" // MACROS ------------------------------------------------------------------ @@ -92,8 +93,8 @@ static const BYTE CtrlTranslate[15] = // //========================================================================== -MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), MusBuffer(0) +MUSSong2::MUSSong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), MusBuffer(0) { #ifdef _WIN32 if (ExitEvent == NULL) @@ -210,23 +211,44 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { - WORD *work = (WORD *)alloca(MusHeader->NumInstruments * sizeof(WORD)); - const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / sizeof(WORD); - int i, j; + TArray work(MusHeader->NumInstruments); + const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); + int i, k; - for (i = j = 0; i < MusHeader->NumInstruments; ++i) + for (i = k = 0; i < MusHeader->NumInstruments; ++i) { - WORD instr = LittleShort(used[i]); + BYTE instr = used[k++]; + WORD val; if (instr < 128) { - work[j++] = instr; + val = instr; } - else if (used[i] >= 135 && used[i] <= 181) + else if (instr >= 135 && instr <= 188) { // Percussions are 100-based, not 128-based, eh? - work[j++] = instr - 100 + (1 << 14); + val = instr - 100 + (1 << 14); + } + else + { + // skip it. + val = used[k++]; + k += val; + continue; + } + + int numbanks = used[k++]; + if (numbanks > 0) + { + for (int b = 0; b < numbanks; b++) + { + work.Push(val | (used[k++] << 7)); + } + } + else + { + work.Push(val); } } - MIDI->PrecacheInstruments(&work[0], j); + MIDI->PrecacheInstruments(&work[0], work.Size()); } //========================================================================== diff --git a/src/sound/music_mus_opl.cpp b/src/sound/music_mus_opl.cpp index 3b5012ad94..562330ef98 100644 --- a/src/sound/music_mus_opl.cpp +++ b/src/sound/music_mus_opl.cpp @@ -20,16 +20,25 @@ CUSTOM_CVAR (Int, opl_numchips, 2, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) } } -CVAR(Int, opl_core, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +int current_opl_core; -OPLMUSSong::OPLMUSSong (FileReader &reader) +// Get OPL core override from $mididevice +void OPL_SetCore(const char *args) +{ + current_opl_core = opl_core; + if (args != NULL && *args >= '0' && *args < '4') current_opl_core = *args - '0'; +} + +OPLMUSSong::OPLMUSSong (FileReader &reader, const char *args) { int samples = int(OPL_SAMPLE_RATE / 14); + OPL_SetCore(args); Music = new OPLmusicFile (&reader); m_Stream = GSnd->CreateStream (FillStream, samples*4, - (opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); + (current_opl_core == 0 ? SoundStream::Mono : 0) | SoundStream::Float, int(OPL_SAMPLE_RATE), this); if (m_Stream == NULL) { Printf (PRINT_BOLD, "Could not create music stream.\n"); diff --git a/src/sound/music_smf_midiout.cpp b/src/sound/music_smf_midiout.cpp index d5d307ec7e..43755eb082 100644 --- a/src/sound/music_smf_midiout.cpp +++ b/src/sound/music_smf_midiout.cpp @@ -102,8 +102,8 @@ char MIDI_CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // //========================================================================== -MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Tracks(0) +MIDISong2::MIDISong2 (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Tracks(0) { int p; int i; @@ -322,7 +322,12 @@ DWORD *MIDISong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) // Play all events for this tick. do { - DWORD *new_events = SendCommand(events, TrackDue, time); + bool sysex_noroom = false; + DWORD *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom); + if (sysex_noroom) + { + return events; + } TrackDue = FindNextDue(); if (new_events != events) { @@ -366,12 +371,15 @@ void MIDISong2::AdvanceTracks(DWORD time) // //========================================================================== -DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) +DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay, ptrdiff_t room, bool &sysex_noroom) { DWORD len; BYTE event, data1 = 0, data2 = 0; int i; + sysex_noroom = false; + size_t start_p = track->TrackP; + CHECK_FINISHED event = track->TrackBegin[track->TrackP++]; CHECK_FINISHED @@ -588,13 +596,44 @@ DWORD *MIDISong2::SendCommand (DWORD *events, TrackInfo *track, DWORD delay) } else { - // Skip SysEx events just because I don't want to bother with them. - // The old MIDI player ignored them too, so this won't break - // anything that played before. + // SysEx events could potentially not have enough room in the buffer... if (event == MIDI_SYSEX || event == MIDI_SYSEXEND) { - len = track->ReadVarLen (); - track->TrackP += len; + len = track->ReadVarLen(); + if (len >= (MAX_EVENTS-1)*3*4) + { // This message will never fit. Throw it away. + track->TrackP += len; + } + else if (len + 12 >= (size_t)room * 4) + { // Not enough room left in this buffer. Backup and wait for the next one. + track->TrackP = start_p; + sysex_noroom = true; + return events; + } + else + { + events[0] = delay; + events[1] = 0; + BYTE *msg = (BYTE *)&events[3]; + if (event == MIDI_SYSEX) + { // Need to add the SysEx marker to the message. + events[2] = (MEVT_LONGMSG << 24) | (len + 1); + *msg++ = MIDI_SYSEX; + } + else + { + events[2] = (MEVT_LONGMSG << 24) | len; + } + memcpy(msg, &track->TrackBegin[track->TrackP], len); + msg += len; + // Must pad with 0 + while ((size_t)msg & 3) + { + *msg++ = 0; + } + events = (DWORD *)msg; + track->TrackP += len; + } } else if (event == MIDI_META) { diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/music_timidity_mididevice.cpp index 9e5be625b6..a63e1d7237 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/music_timidity_mididevice.cpp @@ -86,10 +86,10 @@ struct FmtChunk // //========================================================================== -TimidityMIDIDevice::TimidityMIDIDevice() +TimidityMIDIDevice::TimidityMIDIDevice(const char *args) { Renderer = NULL; - Renderer = new Timidity::Renderer((float)SampleRate); + Renderer = new Timidity::Renderer((float)SampleRate, args); } //========================================================================== @@ -245,6 +245,7 @@ FString TimidityMIDIDevice::GetStats() //========================================================================== TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, int rate) + :TimidityMIDIDevice(NULL) { File = fopen(filename, "wb"); if (File != NULL) diff --git a/src/sound/music_wildmidi_mididevice.cpp b/src/sound/music_wildmidi_mididevice.cpp new file mode 100644 index 0000000000..88550226a6 --- /dev/null +++ b/src/sound/music_wildmidi_mididevice.cpp @@ -0,0 +1,234 @@ +/* +** music_wildmidi_mididevice.cpp +** Provides access to WildMidi as a generic MIDI device. +** +**--------------------------------------------------------------------------- +** Copyright 2015 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "i_musicinterns.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "w_wad.h" +#include "v_text.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static FString CurrentConfig; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->WildMidiSetOption(WM_MO_REVERB, *self? WM_MO_REVERB:0); +} + +CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (currSong != NULL) + currSong->WildMidiSetOption(WM_MO_ENHANCED_RESAMPLING, *self? WM_MO_ENHANCED_RESAMPLING:0); +} + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// WildMIDIDevice Constructor +// +//========================================================================== + +WildMIDIDevice::WildMIDIDevice(const char *args) +{ + Renderer = NULL; + + if (wildmidi_frequency >= 11025 && wildmidi_frequency < 65536) + { // Use our own sample rate instead of the global one + SampleRate = wildmidi_frequency; + } + else + { // Else make sure we're not outside of WildMidi's range + SampleRate = clamp(SampleRate, 11025, 65535); + } + + if (args == NULL || *args == 0) args = wildmidi_config; + + if (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != WildMidi_GetSampleRate()) + { + if (CurrentConfig.IsNotEmpty()) + { + WildMidi_Shutdown(); + CurrentConfig = ""; + } + if (!WildMidi_Init(args, SampleRate, 0)) + { + CurrentConfig = args; + } + } + if (CurrentConfig.IsNotEmpty()) + { + Renderer = new WildMidi_Renderer(); + int flags = 0; + if (wildmidi_enhanced_resampling) flags |= WM_MO_ENHANCED_RESAMPLING; + if (wildmidi_reverb) flags |= WM_MO_REVERB; + Renderer->SetOption(WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, flags); + } +} + +//========================================================================== +// +// WildMIDIDevice Destructor +// +//========================================================================== + +WildMIDIDevice::~WildMIDIDevice() +{ + Close(); + if (Renderer != NULL) + { + delete Renderer; + } + // Do not shut down the device so that it can be reused for the next song being played. +} + +//========================================================================== +// +// WildMIDIDevice :: Open +// +// Returns 0 on success. +// +//========================================================================== + +int WildMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata) +{ + if (Renderer == NULL) + { + return 1; + } + int ret = OpenStream(2, 0, callback, userdata); + if (ret == 0) + { +// Renderer->Reset(); + } + return ret; +} + +//========================================================================== +// +// WildMIDIDevice :: PrecacheInstruments +// +// Each entry is packed as follows: +// Bits 0- 6: Instrument number +// Bits 7-13: Bank number +// Bit 14: Select drum set if 1, tone bank if 0 +// +//========================================================================== + +void WildMIDIDevice::PrecacheInstruments(const WORD *instruments, int count) +{ + for (int i = 0; i < count; ++i) + { + Renderer->LoadInstrument((instruments[i] >> 7) & 127, instruments[i] >> 14, instruments[i] & 127); + } +} + + +//========================================================================== +// +// WildMIDIDevice :: HandleEvent +// +//========================================================================== + +void WildMIDIDevice::HandleEvent(int status, int parm1, int parm2) +{ + Renderer->ShortEvent(status, parm1, parm2); +} + +//========================================================================== +// +// WildMIDIDevice :: HandleLongEvent +// +//========================================================================== + +void WildMIDIDevice::HandleLongEvent(const BYTE *data, int len) +{ + Renderer->LongEvent(data, len); +} + +//========================================================================== +// +// WildMIDIDevice :: ComputeOutput +// +//========================================================================== + +void WildMIDIDevice::ComputeOutput(float *buffer, int len) +{ + Renderer->ComputeOutput(buffer, len); +} + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +FString WildMIDIDevice::GetStats() +{ + FString out; + out.Format("%3d voices", Renderer->GetVoiceCount()); + return out; +} + +//========================================================================== +// +// WildMIDIDevice :: GetStats +// +//========================================================================== + +void WildMIDIDevice::WildMidiSetOption(int opt, int set) +{ + Renderer->SetOption(opt, set); +} diff --git a/src/sound/music_xmi_midiout.cpp b/src/sound/music_xmi_midiout.cpp index 6c179a16a0..71246f5184 100644 --- a/src/sound/music_xmi_midiout.cpp +++ b/src/sound/music_xmi_midiout.cpp @@ -108,8 +108,8 @@ extern char MIDI_CommonLengths[15]; // //========================================================================== -XMISong::XMISong (FileReader &reader, EMidiDevice type) -: MIDIStreamer(type), MusHeader(0), Songs(0) +XMISong::XMISong (FileReader &reader, EMidiDevice type, const char *args) +: MIDIStreamer(type, args), MusHeader(0), Songs(0) { #ifdef _WIN32 if (ExitEvent == NULL) diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index 447de49f98..5c980a7939 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -1,66 +1,66 @@ -/* -** oalsound.cpp -** System interface for sound; uses OpenAL -** -**--------------------------------------------------------------------------- -** Copyright 2008-2010 Chris Robinson -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define USE_WINDOWS_DWORD -#endif - -#include "except.h" -#include "doomstat.h" -#include "templates.h" -#include "oalsound.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "i_system.h" -#include "v_text.h" -#include "gi.h" -#include "actor.h" -#include "r_state.h" -#include "w_wad.h" -#include "i_music.h" -#include "i_musicinterns.h" -#include "tempfiles.h" - - -CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - - -bool IsOpenALPresent() -{ +/* +** oalsound.cpp +** System interface for sound; uses OpenAL +** +**--------------------------------------------------------------------------- +** Copyright 2008-2010 Chris Robinson +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif + +#include "except.h" +#include "doomstat.h" +#include "templates.h" +#include "oalsound.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "i_system.h" +#include "v_text.h" +#include "gi.h" +#include "actor.h" +#include "r_state.h" +#include "w_wad.h" +#include "i_music.h" +#include "i_musicinterns.h" +#include "tempfiles.h" + + +CVAR (String, snd_aldevice, "Default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, snd_efx, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + + +bool IsOpenALPresent() +{ #ifdef NO_OPENAL return false; #elif !defined _WIN32 @@ -87,1887 +87,1889 @@ bool IsOpenALPresent() } return cached_result; #endif -} - -void I_BuildALDeviceList(FOptionValues *opt) -{ - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; - -#ifndef NO_OPENAL - if (IsOpenALPresent()) - { - const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if (!names) - Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - else while (*names) - { - unsigned int i = opt->mValues.Reserve(1); - opt->mValues[i].TextValue = names; - opt->mValues[i].Text = names; - - names += strlen(names) + 1; - } - } -#endif -} - -#ifndef NO_OPENAL - - -EXTERN_CVAR (Int, snd_channels) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_waterreverb) -EXTERN_CVAR (Bool, snd_pitched) - - -#define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) -#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) - - -static ALenum checkALError(const char *fn, unsigned int ln) -{ - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); - } - return err; -} -#define getALError() checkALError(__FILE__, __LINE__) - -static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) -{ - ALCenum err = alcGetError(device); - if(err != ALC_NO_ERROR) - { - if(strchr(fn, '/')) - fn = strrchr(fn, '/')+1; - else if(strchr(fn, '\\')) - fn = strrchr(fn, '\\')+1; - Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); - } - return err; -} -#define getALCError(d) checkALCError((d), __FILE__, __LINE__) - - -// Fallback methods for when AL_SOFT_deferred_updates isn't available. In most -// cases these don't actually do anything, except on some Creative drivers -// where they act as appropriate fallbacks. -static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) -{ - alcSuspendContext(alcGetCurrentContext()); -} - -static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) -{ - alcProcessContext(alcGetCurrentContext()); -} - - -class OpenALSoundStream : public SoundStream -{ - OpenALSoundRenderer *Renderer; - - SoundStreamCallback Callback; - void *UserData; - - TArray Data; - - ALsizei SampleRate; - ALenum Format; - ALsizei FrameSize; - - static const int BufferCount = 4; - ALuint Buffers[BufferCount]; - ALuint Source; - - bool Playing; - bool Looping; - ALfloat Volume; - - - FileReader *Reader; - SoundDecoder *Decoder; - static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) - { - OpenALSoundStream *self = static_cast(_sstream); - if(length < 0) return false; - - size_t got = self->Decoder->read((char*)ptr, length); - if(got < (unsigned int)length) - { - if(!self->Looping || !self->Decoder->seek(0)) - return false; - got += self->Decoder->read((char*)ptr+got, length-got); - } - - return (got == (unsigned int)length); - } - - - bool SetupSource() - { - /* Get a source, killing the farthest, lowest-priority sound if needed */ - if(Renderer->FreeSfx.Size() == 0) - { - FSoundChan *lowest = Renderer->FindLowestChannel(); - if(lowest) Renderer->StopChannel(lowest); - - if(Renderer->FreeSfx.Size() == 0) - return false; - } - Renderer->FreeSfx.Pop(Source); - - /* Set the default properties for localized playback */ - alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); - alSourcef(Source, AL_MAX_GAIN, 1.f); - alSourcef(Source, AL_GAIN, 1.f); - alSourcef(Source, AL_PITCH, 1.f); - alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_SEC_OFFSET, 0.f); - alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(Source, AL_LOOPING, AL_FALSE); - if(Renderer->EnvSlot) - { - alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); - alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - - alGenBuffers(BufferCount, Buffers); - return (getALError() == AL_NO_ERROR); - } - -public: - OpenALSoundStream(OpenALSoundRenderer *renderer) - : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) - { - Renderer->Streams.Push(this); - memset(Buffers, 0, sizeof(Buffers)); - } - - virtual ~OpenALSoundStream() - { - if(Source) - { - alSourceRewind(Source); - alSourcei(Source, AL_BUFFER, 0); - - Renderer->FreeSfx.Push(Source); - Source = 0; - } - - if(Buffers[0]) - { - alDeleteBuffers(BufferCount, &Buffers[0]); - memset(Buffers, 0, sizeof(Buffers)); - } - getALError(); - - Renderer->Streams.Delete(Renderer->Streams.Find(this)); - Renderer = NULL; - - delete Decoder; - delete Reader; - } - - - virtual bool Play(bool loop, float vol) - { - SetVolume(vol); - - if(Playing) - return true; - - /* Clear the buffer queue, then fill and queue each buffer */ - alSourcei(Source, AL_BUFFER, 0); - for(int i = 0;i < BufferCount;i++) - { - if(!Callback(this, &Data[0], Data.Size(), UserData)) - { - if(i == 0) - return false; - break; - } - - alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &Buffers[i]); - } - if(getALError() != AL_NO_ERROR) - return false; - - alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); - - return Playing; - } - - virtual void Stop() - { - if(!Playing) - return; - - alSourceStop(Source); - alSourcei(Source, AL_BUFFER, 0); - getALError(); - - Playing = false; - } - - virtual void SetVolume(float vol) - { - Volume = vol; - UpdateVolume(); - } - - void UpdateVolume() - { - alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); - getALError(); - } - - virtual bool SetPaused(bool pause) - { - if(pause) - alSourcePause(Source); - else - alSourcePlay(Source); - return (getALError()==AL_NO_ERROR); - } - - virtual bool SetPosition(unsigned int ms_pos) - { - if(!Decoder->seek(ms_pos)) - return false; - - if(!Playing) - return true; - // Stop the source so that all buffers become processed, then call - // IsEnded() to refill and restart the source queue with the new - // position. - alSourceStop(Source); - getALError(); - return !IsEnded(); - } - - virtual unsigned int GetPosition() - { - ALint offset, queued, state; - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if(getALError() != AL_NO_ERROR) - return 0; - - size_t pos = Decoder->getSampleOffset(); - if(state != AL_STOPPED) - { - size_t rem = queued*(Data.Size()/FrameSize) - offset; - if(pos > rem) pos -= rem; - else pos = 0; - } - return (unsigned int)(pos * 1000.0 / SampleRate); - } - - virtual bool IsEnded() - { - if(!Playing) - return true; - - ALint state, processed; - alGetSourcei(Source, AL_SOURCE_STATE, &state); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - - Playing = (getALError()==AL_NO_ERROR); - if(!Playing) - return true; - - // For each processed buffer in the queue... - while(processed > 0) - { - ALuint bufid; - - // Unqueue the oldest buffer, fill it with more data, and queue it - // on the end - alSourceUnqueueBuffers(Source, 1, &bufid); - processed--; - - if(Callback(this, &Data[0], Data.Size(), UserData)) - { - alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); - alSourceQueueBuffers(Source, 1, &bufid); - } - } - - // If the source is not playing or paused, and there are buffers queued, - // then there was an underrun. Restart the source. - Playing = (getALError()==AL_NO_ERROR); - if(Playing && state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued = 0; - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - - Playing = (getALError() == AL_NO_ERROR) && (queued > 0); - if(Playing) - { - alSourcePlay(Source); - Playing = (getALError()==AL_NO_ERROR); - } - } - - return !Playing; - } - - FString GetStats() - { - FString stats; - size_t pos, len; - ALfloat volume; - ALint offset; - ALint processed; - ALint queued; - ALint state; - ALenum err; - - alGetSourcef(Source, AL_GAIN, &volume); - alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); - alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); - alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); - alGetSourcei(Source, AL_SOURCE_STATE, &state); - if((err=alGetError()) != AL_NO_ERROR) - { - stats = "Error getting stats: "; - stats += alGetString(err); - return stats; - } - - stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : - (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; - - pos = Decoder->getSampleOffset(); - len = Decoder->getSampleLength(); - if(state == AL_STOPPED) - offset = BufferCount * (Data.Size()/FrameSize); - else - { - size_t rem = queued*(Data.Size()/FrameSize) - offset; - if(pos > rem) pos -= rem; - else if(len > 0) pos += len - rem; - else pos = 0; - } - pos = (size_t)(pos * 1000.0 / SampleRate); - len = (size_t)(len * 1000.0 / SampleRate); - stats.AppendFormat(",%3u%% buffered", 100 - 100*offset/(BufferCount*(Data.Size()/FrameSize))); - stats.AppendFormat(", %zu.%03zu", pos/1000, pos%1000); - if(len > 0) - stats.AppendFormat(" / %zu.%03zu", len/1000, len%1000); - if(state == AL_PAUSED) - stats += ", paused"; - if(state == AL_PLAYING) - stats += ", playing"; - stats.AppendFormat(", %uHz", SampleRate); - if(!Playing) - stats += " XX"; - return stats; - } - - bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) - { - if(!SetupSource()) - return false; - - Callback = callback; - UserData = userdata; - SampleRate = samplerate; - - Format = AL_NONE; - if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ - { - if((flags&Mono)) Format = AL_FORMAT_MONO8; - else Format = AL_FORMAT_STEREO8; - } - else if((flags&Float)) - { - if(alIsExtensionPresent("AL_EXT_FLOAT32")) - { - if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; - else Format = AL_FORMAT_STEREO_FLOAT32; - } - } - else if((flags&Bits32)) - { - } - else - { - if((flags&Mono)) Format = AL_FORMAT_MONO16; - else Format = AL_FORMAT_STEREO16; - } - - if(Format == AL_NONE) - { - Printf("Unsupported format: 0x%x\n", flags); - return false; - } - - FrameSize = 1; - if((flags&Bits8)) - FrameSize *= 1; - else if((flags&(Bits32|Float))) - FrameSize *= 4; - else - FrameSize *= 2; - - if((flags&Mono)) - FrameSize *= 1; - else - FrameSize *= 2; - - buffbytes += FrameSize-1; - buffbytes -= buffbytes%FrameSize; - Data.Resize(buffbytes); - - return true; - } - - bool Init(FileReader *reader, bool loop) - { - if(!SetupSource()) - { - delete reader; - return false; - } - - if(Decoder) delete Decoder; - if(Reader) delete Reader; - Reader = reader; - Decoder = Renderer->CreateDecoder(Reader); - if(!Decoder) return false; - - Callback = DecoderCallback; - UserData = NULL; - Format = AL_NONE; - FrameSize = 1; - - ChannelConfig chans; - SampleType type; - int srate; - - Decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; - FrameSize *= 1; - } - if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; - FrameSize *= 2; - } - if(type == SampleType_UInt8) FrameSize *= 1; - if(type == SampleType_Int16) FrameSize *= 2; - - if(Format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - return false; - } - SampleRate = srate; - Looping = loop; - - Data.Resize((size_t)(0.2 * SampleRate) * FrameSize); - - return true; - } -}; - - -extern ReverbContainer *ForcedEnvironment; - -#define AREA_SOUND_RADIUS (128.f) - -#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ - -#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) - - -static float GetRolloff(const FRolloffInfo *rolloff, float distance) -{ - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; - - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; - - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) - return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; -} - -ALCdevice *OpenALSoundRenderer::InitDevice() -{ - ALCdevice *device = NULL; - if (IsOpenALPresent()) - { - if(strcmp(snd_aldevice, "Default") != 0) - { - device = alcOpenDevice(*snd_aldevice); - if(!device) - Printf(TEXTCOLOR_BLUE" Failed to open device " TEXTCOLOR_BOLD"%s" TEXTCOLOR_BLUE". Trying default.\n", *snd_aldevice); - } - - if(!device) - { - device = alcOpenDevice(NULL); - if(!device) - { - Printf(TEXTCOLOR_RED" Could not open audio device\n"); - } - } - } - else - { - Printf(TEXTCOLOR_ORANGE"Failed to load openal32.dll\n"); - } - return device; -} - - -template -static void LoadALFunc(const char *name, T *x) -{ *x = reinterpret_cast(alGetProcAddress(name)); } - -#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) -OpenALSoundRenderer::OpenALSoundRenderer() - : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) -{ - EnvFilters[0] = EnvFilters[1] = 0; - - Printf("I_InitSound: Initializing OpenAL\n"); - - Device = InitDevice(); - if (Device == NULL) return; - - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); - - ALCint major=0, minor=0; - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - DPrintf(" ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - DPrintf(" ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - - TArray attribs; - if(*snd_samplerate > 0) - { - attribs.Push(ALC_FREQUENCY); - attribs.Push(*snd_samplerate); - } - // Make sure one source is capable of stereo output with the rest doing - // mono, without running out of voices - attribs.Push(ALC_MONO_SOURCES); - attribs.Push(MAX(*snd_channels, 2) - 1); - attribs.Push(ALC_STEREO_SOURCES); - attribs.Push(1); - // Other attribs..? - attribs.Push(0); - - Context = alcCreateContext(Device, &attribs[0]); - if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) - { - Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); - if(Context) - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - attribs.Clear(); - - DPrintf(" Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - DPrintf(" Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - DPrintf(" Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - DPrintf(" Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - - ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); - ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect");; - AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); - AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); - AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); - - alDopplerFactor(0.5f); - alSpeedOfSound(343.3f * 96.0f); - alDistanceModel(AL_INVERSE_DISTANCE); - if(AL.EXT_source_distance_model) - alEnable(AL_SOURCE_DISTANCE_MODEL); - - if(AL.SOFT_deferred_updates) - { - LOAD_FUNC(alDeferUpdatesSOFT); - LOAD_FUNC(alProcessUpdatesSOFT); - } - else - { - alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; - alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; - } - - ALenum err = getALError(); - if(err != AL_NO_ERROR) - { - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - - ALCint numMono=0, numStereo=0; - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); - - Sources.Resize(MIN(MAX(*snd_channels, 2), numMono+numStereo)); - for(size_t i = 0;i < Sources.Size();i++) - { - alGenSources(1, &Sources[i]); - if(getALError() != AL_NO_ERROR) - { - Sources.Resize(i); - Sources.ShrinkToFit(); - break; - } - } - if(Sources.Size() == 0) - { - Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; - return; - } - FreeSfx = Sources; - DPrintf(" Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); - - WasInWater = false; - if(*snd_efx && ALC.EXT_EFX) - { - // EFX function pointers - LOAD_FUNC(alGenEffects); - LOAD_FUNC(alDeleteEffects); - LOAD_FUNC(alIsEffect); - LOAD_FUNC(alEffecti); - LOAD_FUNC(alEffectiv); - LOAD_FUNC(alEffectf); - LOAD_FUNC(alEffectfv); - LOAD_FUNC(alGetEffecti); - LOAD_FUNC(alGetEffectiv); - LOAD_FUNC(alGetEffectf); - LOAD_FUNC(alGetEffectfv); - - LOAD_FUNC(alGenFilters); - LOAD_FUNC(alDeleteFilters); - LOAD_FUNC(alIsFilter); - LOAD_FUNC(alFilteri); - LOAD_FUNC(alFilteriv); - LOAD_FUNC(alFilterf); - LOAD_FUNC(alFilterfv); - LOAD_FUNC(alGetFilteri); - LOAD_FUNC(alGetFilteriv); - LOAD_FUNC(alGetFilterf); - LOAD_FUNC(alGetFilterfv); - - LOAD_FUNC(alGenAuxiliaryEffectSlots); - LOAD_FUNC(alDeleteAuxiliaryEffectSlots); - LOAD_FUNC(alIsAuxiliaryEffectSlot); - LOAD_FUNC(alAuxiliaryEffectSloti); - LOAD_FUNC(alAuxiliaryEffectSlotiv); - LOAD_FUNC(alAuxiliaryEffectSlotf); - LOAD_FUNC(alAuxiliaryEffectSlotfv); - LOAD_FUNC(alGetAuxiliaryEffectSloti); - LOAD_FUNC(alGetAuxiliaryEffectSlotiv); - LOAD_FUNC(alGetAuxiliaryEffectSlotf); - LOAD_FUNC(alGetAuxiliaryEffectSlotfv); - if(getALError() == AL_NO_ERROR) - { - ALuint envReverb; - alGenEffects(1, &envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(" EAX Reverb found\n"); - alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - if(alGetError() == AL_NO_ERROR) - DPrintf(" Standard Reverb found\n"); - - alDeleteEffects(1, &envReverb); - getALError(); - } - - alGenAuxiliaryEffectSlots(1, &EnvSlot); - alGenFilters(2, EnvFilters); - if(getALError() == AL_NO_ERROR) - { - alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); - if(getALError() == AL_NO_ERROR) - DPrintf(" Lowpass found\n"); - else - { - alDeleteFilters(2, EnvFilters); - EnvFilters[0] = EnvFilters[1] = 0; - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvSlot = 0; - getALError(); - } - } - else - { - alDeleteFilters(2, EnvFilters); - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - EnvFilters[0] = EnvFilters[1] = 0; - EnvSlot = 0; - getALError(); - } - } - } - - if(EnvSlot) - Printf(" EFX enabled\n"); -} -#undef LOAD_FUNC - -OpenALSoundRenderer::~OpenALSoundRenderer() -{ - if(!Device) - return; - - while(Streams.Size() > 0) - delete Streams[0]; - - alDeleteSources(Sources.Size(), &Sources[0]); - Sources.Clear(); - FreeSfx.Clear(); - SfxGroup.Clear(); - PausableSfx.Clear(); - ReverbSfx.Clear(); - - if(EnvEffects.CountUsed() > 0) - { - EffectMapIter iter(EnvEffects); - EffectMap::Pair *pair; - while(iter.NextPair(pair)) - alDeleteEffects(1, &(pair->Value)); - } - EnvEffects.Clear(); - - if(EnvSlot) - { - alDeleteAuxiliaryEffectSlots(1, &EnvSlot); - alDeleteFilters(2, EnvFilters); - } - EnvSlot = 0; - EnvFilters[0] = EnvFilters[1] = 0; - - alcMakeContextCurrent(NULL); - alcDestroyContext(Context); - Context = NULL; - alcCloseDevice(Device); - Device = NULL; -} - -void OpenALSoundRenderer::SetSfxVolume(float volume) -{ - SfxVolume = volume; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL) - { - ALuint source = GET_PTRID(schan->SysChannel); - volume = SfxVolume; - - alDeferUpdatesSOFT(); - alSourcef(source, AL_MAX_GAIN, volume); - alSourcef(source, AL_GAIN, volume * schan->Volume); - } - schan = schan->NextChan; - } - - getALError(); -} - -void OpenALSoundRenderer::SetMusicVolume(float volume) -{ - MusicVolume = volume; - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->UpdateVolume(); -} - -unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) -{ - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - if(alIsBuffer(buffer)) - { - ALint bits, channels, freq, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_FREQUENCY, &freq); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (unsigned int)(size / (channels*bits/8) * 1000. / freq); - } - } - return 0; -} - -unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) -{ - if(sfx.data) - { - ALuint buffer = GET_PTRID(sfx.data); - ALint bits, channels, size; - alGetBufferi(buffer, AL_BITS, &bits); - alGetBufferi(buffer, AL_CHANNELS, &channels); - alGetBufferi(buffer, AL_SIZE, &size); - if(getALError() == AL_NO_ERROR) - return (ALsizei)(size / (channels * bits / 8)); - } - return 0; -} - -float OpenALSoundRenderer::GetOutputRate() -{ - ALCint rate = 44100; // Default, just in case - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); - return (float)rate; -} - - -SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) -{ - SoundHandle retval = { NULL }; - - if(length == 0) return retval; - - if(bits == -8) - { - // Simple signed->unsigned conversion - for(int i = 0;i < length;i++) - sfxdata[i] ^= 0x80; - bits = -bits; - } - - ALenum format = AL_NONE; - if(bits == 16) - { - if(channels == 1) format = AL_FORMAT_MONO16; - if(channels == 2) format = AL_FORMAT_STEREO16; - } - else if(bits == 8) - { - if(channels == 1) format = AL_FORMAT_MONO8; - if(channels == 2) format = AL_FORMAT_STEREO8; - } - - if(format == AL_NONE || frequency <= 0) - { - Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); - return retval; - } - length -= length%(channels*bits/8); - - ALenum err; - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, sfxdata, length, frequency); - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - return retval; - } - - if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) - { - if(loopstart < 0) - loopstart = 0; - if(loopend < loopstart) - loopend = length / (channels*bits/8); - - ALint loops[2] = { loopstart, loopend }; - DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); - alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); - getALError(); - } - else if(loopstart > 0 || loopend > 0) - { - static bool warned = false; - if(!warned) - Printf("Loop points not supported!\n"); - warned = true; - } - - retval.data = MAKE_PTRID(buffer); - return retval; -} - -SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) -{ - SoundHandle retval = { NULL }; - MemoryReader reader((const char*)sfxdata, length); - ALenum format = AL_NONE; - ChannelConfig chans; - SampleType type; - int srate; - - SoundDecoder *decoder = CreateDecoder(&reader); - if(!decoder) return retval; - - decoder->getInfo(&srate, &chans, &type); - if(chans == ChannelConfig_Mono) - { - if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; - if(type == SampleType_Int16) format = AL_FORMAT_MONO16; - } - if(chans == ChannelConfig_Stereo) - { - if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; - if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; - } - - if(format == AL_NONE) - { - Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), - GetSampleTypeName(type)); - delete decoder; - return retval; - } - - TArray data = decoder->readAll(); - - ALuint buffer = 0; - alGenBuffers(1, &buffer); - alBufferData(buffer, format, &data[0], data.Size(), srate); - - ALenum err; - if((err=getALError()) != AL_NO_ERROR) - { - Printf("Failed to buffer data: %s\n", alGetString(err)); - alDeleteBuffers(1, &buffer); - getALError(); - delete decoder; - return retval; - } - - retval.data = MAKE_PTRID(buffer); - delete decoder; - return retval; -} - -void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) -{ - if(!sfx.data) - return; - - ALuint buffer = GET_PTRID(sfx.data); - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel) - { - ALint bufID = 0; - alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); - if((ALuint)bufID == buffer) - { - FSoundChan *next = schan->NextChan; - StopChannel(schan); - schan = next; - continue; - } - } - schan = schan->NextChan; - } - - alDeleteBuffers(1, &buffer); - getALError(); -} - - -SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) -{ - OpenALSoundStream *stream = new OpenALSoundStream(this); - if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) - { - delete stream; - return NULL; - } - return stream; -} - -SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) -{ - OpenALSoundStream *stream = new OpenALSoundStream(this); - if (!stream->Init(reader, !!(flags&SoundStream::Loop))) - { - delete stream; - return NULL; - } - return stream; -} - -FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) -{ - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) StopChannel(lowest); - - if(FreeSfx.Size() == 0) - return NULL; - } - - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); - alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); - - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 1000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume*vol); - - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); - - if(!reuse_chan) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - // FIXME: set offset based on the current time and the StartTime - alSourcef(source, AL_SEC_OFFSET, 0.f); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; - - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } - - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); - - chan->Rolloff.RolloffType = ROLLOFF_Log; - chan->Rolloff.RolloffFactor = 0.f; - chan->Rolloff.MinDistance = 1.f; - chan->DistanceScale = 1.f; - chan->DistanceSqr = 0.f; - chan->ManualRolloff = false; - - return chan; -} - -FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, - FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, - int channum, int chanflags, FISoundChannel *reuse_chan) -{ - float dist_sqr = (float)(pos - listener->position).LengthSquared(); - - if(FreeSfx.Size() == 0) - { - FSoundChan *lowest = FindLowestChannel(); - if(lowest) - { - if(lowest->Priority < priority || (lowest->Priority == priority && - lowest->DistanceSqr > dist_sqr)) - StopChannel(lowest); - } - if(FreeSfx.Size() == 0) - return NULL; - } - - bool manualRolloff = true; - ALuint buffer = GET_PTRID(sfx.data); - ALuint source = FreeSfx.Last(); - if(rolloff->RolloffType == ROLLOFF_Log) - { - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); - manualRolloff = false; - } - else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) - { - alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); - alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - manualRolloff = false; - } - if(manualRolloff) - { - // How manual rolloff works: - // - // If a sound is using Custom or Doom style rolloff, or Linear style - // when AL_EXT_source_distance_model is not supported, we have to play - // around a bit to get appropriate distance attenation. What we do is - // calculate the attenuation that should be applied, then given an - // Inverse Distance rolloff model with OpenAL, reverse the calculation - // to get the distance needed for that much attenuation. The Inverse - // Distance calculation is: - // - // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) - // - // Thus, the reverse is: - // - // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist - // - // This can be simplified by using a MinDist and RolloffFactor of 1, - // which makes it: - // - // Distance = 1.0f/Gain; - // - // The source position is then set that many units away from the - // listener position, and OpenAL takes care of the rest. - if(AL.EXT_source_distance_model) - alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); - alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); - alSourcef(source, AL_MAX_DISTANCE, 100000.f); - alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); - - FVector3 dir = pos - listener->position; - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(rolloff, sqrt(dist_sqr) * distscale); - dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; - - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 dir = pos - listener->position; - - float mindist = rolloff->MinDistance/distscale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - - dir += listener->position; - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - } - else - alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); - - alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); - - alSourcef(source, AL_MAX_GAIN, SfxVolume); - alSourcef(source, AL_GAIN, SfxVolume); - - if(EnvSlot) - { - if(!(chanflags&SNDF_NOREVERB)) - { - alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - else - { - alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); - alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); - } - alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); - } - if(WasInWater && !(chanflags&SNDF_NOREVERB)) - alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); - else - alSourcef(source, AL_PITCH, PITCH(pitch)); - - if(!reuse_chan) - alSourcef(source, AL_SEC_OFFSET, 0.f); - else - { - if((chanflags&SNDF_ABSTIME)) - alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); - else - { - // FIXME: set offset based on the current time and the StartTime - alSourcef(source, AL_SAMPLE_OFFSET, 0.f); - } - } - if(getALError() != AL_NO_ERROR) - return NULL; - - alSourcei(source, AL_BUFFER, buffer); - if((chanflags&SNDF_NOPAUSE) || !SFXPaused) - alSourcePlay(source); - if(getALError() != AL_NO_ERROR) - { - alSourcei(source, AL_BUFFER, 0); - getALError(); - return NULL; - } - - if(!(chanflags&SNDF_NOREVERB)) - ReverbSfx.Push(source); - if(!(chanflags&SNDF_NOPAUSE)) - PausableSfx.Push(source); - SfxGroup.Push(source); - FreeSfx.Pop(); - - FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); - else chan->SysChannel = MAKE_PTRID(source); - - chan->Rolloff = *rolloff; - chan->DistanceScale = distscale; - chan->DistanceSqr = dist_sqr; - chan->ManualRolloff = manualRolloff; - - return chan; -} - -void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - alDeferUpdatesSOFT(); - - ALuint source = GET_PTRID(chan->SysChannel); - alSourcef(source, AL_GAIN, SfxVolume * volume); -} - -void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - ALuint source = GET_PTRID(chan->SysChannel); - // Release first, so it can be properly marked as evicted if it's being - // forcefully killed - S_ChannelEnded(chan); - - alSourceRewind(source); - alSourcei(source, AL_BUFFER, 0); - getALError(); - - uint32 i; - if((i=PausableSfx.Find(source)) < PausableSfx.Size()) - PausableSfx.Delete(i); - if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) - ReverbSfx.Delete(i); - - SfxGroup.Delete(SfxGroup.Find(source)); - FreeSfx.Push(source); -} - -unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return 0; - - ALint pos; - alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); - if(getALError() == AL_NO_ERROR) - return pos; - return 0; -} - - -void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) -{ - int oldslots = SFXPaused; - - if(paused) - { - SFXPaused |= 1 << slot; - if(oldslots == 0 && PausableSfx.Size() > 0) - { - alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - SFXPaused &= ~(1 << slot); - if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) - { - alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); - getALError(); - } - } -} - -void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) -{ - switch(state) - { - case SoundRenderer::INACTIVE_Active: - alListenerf(AL_GAIN, 1.0f); - break; - - /* FIXME: This doesn't stop anything. */ - case SoundRenderer::INACTIVE_Complete: - case SoundRenderer::INACTIVE_Mute: - alListenerf(AL_GAIN, 0.0f); - break; - } -} - -void OpenALSoundRenderer::Sync(bool sync) -{ - if(sync) - { - if(SfxGroup.Size() > 0) - { - alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); - getALError(); - PurgeStoppedSources(); - } - } - else - { - // Might already be something to handle this; basically, get a vector - // of all values in SfxGroup that are not also in PausableSfx (when - // SFXPaused is non-0). - TArray toplay = SfxGroup; - if(SFXPaused) - { - uint32 i = 0; - while(i < toplay.Size()) - { - uint32 p = PausableSfx.Find(toplay[i]); - if(p < PausableSfx.Size()) - toplay.Delete(i); - else - i++; - } - } - if(toplay.Size() > 0) - { - alSourcePlayv(toplay.Size(), &toplay[0]); - getALError(); - } - } -} - -void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) -{ - if(chan == NULL || chan->SysChannel == NULL) - return; - - alDeferUpdatesSOFT(); - - FVector3 dir = pos - listener->position; - chan->DistanceSqr = (float)dir.LengthSquared(); - - if(chan->ManualRolloff) - { - if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) - { - float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); - dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); - } - if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); - float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - } - else if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) - { - float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; - FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); - float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; - dir = amb + (dir-amb)*a; - } - dir += listener->position; - - ALuint source = GET_PTRID(chan->SysChannel); - alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); - alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); - getALError(); -} - -void OpenALSoundRenderer::UpdateListener(SoundListener *listener) -{ - if(!listener->valid) - return; - - alDeferUpdatesSOFT(); - - float angle = listener->angle; - ALfloat orient[6]; - // forward - orient[0] = cos(angle); - orient[1] = 0.f; - orient[2] = -sin(angle); - // up - orient[3] = 0.f; - orient[4] = 1.f; - orient[5] = 0.f; - - alListenerfv(AL_ORIENTATION, orient); - alListener3f(AL_POSITION, listener->position.X, - listener->position.Y, - -listener->position.Z); - alListener3f(AL_VELOCITY, listener->velocity.X, - listener->velocity.Y, - -listener->velocity.Z); - getALError(); - - const ReverbContainer *env = ForcedEnvironment; - if(!env) - { - env = listener->Environment; - if(!env) - env = DefaultEnvironments[0]; - } - if(env != PrevEnvironment || env->Modified) - { - PrevEnvironment = env; - DPrintf("Reverb Environment %s\n", env->Name); - - if(EnvSlot != 0) - LoadReverb(env); - - const_cast(env)->Modified = false; - } - - // NOTE: Moving into and out of water will undo pitch variations on sounds. - if(listener->underwater || env->SoftwareWater) - { - if(!WasInWater) - { - WasInWater = true; - - if(EnvSlot != 0 && *snd_waterreverb) - { - // Find the "Underwater" reverb environment - env = S_FindEnvironment(0x1600); - LoadReverb(env ? env : DefaultEnvironments[0]); - - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - - // Apply the updated filters on the sources - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); - getALError(); - } - } - else if(WasInWater) - { - WasInWater = false; - - if(EnvSlot != 0) - { - LoadReverb(env); - - alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); - alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - for(uint32 i = 0;i < ReverbSfx.Size();++i) - { - alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); - alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); - } - } - - for(uint32 i = 0;i < ReverbSfx.Size();++i) - alSourcef(ReverbSfx[i], AL_PITCH, 1.f); - getALError(); - } -} - -void OpenALSoundRenderer::UpdateSounds() -{ - alProcessUpdatesSOFT(); - - if(ALC.EXT_disconnect) - { - ALCint connected = ALC_TRUE; - alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); - if(connected == ALC_FALSE) - { - Printf("Sound device disconnected; restarting...\n"); - static char snd_reset[] = "snd_reset"; - AddCommandString(snd_reset); - return; - } - } - - PurgeStoppedSources(); -} - -void OpenALSoundRenderer::UpdateMusic() -{ - // For some reason this isn't being called? - for(uint32 i = 0;i < Streams.Size();++i) - Streams[i]->IsEnded(); -} - -bool OpenALSoundRenderer::IsValid() -{ - return Device != NULL; -} - -void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) -{ - // FIXME: Get current time (preferably from the audio clock, but the system - // time will have to do) - chan->StartTime.AsOne = 0; -} - -float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) -{ - if(chan == NULL || chan->SysChannel == NULL) - return 0.f; - - ALuint source = GET_PTRID(chan->SysChannel); - ALfloat volume = 0.f; - - alGetSourcef(source, AL_GAIN, &volume); - getALError(); - - volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); - return volume; -} - - -void OpenALSoundRenderer::PrintStatus() -{ - Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); - getALCError(Device); - - ALCint frequency, major, minor, mono, stereo; - alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); - alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); - alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); - Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); - Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); - } - if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) - Printf("EFX not found\n"); - else - { - ALCint sends; - alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); - alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); - alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); - if(getALCError(Device) == AL_NO_ERROR) - { - Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); - Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); - } - } - Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); - Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); - Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); - Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); - getALError(); -} - -FString OpenALSoundRenderer::GatherStats() -{ - ALCint updates = 1; - alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); - getALCError(Device); - - uint32 total = Sources.Size(); - uint32 used = SfxGroup.Size()+Streams.Size(); - uint32 unused = FreeSfx.Size(); - - FString out; - out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", - total, used, unused, 1000/updates); - return out; -} - -void OpenALSoundRenderer::PrintDriversList() -{ - const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if(drivers == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - return; - } - - const ALCchar *current = NULL; - if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) - current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); - if(alcGetError(Device) != ALC_NO_ERROR || !current) - current = alcGetString(Device, ALC_DEVICE_SPECIFIER); - if(current == NULL) - { - Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); - return; - } - - Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, - "Default"); - for(int i = 1;*drivers;i++) - { - Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), - ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, - drivers); - drivers += strlen(drivers)+1; - } -} - -void OpenALSoundRenderer::PurgeStoppedSources() -{ - // Release channels that are stopped - for(uint32 i = 0;i < SfxGroup.Size();++i) - { - ALuint src = SfxGroup[i]; - ALint state = AL_INITIAL; - alGetSourcei(src, AL_SOURCE_STATE, &state); - if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) - continue; - - FSoundChan *schan = Channels; - while(schan) - { - if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) - { - StopChannel(schan); - break; - } - schan = schan->NextChan; - } - } - getALError(); -} - -void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) -{ - ALuint *envReverb = EnvEffects.CheckKey(env->ID); - bool doLoad = (env->Modified || !envReverb); - - if(!envReverb) - { - bool ok = false; - - envReverb = &EnvEffects.Insert(env->ID, 0); - alGenEffects(1, envReverb); - if(getALError() == AL_NO_ERROR) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); - ok = (alGetError() == AL_NO_ERROR); - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); - ok = (alGetError() == AL_NO_ERROR); - } - if(!ok) - { - alDeleteEffects(1, envReverb); - getALError(); - } - } - if(!ok) - { - *envReverb = 0; - doLoad = false; - } - } - - if(doLoad) - { - const REVERB_PROPERTIES &props = env->Properties; - ALint type = AL_EFFECT_NULL; - - alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); -#define mB2Gain(x) ((float)pow(10., (x)/2000.)) - if(type == AL_EFFECT_EAXREVERB) - { - ALfloat reflectpan[3] = { props.ReflectionsPan0, - props.ReflectionsPan1, - props.ReflectionsPan2 }; - ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, - props.ReverbPan2 }; -#undef SETPARAM -#define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); - SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); - SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); - SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); - SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, HFREFERENCE, props.HFReference); - SETPARAM(*envReverb, LFREFERENCE, props.LFReference); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); -#undef SETPARAM - } - else if(type == AL_EFFECT_REVERB) - { -#define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) - SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); - SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); - SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); - SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); - SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); - SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); - SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); - SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); - SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); - SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); - SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); - SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); - alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, - (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); -#undef SETPARAM - } -#undef mB2Gain - } - - alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); - getALError(); -} - -FSoundChan *OpenALSoundRenderer::FindLowestChannel() -{ - FSoundChan *schan = Channels; - FSoundChan *lowest = NULL; - while(schan) - { - if(schan->SysChannel != NULL) - { - if(!lowest || schan->Priority < lowest->Priority || - (schan->Priority == lowest->Priority && - schan->DistanceSqr > lowest->DistanceSqr)) - lowest = schan; - } - schan = schan->NextChan; - } - return lowest; -} - -#endif // NO_OPENAL +} + +void I_BuildALDeviceList(FOptionValues *opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + if (IsOpenALPresent()) + { + const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if (!names) + Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + else while (*names) + { + unsigned int i = opt->mValues.Reserve(1); + opt->mValues[i].TextValue = names; + opt->mValues[i].Text = names; + + names += strlen(names) + 1; + } + } +#endif +} + +#ifndef NO_OPENAL + + +EXTERN_CVAR (Int, snd_channels) +EXTERN_CVAR (Int, snd_samplerate) +EXTERN_CVAR (Bool, snd_waterreverb) +EXTERN_CVAR (Bool, snd_pitched) + + +#define MAKE_PTRID(x) ((void*)(uintptr_t)(x)) +#define GET_PTRID(x) ((uint32)(uintptr_t)(x)) + + +static ALenum checkALError(const char *fn, unsigned int ln) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received AL error %s (%#x), %s:%u\n", alGetString(err), err, fn, ln); + } + return err; +} +#define getALError() checkALError(__FILE__, __LINE__) + +static ALCenum checkALCError(ALCdevice *device, const char *fn, unsigned int ln) +{ + ALCenum err = alcGetError(device); + if(err != ALC_NO_ERROR) + { + if(strchr(fn, '/')) + fn = strrchr(fn, '/')+1; + else if(strchr(fn, '\\')) + fn = strrchr(fn, '\\')+1; + Printf(">>>>>>>>>>>> Received ALC error %s (%#x), %s:%u\n", alcGetString(device, err), err, fn, ln); + } + return err; +} +#define getALCError(d) checkALCError((d), __FILE__, __LINE__) + + +// Fallback methods for when AL_SOFT_deferred_updates isn't available. In most +// cases these don't actually do anything, except on some Creative drivers +// where they act as appropriate fallbacks. +static ALvoid AL_APIENTRY _wrap_DeferUpdatesSOFT(void) +{ + alcSuspendContext(alcGetCurrentContext()); +} + +static ALvoid AL_APIENTRY _wrap_ProcessUpdatesSOFT(void) +{ + alcProcessContext(alcGetCurrentContext()); +} + + +class OpenALSoundStream : public SoundStream +{ + OpenALSoundRenderer *Renderer; + + SoundStreamCallback Callback; + void *UserData; + + TArray Data; + + ALsizei SampleRate; + ALenum Format; + ALsizei FrameSize; + + static const int BufferCount = 4; + ALuint Buffers[BufferCount]; + ALuint Source; + + bool Playing; + bool Looping; + ALfloat Volume; + + + FileReader *Reader; + SoundDecoder *Decoder; + static bool DecoderCallback(SoundStream *_sstream, void *ptr, int length, void *user) + { + OpenALSoundStream *self = static_cast(_sstream); + if(length < 0) return false; + + size_t got = self->Decoder->read((char*)ptr, length); + if(got < (unsigned int)length) + { + if(!self->Looping || !self->Decoder->seek(0)) + return false; + got += self->Decoder->read((char*)ptr+got, length-got); + } + + return (got == (unsigned int)length); + } + + + bool SetupSource() + { + /* Get a source, killing the farthest, lowest-priority sound if needed */ + if(Renderer->FreeSfx.Size() == 0) + { + FSoundChan *lowest = Renderer->FindLowestChannel(); + if(lowest) Renderer->StopChannel(lowest); + + if(Renderer->FreeSfx.Size() == 0) + return false; + } + Renderer->FreeSfx.Pop(Source); + + /* Set the default properties for localized playback */ + alSource3f(Source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSource3f(Source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(Source, AL_POSITION, 0.f, 0.f, 0.f); + alSourcef(Source, AL_MAX_GAIN, 1.f); + alSourcef(Source, AL_GAIN, 1.f); + alSourcef(Source, AL_PITCH, 1.f); + alSourcef(Source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_SEC_OFFSET, 0.f); + alSourcei(Source, AL_SOURCE_RELATIVE, AL_TRUE); + alSourcei(Source, AL_LOOPING, AL_FALSE); + if(Renderer->EnvSlot) + { + alSourcef(Source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + alSourcef(Source, AL_AIR_ABSORPTION_FACTOR, 0.f); + alSourcei(Source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(Source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + + alGenBuffers(BufferCount, Buffers); + return (getALError() == AL_NO_ERROR); + } + +public: + OpenALSoundStream(OpenALSoundRenderer *renderer) + : Renderer(renderer), Source(0), Playing(false), Looping(false), Volume(1.0f), Reader(NULL), Decoder(NULL) + { + Renderer->Streams.Push(this); + memset(Buffers, 0, sizeof(Buffers)); + } + + virtual ~OpenALSoundStream() + { + if(Source) + { + alSourceRewind(Source); + alSourcei(Source, AL_BUFFER, 0); + + Renderer->FreeSfx.Push(Source); + Source = 0; + } + + if(Buffers[0]) + { + alDeleteBuffers(BufferCount, &Buffers[0]); + memset(Buffers, 0, sizeof(Buffers)); + } + getALError(); + + Renderer->Streams.Delete(Renderer->Streams.Find(this)); + Renderer = NULL; + + delete Decoder; + delete Reader; + } + + + virtual bool Play(bool loop, float vol) + { + SetVolume(vol); + + if(Playing) + return true; + + /* Clear the buffer queue, then fill and queue each buffer */ + alSourcei(Source, AL_BUFFER, 0); + for(int i = 0;i < BufferCount;i++) + { + if(!Callback(this, &Data[0], Data.Size(), UserData)) + { + if(i == 0) + return false; + break; + } + + alBufferData(Buffers[i], Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &Buffers[i]); + } + if(getALError() != AL_NO_ERROR) + return false; + + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + + return Playing; + } + + virtual void Stop() + { + if(!Playing) + return; + + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + getALError(); + + Playing = false; + } + + virtual void SetVolume(float vol) + { + Volume = vol; + UpdateVolume(); + } + + void UpdateVolume() + { + alSourcef(Source, AL_GAIN, Renderer->MusicVolume*Volume); + getALError(); + } + + virtual bool SetPaused(bool pause) + { + if(pause) + alSourcePause(Source); + else + alSourcePlay(Source); + return (getALError()==AL_NO_ERROR); + } + + virtual bool SetPosition(unsigned int ms_pos) + { + if(!Decoder->seek(ms_pos)) + return false; + + if(!Playing) + return true; + // Stop the source so that all buffers become processed, then call + // IsEnded() to refill and restart the source queue with the new + // position. + alSourceStop(Source); + getALError(); + return !IsEnded(); + } + + virtual unsigned int GetPosition() + { + ALint offset, queued, state; + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if(getALError() != AL_NO_ERROR) + return 0; + + size_t pos = Decoder->getSampleOffset(); + if(state != AL_STOPPED) + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else pos = 0; + } + return (unsigned int)(pos * 1000.0 / SampleRate); + } + + virtual bool IsEnded() + { + if(!Playing) + return true; + + ALint state, processed; + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + + Playing = (getALError()==AL_NO_ERROR); + if(!Playing) + return true; + + // For each processed buffer in the queue... + while(processed > 0) + { + ALuint bufid; + + // Unqueue the oldest buffer, fill it with more data, and queue it + // on the end + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; + + if(Callback(this, &Data[0], Data.Size(), UserData)) + { + alBufferData(bufid, Format, &Data[0], Data.Size(), SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } + + // If the source is not playing or paused, and there are buffers queued, + // then there was an underrun. Restart the source. + Playing = (getALError()==AL_NO_ERROR); + if(Playing && state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued = 0; + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + + Playing = (getALError() == AL_NO_ERROR) && (queued > 0); + if(Playing) + { + alSourcePlay(Source); + Playing = (getALError()==AL_NO_ERROR); + } + } + + return !Playing; + } + + FString GetStats() + { + FString stats; + size_t pos, len; + ALfloat volume; + ALint offset; + ALint processed; + ALint queued; + ALint state; + ALenum err; + + alGetSourcef(Source, AL_GAIN, &volume); + alGetSourcei(Source, AL_SAMPLE_OFFSET, &offset); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + alGetSourcei(Source, AL_SOURCE_STATE, &state); + if((err=alGetError()) != AL_NO_ERROR) + { + stats = "Error getting stats: "; + stats += alGetString(err); + return stats; + } + + stats = (state == AL_INITIAL) ? "Buffering" : (state == AL_STOPPED) ? "Underrun" : + (state == AL_PLAYING || state == AL_PAUSED) ? "Ready" : "Unknown state"; + + pos = Decoder->getSampleOffset(); + len = Decoder->getSampleLength(); + if(state == AL_STOPPED) + offset = BufferCount * (Data.Size()/FrameSize); + else + { + size_t rem = queued*(Data.Size()/FrameSize) - offset; + if(pos > rem) pos -= rem; + else if(len > 0) pos += len - rem; + else pos = 0; + } + pos = (size_t)(pos * 1000.0 / SampleRate); + len = (size_t)(len * 1000.0 / SampleRate); + stats.AppendFormat(",%3u%% buffered", 100 - 100*offset/(BufferCount*(Data.Size()/FrameSize))); + stats.AppendFormat(", %zu.%03zu", pos/1000, pos%1000); + if(len > 0) + stats.AppendFormat(" / %zu.%03zu", len/1000, len%1000); + if(state == AL_PAUSED) + stats += ", paused"; + if(state == AL_PLAYING) + stats += ", playing"; + stats.AppendFormat(", %uHz", SampleRate); + if(!Playing) + stats += " XX"; + return stats; + } + + bool Init(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) + { + if(!SetupSource()) + return false; + + Callback = callback; + UserData = userdata; + SampleRate = samplerate; + + Format = AL_NONE; + if((flags&Bits8)) /* Signed or unsigned? We assume unsigned 8-bit... */ + { + if((flags&Mono)) Format = AL_FORMAT_MONO8; + else Format = AL_FORMAT_STEREO8; + } + else if((flags&Float)) + { + if(alIsExtensionPresent("AL_EXT_FLOAT32")) + { + if((flags&Mono)) Format = AL_FORMAT_MONO_FLOAT32; + else Format = AL_FORMAT_STEREO_FLOAT32; + } + } + else if((flags&Bits32)) + { + } + else + { + if((flags&Mono)) Format = AL_FORMAT_MONO16; + else Format = AL_FORMAT_STEREO16; + } + + if(Format == AL_NONE) + { + Printf("Unsupported format: 0x%x\n", flags); + return false; + } + + FrameSize = 1; + if((flags&Bits8)) + FrameSize *= 1; + else if((flags&(Bits32|Float))) + FrameSize *= 4; + else + FrameSize *= 2; + + if((flags&Mono)) + FrameSize *= 1; + else + FrameSize *= 2; + + buffbytes += FrameSize-1; + buffbytes -= buffbytes%FrameSize; + Data.Resize(buffbytes); + + return true; + } + + bool Init(FileReader *reader, bool loop) + { + if(!SetupSource()) + { + delete reader; + return false; + } + + if(Decoder) delete Decoder; + if(Reader) delete Reader; + Reader = reader; + Decoder = Renderer->CreateDecoder(Reader); + if(!Decoder) return false; + + Callback = DecoderCallback; + UserData = NULL; + Format = AL_NONE; + FrameSize = 1; + + ChannelConfig chans; + SampleType type; + int srate; + + Decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) Format = AL_FORMAT_MONO16; + FrameSize *= 1; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) Format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) Format = AL_FORMAT_STEREO16; + FrameSize *= 2; + } + if(type == SampleType_UInt8) FrameSize *= 1; + if(type == SampleType_Int16) FrameSize *= 2; + + if(Format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + return false; + } + SampleRate = srate; + Looping = loop; + + Data.Resize((size_t)(0.2 * SampleRate) * FrameSize); + + return true; + } +}; + + +extern ReverbContainer *ForcedEnvironment; + +#define AREA_SOUND_RADIUS (128.f) + +#define PITCH_MULT (0.7937005f) /* Approx. 4 semitones lower; what Nash suggested */ + +#define PITCH(pitch) (snd_pitched ? (pitch)/128.f : 1.f) + + +static float GetRolloff(const FRolloffInfo *rolloff, float distance) +{ + if(distance <= rolloff->MinDistance) + return 1.f; + // Logarithmic rolloff has no max distance where it goes silent. + if(rolloff->RolloffType == ROLLOFF_Log) + return rolloff->MinDistance / + (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); + if(distance >= rolloff->MaxDistance) + return 0.f; + + float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if(rolloff->RolloffType == ROLLOFF_Linear) + return volume; + + if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve != NULL) + return S_SoundCurve[int(S_SoundCurveSize * (1.f - volume))] / 127.f; + return (powf(10.f, volume) - 1.f) / 9.f; +} + +ALCdevice *OpenALSoundRenderer::InitDevice() +{ + ALCdevice *device = NULL; + if (IsOpenALPresent()) + { + if(strcmp(snd_aldevice, "Default") != 0) + { + device = alcOpenDevice(*snd_aldevice); + if(!device) + Printf(TEXTCOLOR_BLUE" Failed to open device " TEXTCOLOR_BOLD"%s" TEXTCOLOR_BLUE". Trying default.\n", *snd_aldevice); + } + + if(!device) + { + device = alcOpenDevice(NULL); + if(!device) + { + Printf(TEXTCOLOR_RED" Could not open audio device\n"); + } + } + } + else + { + Printf(TEXTCOLOR_ORANGE"Failed to load openal32.dll\n"); + } + return device; +} + + +template +static void LoadALFunc(const char *name, T *x) +{ *x = reinterpret_cast(alGetProcAddress(name)); } + +#define LOAD_FUNC(x) (LoadALFunc(#x, &x)) +OpenALSoundRenderer::OpenALSoundRenderer() + : Device(NULL), Context(NULL), SFXPaused(0), PrevEnvironment(NULL), EnvSlot(0) +{ + EnvFilters[0] = EnvFilters[1] = 0; + + Printf("I_InitSound: Initializing OpenAL\n"); + + Device = InitDevice(); + if (Device == NULL) return; + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + Printf(" Opened device " TEXTCOLOR_ORANGE"%s\n", current); + + ALCint major=0, minor=0; + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + DPrintf(" ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + DPrintf(" ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + + TArray attribs; + if(*snd_samplerate > 0) + { + attribs.Push(ALC_FREQUENCY); + attribs.Push(*snd_samplerate); + } + // Make sure one source is capable of stereo output with the rest doing + // mono, without running out of voices + attribs.Push(ALC_MONO_SOURCES); + attribs.Push(MAX(*snd_channels, 2) - 1); + attribs.Push(ALC_STEREO_SOURCES); + attribs.Push(1); + // Other attribs..? + attribs.Push(0); + + Context = alcCreateContext(Device, &attribs[0]); + if(!Context || alcMakeContextCurrent(Context) == ALC_FALSE) + { + Printf(TEXTCOLOR_RED" Failed to setup context: %s\n", alcGetString(Device, alcGetError(Device))); + if(Context) + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + attribs.Clear(); + + DPrintf(" Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + DPrintf(" Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + DPrintf(" Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + DPrintf(" Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + + ALC.EXT_EFX = !!alcIsExtensionPresent(Device, "ALC_EXT_EFX"); + ALC.EXT_disconnect = !!alcIsExtensionPresent(Device, "ALC_EXT_disconnect");; + AL.EXT_source_distance_model = !!alIsExtensionPresent("AL_EXT_source_distance_model"); + AL.SOFT_deferred_updates = !!alIsExtensionPresent("AL_SOFT_deferred_updates"); + AL.SOFT_loop_points = !!alIsExtensionPresent("AL_SOFT_loop_points"); + + alDopplerFactor(0.5f); + alSpeedOfSound(343.3f * 96.0f); + alDistanceModel(AL_INVERSE_DISTANCE); + if(AL.EXT_source_distance_model) + alEnable(AL_SOURCE_DISTANCE_MODEL); + + if(AL.SOFT_deferred_updates) + { + LOAD_FUNC(alDeferUpdatesSOFT); + LOAD_FUNC(alProcessUpdatesSOFT); + } + else + { + alDeferUpdatesSOFT = _wrap_DeferUpdatesSOFT; + alProcessUpdatesSOFT = _wrap_ProcessUpdatesSOFT; + } + + ALenum err = getALError(); + if(err != AL_NO_ERROR) + { + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + + ALCint numMono=0, numStereo=0; + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &numMono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &numStereo); + + Sources.Resize(MIN(MAX(*snd_channels, 2), numMono+numStereo)); + for(size_t i = 0;i < Sources.Size();i++) + { + alGenSources(1, &Sources[i]); + if(getALError() != AL_NO_ERROR) + { + Sources.Resize(i); + Sources.ShrinkToFit(); + break; + } + } + if(Sources.Size() == 0) + { + Printf(TEXTCOLOR_RED" Error: could not generate any sound sources!\n"); + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; + return; + } + FreeSfx = Sources; + DPrintf(" Allocated " TEXTCOLOR_BLUE"%u" TEXTCOLOR_NORMAL" sources\n", Sources.Size()); + + WasInWater = false; + if(*snd_efx && ALC.EXT_EFX) + { + // EFX function pointers + LOAD_FUNC(alGenEffects); + LOAD_FUNC(alDeleteEffects); + LOAD_FUNC(alIsEffect); + LOAD_FUNC(alEffecti); + LOAD_FUNC(alEffectiv); + LOAD_FUNC(alEffectf); + LOAD_FUNC(alEffectfv); + LOAD_FUNC(alGetEffecti); + LOAD_FUNC(alGetEffectiv); + LOAD_FUNC(alGetEffectf); + LOAD_FUNC(alGetEffectfv); + + LOAD_FUNC(alGenFilters); + LOAD_FUNC(alDeleteFilters); + LOAD_FUNC(alIsFilter); + LOAD_FUNC(alFilteri); + LOAD_FUNC(alFilteriv); + LOAD_FUNC(alFilterf); + LOAD_FUNC(alFilterfv); + LOAD_FUNC(alGetFilteri); + LOAD_FUNC(alGetFilteriv); + LOAD_FUNC(alGetFilterf); + LOAD_FUNC(alGetFilterfv); + + LOAD_FUNC(alGenAuxiliaryEffectSlots); + LOAD_FUNC(alDeleteAuxiliaryEffectSlots); + LOAD_FUNC(alIsAuxiliaryEffectSlot); + LOAD_FUNC(alAuxiliaryEffectSloti); + LOAD_FUNC(alAuxiliaryEffectSlotiv); + LOAD_FUNC(alAuxiliaryEffectSlotf); + LOAD_FUNC(alAuxiliaryEffectSlotfv); + LOAD_FUNC(alGetAuxiliaryEffectSloti); + LOAD_FUNC(alGetAuxiliaryEffectSlotiv); + LOAD_FUNC(alGetAuxiliaryEffectSlotf); + LOAD_FUNC(alGetAuxiliaryEffectSlotfv); + if(getALError() == AL_NO_ERROR) + { + ALuint envReverb; + alGenEffects(1, &envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" EAX Reverb found\n"); + alEffecti(envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + if(alGetError() == AL_NO_ERROR) + DPrintf(" Standard Reverb found\n"); + + alDeleteEffects(1, &envReverb); + getALError(); + } + + alGenAuxiliaryEffectSlots(1, &EnvSlot); + alGenFilters(2, EnvFilters); + if(getALError() == AL_NO_ERROR) + { + alFilteri(EnvFilters[0], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + alFilteri(EnvFilters[1], AL_FILTER_TYPE, AL_FILTER_LOWPASS); + if(getALError() == AL_NO_ERROR) + DPrintf(" Lowpass found\n"); + else + { + alDeleteFilters(2, EnvFilters); + EnvFilters[0] = EnvFilters[1] = 0; + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvSlot = 0; + getALError(); + } + } + else + { + alDeleteFilters(2, EnvFilters); + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + EnvFilters[0] = EnvFilters[1] = 0; + EnvSlot = 0; + getALError(); + } + } + } + + if(EnvSlot) + Printf(" EFX enabled\n"); +} +#undef LOAD_FUNC + +OpenALSoundRenderer::~OpenALSoundRenderer() +{ + if(!Device) + return; + + while(Streams.Size() > 0) + delete Streams[0]; + + alDeleteSources(Sources.Size(), &Sources[0]); + Sources.Clear(); + FreeSfx.Clear(); + SfxGroup.Clear(); + PausableSfx.Clear(); + ReverbSfx.Clear(); + + if(EnvEffects.CountUsed() > 0) + { + EffectMapIter iter(EnvEffects); + EffectMap::Pair *pair; + while(iter.NextPair(pair)) + alDeleteEffects(1, &(pair->Value)); + } + EnvEffects.Clear(); + + if(EnvSlot) + { + alDeleteAuxiliaryEffectSlots(1, &EnvSlot); + alDeleteFilters(2, EnvFilters); + } + EnvSlot = 0; + EnvFilters[0] = EnvFilters[1] = 0; + + alcMakeContextCurrent(NULL); + alcDestroyContext(Context); + Context = NULL; + alcCloseDevice(Device); + Device = NULL; +} + +void OpenALSoundRenderer::SetSfxVolume(float volume) +{ + SfxVolume = volume; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL) + { + ALuint source = GET_PTRID(schan->SysChannel); + volume = SfxVolume; + + alDeferUpdatesSOFT(); + alSourcef(source, AL_MAX_GAIN, volume); + alSourcef(source, AL_GAIN, volume * schan->Volume); + } + schan = schan->NextChan; + } + + alProcessUpdatesSOFT(); + + getALError(); +} + +void OpenALSoundRenderer::SetMusicVolume(float volume) +{ + MusicVolume = volume; + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->UpdateVolume(); +} + +unsigned int OpenALSoundRenderer::GetMSLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + if(alIsBuffer(buffer)) + { + ALint bits, channels, freq, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_FREQUENCY, &freq); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (unsigned int)(size / (channels*bits/8) * 1000. / freq); + } + } + return 0; +} + +unsigned int OpenALSoundRenderer::GetSampleLength(SoundHandle sfx) +{ + if(sfx.data) + { + ALuint buffer = GET_PTRID(sfx.data); + ALint bits, channels, size; + alGetBufferi(buffer, AL_BITS, &bits); + alGetBufferi(buffer, AL_CHANNELS, &channels); + alGetBufferi(buffer, AL_SIZE, &size); + if(getALError() == AL_NO_ERROR) + return (ALsizei)(size / (channels * bits / 8)); + } + return 0; +} + +float OpenALSoundRenderer::GetOutputRate() +{ + ALCint rate = 44100; // Default, just in case + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &rate); + return (float)rate; +} + + +SoundHandle OpenALSoundRenderer::LoadSoundRaw(BYTE *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend) +{ + SoundHandle retval = { NULL }; + + if(length == 0) return retval; + + if(bits == -8) + { + // Simple signed->unsigned conversion + for(int i = 0;i < length;i++) + sfxdata[i] ^= 0x80; + bits = -bits; + } + + ALenum format = AL_NONE; + if(bits == 16) + { + if(channels == 1) format = AL_FORMAT_MONO16; + if(channels == 2) format = AL_FORMAT_STEREO16; + } + else if(bits == 8) + { + if(channels == 1) format = AL_FORMAT_MONO8; + if(channels == 2) format = AL_FORMAT_STEREO8; + } + + if(format == AL_NONE || frequency <= 0) + { + Printf("Unhandled format: %d bit, %d channel, %d hz\n", bits, channels, frequency); + return retval; + } + length -= length%(channels*bits/8); + + ALenum err; + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, sfxdata, length, frequency); + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + return retval; + } + + if((loopstart > 0 || loopend > 0) && AL.SOFT_loop_points) + { + if(loopstart < 0) + loopstart = 0; + if(loopend < loopstart) + loopend = length / (channels*bits/8); + + ALint loops[2] = { loopstart, loopend }; + DPrintf("Setting loop points %d -> %d\n", loops[0], loops[1]); + alBufferiv(buffer, AL_LOOP_POINTS_SOFT, loops); + getALError(); + } + else if(loopstart > 0 || loopend > 0) + { + static bool warned = false; + if(!warned) + Printf("Loop points not supported!\n"); + warned = true; + } + + retval.data = MAKE_PTRID(buffer); + return retval; +} + +SoundHandle OpenALSoundRenderer::LoadSound(BYTE *sfxdata, int length) +{ + SoundHandle retval = { NULL }; + MemoryReader reader((const char*)sfxdata, length); + ALenum format = AL_NONE; + ChannelConfig chans; + SampleType type; + int srate; + + SoundDecoder *decoder = CreateDecoder(&reader); + if(!decoder) return retval; + + decoder->getInfo(&srate, &chans, &type); + if(chans == ChannelConfig_Mono) + { + if(type == SampleType_UInt8) format = AL_FORMAT_MONO8; + if(type == SampleType_Int16) format = AL_FORMAT_MONO16; + } + if(chans == ChannelConfig_Stereo) + { + if(type == SampleType_UInt8) format = AL_FORMAT_STEREO8; + if(type == SampleType_Int16) format = AL_FORMAT_STEREO16; + } + + if(format == AL_NONE) + { + Printf("Unsupported audio format: %s, %s\n", GetChannelConfigName(chans), + GetSampleTypeName(type)); + delete decoder; + return retval; + } + + TArray data = decoder->readAll(); + + ALuint buffer = 0; + alGenBuffers(1, &buffer); + alBufferData(buffer, format, &data[0], data.Size(), srate); + + ALenum err; + if((err=getALError()) != AL_NO_ERROR) + { + Printf("Failed to buffer data: %s\n", alGetString(err)); + alDeleteBuffers(1, &buffer); + getALError(); + delete decoder; + return retval; + } + + retval.data = MAKE_PTRID(buffer); + delete decoder; + return retval; +} + +void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) +{ + if(!sfx.data) + return; + + ALuint buffer = GET_PTRID(sfx.data); + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel) + { + ALint bufID = 0; + alGetSourcei(GET_PTRID(schan->SysChannel), AL_BUFFER, &bufID); + if((ALuint)bufID == buffer) + { + FSoundChan *next = schan->NextChan; + StopChannel(schan); + schan = next; + continue; + } + } + schan = schan->NextChan; + } + + alDeleteBuffers(1, &buffer); + getALError(); +} + + +SoundStream *OpenALSoundRenderer::CreateStream(SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(callback, buffbytes, flags, samplerate, userdata)) + { + delete stream; + return NULL; + } + return stream; +} + +SoundStream *OpenALSoundRenderer::OpenStream(FileReader *reader, int flags) +{ + OpenALSoundStream *stream = new OpenALSoundStream(this); + if (!stream->Init(reader, !!(flags&SoundStream::Loop))) + { + delete stream; + return NULL; + } + return stream; +} + +FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan) +{ + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) StopChannel(lowest); + + if(FreeSfx.Size() == 0) + return NULL; + } + + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + alSource3f(source, AL_POSITION, 0.f, 0.f, 0.f); + alSource3f(source, AL_VELOCITY, 0.f, 0.f, 0.f); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); + + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 1000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 0.f); + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SEC_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff.RolloffType = ROLLOFF_Log; + chan->Rolloff.RolloffFactor = 0.f; + chan->Rolloff.MinDistance = 1.f; + chan->DistanceScale = 1.f; + chan->DistanceSqr = 0.f; + chan->ManualRolloff = false; + + return chan; +} + +FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, + FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, + int channum, int chanflags, FISoundChannel *reuse_chan) +{ + float dist_sqr = (float)(pos - listener->position).LengthSquared(); + + if(FreeSfx.Size() == 0) + { + FSoundChan *lowest = FindLowestChannel(); + if(lowest) + { + if(lowest->Priority < priority || (lowest->Priority == priority && + lowest->DistanceSqr > dist_sqr)) + StopChannel(lowest); + } + if(FreeSfx.Size() == 0) + return NULL; + } + + bool manualRolloff = true; + ALuint buffer = GET_PTRID(sfx.data); + ALuint source = FreeSfx.Last(); + if(rolloff->RolloffType == ROLLOFF_Log) + { + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, (1000.f+rolloff->MinDistance)/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, rolloff->RolloffFactor); + manualRolloff = false; + } + else if(rolloff->RolloffType == ROLLOFF_Linear && AL.EXT_source_distance_model) + { + alSourcei(source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, rolloff->MinDistance/distscale); + alSourcef(source, AL_MAX_DISTANCE, rolloff->MaxDistance/distscale); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + manualRolloff = false; + } + if(manualRolloff) + { + // How manual rolloff works: + // + // If a sound is using Custom or Doom style rolloff, or Linear style + // when AL_EXT_source_distance_model is not supported, we have to play + // around a bit to get appropriate distance attenation. What we do is + // calculate the attenuation that should be applied, then given an + // Inverse Distance rolloff model with OpenAL, reverse the calculation + // to get the distance needed for that much attenuation. The Inverse + // Distance calculation is: + // + // Gain = MinDist / (MinDist + RolloffFactor*(Distance - MinDist)) + // + // Thus, the reverse is: + // + // Distance = (MinDist/Gain - MinDist)/RolloffFactor + MinDist + // + // This can be simplified by using a MinDist and RolloffFactor of 1, + // which makes it: + // + // Distance = 1.0f/Gain; + // + // The source position is then set that many units away from the + // listener position, and OpenAL takes care of the rest. + if(AL.EXT_source_distance_model) + alSourcei(source, AL_DISTANCE_MODEL, AL_INVERSE_DISTANCE); + alSourcef(source, AL_REFERENCE_DISTANCE, 1.f); + alSourcef(source, AL_MAX_DISTANCE, 100000.f); + alSourcef(source, AL_ROLLOFF_FACTOR, 1.f); + + FVector3 dir = pos - listener->position; + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(rolloff, sqrt(dist_sqr) * distscale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else if((chanflags&SNDF_AREA) && dist_sqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 dir = pos - listener->position; + + float mindist = rolloff->MinDistance/distscale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(dist_sqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + + dir += listener->position; + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + } + else + alSource3f(source, AL_POSITION, pos[0], pos[1], -pos[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + alSource3f(source, AL_DIRECTION, 0.f, 0.f, 0.f); + + alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE); + alSourcei(source, AL_LOOPING, (chanflags&SNDF_LOOP) ? AL_TRUE : AL_FALSE); + + alSourcef(source, AL_MAX_GAIN, SfxVolume); + alSourcef(source, AL_GAIN, SfxVolume*vol); + + if(EnvSlot) + { + if(!(chanflags&SNDF_NOREVERB)) + { + alSourcei(source, AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + else + { + alSourcei(source, AL_DIRECT_FILTER, AL_FILTER_NULL); + alSource3i(source, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); + } + alSourcef(source, AL_ROOM_ROLLOFF_FACTOR, 0.f); + } + if(WasInWater && !(chanflags&SNDF_NOREVERB)) + alSourcef(source, AL_PITCH, PITCH(pitch)*PITCH_MULT); + else + alSourcef(source, AL_PITCH, PITCH(pitch)); + + if(!reuse_chan) + alSourcef(source, AL_SEC_OFFSET, 0.f); + else + { + if((chanflags&SNDF_ABSTIME)) + alSourcef(source, AL_SEC_OFFSET, reuse_chan->StartTime.Lo/1000.f); + else + { + // FIXME: set offset based on the current time and the StartTime + alSourcef(source, AL_SAMPLE_OFFSET, 0.f); + } + } + if(getALError() != AL_NO_ERROR) + return NULL; + + alSourcei(source, AL_BUFFER, buffer); + if((chanflags&SNDF_NOPAUSE) || !SFXPaused) + alSourcePlay(source); + if(getALError() != AL_NO_ERROR) + { + alSourcei(source, AL_BUFFER, 0); + getALError(); + return NULL; + } + + if(!(chanflags&SNDF_NOREVERB)) + ReverbSfx.Push(source); + if(!(chanflags&SNDF_NOPAUSE)) + PausableSfx.Push(source); + SfxGroup.Push(source); + FreeSfx.Pop(); + + FISoundChannel *chan = reuse_chan; + if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + else chan->SysChannel = MAKE_PTRID(source); + + chan->Rolloff = *rolloff; + chan->DistanceScale = distscale; + chan->DistanceSqr = dist_sqr; + chan->ManualRolloff = manualRolloff; + + return chan; +} + +void OpenALSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + ALuint source = GET_PTRID(chan->SysChannel); + alSourcef(source, AL_GAIN, SfxVolume * volume); +} + +void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + ALuint source = GET_PTRID(chan->SysChannel); + // Release first, so it can be properly marked as evicted if it's being + // forcefully killed + S_ChannelEnded(chan); + + alSourceRewind(source); + alSourcei(source, AL_BUFFER, 0); + getALError(); + + uint32 i; + if((i=PausableSfx.Find(source)) < PausableSfx.Size()) + PausableSfx.Delete(i); + if((i=ReverbSfx.Find(source)) < ReverbSfx.Size()) + ReverbSfx.Delete(i); + + SfxGroup.Delete(SfxGroup.Find(source)); + FreeSfx.Push(source); +} + +unsigned int OpenALSoundRenderer::GetPosition(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0; + + ALint pos; + alGetSourcei(GET_PTRID(chan->SysChannel), AL_SAMPLE_OFFSET, &pos); + if(getALError() == AL_NO_ERROR) + return pos; + return 0; +} + + +void OpenALSoundRenderer::SetSfxPaused(bool paused, int slot) +{ + int oldslots = SFXPaused; + + if(paused) + { + SFXPaused |= 1 << slot; + if(oldslots == 0 && PausableSfx.Size() > 0) + { + alSourcePausev(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + SFXPaused &= ~(1 << slot); + if(SFXPaused == 0 && oldslots != 0 && PausableSfx.Size() > 0) + { + alSourcePlayv(PausableSfx.Size(), &PausableSfx[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::SetInactive(SoundRenderer::EInactiveState state) +{ + switch(state) + { + case SoundRenderer::INACTIVE_Active: + alListenerf(AL_GAIN, 1.0f); + break; + + /* FIXME: This doesn't stop anything. */ + case SoundRenderer::INACTIVE_Complete: + case SoundRenderer::INACTIVE_Mute: + alListenerf(AL_GAIN, 0.0f); + break; + } +} + +void OpenALSoundRenderer::Sync(bool sync) +{ + if(sync) + { + if(SfxGroup.Size() > 0) + { + alSourcePausev(SfxGroup.Size(), &SfxGroup[0]); + getALError(); + PurgeStoppedSources(); + } + } + else + { + // Might already be something to handle this; basically, get a vector + // of all values in SfxGroup that are not also in PausableSfx (when + // SFXPaused is non-0). + TArray toplay = SfxGroup; + if(SFXPaused) + { + uint32 i = 0; + while(i < toplay.Size()) + { + uint32 p = PausableSfx.Find(toplay[i]); + if(p < PausableSfx.Size()) + toplay.Delete(i); + else + i++; + } + } + if(toplay.Size() > 0) + { + alSourcePlayv(toplay.Size(), &toplay[0]); + getALError(); + } + } +} + +void OpenALSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) +{ + if(chan == NULL || chan->SysChannel == NULL) + return; + + alDeferUpdatesSOFT(); + + FVector3 dir = pos - listener->position; + chan->DistanceSqr = (float)dir.LengthSquared(); + + if(chan->ManualRolloff) + { + if(dir.DoesNotApproximatelyEqual(FVector3(0.f, 0.f, 0.f))) + { + float gain = GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + dir.Resize((gain > 0.00001f) ? 1.f/gain : 100000.f); + } + if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + FVector3 amb(0.f, !(dir.Y>=0.f) ? -1.f : 1.f, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + } + else if(areasound && chan->DistanceSqr < AREA_SOUND_RADIUS*AREA_SOUND_RADIUS) + { + float mindist = chan->Rolloff.MinDistance / chan->DistanceScale; + FVector3 amb(0.f, !(dir.Y>=0.f) ? -mindist : mindist, 0.f); + float a = sqrt(chan->DistanceSqr) / AREA_SOUND_RADIUS; + dir = amb + (dir-amb)*a; + } + dir += listener->position; + + ALuint source = GET_PTRID(chan->SysChannel); + alSource3f(source, AL_POSITION, dir[0], dir[1], -dir[2]); + alSource3f(source, AL_VELOCITY, vel[0], vel[1], -vel[2]); + getALError(); +} + +void OpenALSoundRenderer::UpdateListener(SoundListener *listener) +{ + if(!listener->valid) + return; + + alDeferUpdatesSOFT(); + + float angle = listener->angle; + ALfloat orient[6]; + // forward + orient[0] = cos(angle); + orient[1] = 0.f; + orient[2] = -sin(angle); + // up + orient[3] = 0.f; + orient[4] = 1.f; + orient[5] = 0.f; + + alListenerfv(AL_ORIENTATION, orient); + alListener3f(AL_POSITION, listener->position.X, + listener->position.Y, + -listener->position.Z); + alListener3f(AL_VELOCITY, listener->velocity.X, + listener->velocity.Y, + -listener->velocity.Z); + getALError(); + + const ReverbContainer *env = ForcedEnvironment; + if(!env) + { + env = listener->Environment; + if(!env) + env = DefaultEnvironments[0]; + } + if(env != PrevEnvironment || env->Modified) + { + PrevEnvironment = env; + DPrintf("Reverb Environment %s\n", env->Name); + + if(EnvSlot != 0) + LoadReverb(env); + + const_cast(env)->Modified = false; + } + + // NOTE: Moving into and out of water will undo pitch variations on sounds. + if(listener->underwater || env->SoftwareWater) + { + if(!WasInWater) + { + WasInWater = true; + + if(EnvSlot != 0 && *snd_waterreverb) + { + // Find the "Underwater" reverb environment + env = S_FindEnvironment(0x1600); + LoadReverb(env ? env : DefaultEnvironments[0]); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 0.125f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + + // Apply the updated filters on the sources + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, PITCH_MULT); + getALError(); + } + } + else if(WasInWater) + { + WasInWater = false; + + if(EnvSlot != 0) + { + LoadReverb(env); + + alFilterf(EnvFilters[0], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[0], AL_LOWPASS_GAINHF, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); + alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); + for(uint32 i = 0;i < ReverbSfx.Size();++i) + { + alSourcei(ReverbSfx[i], AL_DIRECT_FILTER, EnvFilters[0]); + alSource3i(ReverbSfx[i], AL_AUXILIARY_SEND_FILTER, EnvSlot, 0, EnvFilters[1]); + } + } + + for(uint32 i = 0;i < ReverbSfx.Size();++i) + alSourcef(ReverbSfx[i], AL_PITCH, 1.f); + getALError(); + } +} + +void OpenALSoundRenderer::UpdateSounds() +{ + alProcessUpdatesSOFT(); + + if(ALC.EXT_disconnect) + { + ALCint connected = ALC_TRUE; + alcGetIntegerv(Device, ALC_CONNECTED, 1, &connected); + if(connected == ALC_FALSE) + { + Printf("Sound device disconnected; restarting...\n"); + static char snd_reset[] = "snd_reset"; + AddCommandString(snd_reset); + return; + } + } + + PurgeStoppedSources(); +} + +void OpenALSoundRenderer::UpdateMusic() +{ + // For some reason this isn't being called? + for(uint32 i = 0;i < Streams.Size();++i) + Streams[i]->IsEnded(); +} + +bool OpenALSoundRenderer::IsValid() +{ + return Device != NULL; +} + +void OpenALSoundRenderer::MarkStartTime(FISoundChannel *chan) +{ + // FIXME: Get current time (preferably from the audio clock, but the system + // time will have to do) + chan->StartTime.AsOne = 0; +} + +float OpenALSoundRenderer::GetAudibility(FISoundChannel *chan) +{ + if(chan == NULL || chan->SysChannel == NULL) + return 0.f; + + ALuint source = GET_PTRID(chan->SysChannel); + ALfloat volume = 0.f; + + alGetSourcef(source, AL_GAIN, &volume); + getALError(); + + volume *= GetRolloff(&chan->Rolloff, sqrt(chan->DistanceSqr) * chan->DistanceScale); + return volume; +} + + +void OpenALSoundRenderer::PrintStatus() +{ + Printf("Output device: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_DEVICE_SPECIFIER)); + getALCError(Device); + + ALCint frequency, major, minor, mono, stereo; + alcGetIntegerv(Device, ALC_FREQUENCY, 1, &frequency); + alcGetIntegerv(Device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MONO_SOURCES, 1, &mono); + alcGetIntegerv(Device, ALC_STEREO_SOURCES, 1, &stereo); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("Device sample rate: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL"hz\n", frequency); + Printf("ALC Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("ALC Extensions: " TEXTCOLOR_ORANGE"%s\n", alcGetString(Device, ALC_EXTENSIONS)); + Printf("Available sources: " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" (" TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" mono, " TEXTCOLOR_BLUE"%d" TEXTCOLOR_NORMAL" stereo)\n", mono+stereo, mono, stereo); + } + if(!alcIsExtensionPresent(Device, "ALC_EXT_EFX")) + Printf("EFX not found\n"); + else + { + ALCint sends; + alcGetIntegerv(Device, ALC_EFX_MAJOR_VERSION, 1, &major); + alcGetIntegerv(Device, ALC_EFX_MINOR_VERSION, 1, &minor); + alcGetIntegerv(Device, ALC_MAX_AUXILIARY_SENDS, 1, &sends); + if(getALCError(Device) == AL_NO_ERROR) + { + Printf("EFX Version: " TEXTCOLOR_BLUE"%d.%d\n", major, minor); + Printf("Auxiliary sends: " TEXTCOLOR_BLUE"%d\n", sends); + } + } + Printf("Vendor: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VENDOR)); + Printf("Renderer: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_RENDERER)); + Printf("Version: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_VERSION)); + Printf("Extensions: " TEXTCOLOR_ORANGE"%s\n", alGetString(AL_EXTENSIONS)); + getALError(); +} + +FString OpenALSoundRenderer::GatherStats() +{ + ALCint updates = 1; + alcGetIntegerv(Device, ALC_REFRESH, 1, &updates); + getALCError(Device); + + uint32 total = Sources.Size(); + uint32 used = SfxGroup.Size()+Streams.Size(); + uint32 unused = FreeSfx.Size(); + + FString out; + out.Format("%u sources (" TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" active, " TEXTCOLOR_YELLOW"%u" TEXTCOLOR_NORMAL" free), Update interval: " TEXTCOLOR_YELLOW"%d" TEXTCOLOR_NORMAL"ms", + total, used, unused, 1000/updates); + return out; +} + +void OpenALSoundRenderer::PrintDriversList() +{ + const ALCchar *drivers = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if(drivers == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + return; + } + + const ALCchar *current = NULL; + if(alcIsExtensionPresent(Device, "ALC_ENUMERATE_ALL_EXT")) + current = alcGetString(Device, ALC_ALL_DEVICES_SPECIFIER); + if(alcGetError(Device) != ALC_NO_ERROR || !current) + current = alcGetString(Device, ALC_DEVICE_SPECIFIER); + if(current == NULL) + { + Printf(TEXTCOLOR_YELLOW"Failed to retrieve device name: %s\n", alcGetString(Device, alcGetError(Device))); + return; + } + + Printf("%c%s%2d. %s\n", ' ', ((strcmp(snd_aldevice, "Default") == 0) ? TEXTCOLOR_BOLD : ""), 0, + "Default"); + for(int i = 1;*drivers;i++) + { + Printf("%c%s%2d. %s\n", ((strcmp(current, drivers)==0) ? '*' : ' '), + ((strcmp(*snd_aldevice, drivers)==0) ? TEXTCOLOR_BOLD : ""), i, + drivers); + drivers += strlen(drivers)+1; + } +} + +void OpenALSoundRenderer::PurgeStoppedSources() +{ + // Release channels that are stopped + for(uint32 i = 0;i < SfxGroup.Size();++i) + { + ALuint src = SfxGroup[i]; + ALint state = AL_INITIAL; + alGetSourcei(src, AL_SOURCE_STATE, &state); + if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) + continue; + + FSoundChan *schan = Channels; + while(schan) + { + if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) + { + StopChannel(schan); + break; + } + schan = schan->NextChan; + } + } + getALError(); +} + +void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) +{ + ALuint *envReverb = EnvEffects.CheckKey(env->ID); + bool doLoad = (env->Modified || !envReverb); + + if(!envReverb) + { + bool ok = false; + + envReverb = &EnvEffects.Insert(env->ID, 0); + alGenEffects(1, envReverb); + if(getALError() == AL_NO_ERROR) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB); + ok = (alGetError() == AL_NO_ERROR); + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_REVERB); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alEffecti(*envReverb, AL_EFFECT_TYPE, AL_EFFECT_NULL); + ok = (alGetError() == AL_NO_ERROR); + } + if(!ok) + { + alDeleteEffects(1, envReverb); + getALError(); + } + } + if(!ok) + { + *envReverb = 0; + doLoad = false; + } + } + + if(doLoad) + { + const REVERB_PROPERTIES &props = env->Properties; + ALint type = AL_EFFECT_NULL; + + alGetEffecti(*envReverb, AL_EFFECT_TYPE, &type); +#define mB2Gain(x) ((float)pow(10., (x)/2000.)) + if(type == AL_EFFECT_EAXREVERB) + { + ALfloat reflectpan[3] = { props.ReflectionsPan0, + props.ReflectionsPan1, + props.ReflectionsPan2 }; + ALfloat latepan[3] = { props.ReverbPan0, props.ReverbPan1, + props.ReverbPan2 }; +#undef SETPARAM +#define SETPARAM(e,t,v) alEffectf((e), AL_EAXREVERB_##t, clamp((v), AL_EAXREVERB_MIN_##t, AL_EAXREVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, GAINLF, mB2Gain(props.RoomLF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, DECAY_LFRATIO, props.DecayLFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + alEffectfv(*envReverb, AL_EAXREVERB_REFLECTIONS_PAN, reflectpan); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + alEffectfv(*envReverb, AL_EAXREVERB_LATE_REVERB_PAN, latepan); + SETPARAM(*envReverb, ECHO_TIME, props.EchoTime); + SETPARAM(*envReverb, ECHO_DEPTH, props.EchoDepth); + SETPARAM(*envReverb, MODULATION_TIME, props.ModulationTime); + SETPARAM(*envReverb, MODULATION_DEPTH, props.ModulationDepth); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, HFREFERENCE, props.HFReference); + SETPARAM(*envReverb, LFREFERENCE, props.LFReference); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_EAXREVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } + else if(type == AL_EFFECT_REVERB) + { +#define SETPARAM(e,t,v) alEffectf((e), AL_REVERB_##t, clamp((v), AL_REVERB_MIN_##t, AL_REVERB_MAX_##t)) + SETPARAM(*envReverb, DIFFUSION, props.EnvDiffusion); + SETPARAM(*envReverb, DENSITY, powf(props.EnvSize, 3.0f) * 0.0625f); + SETPARAM(*envReverb, GAIN, mB2Gain(props.Room)); + SETPARAM(*envReverb, GAINHF, mB2Gain(props.RoomHF)); + SETPARAM(*envReverb, DECAY_TIME, props.DecayTime); + SETPARAM(*envReverb, DECAY_HFRATIO, props.DecayHFRatio); + SETPARAM(*envReverb, REFLECTIONS_GAIN, mB2Gain(props.Reflections)); + SETPARAM(*envReverb, REFLECTIONS_DELAY, props.ReflectionsDelay); + SETPARAM(*envReverb, LATE_REVERB_GAIN, mB2Gain(props.Reverb)); + SETPARAM(*envReverb, LATE_REVERB_DELAY, props.ReverbDelay); + SETPARAM(*envReverb, AIR_ABSORPTION_GAINHF, mB2Gain(props.AirAbsorptionHF)); + SETPARAM(*envReverb, ROOM_ROLLOFF_FACTOR, props.RoomRolloffFactor); + alEffecti(*envReverb, AL_REVERB_DECAY_HFLIMIT, + (props.Flags&REVERB_FLAGS_DECAYHFLIMIT)?AL_TRUE:AL_FALSE); +#undef SETPARAM + } +#undef mB2Gain + } + + alAuxiliaryEffectSloti(EnvSlot, AL_EFFECTSLOT_EFFECT, *envReverb); + getALError(); +} + +FSoundChan *OpenALSoundRenderer::FindLowestChannel() +{ + FSoundChan *schan = Channels; + FSoundChan *lowest = NULL; + while(schan) + { + if(schan->SysChannel != NULL) + { + if(!lowest || schan->Priority < lowest->Priority || + (schan->Priority == lowest->Priority && + schan->DistanceSqr > lowest->DistanceSqr)) + lowest = schan; + } + schan = schan->NextChan; + } + return lowest; +} + +#endif // NO_OPENAL diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 0290a293fa..b9f4382a63 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -395,6 +395,11 @@ void FTextureManager::ParseAnim (FScanner &sc, int usetype) } else if (sc.Compare ("range")) { + if (picnum.Exists() && Texture(picnum)->Name.IsEmpty()) + { + // long texture name: We cannot do ranged anims on these because they have no defined order + sc.ScriptError ("You cannot use \"range\" for long texture names."); + } if (defined == 2) { sc.ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); @@ -456,12 +461,20 @@ FAnimDef *FTextureManager::ParseRangeAnim (FScanner &sc, FTextureID picnum, int type = FAnimDef::ANIM_Forward; framenum = ParseFramenum (sc, picnum, usetype, missing); + ParseTime (sc, min, max); - if (framenum == picnum || !picnum.Exists()) + if (framenum == picnum || !picnum.Exists() || !framenum.Exists()) { return NULL; // Animation is only one frame or does not exist } + + if (Texture(framenum)->Name.IsEmpty()) + { + // long texture name: We cannot do ranged anims on these because they have no defined order + sc.ScriptError ("You cannot use \"range\" for long texture names."); + } + if (framenum < picnum) { type = FAnimDef::ANIM_Backward; @@ -570,7 +583,7 @@ void FTextureManager::ParseTime (FScanner &sc, DWORD &min, DWORD &max) void FTextureManager::ParseWarp(FScanner &sc) { - const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny | TEXMAN_ShortNameOnly; + const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny; bool isflat = false; bool type2 = sc.Compare ("warp2"); // [GRB] sc.MustGetString (); @@ -591,8 +604,16 @@ void FTextureManager::ParseWarp(FScanner &sc) FTextureID picnum = CheckForTexture (sc.String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); if (picnum.isValid()) { + FTexture *warper = Texture(picnum); + if (warper->Name.IsEmpty()) + { + // long texture name: We cannot do warps on these due to the way the texture manager implements warping as a texture replacement. + sc.ScriptError ("You cannot use \"warp\" for long texture names."); + } + + // don't warp a texture more than once if (!warper->bWarped) { diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 2944e4200b..4fc82ac0d0 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1246,7 +1246,8 @@ void FTextureManager::PrecacheLevel (void) for (unsigned i = 0; i < level.info->PrecacheTextures.Size(); i++) { - hitlist[level.info->PrecacheTextures[i].GetIndex()] |= FTextureManager::HIT_Wall; + FTextureID tex = TexMan.CheckForTexture(level.info->PrecacheTextures[i], FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable|FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_ReturnFirst); + if (tex.Exists()) hitlist[tex.GetIndex()] |= FTextureManager::HIT_Wall; } for (int i = cnt - 1; i >= 0; i--) diff --git a/src/tflags.h b/src/tflags.h index 6c549a860f..48866c8283 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -94,6 +94,10 @@ public: static Self FromInt (TT value) { return Self (static_cast (value)); } private: + template Self operator| (X value) const { return Self::FromInt (Value | value); } + template Self operator& (X value) const { return Self::FromInt (Value & value); } + template Self operator^ (X value) const { return Self::FromInt (Value ^ value); } + TT Value; }; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 15d8291fc8..5fcc69f4b2 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -73,6 +73,7 @@ #include "p_setup.h" #include "gstrings.h" +AActor *SingleActorFromTID (int tid, AActor *defactor); static FRandom pr_camissile ("CustomActorfire"); static FRandom pr_camelee ("CustomMelee"); @@ -659,18 +660,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInsideMeleeRange) //========================================================================== void DoJumpIfCloser(AActor *target, DECLARE_PARAMINFO) { - ACTION_PARAM_START(2); + ACTION_PARAM_START(3); ACTION_PARAM_FIXED(dist, 0); ACTION_PARAM_STATE(jump, 1); + ACTION_PARAM_BOOL(noz, 2); ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! // No target - no jump - if (target != NULL && P_AproxDistance(self->x-target->x, self->y-target->y) < dist && - ( (self->z > target->z && self->z - (target->z + target->height) < dist) || - (self->z <=target->z && target->z - (self->z + self->height) < dist) - ) - ) + if (!target) + return; + if (self->AproxDistance(target) < dist && + (noz || + ((self->z > target->z && self->z - (target->z + target->height) < dist) || + (self->z <= target->z && target->z - (self->z + self->height) < dist)))) { ACTION_JUMP(jump); } @@ -1011,7 +1014,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) targ=owner; missile->target=owner; // automatic handling of seeker missiles - if (self->flags & missile->flags2 & MF2_SEEKERMISSILE) + if (self->flags2 & missile->flags2 & MF2_SEEKERMISSILE) { missile->tracer=self->tracer; } @@ -1319,6 +1322,7 @@ enum FP_Flags { FPF_AIMATANGLE = 1, FPF_TRANSFERTRANSLATION = 2, + FPF_NOAUTOAIM = 4, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireCustomMissile) { @@ -1357,7 +1361,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FireCustomMissile) // Temporarily adjusts the pitch fixed_t SavedPlayerPitch = self->pitch; self->pitch -= pitch; - AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget); + AActor * misl=P_SpawnPlayerMissile (self, x, y, z, ti, shootangle, &linetarget, NULL, false, (Flags & FPF_NOAUTOAIM) != 0); self->pitch = SavedPlayerPitch; // automatic handling of seeker missiles @@ -1410,6 +1414,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) ACTION_PARAM_FIXED(LifeSteal, 5); ACTION_PARAM_INT(lifestealmax, 6); ACTION_PARAM_CLASS(armorbonustype, 7); + ACTION_PARAM_SOUND(MeleeSound, 8); + ACTION_PARAM_SOUND(MissSound, 9); if (!self->player) return; @@ -1439,7 +1445,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) P_LineAttack (self, angle, Range, pitch, Damage, NAME_Melee, PuffType, puffFlags, &linetarget, &actualdamage); - if (linetarget) + if (!linetarget) + { + if (MissSound) S_Sound(self, CHAN_WEAPON, MissSound, 1, ATTN_NORM); + } + else { if (LifeSteal && !(linetarget->flags5 & MF5_DONTDRAIN)) { @@ -1470,16 +1480,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomPunch) if (weapon != NULL) { - S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); + if (MeleeSound) S_Sound(self, CHAN_WEAPON, MeleeSound, 1, ATTN_NORM); + else S_Sound (self, CHAN_WEAPON, weapon->AttackSound, 1, ATTN_NORM); } if (!(flags & CPF_NOTURN)) { // turn to face target - self->angle = R_PointToAngle2 (self->x, - self->y, - linetarget->x, - linetarget->y); + self->angle = self->AngleTo(linetarget); } if (flags & CPF_PULLIN) self->flags |= MF_JUSTATTACKED; @@ -1502,14 +1510,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RailAttack) ACTION_PARAM_COLOR(Color1, 3); ACTION_PARAM_COLOR(Color2, 4); ACTION_PARAM_INT(Flags, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_DOUBLE(MaxDiff, 6); ACTION_PARAM_CLASS(PuffType, 7); ACTION_PARAM_ANGLE(Spread_XY, 8); ACTION_PARAM_ANGLE(Spread_Z, 9); ACTION_PARAM_FIXED(Range, 10); ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_DOUBLE(Sparsity, 12); + ACTION_PARAM_DOUBLE(DriftSpeed, 13); ACTION_PARAM_CLASS(SpawnClass, 14); ACTION_PARAM_FIXED(Spawnofs_Z, 15); ACTION_PARAM_INT(SpiralOffset, 16); @@ -1566,14 +1574,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) ACTION_PARAM_COLOR(Color2, 3); ACTION_PARAM_INT(Flags, 4); ACTION_PARAM_INT(aim, 5); - ACTION_PARAM_FLOAT(MaxDiff, 6); + ACTION_PARAM_DOUBLE(MaxDiff, 6); ACTION_PARAM_CLASS(PuffType, 7); ACTION_PARAM_ANGLE(Spread_XY, 8); ACTION_PARAM_ANGLE(Spread_Z, 9); ACTION_PARAM_FIXED(Range, 10); ACTION_PARAM_INT(Duration, 11); - ACTION_PARAM_FLOAT(Sparsity, 12); - ACTION_PARAM_FLOAT(DriftSpeed, 13); + ACTION_PARAM_DOUBLE(Sparsity, 12); + ACTION_PARAM_DOUBLE(DriftSpeed, 13); ACTION_PARAM_CLASS(SpawnClass, 14); ACTION_PARAM_FIXED(Spawnofs_Z, 15); ACTION_PARAM_INT(SpiralOffset, 16); @@ -1603,16 +1611,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) if (aim) { - self->angle = R_PointToAngle2 (self->x, - self->y, - self->target->x, - self->target->y); + self->angle = self->AngleTo(self->target); } self->pitch = P_AimLineAttack (self, self->angle, MISSILERANGE, &linetarget, ANGLE_1*60, 0, aim ? self->target : NULL); if (linetarget == NULL && aim) { // We probably won't hit the target, but aim at it anyway so we don't look stupid. - FVector2 xydiff(self->target->x - self->x, self->target->y - self->y); + TVector2 xydiff(self->target->x - self->x, self->target->y - self->y); double zdiff = (self->target->z + (self->target->height>>1)) - (self->z + (self->height>>1) - self->floorclip); self->pitch = int(atan2(zdiff, xydiff.Length()) * ANGLE_180 / -M_PI); @@ -1620,9 +1625,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) // Let the aim trail behind the player if (aim) { - saved_angle = self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); + saved_angle = self->angle = self->AngleTo(self->target, -self->target->velx * 3, -self->target->vely * 3); if (aim == CRF_AIMDIRECT) { @@ -1631,9 +1634,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun) self->x += Spawnofs_XY * finecosine[self->angle]; self->y += Spawnofs_XY * finesine[self->angle]; Spawnofs_XY = 0; - self->angle = R_PointToAngle2 (self->x, self->y, - self->target->x - self->target->velx * 3, - self->target->y - self->target->vely * 3); + self->angle = self->AngleTo(self->target,- self->target->velx * 3, -self->target->vely * 3); } if (self->target->flags & MF_SHADOW) @@ -1856,6 +1857,9 @@ enum SIX_Flags SIXF_ORIGINATOR = 0x00800000, SIXF_TRANSFERSPRITEFRAME = 0x01000000, SIXF_TRANSFERROLL = 0x02000000, + SIXF_ISTARGET = 0x04000000, + SIXF_ISMASTER = 0x08000000, + SIXF_ISTRACER = 0x10000000, }; static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) @@ -2013,6 +2017,18 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) mo->roll = self->roll; } + if (flags & SIXF_ISTARGET) + { + self->target = mo; + } + if (flags & SIXF_ISMASTER) + { + self->master = mo; + } + if (flags & SIXF_ISTRACER) + { + self->tracer = mo; + } return true; } @@ -2423,7 +2439,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) if (flags & FTF_CLAMP) self->alpha = (FRACUNIT * 1); if (flags & FTF_REMOVE) - self->Destroy(); + P_RemoveThing(self); } } @@ -2451,7 +2467,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) if (flags & FTF_CLAMP) self->alpha = 0; if (flags & FTF_REMOVE) - self->Destroy(); + P_RemoveThing(self); } } @@ -2499,7 +2515,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) } if (self->alpha == target && (flags & FTF_REMOVE)) { - self->Destroy(); + P_RemoveThing(self); } } @@ -3001,23 +3017,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Respawn) fixed_t oldz = self->z; self->flags |= MF_SOLID; self->height = self->GetDefault()->height; + self->radius = self->GetDefault()->radius; CALL_ACTION(A_RestoreSpecialPosition, self); if (flags & RSF_TELEFRAG) { // [KS] DIE DIE DIE DIE erm *ahem* =) - oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true); - if (oktorespawn) - { // Need to do this over again, since P_TeleportMove() will redo - // it with the proper point-on-side calculation. - self->UnlinkFromWorld(); - self->LinkToWorld(true); - sector_t *sec = self->Sector; - self->dropoffz = - self->floorz = sec->floorplane.ZatPoint(self->x, self->y); - self->ceilingz = sec->ceilingplane.ZatPoint(self->x, self->y); - P_FindFloorCeiling(self, FFCF_ONLYSPAWNPOS); - } + oktorespawn = P_TeleportMove(self, self->x, self->y, self->z, true, false); } else { @@ -3315,8 +3321,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) if (target) { - FVector2 xyvec(target->x - x1, target->y - y1); - fixed_t distance = P_AproxDistance((fixed_t)xyvec.Length(), target->z - z1); + fixed_t xydist = self->Distance2D(target); + fixed_t distance = P_AproxDistance(xydist, target->z - z1); if (range && !(flags & CLOFF_CHECKPARTIAL)) { @@ -3330,7 +3336,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) { ang = self->angle; } - else ang = R_PointToAngle2 (x1, y1, target->x, target->y); + else ang = self->AngleTo (target); angle += ang; @@ -3345,11 +3351,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) } else if (flags & CLOFF_AIM_VERT_NOOFFSET) { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + offsetheight + target->height / 2); + pitch += R_PointToAngle2 (0,0, xydist, target->z - z1 + offsetheight + target->height / 2); } else { - pitch += R_PointToAngle2 (0,0, (fixed_t)xyvec.Length(), target->z - z1 + target->height / 2); + pitch += R_PointToAngle2 (0,0, xydist, target->z - z1 + target->height / 2); } } else if (flags & CLOFF_ALLOWNULL) @@ -3394,7 +3400,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) lof_data.Flags = flags; lof_data.BadActor = false; - Trace(x1, y1, z1, sec, vx, vy, vz, range, 0xFFFFFFFF, ML_BLOCKEVERYTHING, self, trace, 0, + Trace(x1, y1, z1, sec, vx, vy, vz, range, ActorFlags::FromInt(0xFFFFFFFF), ML_BLOCKEVERYTHING, self, trace, 0, CheckLOFTraceFunc, &lof_data); if (trace.HitType == TRACE_HitActor || @@ -3519,8 +3525,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) // [FDARI] If actors share team, don't jump if ((flags & JLOSF_ALLYNOJUMP) && self->IsFriend(target)) return; - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); + fixed_t distance = self->AproxDistance3D(target); if (dist_max && (distance > dist_max)) return; @@ -3550,11 +3555,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) if (fov && (fov < ANGLE_MAX)) { - an = R_PointToAngle2 (viewport->x, - viewport->y, - target->x, - target->y) - - viewport->angle; + an = viewport->AngleTo(target) - viewport->angle; if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) { @@ -3608,8 +3609,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) if ((flags & JLOSF_DEADNOJUMP) && (target->health <= 0)) return; - fixed_t distance = P_AproxDistance(target->x - self->x, target->y - self->y); - distance = P_AproxDistance(distance, target->z - self->z); + fixed_t distance = self->AproxDistance3D(target); if (dist_max && (distance > dist_max)) return; @@ -3629,11 +3629,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfInTargetLOS) if (fov && (fov < ANGLE_MAX)) { - an = R_PointToAngle2 (target->x, - target->y, - self->x, - self->y) - - target->angle; + an = target->AngleTo(self) - target->angle; if (an > (fov / 2) && an < (ANGLE_MAX - (fov / 2))) { @@ -4039,7 +4035,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) return; } - INTBOOL was_moving = self->velx | self->vely | self->velz; + INTBOOL was_moving = ref->velx | ref->vely | ref->velz; ref->velx = FixedMul(ref->velx, scale); ref->vely = FixedMul(ref->vely, scale); @@ -4061,7 +4057,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ScaleVelocity) DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) { - ACTION_PARAM_START(4); + ACTION_PARAM_START(5); ACTION_PARAM_FIXED(x, 0); ACTION_PARAM_FIXED(y, 1); ACTION_PARAM_FIXED(z, 2); @@ -4102,7 +4098,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeVelocity) if (was_moving) { - CheckStopped(self); + CheckStopped(ref); } } @@ -4563,7 +4559,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) // Target can dodge if it can see enemy - angle_t angle = R_PointToAngle2(self->target->x, self->target->y, self->x, self->y) - self->target->angle; + angle_t angle = self->target->AngleTo(self) - self->target->angle; angle >>= 24; bool dodge = (P_CheckSight(self->target, self) && (angle>226 || angle<30)); @@ -4602,7 +4598,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) dx = self->target->x; dy = self->target->y; dz = self->target->z + (self->target->height>>1); - angle = R_PointToAngle2(dx, dy, self->x, self->y); + angle = self->target->AngleTo(self); dx += FixedMul(self->target->radius, finecosine[angle>>ANGLETOFINESHIFT]); dy += FixedMul(self->target->radius, finesine[angle>>ANGLETOFINESHIFT]); @@ -4637,7 +4633,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) if (spawnblood) { P_SpawnBlood(dx, dy, dz, angle, newdam > 0 ? newdam : damage, self->target); - P_TraceBleed(newdam > 0 ? newdam : damage, self->target, R_PointToAngle2(self->x, self->y, dx, dy), 0); + P_TraceBleed(newdam > 0 ? newdam : damage, self->target, self->AngleTo(dx, dy, self->target), 0); } } } @@ -4653,29 +4649,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_WolfAttack) // //========================================================================== -enum WARPF -{ - WARPF_ABSOLUTEOFFSET = 0x1, - WARPF_ABSOLUTEANGLE = 0x2, - WARPF_USECALLERANGLE = 0x4, - - WARPF_NOCHECKPOSITION = 0x8, - - WARPF_INTERPOLATE = 0x10, - WARPF_WARPINTERPOLATION = 0x20, - WARPF_COPYINTERPOLATION = 0x40, - - WARPF_STOP = 0x80, - WARPF_TOFLOOR = 0x100, - WARPF_TESTONLY = 0x200, - WARPF_ABSOLUTEPOSITION = 0x400, - WARPF_BOB = 0x800, - WARPF_MOVEPTR = 0x1000, -}; - DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) { - ACTION_PARAM_START(7); + ACTION_PARAM_START(10); ACTION_PARAM_INT(destination_selector, 0); ACTION_PARAM_FIXED(xofs, 1); @@ -4684,8 +4660,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) ACTION_PARAM_ANGLE(angle, 4); ACTION_PARAM_INT(flags, 5); ACTION_PARAM_STATE(success_state, 6); - - AActor *reference = COPY_AAPTR(self, destination_selector); + ACTION_PARAM_FIXED(heightoffset, 7); + ACTION_PARAM_FIXED(radiusoffset, 8); + ACTION_PARAM_ANGLE(pitch, 9); + + AActor *reference; + + if((flags & WARPF_USETID)) + { + reference = SingleActorFromTID(destination_selector, self); + } + else + { + reference = COPY_AAPTR(self, destination_selector); + } //If there is no actor to warp to, fail. if (!reference) @@ -4694,130 +4682,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) return; } - AActor *caller = self; - - if (flags & WARPF_MOVEPTR) + if (P_Thing_Warp(self, reference, xofs, yofs, zofs, angle, flags, heightoffset, radiusoffset, pitch)) { - AActor *temp = reference; - reference = caller; - caller = temp; - } - - fixed_t oldx = caller->x; - fixed_t oldy = caller->y; - fixed_t oldz = caller->z; - - if (!(flags & WARPF_ABSOLUTEANGLE)) - { - angle += (flags & WARPF_USECALLERANGLE) ? caller->angle : reference->angle; - } - if (!(flags & WARPF_ABSOLUTEPOSITION)) - { - if (!(flags & WARPF_ABSOLUTEOFFSET)) - { - angle_t fineangle = angle >> ANGLETOFINESHIFT; - fixed_t xofs1 = xofs; - - // (borrowed from A_SpawnItemEx, assumed workable) - // in relative mode negative y values mean 'left' and positive ones mean 'right' - // This is the inverse orientation of the absolute mode! - - xofs = FixedMul(xofs1, finecosine[fineangle]) + FixedMul(yofs, finesine[fineangle]); - yofs = FixedMul(xofs1, finesine[fineangle]) - FixedMul(yofs, finecosine[fineangle]); - } - - if (flags & WARPF_TOFLOOR) - { - // set correct xy - - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z); - - // now the caller's floorz should be appropriate for the assigned xy-position - // assigning position again with - - if (zofs) - { - // extra unlink, link and environment calculation - caller->SetOrigin( - caller->x, - caller->y, - caller->floorz + zofs); - } - else - { - // if there is no offset, there should be no ill effect from moving down to the - // already identified floor - - // A_Teleport does the same thing anyway - caller->z = caller->floorz; - } - } - else - { - caller->SetOrigin( - reference->x + xofs, - reference->y + yofs, - reference->z + zofs); - } - } - else //[MC] The idea behind "absolute" is meant to be "absolute". Override everything, just like A_SpawnItemEx's. - { - if (flags & WARPF_TOFLOOR) - { - caller->SetOrigin(xofs, yofs, caller->floorz + zofs); - } - else - { - caller->SetOrigin(xofs, yofs, zofs); - } - } - - if ((flags & WARPF_NOCHECKPOSITION) || P_TestMobjLocation(caller)) - { - if (flags & WARPF_TESTONLY) - { - caller->SetOrigin(oldx, oldy, oldz); - } - else - { - caller->angle = angle; - - if (flags & WARPF_STOP) - { - caller->velx = 0; - caller->vely = 0; - caller->velz = 0; - } - - if (flags & WARPF_WARPINTERPOLATION) - { - caller->PrevX += caller->x - oldx; - caller->PrevY += caller->y - oldy; - caller->PrevZ += caller->z - oldz; - } - else if (flags & WARPF_COPYINTERPOLATION) - { - caller->PrevX = caller->x + reference->PrevX - reference->x; - caller->PrevY = caller->y + reference->PrevY - reference->y; - caller->PrevZ = caller->z + reference->PrevZ - reference->z; - } - else if (!(flags & WARPF_INTERPOLATE)) - { - caller->PrevX = caller->x; - caller->PrevY = caller->y; - caller->PrevZ = caller->z; - } - - if ((flags & WARPF_BOB) && (reference->flags2 & MF2_FLOATBOB)) - { - caller->z += reference->GetBobOffset(); - } - } - - if (success_state) { ACTION_SET_RESULT(false); // Jumps should never set the result for inventory state chains! @@ -4830,7 +4696,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp) } else { - caller->SetOrigin(oldx, oldy, oldz); ACTION_SET_RESULT(false); } @@ -4948,12 +4813,25 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) } +static bool DoCheckSpecies(AActor *mo, FName filterSpecies, bool exclude) +{ + FName actorSpecies = mo->GetSpecies(); + if (filterSpecies == NAME_None) return true; + return exclude ? (actorSpecies != filterSpecies) : (actorSpecies == filterSpecies); +} + +static bool DoCheckClass(AActor *mo, const PClass *filterClass, bool exclude) +{ + const PClass *actorClass = mo->GetClass(); + if (filterClass == NULL) return true; + return exclude ? (actorClass != filterClass) : (actorClass == filterClass); +} //========================================================================== // -// A_RadiusGive +// A_RadiusGive(item, distance, flags, amount, filter, species) // // Uses code roughly similar to A_Explode (but without all the compatibility -// baggage and damage computation code to give an item to all eligible mobjs +// baggage and damage computation code) to give an item to all eligible mobjs // in range. // //========================================================================== @@ -4972,6 +4850,12 @@ enum RadiusGiveFlags RGF_CUBE = 1 << 9, RGF_NOSIGHT = 1 << 10, RGF_MISSILES = 1 << 11, + RGF_INCLUSIVE = 1 << 12, + RGF_ITEMS = 1 << 13, + RGF_KILLED = 1 << 14, + RGF_EXFILTER = 1 << 15, + RGF_EXSPECIES = 1 << 16, + RGF_EITHER = 1 << 17, }; DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) @@ -4981,127 +4865,143 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) ACTION_PARAM_FIXED(distance, 1); ACTION_PARAM_INT(flags, 2); ACTION_PARAM_INT(amount, 3); + ACTION_PARAM_CLASS(filter, 4); + ACTION_PARAM_NAME(species, 5); + ACTION_PARAM_FIXED(mindist, 6); // We need a valid item, valid targets, and a valid range - if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) + if (item == NULL || (flags & RGF_MASK) == 0 || !flags || distance <= 0 || mindist >= distance) { + ACTION_SET_RESULT(false); return; } + if (amount == 0) { amount = 1; } FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); - double distsquared = double(distance) * double(distance); AActor *thing; + bool given = false; while ((thing = it.Next())) { - // Don't give to inventory items - if (thing->flags & MF_SPECIAL) + //[MC] Check for a filter, species, and the related exfilter/expecies/either flag(s). + bool filterpass = DoCheckClass(thing, filter, !!(flags & RGF_EXFILTER)), + speciespass = DoCheckSpecies(thing, species, !!(flags & RGF_EXSPECIES)); + + if ((flags & RGF_EITHER) ? (!(filterpass || speciespass)) : (!(filterpass && speciespass))) { - continue; - } - // Avoid giving to self unless requested - if (thing == self && !(flags & RGF_GIVESELF)) - { - continue; - } - // Avoiding special pointers if requested - if (((thing == self->target) && (flags & RGF_NOTARGET)) || - ((thing == self->tracer) && (flags & RGF_NOTRACER)) || - ((thing == self->master) && (flags & RGF_NOMASTER))) - { - continue; - } - // Don't give to dead thing unless requested - if (thing->flags & MF_CORPSE) - { - if (!(flags & RGF_CORPSES)) - { + if (thing != self) //Don't let filter and species obstruct RGF_GIVESELF. continue; - } - } - else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) - { - continue; - } - // Players, monsters, and other shootable objects - if (thing->player) - { - if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) - { - continue; - } - if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) - { - continue; - } - } - else if (thing->flags3 & MF3_ISMONSTER) - { - if (!(flags & RGF_MONSTERS)) - { - continue; - } - } - else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) - { - if (!(flags & RGF_OBJECTS)) - { - continue; - } - } - else if (thing->flags & MF_MISSILE) - { - if (!(flags & RGF_MISSILES)) - { - continue; - } - } - else - { - continue; } - if (flags & RGF_CUBE) - { // check if inside a cube - if (fabs((double)thing->x - self->x) > (double)distance || - fabs((double)thing->y - self->y) > (double)distance || - fabs((double)(thing->z + thing->height/2) - (self->z + self->height/2)) > (double)distance) - { + if (thing == self) + { + if (!(flags & RGF_GIVESELF)) continue; - } } - else - { // check if inside a sphere - TVector3 tpos(thing->x, thing->y, thing->z + thing->height/2); - TVector3 spos(self->x, self->y, self->z + self->height/2); - if ((tpos - spos).LengthSquared() > distsquared) - { + + //Check for target, master, and tracer flagging. + bool targetPass = true; + bool masterPass = true; + bool tracerPass = true; + bool ptrPass = false; + if ((thing != self) && (flags & (RGF_NOTARGET | RGF_NOMASTER | RGF_NOTRACER))) + { + if ((thing == self->target) && (flags & RGF_NOTARGET)) + targetPass = false; + if ((thing == self->master) && (flags & RGF_NOMASTER)) + masterPass = false; + if ((thing == self->tracer) && (flags & RGF_NOTRACER)) + tracerPass = false; + + ptrPass = (flags & RGF_INCLUSIVE) ? (targetPass || masterPass || tracerPass) : (targetPass && masterPass && tracerPass); + + //We should not care about what the actor is here. It's safe to abort this actor. + if (!ptrPass) continue; + } + + //Next, actor flag checking. + bool selfPass = !!((flags & RGF_GIVESELF) && thing == self); + bool corpsePass = !!((flags & RGF_CORPSES) && thing->flags & MF_CORPSE); + bool killedPass = !!((flags & RGF_KILLED) && thing->flags6 & MF6_KILLED); + bool monsterPass = !!((flags & RGF_MONSTERS) && thing->flags3 & MF3_ISMONSTER); + bool objectPass = !!((flags & RGF_OBJECTS) && (thing->player == NULL) && (!(thing->flags3 & MF3_ISMONSTER)) + && ((thing->flags & MF_SHOOTABLE) || (thing->flags6 & MF6_VULNERABLE))); + bool playerPass = !!((flags & RGF_PLAYERS) && (thing->player != NULL) && (thing->player->mo == thing)); + bool voodooPass = !!((flags & RGF_VOODOO) && (thing->player != NULL) && (thing->player->mo != thing)); + //Self calls priority over the rest of this. + if (!selfPass) + { + //If it's specifically a monster/object/player/voodoo... Can be either or... + if (monsterPass || objectPass || playerPass || voodooPass) + { + //...and is dead, without desire to give to the dead... + if (((thing->health <= 0) && !(corpsePass || killedPass))) + { + //Skip! + continue; + } } } - if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) - { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. - AInventory *gift = static_cast(Spawn (item, 0, 0, 0, NO_REPLACE)); - if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) - { - gift->Amount *= amount; + bool itemPass = !!((flags & RGF_ITEMS) && thing->IsKindOf(RUNTIME_CLASS(AInventory))); + bool missilePass = !!((flags & RGF_MISSILES) && thing->flags & MF_MISSILE); + + if (selfPass || monsterPass || corpsePass || killedPass || itemPass || objectPass || missilePass || playerPass || voodooPass) + { + + if (flags & RGF_CUBE) + { // check if inside a cube + double dx = fabs((double)(thing->x - self->x)); + double dy = fabs((double)(thing->y - self->y)); + double dz = fabs((double)(thing->z + thing->height / 2) - (self->z + self->height / 2)); + double dist = (double)distance; + double min = (double)mindist; + if ((dx > dist || dy > dist || dz > dist) || (min && (dx < min && dy < min && dz < min))) + { + continue; + } } else - { - gift->Amount = amount; + { // check if inside a sphere + double distsquared = double(distance) * double(distance); + double minsquared = double(mindist) * double(mindist); + TVector3 tpos(thing->x, thing->y, thing->z + thing->height / 2); + TVector3 spos(self->x, self->y, self->z + self->height / 2); + if ((tpos - spos).LengthSquared() > distsquared || (minsquared && ((tpos - spos).LengthSquared() < minsquared))) + { + continue; + } } - gift->flags |= MF_DROPPED; - gift->ClearCounters(); - if (!gift->CallTryPickup (thing)) - { - gift->Destroy (); + + if ((flags & RGF_NOSIGHT) || P_CheckSight(thing, self, SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight. + AInventory *gift = static_cast(Spawn(item, 0, 0, 0, NO_REPLACE)); + if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + { + gift->Amount *= amount; + } + else + { + gift->Amount = amount; + } + gift->flags |= MF_DROPPED; + gift->ClearCounters(); + if (!gift->CallTryPickup(thing)) + { + gift->Destroy(); + } + else + { + given = true; + } } } } + ACTION_SET_RESULT(given); } @@ -5166,7 +5066,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_DropItem) // A_SetSpeed // //========================================================================== - DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) { ACTION_PARAM_START(2); @@ -5184,18 +5083,48 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpeed) ref->Speed = speed; } -static bool DoCheckSpecies(AActor *mo, FName filterSpecies, bool exclude) +//========================================================================== +// +// A_SetFloatSpeed +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetFloatSpeed) { - FName actorSpecies = mo->GetSpecies(); - if (filterSpecies == NAME_None) return true; - return exclude ? (actorSpecies != filterSpecies) : (actorSpecies == filterSpecies); + ACTION_PARAM_START(2); + ACTION_PARAM_FIXED(speed, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->FloatSpeed = speed; } -static bool DoCheckClass(AActor *mo, const PClass *filterClass, bool exclude) +//========================================================================== +// +// A_SetPainThreshold +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetPainThreshold) { - const PClass *actorClass = mo->GetClass(); - if (filterClass == NULL) return true; - return exclude ? (actorClass != filterClass) : (actorClass == filterClass); + ACTION_PARAM_START(2); + ACTION_PARAM_INT(threshold, 0); + ACTION_PARAM_INT(ptr, 1); + + AActor *ref = COPY_AAPTR(self, ptr); + + if (!ref) + { + ACTION_SET_RESULT(false); + return; + } + + ref->PainThreshold = threshold; } //=========================================================================== @@ -5874,6 +5803,25 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfHigherOrLower) ACTION_JUMP(low); } +//=========================================================================== +// A_SetSpecies(str species, ptr) +// +// Sets the species of the calling actor('s pointer). +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetSpecies) +{ + ACTION_PARAM_START(2); + ACTION_PARAM_NAME(species, 0); + ACTION_PARAM_INT(ptr, 1); + AActor *mobj = COPY_AAPTR(self, ptr); + if (!mobj) + { + ACTION_SET_RESULT(false); + return; + } + + mobj->Species = species; +} //=========================================================================== // @@ -5913,3 +5861,160 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax) ACTION_PARAM_INT(max, 0); self->RipLevelMax = max; } + +//========================================================================== +// +// A_CheckProximity(jump, classname, distance, count, flags, ptr) +// +// Checks to see if a certain actor class is close to the +// actor/pointer within distance, in numbers. +//========================================================================== +enum CPXFflags +{ + CPXF_ANCESTOR = 1, + CPXF_LESSOREQUAL = 1 << 1, + CPXF_NOZ = 1 << 2, + CPXF_COUNTDEAD = 1 << 3, + CPXF_DEADONLY = 1 << 4, + CPXF_EXACT = 1 << 5, +}; +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckProximity) +{ + ACTION_PARAM_START(6); + ACTION_PARAM_STATE(jump, 0); + ACTION_PARAM_CLASS(classname, 1); + ACTION_PARAM_FIXED(distance, 2); + ACTION_PARAM_INT(count, 3); + ACTION_PARAM_INT(flags, 4); + ACTION_PARAM_INT(ptr, 5); + + ACTION_SET_RESULT(false); //No inventory chain results please. + AActor *ref = COPY_AAPTR(self, ptr); + + //We need these to check out. + if (!ref || !jump || !classname || distance <= 0) + return; + + int counter = 0; + bool result = false; + + TThinkerIterator it; + AActor * mo; + + //[MC] Process of elimination, I think, will get through this as quickly and + //efficiently as possible. + while ((mo = it.Next())) + { + if (mo == ref) //Don't count self. + continue; + + //Check inheritance for the classname. Taken partly from CheckClass DECORATE function. + if (flags & CPXF_ANCESTOR) + { + if (!(mo->GetClass()->IsAncestorOf(classname))) + continue; + } + //Otherwise, just check for the regular class name. + else if (classname != mo->GetClass()) + continue; + + //Make sure it's in range and respect the desire for Z or not. + if (ref->AproxDistance(mo) < distance && + ((flags & CPXF_NOZ) || + ((ref->z > mo->z && ref->z - (mo->z + mo->height) < distance) || + (ref->z <= mo->z && mo->z - (ref->z + ref->height) < distance)))) + { + if (mo->flags6 & MF6_KILLED) + { + if (!(flags & (CPXF_COUNTDEAD | CPXF_DEADONLY))) + continue; + counter++; + } + else + { + if (flags & CPXF_DEADONLY) + continue; + counter++; + } + + //Abort if the number of matching classes nearby is greater, we have obviously succeeded in our goal. + if (counter > count) + { + result = (flags & (CPXF_LESSOREQUAL | CPXF_EXACT)) ? false : true; + break; + } + } + } + + if (counter == count) + result = true; + else if (counter < count) + result = !!((flags & CPXF_LESSOREQUAL) && !(flags & CPXF_EXACT)); + + + + if (result) + { + ACTION_JUMP(jump); + } +} + +/*=========================================================================== +A_CheckBlock +(state block, int flags, int ptr) + +Checks if something is blocking the actor('s pointer) 'ptr'. + +The SET pointer flags only affect the caller, not the pointer. +===========================================================================*/ +enum CBF +{ + CBF_NOLINES = 1 << 0, //Don't check actors. + CBF_SETTARGET = 1 << 1, //Sets the caller/pointer's target to the actor blocking it. Actors only. + CBF_SETMASTER = 1 << 2, //^ but with master. + CBF_SETTRACER = 1 << 3, //^ but with tracer. + CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckBlock) +{ + ACTION_PARAM_START(3); + ACTION_PARAM_STATE(block, 0); + ACTION_PARAM_INT(flags, 1); + ACTION_PARAM_INT(ptr, 2); + + AActor *mobj = COPY_AAPTR(self, ptr); + + ACTION_SET_RESULT(false); + //Needs at least one state jump to work. + if (!mobj) + { + return; + } + + //Nothing to block it so skip the rest. + if (P_TestMobjLocation(mobj)) return; + + if (mobj->BlockingMobj) + { + AActor *setter = (flags & CBF_SETONPTR) ? mobj : self; + if (setter) + { + if (flags & CBF_SETTARGET) setter->target = mobj->BlockingMobj; + if (flags & CBF_SETMASTER) setter->master = mobj->BlockingMobj; + if (flags & CBF_SETTRACER) setter->tracer = mobj->BlockingMobj; + } + } + + //[MC] If modders don't want jumping, but just getting the pointer, only abort at + //this point. I.e. A_CheckBlock("",CBF_SETTRACER) is like having CBF_NOLINES. + //It gets the mobj blocking, if any, and doesn't jump at all. + if (!block) + return; + + //[MC] Easiest way to tell if an actor is blocking it, use the pointers. + if (mobj->BlockingMobj || (!(flags & CBF_NOLINES) && mobj->BlockingLine != NULL)) + { + ACTION_JUMP(block); + } +} diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index b1387ba524..f9a282c768 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -253,12 +253,10 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF7, HITTARGET, AActor, flags7), DEFINE_FLAG(MF7, HITMASTER, AActor, flags7), DEFINE_FLAG(MF7, HITTRACER, AActor, flags7), - - // [ZK] Decal flags - DEFINE_FLAG(MF7, NODECAL, AActor, flags7), + DEFINE_FLAG(MF7, NODECAL, AActor, flags7), // [ZK] Decal flags DEFINE_FLAG(MF7, FORCEDECAL, AActor, flags7), - DEFINE_FLAG(MF7, LAXTELEFRAGDMG, AActor, flags7), + DEFINE_FLAG(MF7, ICESHATTER, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), @@ -331,9 +329,11 @@ static FFlagDef InventoryFlagDefs[] = DEFINE_FLAG(IF, TOSSED, AInventory, ItemFlags), DEFINE_FLAG(IF, ALWAYSRESPAWN, AInventory, ItemFlags), DEFINE_FLAG(IF, TRANSFER, AInventory, ItemFlags), + DEFINE_FLAG(IF, NOTELEPORTFREEZE, AInventory, ItemFlags), DEFINE_DEPRECATED_FLAG(PICKUPFLASH), - DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),}; + DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP), +}; static FFlagDef WeaponFlagDefs[] = { diff --git a/src/timidity/common.cpp b/src/timidity/common.cpp index 92d9b009a1..57fd26e0c1 100644 --- a/src/timidity/common.cpp +++ b/src/timidity/common.cpp @@ -35,82 +35,6 @@ namespace Timidity { -static TArray PathList; - -FString BuildPath(FString base, const char *name) -{ - FString current; - if (base.IsNotEmpty()) - { - current = base; - if (current[current.Len() - 1] != '/') current += '/'; - } - current += name; - return current; -} - -/* This is meant to find and open files for reading. */ -FileReader *open_filereader(const char *name, int open, int *plumpnum) -{ - FileReader *fp; - FString current_filename; - - if (!name || !(*name)) - { - return 0; - } - - /* First try the given name */ - current_filename = name; - FixPathSeperator(current_filename); - - int lumpnum = Wads.CheckNumForFullName(current_filename); - - if (open != OM_FILE) - { - if (lumpnum >= 0) - { - fp = Wads.ReopenLumpNum(lumpnum); - if (plumpnum) *plumpnum = lumpnum; - return fp; - } - if (open == OM_LUMP) // search the path list when not loading the main config - { - for (unsigned int plp = PathList.Size(); plp-- != 0; ) - { /* Try along the path then */ - current_filename = BuildPath(PathList[plp], name); - lumpnum = Wads.CheckNumForFullName(current_filename); - if (lumpnum >= 0) - { - fp = Wads.ReopenLumpNum(lumpnum); - if (plumpnum) *plumpnum = lumpnum; - return fp; - } - } - return NULL; - } - } - if (plumpnum) *plumpnum = -1; - - - fp = new FileReader; - if (fp->Open(current_filename)) return fp; - - if (name[0] != '/') - { - for (unsigned int plp = PathList.Size(); plp-- != 0; ) - { /* Try along the path then */ - current_filename = BuildPath(PathList[plp], name); - if (fp->Open(current_filename)) return fp; - } - } - delete fp; - - /* Nothing could be opened. */ - current_filename = ""; - return NULL; -} - /* This'll allocate memory or die. */ @@ -132,17 +56,5 @@ void *safe_malloc(size_t count) return 0; // Unreachable. } -/* This adds a directory to the path list */ -void add_to_pathlist(const char *s) -{ - FString copy = s; - FixPathSeperator(copy); - PathList.Push(copy); -} - -void clear_pathlist() -{ - PathList.Clear(); -} } diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index 7454ca7a4e..fa93602678 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -142,7 +142,7 @@ static void reverse_data(sample_t *sp, int ls, int le) TODO: do reverse loops right */ static Instrument *load_instrument(Renderer *song, const char *name, int percussion, - int panning, int amp, int note_to_use, + int panning, int note_to_use, int strip_loop, int strip_envelope, int strip_tail) { @@ -159,16 +159,16 @@ static Instrument *load_instrument(Renderer *song, const char *name, int percuss if (!name) return 0; /* Open patch file */ - if ((fp = open_filereader(name, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(name, NULL)) == NULL) { /* Try with various extensions */ FString tmp = name; tmp += ".pat"; - if ((fp = open_filereader(tmp, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL) { #ifdef __unix__ // Windows isn't case-sensitive. tmp.ToUpper(); - if ((fp = open_filereader(tmp, openmode, NULL)) == NULL) + if ((fp = pathExpander.openFileReader(tmp, NULL)) == NULL) #endif { noluck = true; @@ -372,7 +372,6 @@ fail: { sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD); } - sp->modes |= PATCH_T_NO_LOOP; } if (strip_envelope == 1) @@ -398,37 +397,6 @@ fail: patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0]; } } - sp->modes |= PATCH_T_NO_ENVELOPE; - } - else if (strip_envelope != 0) - { - /* Have to make a guess. */ - if (!(sp->modes & (PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD))) - { - /* No loop? Then what's there to sustain? No envelope needed either... */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - No loop, removing sustain and envelope\n"); - } - else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[GF1_RELEASEC] >= 100) - { - /* Envelope rates all maxed out? Envelope end at a high "offset"? - That's a weird envelope. Take it out. */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - Weirdness, removing envelope\n"); - } - else if (!(sp->modes & PATCH_SUSTAIN)) - { - /* No sustain? Then no envelope. I don't know if this is - justified, but patches without sustain usually don't need the - envelope either... at least the Gravis ones. They're mostly - drums. I think. */ - sp->modes |= PATCH_T_NO_ENVELOPE; - cmsg(CMSG_INFO, VERB_DEBUG, " - No sustain, removing envelope\n"); - } - } - if (!(sp->modes & PATCH_NO_SRELEASE)) - { // TiMidity thinks that this is an envelope enable flag. - sp->modes |= PATCH_T_NO_ENVELOPE; } for (j = 0; j < 6; j++) @@ -470,29 +438,6 @@ fail: sp->modes |= PATCH_LOOPEN; /* just in case */ } - if (amp != -1) - { - sp->volume = (amp) / 100.f; - } - else - { - /* Try to determine a volume scaling factor for the sample. - This is a very crude adjustment, but things sound more - balanced with it. Still, this should be a runtime option. - (This is ignored unless midi_timiditylike is turned on.) */ - int i; - sample_t maxamp = 0, a; - sample_t *tmp; - for (i = sp->data_length, tmp = sp->data; i; --i) - { - a = fabsf(*tmp++); - if (a > maxamp) - maxamp = a; - } - sp->volume = 1 / maxamp; - cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f\n", sp->volume); - } - /* Then fractional samples */ sp->data_length <<= FRACTION_BITS; sp->loop_start <<= FRACTION_BITS; @@ -653,7 +598,6 @@ static int fill_bank(Renderer *song, int dr, int b) ip = load_instrument(song, bank->tone[i].name, (dr) ? 1 : 0, bank->tone[i].pan, - bank->tone[i].amp, (bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1), (bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1), (bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1), @@ -731,7 +675,7 @@ void free_instruments() int Renderer::set_default_instrument(const char *name) { Instrument *ip; - if ((ip = load_instrument(this, name, 0, -1, -1, -1, 0, 0, 0)) == NULL) + if ((ip = load_instrument(this, name, 0, -1, -1, 0, 0, 0)) == NULL) { return -1; } diff --git a/src/timidity/instrum_dls.cpp b/src/timidity/instrum_dls.cpp index fe5e4fdcd3..02b8e59c0e 100644 --- a/src/timidity/instrum_dls.cpp +++ b/src/timidity/instrum_dls.cpp @@ -1126,7 +1126,7 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, sample->key_group = (SBYTE)rgn->header->usKeyGroup; sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow); sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh); - sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote); + sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote + rgn->wsmp->sFineTune * .01f); sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow; sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh; @@ -1137,16 +1137,17 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, convert_sample_data(sample, wave->data); if (rgn->wsmp->cSampleLoops) { - sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN); + sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN/* | PATCH_NO_SRELEASE*/); sample->loop_start = rgn->wsmp_loop->ulStart / 2; sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2); } - sample->volume = 1.0f; + sample->scale_factor = 1024; + sample->scale_note = rgn->wsmp->usUnityNote; if (sample->modes & PATCH_SUSTAIN) { int value; - double attack, hold, decay, release; int sustain; + int attack, hold, decay, release; int sustain; CONNECTIONLIST *art = NULL; CONNECTION *artList = NULL; @@ -1158,16 +1159,11 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, artList = rgn->artList; } - value = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); - attack = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); - hold = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); - decay = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); - release = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); - sustain = (int)((1.0 - to_normalized_percent(value)) * 250.0); + attack = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); + hold = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); + decay = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); + release = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); + sustain = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); value = load_connection(art->cConnections, artList, CONN_DST_PAN); sample->panning = (int)((0.5 + to_normalized_percent(value)) * 16383.f); @@ -1175,12 +1171,12 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release); */ - sample->envelope.sf2.decay_vol = 0; - sample->envelope.sf2.attack_vol = (short)attack; - sample->envelope.sf2.hold_vol = (short)hold; - sample->envelope.sf2.decay_vol = (short)decay; - sample->envelope.sf2.release_vol = (short)release; - sample->envelope.sf2.sustain_vol = (short)sustain; + sample->envelope.sf2.delay_vol = -32768; + sample->envelope.sf2.attack_vol = (short)(attack >> 16); + sample->envelope.sf2.hold_vol = (short)(hold >> 16); + sample->envelope.sf2.decay_vol = (short)(decay >> 16); + sample->envelope.sf2.release_vol = (short)(release >> 16); + sample->envelope.sf2.sustain_vol = (short)(sustain >> 16); } sample->data_length <<= FRACTION_BITS; diff --git a/src/timidity/instrum_font.cpp b/src/timidity/instrum_font.cpp index 995558bd47..069eb9c847 100644 --- a/src/timidity/instrum_font.cpp +++ b/src/timidity/instrum_font.cpp @@ -54,7 +54,7 @@ void font_add(const char *filename, int load_order) } else { - FileReader *fp = open_filereader(filename, openmode, NULL); + FileReader *fp = pathExpander.openFileReader(filename, NULL); if (fp != NULL) { diff --git a/src/timidity/instrum_sf2.cpp b/src/timidity/instrum_sf2.cpp index dae3306442..01576e64c5 100644 --- a/src/timidity/instrum_sf2.cpp +++ b/src/timidity/instrum_sf2.cpp @@ -1441,7 +1441,6 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend sp->root_freq = note_to_freq(sp->scale_note); sp->sample_rate = sfsamp->SampleRate; sp->key_group = gen->exclusiveClass; - sp->volume = 1; // Set key scaling if (gen->keynum >= 0 && gen->keynum <= 127) @@ -1502,7 +1501,7 @@ void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Rend void SFFile::LoadSample(SFSample *sample) { - FileReader *fp = open_filereader(Filename, openmode, NULL); + FileReader *fp = pathExpander.openFileReader(Filename, NULL); DWORD i; if (fp == NULL) diff --git a/src/timidity/mix.cpp b/src/timidity/mix.cpp index b4fc0f8185..beda91e16c 100644 --- a/src/timidity/mix.cpp +++ b/src/timidity/mix.cpp @@ -29,8 +29,6 @@ #include "templates.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -78,16 +76,7 @@ void GF1Envelope::Init(Renderer *song, Voice *v) void GF1Envelope::Release(Voice *v) { - if (midi_timiditylike) - { - if (!(v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - stage = GF1_RELEASE; - Recompute(v); - } - // else ... loop was already turned off by the caller - } - else if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL)) + if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL)) { /* ramp out to minimum volume with rate from final release stage */ stage = GF1_RELEASEC+1; @@ -119,23 +108,11 @@ bool GF1Envelope::Recompute(Voice *v) bUpdating = false; v->status &= ~(VOICE_SUSTAINING | VOICE_LPE); v->status |= VOICE_RELEASING; - if (midi_timiditylike) - { /* kill the voice ... or not */ - if (volume <= 0) - { - v->status |= VOICE_STOPPING; - } - return 1; - } - else - { /* play sampled release */ - } + /* play sampled release */ return 0; } - if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && - ((!midi_timiditylike && (v->sample->modes & PATCH_SUSTAIN)) || - (midi_timiditylike && !(v->sample->modes & PATCH_T_NO_ENVELOPE)))) + if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN)) { v->status |= VOICE_SUSTAINING; /* Freeze envelope until note turns off. Trumpets want this. */ @@ -161,10 +138,6 @@ bool GF1Envelope::Recompute(Voice *v) bool GF1Envelope::Update(Voice *v) { - if (midi_timiditylike && (v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - return 0; - } volume += increment; if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target))) { @@ -182,36 +155,14 @@ void GF1Envelope::ApplyToAmp(Voice *v) double env_vol = v->attenuation; double final_amp; - if (midi_timiditylike) - { - final_amp = v->sample->volume * FINAL_MIX_TIMIDITY_SCALE; - if (v->tremolo_phase_increment != 0) - { - env_vol *= v->tremolo_volume; - } - if (!(v->sample->modes & PATCH_T_NO_ENVELOPE)) - { - if (stage > GF1_ATTACK) - { - env_vol *= pow(2.0, volume * (6.0 / (1 << 30)) - 6.0); - } - else - { - env_vol *= volume / float(1 << 30); - } - } - } - else - { - final_amp = FINAL_MIX_SCALE; - if (v->tremolo_phase_increment != 0) - { // [RH] FIXME: This is wrong. Tremolo should offset the - // envelope volume, not scale it. - env_vol *= v->tremolo_volume; - } - env_vol *= volume / float(1 << 30); - env_vol = calc_gf1_amp(env_vol); + final_amp = FINAL_MIX_SCALE; + if (v->tremolo_phase_increment != 0) + { // [RH] FIXME: This is wrong. Tremolo should offset the + // envelope volume, not scale it. + env_vol *= v->tremolo_volume; } + env_vol *= volume / float(1 << 30); + env_vol = calc_gf1_amp(env_vol); env_vol *= final_amp; v->left_mix = float(env_vol * v->left_offset); v->right_mix = float(env_vol * v->right_offset); @@ -280,6 +231,10 @@ bool SF2Envelope::Update(Voice *v) double sec; double newvolume = 0; + // NOTE! The volume scale is different for different stages of the + // envelope generator: + // Attack stage goes from 0.0 -> 1.0, multiplied directly to the output. + // The following stages go from 0 -> -1000 cB (but recorded positively) switch (stage) { case SF2_DELAY: @@ -382,6 +337,11 @@ bool SF2Envelope::Update(Voice *v) #define FLUID_ATTEN_POWER_FACTOR (-531.509) #define atten2amp(x) pow(10.0, (x) / FLUID_ATTEN_POWER_FACTOR) +static double cb_to_amp(double x) // centibels to amp +{ + return pow(10, x / -200.f); +} + void SF2Envelope::ApplyToAmp(Voice *v) { double amp; @@ -392,13 +352,21 @@ void SF2Envelope::ApplyToAmp(Voice *v) v->right_mix = 0; return; } - else if (stage == SF2_ATTACK) + + amp = v->sample->type == INST_SF2 ? atten2amp(v->attenuation) : cb_to_amp(v->attenuation); + + switch (stage) { - amp = atten2amp(v->attenuation) * volume; - } - else - { - amp = atten2amp(v->attenuation) * cb_to_amp(volume); + case SF2_ATTACK: + amp *= volume; + break; + + case SF2_HOLD: + break; + + default: + amp *= cb_to_amp(volume); + break; } amp *= FINAL_MIX_SCALE * 0.5; v->left_mix = float(amp * v->left_offset); diff --git a/src/timidity/playmidi.cpp b/src/timidity/playmidi.cpp index 5db4f61bc9..7d8294384e 100644 --- a/src/timidity/playmidi.cpp +++ b/src/timidity/playmidi.cpp @@ -29,8 +29,6 @@ #include "timidity.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -118,7 +116,7 @@ void Renderer::recompute_freq(int v) voice[v].sample_increment = (int)(a); } -static BYTE vol_table[] = { +static const BYTE vol_table[] = { 000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */, 161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */, 177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */, @@ -161,16 +159,7 @@ void Renderer::recompute_amp(Voice *v) if (v->sample->type == INST_GUS) { - if (midi_timiditylike) - { - v->attenuation = float(timidityxx_perceived_vol(v->velocity / 127.0) * - timidityxx_perceived_vol(chanvol / 127.0) * - timidityxx_perceived_vol(chanexpr / 127.0)); - } - else - { - v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f); - } + v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f); } else { @@ -199,7 +188,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ } else { - if (type == INST_GUS && !midi_timiditylike) + if (type == INST_GUS) { /* Original amp equation looks like this: * calc_gf1_amp(atten + offset) @@ -218,9 +207,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ } else { - /* I have no idea what equation, if any, will reproduce the sc_pan_table - * that TiMidity++ uses, so midi_timiditylike gets the same Equal Power - * Panning as SF2/DLS. + /* Equal Power Panning for SF2/DLS. */ left_offset = (float)sqrt(1 - pan); right_offset = (float)sqrt(pan); @@ -561,7 +548,7 @@ void Renderer::finish_note(int i) v->status &= ~VOICE_SUSTAINING; v->status |= VOICE_RELEASING; - if (!(v->sample->modes & PATCH_NO_SRELEASE) || midi_timiditylike) + if (!(v->sample->modes & PATCH_NO_SRELEASE)) { v->status &= ~VOICE_LPE; /* sampled release */ } diff --git a/src/timidity/resample.cpp b/src/timidity/resample.cpp index 0ee6aa8569..6b9bdb9f59 100644 --- a/src/timidity/resample.cpp +++ b/src/timidity/resample.cpp @@ -28,8 +28,6 @@ #include "timidity.h" #include "c_cvars.h" -EXTERN_CVAR(Bool, midi_timiditylike) - namespace Timidity { @@ -522,7 +520,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr) if (vp->vibrato_control_ratio) { - if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP)) + if (vp->status & VOICE_LPE) { if (modes & PATCH_BIDIR) return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr); @@ -536,7 +534,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr) } else { - if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP)) + if (vp->status & VOICE_LPE) { if (modes & PATCH_BIDIR) return rs_bidir(song->resample_buffer, vp, *countptr); diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 0f0fcdeb5d..267edbb7e2 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -34,7 +34,6 @@ CVAR(String, midi_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, midi_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR(Bool, midi_timiditylike, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, gus_patchdir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, midi_dmxgus, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -42,10 +41,10 @@ CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) namespace Timidity { +PathExpander pathExpander; ToneBank *tonebank[MAXBANK], *drumset[MAXBANK]; static FString def_instr_name; -int openmode = OM_FILEORLUMP; static int read_config_file(const char *name, bool ismain) { @@ -62,21 +61,21 @@ static int read_config_file(const char *name, bool ismain) return (-1); } - if (ismain) openmode = OM_FILEORLUMP; + if (ismain) pathExpander.openmode = PathExpander::OM_FILEORLUMP; - if (!(fp = open_filereader(name, openmode, &lumpnum))) + if (!(fp = pathExpander.openFileReader(name, &lumpnum))) return -1; if (ismain) { if (lumpnum > 0) { - openmode = OM_LUMP; - clear_pathlist(); // when reading from a PK3 we won't want to use any external path + pathExpander.openmode = PathExpander::OM_LUMP; + pathExpander.clearPathlist(); // when reading from a PK3 we don't want to use any external path } else { - openmode = OM_FILE; + pathExpander.openmode = PathExpander::OM_FILE; } } @@ -288,7 +287,7 @@ static int read_config_file(const char *name, bool ismain) return -2; } for (i = 1; i < words; i++) - add_to_pathlist(w[i]); + pathExpander.addToPathlist(w[i]); } else if (!strcmp(w[0], "source")) { @@ -377,7 +376,7 @@ static int read_config_file(const char *name, bool ismain) delete fp; return -2; } - bank->tone[i].note = bank->tone[i].amp = bank->tone[i].pan = + bank->tone[i].note = bank->tone[i].pan = bank->tone[i].fontbank = bank->tone[i].fontpreset = bank->tone[i].fontnote = bank->tone[i].strip_loop = bank->tone[i].strip_envelope = bank->tone[i].strip_tail = -1; @@ -415,14 +414,7 @@ static int read_config_file(const char *name, bool ismain) *cp++ = 0; if (!strcmp(w[j], "amp")) { - k = atoi(cp); - if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9')) - { - Printf("%s: line %d: amplification must be between 0 and %d\n", name, line, MAX_AMPLIFICATION); - delete fp; - return -2; - } - bank->tone[i].amp = k; + /* Ignored */ } else if (!strcmp(w[j], "note")) { @@ -532,15 +524,15 @@ int LoadConfig(const char *filename) * file itself since that file should contain any other directory * that needs to be added to the search path. */ - clear_pathlist(); + pathExpander.clearPathlist(); #ifdef _WIN32 - add_to_pathlist("C:\\TIMIDITY"); - add_to_pathlist("\\TIMIDITY"); - add_to_pathlist(progdir); + pathExpander.addToPathlist("C:\\TIMIDITY"); + pathExpander.addToPathlist("\\TIMIDITY"); + pathExpander.addToPathlist(progdir); #else - add_to_pathlist("/usr/local/lib/timidity"); - add_to_pathlist("/etc/timidity"); - add_to_pathlist("/etc"); + pathExpander.addToPathlist("/usr/local/lib/timidity"); + pathExpander.addToPathlist("/etc/timidity"); + pathExpander.addToPathlist("/etc"); #endif /* Some functions get aggravated if not even the standard banks are available. */ @@ -579,10 +571,10 @@ int LoadDMXGUS() if (ultradir.IsNotEmpty()) { ultradir += "/midi"; - add_to_pathlist(ultradir.GetChars()); + pathExpander.addToPathlist(ultradir.GetChars()); } // Load DMXGUS lump and patches from gus_patchdir - add_to_pathlist(gus_patchdir); + pathExpander.addToPathlist(gus_patchdir); char readbuffer[1024]; long size = data.GetLength(); @@ -676,7 +668,7 @@ int LoadDMXGUS() continue; int val = k % 128; - bank->tone[val].note = bank->tone[val].amp = bank->tone[val].pan = + bank->tone[val].note = bank->tone[val].pan = bank->tone[val].fontbank = bank->tone[val].fontpreset = bank->tone[val].fontnote = bank->tone[val].strip_loop = bank->tone[val].strip_envelope = bank->tone[val].strip_tail = -1; @@ -686,8 +678,12 @@ int LoadDMXGUS() return 0; } -Renderer::Renderer(float sample_rate) +DLS_Data *LoadDLS(FILE *src); +void FreeDLS(DLS_Data *data); + +Renderer::Renderer(float sample_rate, const char *args) { + // 'args' should be used to load a custom config or DMXGUS, but since setup currently requires a snd_reset call, this will need some refactoring first rate = sample_rate; patches = NULL; resample_buffer_size = 0; @@ -705,9 +701,14 @@ Renderer::Renderer(float sample_rate) if (def_instr_name.IsNotEmpty()) set_default_instrument(def_instr_name); - voices = clamp(midi_voices, 16, 256); + voices = MAX(*midi_voices, 16); voice = new Voice[voices]; drumchannels = DEFAULT_DRUMCHANNELS; +#if 0 + FILE *f = fopen("c:\\windows\\system32\\drivers\\gm.dls", "rb"); + patches = LoadDLS(f); + fclose(f); +#endif } Renderer::~Renderer() @@ -720,6 +721,10 @@ Renderer::~Renderer() { delete[] voice; } + if (patches != NULL) + { + FreeDLS(patches); + } } void Renderer::ComputeOutput(float *buffer, int count) diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 6c46317ddc..ac423c1e2a 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -21,6 +21,7 @@ #define TIMIDITY_H #include "doomtype.h" +#include "pathexpander.h" class FileReader; @@ -54,10 +55,6 @@ config.h volume level of FMOD's built-in MIDI player. */ #define FINAL_MIX_SCALE 0.5 -/* This value is used instead when midi_timiditylike is turned on, - because TiMidity++ is louder than a GUS. */ -#define FINAL_MIX_TIMIDITY_SCALE 0.3 - /* How many bits to use for the fractional part of sample positions. This affects tonal accuracy. The entire position counter must fit in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of @@ -172,17 +169,8 @@ extern __inline__ double pow_x87_inline(double x,double y) common.h */ -#define OM_FILEORLUMP 0 -#define OM_LUMP 1 -#define OM_FILE 2 - -extern void add_to_pathlist(const char *s); -extern void clear_pathlist(); extern void *safe_malloc(size_t count); -FileReader *open_filereader(const char *name, int open, int *plumpnum); -extern int openmode; - /* controls.h */ @@ -219,9 +207,6 @@ enum PATCH_SUSTAIN = (1<<5), PATCH_NO_SRELEASE = (1<<6), PATCH_FAST_REL = (1<<7), - - PATCH_T_NO_ENVELOPE = (1<<8), - PATCH_T_NO_LOOP = (1<<9) }; struct Sample @@ -247,8 +232,6 @@ struct Sample short release_vol; } sf2; } envelope; - float - volume; sample_t *data; SDWORD tremolo_sweep_increment, tremolo_phase_increment, @@ -324,11 +307,12 @@ struct Instrument struct ToneBankElement { ToneBankElement() : - note(0), amp(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0) + note(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0) {} FString name; - int note, amp, pan, fontbank, fontpreset, fontnote, strip_loop, strip_envelope, strip_tail; + int note, pan, fontbank, fontpreset, fontnote; + SBYTE strip_loop, strip_envelope, strip_tail; }; /* A hack to delay instrument loading until after reading the entire MIDI file. */ @@ -461,7 +445,7 @@ struct Channel struct MinEnvelope { - int stage; + BYTE stage; BYTE bUpdating; }; @@ -615,9 +599,6 @@ const double log_of_2 = 0.69314718055994529; #define freq_to_note(x) (log((x) / 8175.7989473096690661233836992789) * (12.0 / log_of_2)) #define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation -#define cb_to_amp(x) (pow(10.0, (x) * (1 / -200.0))) // centibels to amp -#define db_to_amp(x) (pow(10.0, (x) * (1 / -20.0))) // decibels to map -#define timidityxx_perceived_vol(x) (pow((x), 1.66096404744)) /* timidity.h @@ -627,6 +608,7 @@ int LoadConfig(const char *filename); int LoadDMXGUS(); extern int LoadConfig(); extern void FreeAll(); +extern PathExpander pathExpander; extern ToneBank *tonebank[MAXBANK]; extern ToneBank *drumset[MAXBANK]; @@ -647,7 +629,7 @@ struct Renderer int voices; int lost_notes, cut_notes; - Renderer(float sample_rate); + Renderer(float sample_rate, const char *args); ~Renderer(); void HandleEvent(int status, int parm1, int parm2); diff --git a/src/version.h b/src/version.h index 3d28dd2ac3..b8c4facb0d 100644 --- a/src/version.h +++ b/src/version.h @@ -61,11 +61,11 @@ const char *GetVersionString(); // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. // Otherwise, it should be safe to leave it alone. -#define DEMOGAMEVERSION 0x21B +#define DEMOGAMEVERSION 0x21C // Minimum demo version we can play. // Bump it whenever you change or remove existing DEM_ commands. -#define MINDEMOVERSION 0x21B +#define MINDEMOVERSION 0x21C // SAVEVER is the version of the information stored in level snapshots. // Note that SAVEVER is not directly comparable to VERSION. @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4523 +#define SAVEVER 4531 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/src/w_wad.cpp b/src/w_wad.cpp index efeb38571c..42e5c17b9f 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -184,6 +184,7 @@ void FWadCollection::InitMultipleFiles (TArray &filenames) } RenameNerve(); RenameSprites(); + FixMacHexen(); // [RH] Set up hash table FirstLumpIndex = new DWORD[NumLumps]; @@ -956,6 +957,72 @@ void FWadCollection::RenameNerve () } } +//========================================================================== +// +// FixMacHexen +// +// Discard all extra lumps in Mac version of Hexen IWAD (demo or full) +// to avoid any issues caused by names of these lumps, including: +// * Wrong height of small font +// * Broken life bar of mage class +// +//========================================================================== + +void FWadCollection::FixMacHexen() +{ + if (GAME_Hexen != gameinfo.gametype) + { + return; + } + + FileReader* const reader = GetFileReader(IWAD_FILENUM); + const long iwadSize = reader->GetLength(); + + static const long DEMO_SIZE = 13596228; + static const long FULL_SIZE = 21078584; + + if ( DEMO_SIZE != iwadSize + && FULL_SIZE != iwadSize) + { + return; + } + + reader->Seek(0, SEEK_SET); + + BYTE checksum[16]; + MD5Context md5; + md5.Update(reader, iwadSize); + md5.Final(checksum); + + static const BYTE HEXEN_DEMO_MD5[16] = + { + 0x92, 0x5f, 0x9f, 0x50, 0x00, 0xe1, 0x7d, 0xc8, + 0x4b, 0x0a, 0x6a, 0x3b, 0xed, 0x3a, 0x6f, 0x31 + }; + + static const BYTE HEXEN_FULL_MD5[16] = + { + 0xb6, 0x81, 0x40, 0xa7, 0x96, 0xf6, 0xfd, 0x7f, + 0x3a, 0x5d, 0x32, 0x26, 0xa3, 0x2b, 0x93, 0xbe + }; + + if ( 0 != memcmp(HEXEN_DEMO_MD5, checksum, sizeof checksum) + && 0 != memcmp(HEXEN_FULL_MD5, checksum, sizeof checksum)) + { + return; + } + + static const int EXTRA_LUMPS = 299; + + const int lastLump = GetLastLump(IWAD_FILENUM); + assert(GetFirstLump(IWAD_FILENUM) + 299 < lastLump); + + for (int i = lastLump - EXTRA_LUMPS + 1; i <= lastLump; ++i) + { + LumpInfo[i].lump->Name[0] = '\0'; + } +} + //========================================================================== // // W_FindLump diff --git a/src/w_wad.h b/src/w_wad.h index 323f12df24..dcac6a1b2c 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -238,6 +238,7 @@ protected: private: void RenameSprites(); void RenameNerve(); + void FixMacHexen(); void DeleteAll(); }; diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 02492f272c..653f6b9071 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -1808,18 +1808,16 @@ void WI_updateStats () if (acceleratestage && sp_state != 10) { - if (acceleratestage) - { - acceleratestage = 0; - sp_state = 10; - S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); - } + acceleratestage = 0; + sp_state = 10; + S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE); + cnt_kills[0] = plrs[me].skills; cnt_items[0] = plrs[me].sitems; cnt_secret[0] = plrs[me].ssecret; cnt_time = Tics2Seconds(plrs[me].stime); cnt_par = wbs->partime / TICRATE; - cnt_total_time = Tics2Seconds(wbs->totaltime); + cnt_total_time = Tics2Seconds(wbs->totaltime); } if (sp_state == 2) @@ -1959,9 +1957,9 @@ void WI_drawStats (void) } else { - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, "KILLS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE); - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, "ITEMS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE); - screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, "SECRETS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, GStrings("TXT_IMKILLS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, GStrings("TXT_IMITEMS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); + screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, GStrings("TXT_IMSECRETS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); int countpos = gameinfo.gametype==GAME_Strife? 285:270; if (sp_state >= 2) @@ -1978,7 +1976,7 @@ void WI_drawStats (void) } if (sp_state >= 8) { - screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, "TIME", + screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, GStrings("TXT_IMTIME"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE); WI_drawTime (249, 160, cnt_time); if (wi_showtotaltime) diff --git a/src/wildmidi/common.h b/src/wildmidi/common.h new file mode 100644 index 0000000000..a089329e0c --- /dev/null +++ b/src/wildmidi/common.h @@ -0,0 +1,94 @@ +/* + common.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __COMMON_H +#define __COMMON_H + +#define SAMPLE_16BIT 0x01 +#define SAMPLE_UNSIGNED 0x02 +#define SAMPLE_LOOP 0x04 +#define SAMPLE_PINGPONG 0x08 +#define SAMPLE_REVERSE 0x10 +#define SAMPLE_SUSTAIN 0x20 +#define SAMPLE_ENVELOPE 0x40 +#define SAMPLE_CLAMPED 0x80 + +#ifdef DEBUG_SAMPLES +#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx) +#else +#define SAMPLE_CONVERT_DEBUG(dx) +#endif + +extern unsigned short int _WM_SampleRate; + +struct _sample { + unsigned int data_length; + unsigned int loop_start; + unsigned int loop_end; + unsigned int loop_size; + unsigned char loop_fraction; + unsigned short rate; + unsigned int freq_low; + unsigned int freq_high; + unsigned int freq_root; + unsigned char modes; + signed int env_rate[7]; + signed int env_target[7]; + unsigned int inc_div; + signed short *data; + struct _sample *next; +}; + +struct _env { + float time; + float level; + unsigned char set; +}; + +struct _patch { + unsigned short patchid; + unsigned char loaded; + char *filename; + signed short int amp; + unsigned char keep; + unsigned char remove; + struct _env env[6]; + unsigned char note; + unsigned long int inuse_count; + struct _sample *first_sample; + struct _patch *next; +}; + +/* Set our global defines here */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +#endif /* __COMMON_H */ diff --git a/src/wildmidi/file_io.cpp b/src/wildmidi/file_io.cpp new file mode 100644 index 0000000000..50a2a6b6cf --- /dev/null +++ b/src/wildmidi/file_io.cpp @@ -0,0 +1,112 @@ +/* +** file_io.cpp +** ZDoom compatible file IO interface for WildMIDI +** (This file was completely redone to remove the low level IO code references) +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "../files.h" +#include "wm_error.h" +#include "file_io.h" +#include "pathexpander.h" +#include "cmdlib.h" + +static PathExpander wmPathExpander; + +unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size, bool ismain) +{ + FileReader *fp; + int lumpnum; + + if (ismain) + { + wmPathExpander.openmode = PathExpander::OM_FILEORLUMP; + wmPathExpander.clearPathlist(); +#ifdef _WIN32 + wmPathExpander.addToPathlist("C:\\TIMIDITY"); + wmPathExpander.addToPathlist("\\TIMIDITY"); + wmPathExpander.addToPathlist(progdir); +#else + wmPathExpander.addToPathlist("/usr/local/lib/timidity"); + wmPathExpander.addToPathlist("/etc/timidity"); + wmPathExpander.addToPathlist("/etc"); +#endif + } + + if (!(fp = wmPathExpander.openFileReader(filename, &lumpnum))) + return NULL; + + if (ismain) + { + if (lumpnum > 0) + { + wmPathExpander.openmode = PathExpander::OM_LUMP; + wmPathExpander.clearPathlist(); // when reading from a PK3 we don't want to use any external path + } + else + { + wmPathExpander.openmode = PathExpander::OM_FILE; + } + } + + + + if (fp == NULL) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + long fsize = fp->GetLength(); + + if (fsize > WM_MAXFILESIZE) + { + /* don't bother loading suspiciously long files */ + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, filename, 0); + return NULL; + } + + unsigned char *data = (unsigned char*)malloc(fsize+1); + if (data == NULL) + { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); + return NULL; + } + + fp->Read(data, fsize); + delete fp; + data[fsize] = 0; + *size = fsize; + return data; +} diff --git a/src/wildmidi/file_io.h b/src/wildmidi/file_io.h new file mode 100644 index 0000000000..550a8a4b25 --- /dev/null +++ b/src/wildmidi/file_io.h @@ -0,0 +1,33 @@ +/* + file_io.c + + file handling + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __FILE_IO_H +#define __FILE_IO_H + +#define WM_MAXFILESIZE 0x1fffffff +extern unsigned char *_WM_BufferFile (const char *filename, unsigned long int *size, bool mainfile = false); + +#endif /* __FILE_IO_H */ diff --git a/src/wildmidi/gus_pat.cpp b/src/wildmidi/gus_pat.cpp new file mode 100644 index 0000000000..8f4824dc53 --- /dev/null +++ b/src/wildmidi/gus_pat.cpp @@ -0,0 +1,981 @@ +/* + * gus_pat.c -- Midi Wavetable Processing library + * + * Copyright (C) WildMIDI Developers 2001-2015 + * + * This file is part of WildMIDI. + * + * WildMIDI is free software: you can redistribute and/or modify the player + * under the terms of the GNU General Public License and you can redistribute + * and/or modify the library under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 3 of + * the licenses, or(at your option) any later version. + * + * WildMIDI is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the + * GNU Lesser General Public License along with WildMIDI. If not, see + * . + */ + +#include +#include +#include +#include + +#include "gus_pat.h" +#include "common.h" +#include "wm_error.h" +#include "file_io.h" + +#ifdef DEBUG_GUSPAT +#define GUSPAT_FILENAME_DEBUG(dx) fprintf(stderr,"\r%s\n",dx) + +#define GUSPAT_INT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %i\n",dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %f\n",dx,dy) +#define GUSPAT_START_DEBUG() fprintf(stderr,"\r") +#define GUSPAT_MODE_DEBUG(dx,dy,dz) if (dx & dy) fprintf(stderr,"%s",dz) +#define GUSPAT_END_DEBUG() fprintf(stderr,"\n") +#else +#define GUSPAT_FILENAME_DEBUG(dx) +#define GUSPAT_INT_DEBUG(dx,dy) +#define GUSPAT_FLOAT_DEBUG(dx,dy) +#define GUSPAT_START_DEBUG() +#define GUSPAT_MODE_DEBUG(dx,dy,dz) +#define GUSPAT_END_DEBUG() +#endif + +/* + * sample data conversion functions + * convert data to signed shorts + */ + +/* 8bit signed */ +static int convert_8s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + return 0; + } + + _WM_ERROR_NEW("(%s:%i) ERROR: calloc failed (%s)", __FUNCTION__, __LINE__, + strerror(errno)); + return -1; +} + +/* 8bit signed ping pong */ +static int convert_8sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data++) << 8; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data++ << 8); + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = (*read_data++) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse */ +static int convert_8sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = (*read_data++) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit signed reverse ping pong */ +static int convert_8srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = (*read_data--) << 8; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = (*read_data-- << 8); + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = (*read_data--) << 8; + write_data_b++; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned */ +static int convert_8u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned ping pong */ +static int convert_8up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_end; + do { + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b++ = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse */ +static int convert_8ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((gus_sample->data_length + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + gus_sample->data_length - 1; + do { + *write_data-- = ((*read_data++) ^ 0x80) << 8; + } while (read_data != read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 8bit unsigned reverse ping pong */ +static int convert_8urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc((new_length + 2), sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + write_data_a = write_data + dloop_length; + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + dloop_length; + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data != read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b++ = ((*read_data--) ^ 0x80) << 8; + } while (read_data != read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed */ +static int convert_16s(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed ping pong */ +static int convert_16sp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= (*read_data++) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= (*read_data++) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = *(read_data++); + *write_data |= (*read_data++) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = *(read_data++); + *write_data_b++ |= (*read_data++) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse */ +static int convert_16sr(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= (*read_data++) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit signed reverse ping pong */ +static int convert_16srp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data--) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = (*read_data-- << 8); + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = (*read_data--) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) << 8); + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = (*read_data--) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned */ +static int convert_16u(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = *read_data++; + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned ping pong */ +static int convert_16up(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->loop_start; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = (*read_data++); + *write_data++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_end; + do { + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = (*read_data++); + *write_data |= ((*read_data++) ^ 0x80) << 8; + *write_data_b++ = *write_data; + read_end = data + gus_sample->data_length; + if (read_data != read_end) { + do { + *write_data_b = (*read_data++); + *write_data_b++ |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + } + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG; + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse */ +static int convert_16ur(unsigned char *data, struct _sample *gus_sample) { + unsigned char *read_data = data; + unsigned char *read_end = data + gus_sample->data_length; + signed short int *write_data = NULL; + unsigned long int tmp_loop = 0; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((gus_sample->data_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1; + do { + *write_data = *read_data++; + *write_data-- |= ((*read_data++) ^ 0x80) << 8; + } while (read_data < read_end); + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start; + gus_sample->loop_start = gus_sample->data_length - tmp_loop; + gus_sample->loop_fraction = ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + gus_sample->loop_start >>= 1; + gus_sample->loop_end >>= 1; + gus_sample->data_length >>= 1; + gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* 16bit unsigned reverse ping pong */ +static int convert_16urp(unsigned char *data, struct _sample *gus_sample) { + unsigned long int loop_length = gus_sample->loop_end + - gus_sample->loop_start; + unsigned long int dloop_length = loop_length * 2; + unsigned long int new_length = gus_sample->data_length + dloop_length; + unsigned char *read_data = data + gus_sample->data_length - 1; + unsigned char *read_end = data + gus_sample->loop_end; + signed short int *write_data = NULL; + signed short int *write_data_a = NULL; + signed short int *write_data_b = NULL; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); + gus_sample->data = (short*)calloc(((new_length >> 1) + 2), + sizeof(signed short int)); + if (gus_sample->data != NULL) { + write_data = gus_sample->data; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data++ |= *read_data--; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + write_data_a = write_data + (dloop_length >> 1); + *write_data_a-- = *write_data; + write_data++; + write_data_b = write_data + (dloop_length >> 1); + read_end = data + gus_sample->loop_start; + do { + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_a-- = *write_data; + *write_data_b++ = *write_data; + write_data++; + } while (read_data < read_end); + + *write_data = ((*read_data--) ^ 0x80) << 8; + *write_data |= *read_data--; + *write_data_b++ = *write_data; + read_end = data - 1; + do { + *write_data_b = ((*read_data--) ^ 0x80) << 8; + *write_data_b++ |= *read_data--; + } while (read_data < read_end); + gus_sample->loop_start += loop_length; + gus_sample->loop_end += dloop_length; + gus_sample->data_length = new_length; + gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED; + return 0; + } + + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); + return -1; +} + +/* sample loading */ + +struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) { + unsigned char *gus_patch; + unsigned long int gus_size; + unsigned long int gus_ptr; + unsigned char no_of_samples; + struct _sample *gus_sample = NULL; + struct _sample *first_gus_sample = NULL; + unsigned long int i = 0; + + int (*do_convert[])(unsigned char *data, struct _sample *gus_sample) = { + convert_8s, + convert_16s, + convert_8u, + convert_16u, + convert_8sp, + convert_16sp, + convert_8up, + convert_16up, + convert_8sr, + convert_16sr, + convert_8ur, + convert_16ur, + convert_8srp, + convert_16srp, + convert_8urp, + convert_16urp + }; + unsigned long int tmp_loop; + + /*unused*/fix_release; + + SAMPLE_CONVERT_DEBUG(__FUNCTION__); SAMPLE_CONVERT_DEBUG(filename); + + if ((gus_patch = _WM_BufferFile(filename, &gus_size)) == NULL) { + return NULL; + } + if (gus_size < 239) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) + && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[82] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + if (gus_patch[151] > 1) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(unsupported format)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + GUSPAT_FILENAME_DEBUG(filename); GUSPAT_INT_DEBUG("voices",gus_patch[83]); + + no_of_samples = gus_patch[198]; + gus_ptr = 239; + while (no_of_samples) { + unsigned long int tmp_cnt; + if (first_gus_sample == NULL) { + first_gus_sample = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = first_gus_sample; + } else { + gus_sample->next = (struct _sample*)malloc(sizeof(struct _sample)); + gus_sample = gus_sample->next; + } + if (gus_sample == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0); + free(gus_patch); + return NULL; + } + + gus_sample->next = NULL; + gus_sample->loop_fraction = gus_patch[gus_ptr + 7]; + gus_sample->data_length = (gus_patch[gus_ptr + 11] << 24) + | (gus_patch[gus_ptr + 10] << 16) + | (gus_patch[gus_ptr + 9] << 8) | gus_patch[gus_ptr + 8]; + gus_sample->loop_start = (gus_patch[gus_ptr + 15] << 24) + | (gus_patch[gus_ptr + 14] << 16) + | (gus_patch[gus_ptr + 13] << 8) | gus_patch[gus_ptr + 12]; + gus_sample->loop_end = (gus_patch[gus_ptr + 19] << 24) + | (gus_patch[gus_ptr + 18] << 16) + | (gus_patch[gus_ptr + 17] << 8) | gus_patch[gus_ptr + 16]; + gus_sample->rate = (gus_patch[gus_ptr + 21] << 8) + | gus_patch[gus_ptr + 20]; + gus_sample->freq_low = ((gus_patch[gus_ptr + 25] << 24) + | (gus_patch[gus_ptr + 24] << 16) + | (gus_patch[gus_ptr + 23] << 8) | gus_patch[gus_ptr + 22]); + gus_sample->freq_high = ((gus_patch[gus_ptr + 29] << 24) + | (gus_patch[gus_ptr + 28] << 16) + | (gus_patch[gus_ptr + 27] << 8) | gus_patch[gus_ptr + 26]); + gus_sample->freq_root = ((gus_patch[gus_ptr + 33] << 24) + | (gus_patch[gus_ptr + 32] << 16) + | (gus_patch[gus_ptr + 31] << 8) | gus_patch[gus_ptr + 30]); + + /* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */ + /* Result is 0.001% inacurate */ + gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2; + +#if 0 + /* We dont use this info at this time, kept in here for info */ + printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]); + printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n", + gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]); +#endif + gus_sample->modes = gus_patch[gus_ptr + 55]; + GUSPAT_START_DEBUG(); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_16BIT, "16bit "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_UNSIGNED, "Unsigned "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_LOOP, "Loop "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_PINGPONG, "PingPong "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_REVERSE, "Reverse "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_SUSTAIN, "Sustain "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_ENVELOPE, "Envelope "); GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_CLAMPED, "Clamped "); GUSPAT_END_DEBUG(); + + if (gus_sample->loop_start > gus_sample->loop_end) { + tmp_loop = gus_sample->loop_end; + gus_sample->loop_end = gus_sample->loop_start; + gus_sample->loop_start = tmp_loop; + gus_sample->loop_fraction = + ((gus_sample->loop_fraction & 0x0f) << 4) + | ((gus_sample->loop_fraction & 0xf0) >> 4); + } + + /* + FIXME: Experimental Hacky Fix + + This looks for "dodgy" release envelope settings that faulty editors + may have set and attempts to corrects it. + if (fix_release) + Lets make this automatic ... + */ + { + /* + After studying faulty gus_pats this way may work better + Testing to determine if any further adjustments are required + */ + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 41]]) { + unsigned char tmp_hack_rate = 0; + + if (env_time_table[gus_patch[gus_ptr + 41]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 2 3 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } else if (env_time_table[gus_patch[gus_ptr + 41]] == env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 2 2 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + + } else { + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 1 3 2 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } else { + // 2 3 1 or 1 2 1 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = tmp_hack_rate; + } + } + } else if (env_time_table[gus_patch[gus_ptr + 41]] < env_time_table[gus_patch[gus_ptr + 42]]) { + unsigned char tmp_hack_rate = 0; + + if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 42]]) { + // 2 1 3 + tmp_hack_rate = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = tmp_hack_rate; + } else { + // 3 1 2 + tmp_hack_rate = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 42]; + gus_patch[gus_ptr + 42] = tmp_hack_rate; + } + } + +#if 0 + if ((env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 41]]) && (env_time_table[gus_patch[gus_ptr + 41]] == env_time_table[gus_patch[gus_ptr + 42]])) { + uint8_t tmp_hack_rate = 0; + tmp_hack_rate = gus_patch[gus_ptr + 41]; + gus_patch[gus_ptr + 41] = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 42] = gus_patch[gus_ptr + 40]; + gus_patch[gus_ptr + 40] = tmp_hack_rate; + tmp_hack_rate = gus_patch[gus_ptr + 47]; + gus_patch[gus_ptr + 47] = gus_patch[gus_ptr + 46]; + gus_patch[gus_ptr + 48] = gus_patch[gus_ptr + 46]; + gus_patch[gus_ptr + 46] = tmp_hack_rate; + } +#endif + } + + for (i = 0; i < 6; i++) { + GUSPAT_INT_DEBUG("Envelope #",i); + if (gus_sample->modes & SAMPLE_ENVELOPE) { + unsigned char env_rate = gus_patch[gus_ptr + 37 + i]; + gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr + 43 + i]; + GUSPAT_INT_DEBUG("Envelope Level",gus_patch[gus_ptr+43+i]); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[env_rate]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[env_rate])); + GUSPAT_INT_DEBUG("Envelope Rate",gus_sample->env_rate[i]); GUSPAT_INT_DEBUG("GUSPAT Rate",env_rate); + if (gus_sample->env_rate[i] == 0) { + _WM_ERROR_NEW("%s: Warning: found invalid envelope(%lu) rate setting in %s. Using %f instead.", + __FUNCTION__, i, filename, env_time_table[63]); + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } else { + gus_sample->env_target[i] = 4194303; + gus_sample->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); + } + } + + gus_sample->env_target[6] = 0; + gus_sample->env_rate[6] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + + gus_ptr += 96; + tmp_cnt = gus_sample->data_length; + + if (do_convert[(((gus_sample->modes & 0x18) >> 1) + | (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) + == -1) { + free(gus_patch); + return NULL; + } + + /* + Test and set decay expected decay time after a note off + NOTE: This sets samples for full range decay + */ + if (gus_sample->modes & SAMPLE_ENVELOPE) { + double samples_f = 0.0; + + if (gus_sample->modes & SAMPLE_CLAMPED) { + samples_f = (4194301.0 - (float)gus_sample->env_target[5]) / gus_sample->env_rate[5]; + } else { + if (gus_sample->modes & SAMPLE_SUSTAIN) { + samples_f = (4194301.0 - (float)gus_sample->env_target[3]) / gus_sample->env_rate[3]; + samples_f += (float)(gus_sample->env_target[3] - gus_sample->env_target[4]) / gus_sample->env_rate[4]; + } else { + samples_f = (4194301.0 - (float)gus_sample->env_target[4]) / gus_sample->env_rate[4]; + } + samples_f += (float)(gus_sample->env_target[4] - gus_sample->env_target[5]) / gus_sample->env_rate[5]; + } + samples_f += (float)gus_sample->env_target[5] / gus_sample->env_rate[6]; + } + + gus_ptr += tmp_cnt; + gus_sample->loop_start = (gus_sample->loop_start << 10) + | (((gus_sample->loop_fraction & 0x0f) << 10) / 16); + gus_sample->loop_end = (gus_sample->loop_end << 10) + | (((gus_sample->loop_fraction & 0xf0) << 6) / 16); + gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start; + gus_sample->data_length = gus_sample->data_length << 10; + no_of_samples--; + } + free(gus_patch); + return first_gus_sample; +} diff --git a/src/wildmidi/gus_pat.h b/src/wildmidi/gus_pat.h new file mode 100644 index 0000000000..084b8473ab --- /dev/null +++ b/src/wildmidi/gus_pat.h @@ -0,0 +1,77 @@ +/* + gus_pat.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __GUS_PAT_H +#define __GUS_PAT_H + +/* Guspat Envelope Rate Timings */ + +static float env_time_table[] = { +/* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */ + 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, + 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, + 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, + 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, + 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, + 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, + 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, + 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, + +/* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */ + 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, + 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, + 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, + 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, + 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, + 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, + 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, + 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, + +/* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */ + 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, + 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, + 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, + 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, + 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, + 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, + 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, + 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, + +/* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */ + 0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, + 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, + 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, + 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, + 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, + 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, + 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, + 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f +}; + +extern struct _sample * _WM_load_gus_pat (const char *filename, int _fix_release); + +#endif /* __GUS_PAT_H */ + diff --git a/src/wildmidi/reverb.cpp b/src/wildmidi/reverb.cpp new file mode 100644 index 0000000000..6c6af88d4b --- /dev/null +++ b/src/wildmidi/reverb.cpp @@ -0,0 +1,398 @@ +/* + reverb.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include + +#include "common.h" +#include "reverb.h" + +/* + reverb function + */ +void _WM_reset_reverb(struct _rvb *rvb) { + int i, j, k; + for (i = 0; i < rvb->l_buf_size; i++) { + rvb->l_buf[i] = 0; + } + for (i = 0; i < rvb->r_buf_size; i++) { + rvb->r_buf[i] = 0; + } + for (k = 0; k < 8; k++) { + for (i = 0; i < 6; i++) { + for (j = 0; j < 2; j++) { + rvb->l_buf_flt_in[k][i][j] = 0; + rvb->l_buf_flt_out[k][i][j] = 0; + rvb->r_buf_flt_in[k][i][j] = 0; + rvb->r_buf_flt_out[k][i][j] = 0; + } + } + } +} + +/* + _WM_init_reverb + + ========================= + Engine Description + + 8 reflective points around the room + 2 speaker positions + 1 listener position + + Sounds come from the speakers to all points and to the listener. + Sound comes from the reflective points to the listener. + These sounds are combined, put through a filter that mimics surface absorbtion. + The combined sounds are also sent to the reflective points on the opposite side. + + */ +struct _rvb * +_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, + float listen_y) { + + /* filters set at 125Hz, 250Hz, 500Hz, 1000Hz, 2000Hz, 4000Hz */ + double Freq[] = {125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0}; + + /* numbers calculated from + * 101.325 kPa, 20 deg C, 50% relative humidity */ + double dbAirAbs[] = {-0.00044, -0.00131, -0.002728, -0.004665, -0.009887, -0.029665}; + + /* modify these to adjust the absorption qualities of the surface. + * Remember that lower frequencies are less effected by surfaces + * Note: I am currently playing with the values and finding the ideal surfaces + * for nice default reverb. + */ + double dbAttn[8][6] = { + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-0.131, -6.205, -12.059, -20.933, -20.933, -15.944}, + {-1.839, -6.205, -8.891, -12.059, -15.935, -20.942} + }; + /* + double dbAttn[6] = { + // concrete covered in carpet + // -0.175, -0.537, -1.412, -4.437, -7.959, -7.959 + // pleated drapes + -0.630, -3.223, -5.849, -12.041, -10.458, -7.959 + }; + */ + + /* distance */ + double SPL_DST[8] = {0.0}; + double SPR_DST[8] = {0.0}; + double RFN_DST[8] = {0.0}; + + double MAXL_DST = 0.0; + double MAXR_DST = 0.0; + + double SPL_LSN_XOFS = 0.0; + double SPL_LSN_YOFS = 0.0; + double SPL_LSN_DST = 0.0; + + double SPR_LSN_XOFS = 0.0; + double SPR_LSN_YOFS = 0.0; + double SPR_LSN_DST = 0.0; + + + struct _rvb *rtn_rvb = (struct _rvb*)malloc(sizeof(struct _rvb)); + int j = 0; + int i = 0; + + struct _coord { + double x; + double y; + }; + +#if 0 + struct _coord SPL = {2.5, 5.0}; /* Left Speaker Position */ + struct _coord SPR = {7.5, 5.0}; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[] = { + { 5.0, 0.0}, + { 0.0, 6.66666}, + { 0.0, 13.3333}, + { 5.0, 20.0}, + { 10.0, 20.0}, + { 15.0, 13.3333}, + { 15.0, 6.66666}, + { 10.0, 0.0} + }; +#else + struct _coord SPL; /* Left Speaker Position */ + struct _coord SPR; /* Right Speaker Position */ + /* position of the reflective points */ + struct _coord RFN[8]; + + SPL.x = room_x / 4.0; + SPR.x = room_x / 4.0 * 3.0; + SPL.y = room_y / 10.0; + SPR.y = room_y / 10.0; + + RFN[0].x = room_x / 3.0; + RFN[0].y = 0.0; + RFN[1].x = 0.0; + RFN[1].y = room_y / 3.0; + RFN[2].x = 0.0; + RFN[2].y = room_y / 3.0 * 2.0; + RFN[3].x = room_x / 3.0; + RFN[3].y = room_y; + RFN[4].x = room_x / 3.0 * 2.0; + RFN[4].y = room_y; + RFN[5].x = room_x; + RFN[5].y = room_y / 3.0 * 2.0; + RFN[6].x = room_x; + RFN[6].y = room_y / 3.0; + RFN[7].x = room_x / 3.0 * 2.0; + RFN[7].y = 0.0; +#endif + + SPL_LSN_XOFS = SPL.x - listen_x; + SPL_LSN_YOFS = SPL.y - listen_y; + SPL_LSN_DST = sqrt((SPL_LSN_XOFS * SPL_LSN_XOFS) + (SPL_LSN_YOFS * SPL_LSN_YOFS)); + + if (SPL_LSN_DST > MAXL_DST) + MAXL_DST = SPL_LSN_DST; + + SPR_LSN_XOFS = SPR.x - listen_x; + SPR_LSN_YOFS = SPR.y - listen_y; + SPR_LSN_DST = sqrt((SPR_LSN_XOFS * SPR_LSN_XOFS) + (SPR_LSN_YOFS * SPR_LSN_YOFS)); + + if (SPR_LSN_DST > MAXR_DST) + MAXR_DST = SPR_LSN_DST; + + if (rtn_rvb == NULL) { + return NULL; + } + + for (j = 0; j < 8; j++) { + double SPL_RFL_XOFS = 0; + double SPL_RFL_YOFS = 0; + double SPR_RFL_XOFS = 0; + double SPR_RFL_YOFS = 0; + double RFN_XOFS = listen_x - RFN[j].x; + double RFN_YOFS = listen_y - RFN[j].y; + RFN_DST[j] = sqrt((RFN_XOFS * RFN_XOFS) + (RFN_YOFS * RFN_YOFS)); + + SPL_RFL_XOFS = SPL.x - RFN[i].x; + SPL_RFL_YOFS = SPL.y - RFN[i].y; + SPR_RFL_XOFS = SPR.x - RFN[i].x; + SPR_RFL_YOFS = SPR.y - RFN[i].y; + SPL_DST[i] = sqrt( + (SPL_RFL_XOFS * SPL_RFL_XOFS) + (SPL_RFL_YOFS * SPL_RFL_YOFS)); + SPR_DST[i] = sqrt( + (SPR_RFL_XOFS * SPR_RFL_XOFS) + (SPR_RFL_YOFS * SPR_RFL_YOFS)); + /* + add the 2 distances together and remove the speaker to listener distance + so we dont have to delay the initial output + */ + SPL_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPL_DST[i] -= SPL_LSN_DST; + + if (i < 4) { + if (SPL_DST[i] > MAXL_DST) + MAXL_DST = SPL_DST[i]; + } else { + if (SPL_DST[i] > MAXR_DST) + MAXR_DST = SPL_DST[i]; + } + + SPR_DST[i] += RFN_DST[i]; + + /* so i dont have to delay speaker output */ + SPR_DST[i] -= SPR_LSN_DST; + + if (i < 4) { + if (SPR_DST[i] > MAXL_DST) + MAXL_DST = SPR_DST[i]; + } else { + if (SPR_DST[i] > MAXR_DST) + MAXR_DST = SPR_DST[i]; + } + + RFN_DST[j] *= 2.0; + + if (j < 4) { + if (RFN_DST[j] > MAXL_DST) + MAXL_DST = RFN_DST[j]; + } else { + if (RFN_DST[j] > MAXR_DST) + MAXR_DST = RFN_DST[j]; + } + + for (i = 0; i < 6; i++) { + double srate = (double) rate; + double bandwidth = 2.0; + double omega = 2.0 * M_PI * Freq[i] / srate; + double sn = sin(omega); + double cs = cos(omega); + double alpha = sn * sinh(M_LN2 / 2 * bandwidth * omega / sn); + double A = pow(10.0, ((/*dbAttn[i]*/dbAttn[j][i] + + (dbAirAbs[i] * RFN_DST[j])) / 40.0) ); + /* + Peaking band EQ filter + */ + double b0 = 1 + (alpha * A); + double b1 = -2 * cs; + double b2 = 1 - (alpha * A); + double a0 = 1 + (alpha / A); + double a1 = -2 * cs; + double a2 = 1 - (alpha / A); + + rtn_rvb->coeff[j][i][0] = (signed int) ((b0 / a0) * 1024.0); + rtn_rvb->coeff[j][i][1] = (signed int) ((b1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][2] = (signed int) ((b2 / a0) * 1024.0); + rtn_rvb->coeff[j][i][3] = (signed int) ((a1 / a0) * 1024.0); + rtn_rvb->coeff[j][i][4] = (signed int) ((a2 / a0) * 1024.0); + } + } + + /* init the reverb buffers */ + rtn_rvb->l_buf_size = (int) ((float) rate * (MAXL_DST / 340.29)); + rtn_rvb->l_buf = (int*)malloc( + sizeof(signed int) * (rtn_rvb->l_buf_size + 1)); + rtn_rvb->l_out = 0; + + rtn_rvb->r_buf_size = (int) ((float) rate * (MAXR_DST / 340.29)); + rtn_rvb->r_buf = (int*)malloc( + sizeof(signed int) * (rtn_rvb->r_buf_size + 1)); + rtn_rvb->r_out = 0; + + for (i = 0; i < 4; i++) { + rtn_rvb->l_sp_in[i] = (int) ((float) rate * (SPL_DST[i] / 340.29)); + rtn_rvb->l_sp_in[i + 4] = (int) ((float) rate + * (SPL_DST[i + 4] / 340.29)); + rtn_rvb->r_sp_in[i] = (int) ((float) rate * (SPR_DST[i] / 340.29)); + rtn_rvb->r_sp_in[i + 4] = (int) ((float) rate + * (SPR_DST[i + 4] / 340.29)); + rtn_rvb->l_in[i] = (int) ((float) rate * (RFN_DST[i] / 340.29)); + rtn_rvb->r_in[i] = (int) ((float) rate * (RFN_DST[i + 4] / 340.29)); + } + + rtn_rvb->gain = 4; + + _WM_reset_reverb(rtn_rvb); + return rtn_rvb; +} + +/* _WM_free_reverb - free up memory used for reverb */ +void _WM_free_reverb(struct _rvb *rvb) { + if (!rvb) return; + free(rvb->l_buf); + free(rvb->r_buf); + free(rvb); +} + +void _WM_do_reverb(struct _rvb *rvb, signed int *buffer, int size) { + int i, j, k; + signed int l_buf_flt = 0; + signed int r_buf_flt = 0; + signed int l_rfl = 0; + signed int r_rfl = 0; + int vol_div = 64; + + for (i = 0; i < size; i += 2) { + signed int tmp_l_val = 0; + signed int tmp_r_val = 0; + /* + add the initial reflections + from each speaker, 4 to go the left, 4 go to the right buffers + */ + tmp_l_val = buffer[i] / vol_div; + tmp_r_val = buffer[i + 1] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_sp_in[j]] += tmp_l_val; + rvb->l_sp_in[j] = (rvb->l_sp_in[j] + 1) % rvb->l_buf_size; + rvb->l_buf[rvb->r_sp_in[j]] += tmp_r_val; + rvb->r_sp_in[j] = (rvb->r_sp_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->l_sp_in[j + 4]] += tmp_l_val; + rvb->l_sp_in[j + 4] = (rvb->l_sp_in[j + 4] + 1) % rvb->r_buf_size; + rvb->r_buf[rvb->r_sp_in[j + 4]] += tmp_r_val; + rvb->r_sp_in[j + 4] = (rvb->r_sp_in[j + 4] + 1) % rvb->r_buf_size; + } + + /* + filter the reverb output and add to buffer + */ + l_rfl = rvb->l_buf[rvb->l_out]; + rvb->l_buf[rvb->l_out] = 0; + rvb->l_out = (rvb->l_out + 1) % rvb->l_buf_size; + + r_rfl = rvb->r_buf[rvb->r_out]; + rvb->r_buf[rvb->r_out] = 0; + rvb->r_out = (rvb->r_out + 1) % rvb->r_buf_size; + + for (k = 0; k < 8; k++) { + for (j = 0; j < 6; j++) { + l_buf_flt = ((l_rfl * rvb->coeff[k][j][0]) + + (rvb->l_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->l_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->l_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->l_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->l_buf_flt_in[k][j][1] = rvb->l_buf_flt_in[k][j][0]; + rvb->l_buf_flt_in[k][j][0] = l_rfl; + rvb->l_buf_flt_out[k][j][1] = rvb->l_buf_flt_out[k][j][0]; + rvb->l_buf_flt_out[k][j][0] = l_buf_flt; + buffer[i] += l_buf_flt / 8; + + r_buf_flt = ((r_rfl * rvb->coeff[k][j][0]) + + (rvb->r_buf_flt_in[k][j][0] * rvb->coeff[k][j][1]) + + (rvb->r_buf_flt_in[k][j][1] * rvb->coeff[k][j][2]) + - (rvb->r_buf_flt_out[k][j][0] * rvb->coeff[k][j][3]) + - (rvb->r_buf_flt_out[k][j][1] * rvb->coeff[k][j][4])) + / 1024; + rvb->r_buf_flt_in[k][j][1] = rvb->r_buf_flt_in[k][j][0]; + rvb->r_buf_flt_in[k][j][0] = r_rfl; + rvb->r_buf_flt_out[k][j][1] = rvb->r_buf_flt_out[k][j][0]; + rvb->r_buf_flt_out[k][j][0] = r_buf_flt; + buffer[i + 1] += r_buf_flt / 8; + } + } + + /* + add filtered result back into the buffers but on the opposite side + */ + tmp_l_val = buffer[i + 1] / vol_div; + tmp_r_val = buffer[i] / vol_div; + for (j = 0; j < 4; j++) { + rvb->l_buf[rvb->l_in[j]] += tmp_l_val; + rvb->l_in[j] = (rvb->l_in[j] + 1) % rvb->l_buf_size; + + rvb->r_buf[rvb->r_in[j]] += tmp_r_val; + rvb->r_in[j] = (rvb->r_in[j] + 1) % rvb->r_buf_size; + } + } +} + diff --git a/src/wildmidi/reverb.h b/src/wildmidi/reverb.h new file mode 100644 index 0000000000..162de61eb4 --- /dev/null +++ b/src/wildmidi/reverb.h @@ -0,0 +1,57 @@ +/* + reverb.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __REVERB_H +#define __REVERB_H + +struct _rvb { + /* filter data */ + signed int l_buf_flt_in[8][6][2]; + signed int l_buf_flt_out[8][6][2]; + signed int r_buf_flt_in[8][6][2]; + signed int r_buf_flt_out[8][6][2]; + signed int coeff[8][6][5]; + /* buffer data */ + signed int *l_buf; + signed int *r_buf; + int l_buf_size; + int r_buf_size; + int l_out; + int r_out; + int l_sp_in[8]; + int r_sp_in[8]; + int l_in[4]; + int r_in[4]; + int gain; + unsigned long int max_reverb_time; +}; + + extern void _WM_reset_reverb (struct _rvb *rvb); + extern struct _rvb *_WM_init_reverb(int rate, float room_x, float room_y, float listen_x, float listen_y); + extern void _WM_free_reverb (struct _rvb *rvb); + extern void _WM_do_reverb (struct _rvb *rvb, signed int *buffer, int size); + +#endif /* __REVERB_H */ diff --git a/src/wildmidi/wildmidi_lib.cpp b/src/wildmidi/wildmidi_lib.cpp new file mode 100644 index 0000000000..5f711bf35c --- /dev/null +++ b/src/wildmidi/wildmidi_lib.cpp @@ -0,0 +1,2954 @@ +/* + wildmidi_lib.c + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2014 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#define UNUSED(x) (void)(x) + +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "wm_error.h" +#include "file_io.h" +#include "reverb.h" +#include "gus_pat.h" +#include "wildmidi_lib.h" +#include "critsec.h" + +#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') +#ifdef _WIN32 +#define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':')) +#else +#define HAS_DRIVE_SPEC(f) (0) +#endif +#define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f))) + + +/* + * ========================= + * Global Data and Data Structs + * ========================= + */ + +#define MEM_CHUNK 8192 + +static int WM_Initialized = 0; +static signed short int WM_MasterVolume = 948; +static unsigned short int WM_MixerOptions = 0; + +static char WM_Version[] = "WildMidi Processing Library"; + +unsigned short int _WM_SampleRate; + +static struct _patch *patch[128]; + +static float reverb_room_width = 16.875f; +static float reverb_room_length = 22.5f; + +static float reverb_listen_posx = 8.4375f; +static float reverb_listen_posy = 16.875f; + +static int fix_release = 0; +static int auto_amp = 0; +static int auto_amp_with_amp = 0; + +static FCriticalSection patch_lock; + +struct _channel { + unsigned char bank; + struct _patch *patch; + unsigned char hold; + unsigned char volume; + unsigned char pressure; + unsigned char expression; + signed char balance; + signed char pan; + signed short int left_adjust; + signed short int right_adjust; + signed short int pitch; + signed short int pitch_range; + signed long int pitch_adjust; + unsigned short reg_data; + unsigned char reg_non; + unsigned char isdrum; +}; + +#define HOLD_OFF 0x02 + +struct _note { + unsigned short noteid; + unsigned char velocity; + struct _patch *patch; + struct _sample *sample; + unsigned int sample_pos; + unsigned int sample_inc; + signed int env_inc; + unsigned char env; + signed int env_level; + unsigned char modes; + unsigned char hold; + unsigned char active; + struct _note *replay; + struct _note *next; + unsigned int left_mix_volume; + unsigned int right_mix_volume; + unsigned char is_off; +}; + +struct _event_data { + unsigned char channel; + unsigned int data; +}; + +struct _mdi { + _mdi() + { + samples_to_mix = 0; + midi_master_vol = 0; + memset(&info, 0, sizeof(info)); + tmp_info = NULL; + memset(&channel, 0, sizeof(channel)); + note = NULL; + memset(note_table, 0, sizeof(note_table)); + patches = NULL; + patch_count = 0; + amp = 0; + mix_buffer = NULL; + mix_buffer_size = 0; + reverb = NULL; + } + + FCriticalSection lock; + unsigned long int samples_to_mix; + + unsigned short midi_master_vol; + struct _WM_Info info; + struct _WM_Info *tmp_info; + struct _channel channel[16]; + struct _note *note; + struct _note note_table[2][16][128]; + + struct _patch **patches; + unsigned long int patch_count; + signed short int amp; + + signed int *mix_buffer; + unsigned long int mix_buffer_size; + + struct _rvb *reverb; +}; + +#define FPBITS 10 +#define FPMASK ((1L<> 1); + int j; + int sign; + double ck; + double x, x_inc, xz; + double z[35]; + double *gptr, *t; + + gauss_lock.Enter(); + if (gauss_table) { + gauss_lock.Leave(); + return; + } + + newt_coeffs[0][0] = 1; + for (i = 0; i <= n; i++) { + newt_coeffs[i][0] = 1; + newt_coeffs[i][i] = 1; + + if (i > 1) { + newt_coeffs[i][0] = newt_coeffs[i - 1][0] / i; + newt_coeffs[i][i] = newt_coeffs[i - 1][0] / i; + } + + for (j = 1; j < i; j++) { + newt_coeffs[i][j] = newt_coeffs[i - 1][j - 1] + + newt_coeffs[i - 1][j]; + if (i > 1) + newt_coeffs[i][j] /= i; + } + z[i] = i / (4 * M_PI); + } + + for (i = 0; i <= n; i++) + for (j = 0, sign = (int)pow(-1., i); j <= i; j++, sign *= -1) + newt_coeffs[i][j] *= sign; + + t = (double*)malloc((1<first_sample) { + tmp_sample = patch[i]->first_sample->next; + free(patch[i]->first_sample->data); + free(patch[i]->first_sample); + patch[i]->first_sample = tmp_sample; + } + free(patch[i]->filename); + tmp_patch = patch[i]->next; + free(patch[i]); + patch[i] = tmp_patch; + } + } + patch_lock.Leave(); +} + +/* wm_strdup -- adds extra space for appending up to 4 chars */ +static char *wm_strdup (const char *str) { + size_t l = strlen(str) + 5; + char *d = (char *) malloc(l * sizeof(char)); + if (d) { + strcpy(d, str); + return d; + } + return NULL; +} + +static inline int wm_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +#define TOKEN_CNT_INC 8 +static char** WM_LC_Tokenize_Line(char *line_data) +{ + int line_length = (int)strlen(line_data); + int token_data_length = 0; + int line_ofs = 0; + int token_start = 0; + char **token_data = NULL; + int token_count = 0; + bool in_quotes = false; + + if (line_length == 0) + return NULL; + + do { + /* ignore everything after # */ + if (line_data[line_ofs] == '#') { + break; + } + if (line_data[line_ofs] == '"') + { + in_quotes = !in_quotes; + } + else if (!in_quotes && ((line_data[line_ofs] == ' ') || (line_data[line_ofs] == '\t'))) { + /* whitespace means we aren't in a token */ + if (token_start) { + token_start = 0; + line_data[line_ofs] = '\0'; + } + } else { + if (!token_start) { + /* the start of a token in the line */ + token_start = 1; + if (token_count >= token_data_length) { + token_data_length += TOKEN_CNT_INC; + token_data = (char**)realloc(token_data, token_data_length * sizeof(char *)); + if (token_data == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,"to parse config", errno); + return NULL; + } + } + + token_data[token_count] = &line_data[line_ofs]; + token_count++; + } + } + line_ofs++; + } while (line_ofs != line_length); + + /* if we have found some tokens then add a null token to the end */ + if (token_count) { + if (token_count >= token_data_length) { + token_data = (char**)realloc(token_data, + ((token_count + 1) * sizeof(char *))); + } + token_data[token_count] = NULL; + } + + return token_data; +} + +static int WM_LoadConfig(const char *config_file) { + unsigned long int config_size = 0; + char *config_buffer = NULL; + const char *dir_end = NULL; + char *config_dir = NULL; + unsigned long int config_ptr = 0; + unsigned long int line_start_ptr = 0; + unsigned short int patchid = 0; + struct _patch * tmp_patch; + char **line_tokens = NULL; + int token_count = 0; + + config_buffer = (char *) _WM_BufferFile(config_file, &config_size, true); + if (!config_buffer) { + WM_FreePatches(); + return -1; + } + + + // This part was rewritten because the original depended on a header that was GPL'd. + dir_end = strrchr(config_file, '/'); +#ifdef _WIN32 + const char *dir_end2 = strrchr(config_file, '\\'); + if (dir_end2 > dir_end) dir_end = dir_end2; +#endif + + if (dir_end) { + config_dir = (char*)malloc((dir_end - config_file + 2)); + if (config_dir == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", + errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free(config_buffer); + return -1; + } + strncpy(config_dir, config_file, (dir_end - config_file + 1)); + config_dir[dir_end - config_file + 1] = '\0'; + } + + config_ptr = 0; + line_start_ptr = 0; + + /* handle files without a newline at the end: this relies on + * _WM_BufferFile() allocating the buffer with one extra byte */ + config_buffer[config_size] = '\n'; + + while (config_ptr <= config_size) { + if (config_buffer[config_ptr] == '\r' || + config_buffer[config_ptr] == '\n') + { + config_buffer[config_ptr] = '\0'; + + if (config_ptr != line_start_ptr) { + line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]); + if (line_tokens) { + if (stricmp(line_tokens[0], "dir") == 0) { + free(config_dir); + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in dir line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!(config_dir = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) { + config_dir[strlen(config_dir) + 1] = '\0'; + config_dir[strlen(config_dir)] = '/'; + } + } else if (stricmp(line_tokens[0], "source") == 0) { + char *new_config = NULL; + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in source line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + new_config = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 1); + if (new_config == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(new_config, config_dir); + strcpy(&new_config[strlen(config_dir)], line_tokens[1]); + } else { + if (!(new_config = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + "to parse config", errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (WM_LoadConfig(new_config) == -1) { + free(new_config); + free(line_tokens); + free(config_buffer); + free(config_dir); + return -1; + } + free(new_config); + } else if (stricmp(line_tokens[0], "bank") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in bank line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = (atoi(line_tokens[1]) & 0xFF) << 8; + } else if (stricmp(line_tokens[0], "drumset") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in drumset line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80; + } else if (stricmp(line_tokens[0], "reverb_room_width") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_width line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_width = (float) atof(line_tokens[1]); + if (reverb_room_width < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_width = 1.0f; + } else if (reverb_room_width > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_width > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_width = 100.0f; + } + } else if (stricmp(line_tokens[0], "reverb_room_length") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_room_length line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_room_length = (float) atof(line_tokens[1]); + if (reverb_room_length < 1.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length < 1 meter, setting to minimum of 1 meter)", + 0); + reverb_room_length = 1.0f; + } else if (reverb_room_length > 100.0f) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_room_length > 100 meters, setting to maximum of 100 meters)", + 0); + reverb_room_length = 100.0f; + } + } else if (stricmp(line_tokens[0], "reverb_listener_posx") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posx line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posx = (float) atof(line_tokens[1]); + if ((reverb_listen_posx > reverb_room_width) + || (reverb_listen_posx < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posx set outside of room)", + 0); + reverb_listen_posx = reverb_room_width / 2.0f; + } + } else if (stricmp(line_tokens[0], + "reverb_listener_posy") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(syntax error in reverb_listen_posy line)", + 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + reverb_listen_posy = (float) atof(line_tokens[1]); + if ((reverb_listen_posy > reverb_room_width) + || (reverb_listen_posy < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(reverb_listen_posy set outside of room)", + 0); + reverb_listen_posy = reverb_room_length * 0.75f; + } + } else if (stricmp(line_tokens[0], + "guspat_editor_author_cant_read_so_fix_release_time_for_me") + == 0) { + fix_release = 1; + } else if (stricmp(line_tokens[0], "auto_amp") == 0) { + auto_amp = 1; + } else if (stricmp(line_tokens[0], "auto_amp_with_amp") + == 0) { + auto_amp = 1; + auto_amp_with_amp = 1; + } else if (wm_isdigit(line_tokens[0][0])) { + patchid = (patchid & 0xFF80) + | (atoi(line_tokens[0]) & 0x7F); + if (patch[(patchid & 0x7F)] == NULL) { + patch[(patchid & 0x7F)] = (struct _patch*)malloc( + sizeof(struct _patch)); + if (patch[(patchid & 0x7F)] == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = patch[(patchid & 0x7F)]; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = patch[(patchid & 0x7F)]; + if (tmp_patch->patchid == patchid) { + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } else { + if (tmp_patch->next) { + while (tmp_patch->next) { + if (tmp_patch->next->patchid == patchid) + break; + tmp_patch = tmp_patch->next; + } + if (tmp_patch->next == NULL) { + if ((tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch))) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, + 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } else { + tmp_patch = tmp_patch->next; + free(tmp_patch->filename); + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + } + } else { + tmp_patch->next = (struct _patch*)malloc( + sizeof(struct _patch)); + if (tmp_patch->next == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_MEM, NULL, errno); + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_LOAD, config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + tmp_patch = tmp_patch->next; + tmp_patch->patchid = patchid; + tmp_patch->filename = NULL; + tmp_patch->amp = 1024; + tmp_patch->note = 0; + tmp_patch->next = NULL; + tmp_patch->first_sample = NULL; + tmp_patch->loaded = 0; + tmp_patch->inuse_count = 0; + } + } + } + if (!line_tokens[1]) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in patch line)", 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + tmp_patch->filename = (char*)malloc( + strlen(config_dir) + strlen(line_tokens[1]) + + 5); + if (tmp_patch->filename == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + strcpy(tmp_patch->filename, config_dir); + strcat(tmp_patch->filename, line_tokens[1]); + } else { + if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, + NULL, 0); + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return -1; + } + } + if (strnicmp( + &tmp_patch->filename[strlen(tmp_patch->filename) + - 4], ".pat", 4) != 0) { + strcat(tmp_patch->filename, ".pat"); + } + tmp_patch->env[0].set = 0x00; + tmp_patch->env[1].set = 0x00; + tmp_patch->env[2].set = 0x00; + tmp_patch->env[3].set = 0x00; + tmp_patch->env[4].set = 0x00; + tmp_patch->env[5].set = 0x00; + tmp_patch->keep = 0; + tmp_patch->remove = 0; + + token_count = 0; + while (line_tokens[token_count]) { + if (strnicmp(line_tokens[token_count], "amp=", 4) + == 0) { + if (!wm_isdigit(line_tokens[token_count][4])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->amp = (atoi( + &line_tokens[token_count][4]) << 10) + / 100; + } + } else if (strnicmp(line_tokens[token_count], + "note=", 5) == 0) { + if (!wm_isdigit(line_tokens[token_count][5])) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + tmp_patch->note = atoi( + &line_tokens[token_count][5]); + } + } else if (strnicmp(line_tokens[token_count], + "env_time", 8) == 0) { + if ((!wm_isdigit(line_tokens[token_count][8])) + || (!wm_isdigit( + line_tokens[token_count][10])) + || (line_tokens[token_count][9] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][8]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].time = + (float) atof( + &line_tokens[token_count][10]); + if ((tmp_patch->env[env_no].time + > 45000.0f) + || (tmp_patch->env[env_no].time + < 1.47f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFE; + } else { + tmp_patch->env[env_no].set |= 0x01; + } + } + } + } else if (strnicmp(line_tokens[token_count], + "env_level", 9) == 0) { + if ((!wm_isdigit(line_tokens[token_count][9])) + || (!wm_isdigit( + line_tokens[token_count][11])) + || (line_tokens[token_count][10] != '=')) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", 0); + } else { + unsigned int env_no = atoi( + &line_tokens[token_count][9]); + if (env_no > 5) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(syntax error in patch line)", + 0); + } else { + tmp_patch->env[env_no].level = + (float) atof( + &line_tokens[token_count][11]); + if ((tmp_patch->env[env_no].level > 1.0f) + || (tmp_patch->env[env_no].level + < 0.0f)) { + _WM_ERROR(__FUNCTION__, __LINE__, + WM_ERR_INVALID_ARG, + "(range error in patch line)", + 0); + tmp_patch->env[env_no].set &= 0xFD; + } else { + tmp_patch->env[env_no].set |= 0x02; + } + } + } + } else if (stricmp(line_tokens[token_count], + "keep=loop") == 0) { + tmp_patch->keep |= SAMPLE_LOOP; + } else if (stricmp(line_tokens[token_count], + "keep=env") == 0) { + tmp_patch->keep |= SAMPLE_ENVELOPE; + } else if (stricmp(line_tokens[token_count], + "remove=sustain") == 0) { + tmp_patch->remove |= SAMPLE_SUSTAIN; + } else if (stricmp(line_tokens[token_count], + "remove=clamped") == 0) { + tmp_patch->remove |= SAMPLE_CLAMPED; + } + token_count++; + } + } + } + /* free up tokens */ + free(line_tokens); + } + line_start_ptr = config_ptr + 1; + } + config_ptr++; + } + + free(config_buffer); + free(config_dir); + + return 0; +} + +/* sample loading */ + +static int load_sample(struct _patch *sample_patch) { + struct _sample *guspat = NULL; + struct _sample *tmp_sample = NULL; + unsigned int i = 0; + + /* we only want to try loading the guspat once. */ + sample_patch->loaded = 1; + + if ((guspat = _WM_load_gus_pat(sample_patch->filename, fix_release)) == NULL) { + return -1; + } + + if (auto_amp) { + signed short int tmp_max = 0; + signed short int tmp_min = 0; + signed short samp_max = 0; + signed short samp_min = 0; + tmp_sample = guspat; + do { + samp_max = 0; + samp_min = 0; + for (i = 0; i < (tmp_sample->data_length >> 10); i++) { + if (tmp_sample->data[i] > samp_max) + samp_max = tmp_sample->data[i]; + if (tmp_sample->data[i] < samp_min) + samp_min = tmp_sample->data[i]; + } + if (samp_max > tmp_max) + tmp_max = samp_max; + if (samp_min < tmp_min) + tmp_min = samp_min; + tmp_sample = tmp_sample->next; + } while (tmp_sample); + if (auto_amp_with_amp) { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (sample_patch->amp + * ((32767 << 10) / tmp_max)) >> 10; + } else { + sample_patch->amp = (sample_patch->amp + * ((32768 << 10) / -tmp_min)) >> 10; + } + } else { + if (tmp_max >= -tmp_min) { + sample_patch->amp = (32767 << 10) / tmp_max; + } else { + sample_patch->amp = (32768 << 10) / -tmp_min; + } + } + } + + sample_patch->first_sample = guspat; + + if (sample_patch->patchid & 0x0080) { + if (!(sample_patch->keep & SAMPLE_LOOP)) { + do { + guspat->modes &= 0xFB; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + if (!(sample_patch->keep & SAMPLE_ENVELOPE)) { + do { + guspat->modes &= 0xBF; + guspat = guspat->next; + } while (guspat); + } + guspat = sample_patch->first_sample; + } + + if (sample_patch->patchid == 47) { + do { + if (!(guspat->modes & SAMPLE_LOOP)) { + for (i = 3; i < 6; i++) { + guspat->env_target[i] = guspat->env_target[2]; + guspat->env_rate[i] = guspat->env_rate[2]; + } + } + guspat = guspat->next; + } while (guspat); + guspat = sample_patch->first_sample; + } + + do { + if ((sample_patch->remove & SAMPLE_SUSTAIN) + && (guspat->modes & SAMPLE_SUSTAIN)) { + guspat->modes ^= SAMPLE_SUSTAIN; + } + if ((sample_patch->remove & SAMPLE_CLAMPED) + && (guspat->modes & SAMPLE_CLAMPED)) { + guspat->modes ^= SAMPLE_CLAMPED; + } + if (sample_patch->keep & SAMPLE_ENVELOPE) { + guspat->modes |= SAMPLE_ENVELOPE; + } + + for (i = 0; i < 6; i++) { + if (guspat->modes & SAMPLE_ENVELOPE) { + if (sample_patch->env[i].set & 0x02) { + guspat->env_target[i] = 16448 + * (signed long int) (255.0 + * sample_patch->env[i].level); + } + + if (sample_patch->env[i].set & 0x01) { + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate + * (sample_patch->env[i].time / 1000.0))); + } + } else { + guspat->env_target[i] = 4194303; + guspat->env_rate[i] = (signed long int) (4194303.0 + / ((float) _WM_SampleRate * env_time_table[63])); + } + } + + guspat = guspat->next; + } while (guspat); + return 0; +} + +static struct _patch * +get_patch_data(unsigned short patchid) { + struct _patch *search_patch; + + patch_lock.Enter(); + + search_patch = patch[patchid & 0x007F]; + + if (search_patch == NULL) { + patch_lock.Leave(); + return NULL; + } + + while (search_patch) { + if (search_patch->patchid == patchid) { + patch_lock.Leave(); + return search_patch; + } + search_patch = search_patch->next; + } + if ((patchid >> 8) != 0) { + patch_lock.Leave(); + return (get_patch_data(patchid & 0x00FF)); + } + patch_lock.Leave(); + return NULL; +} + +static void load_patch(struct _mdi *mdi, unsigned short patchid) { + unsigned int i; + struct _patch *tmp_patch = NULL; + + for (i = 0; i < mdi->patch_count; i++) { + if (mdi->patches[i]->patchid == patchid) { + return; + } + } + + tmp_patch = get_patch_data(patchid); + if (tmp_patch == NULL) { + return; + } + + patch_lock.Enter(); + if (!tmp_patch->loaded) { + if (load_sample(tmp_patch) == -1) { + patch_lock.Leave(); + return; + } + } + + if (tmp_patch->first_sample == NULL) { + patch_lock.Leave(); + return; + } + + mdi->patch_count++; + mdi->patches = (struct _patch**)realloc(mdi->patches, + (sizeof(struct _patch*) * mdi->patch_count)); + mdi->patches[mdi->patch_count - 1] = tmp_patch; + tmp_patch->inuse_count++; + patch_lock.Leave(); +} + +static struct _sample * +get_sample_data(struct _patch *sample_patch, unsigned long int freq) { + struct _sample *last_sample = NULL; + struct _sample *return_sample = NULL; + + patch_lock.Enter(); + if (sample_patch == NULL) { + patch_lock.Leave(); + return NULL; + } + if (sample_patch->first_sample == NULL) { + patch_lock.Leave(); + return NULL; + } + if (freq == 0) { + patch_lock.Leave(); + return sample_patch->first_sample; + } + + return_sample = sample_patch->first_sample; + last_sample = sample_patch->first_sample; + while (last_sample) { + if (freq > last_sample->freq_low) { + if (freq < last_sample->freq_high) { + patch_lock.Leave(); + return last_sample; + } else { + return_sample = last_sample; + } + } + last_sample = last_sample->next; + } + patch_lock.Leave(); + return return_sample; +} + +/* Should be called in any function that effects note volumes */ +void _WM_AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte) { + double premix_dBm; + double premix_lin; + int pan_ofs; + double premix_dBm_left; + double premix_dBm_right; + double premix_left; + double premix_right; + double volume_adj; + unsigned vol_ofs; + + /* + Pointless CPU heating checks to shoosh up a compiler + */ + if (ch > 0x0f) ch = 0x0f; + + pan_ofs = mdi->channel[ch].balance + mdi->channel[ch].pan - 64; + + vol_ofs = (nte->velocity * ((mdi->channel[ch].expression * mdi->channel[ch].volume) / 127)) / 127; + + /* + This value is to reduce the chance of clipping. + Higher value means lower overall volume, + Lower value means higher overall volume. + NOTE: The lower the value the higher the chance of clipping. + FIXME: Still needs tuning. Clipping heard at a value of 3.75 + */ +#define VOL_DIVISOR 4.0 + volume_adj = ((float)WM_MasterVolume / 1024.0) / VOL_DIVISOR; + + // Pan 0 and 1 are both hard left so 64 can be centered + if (pan_ofs > 127) pan_ofs = 127; + if (--pan_ofs < 0) pan_ofs = 0; + premix_dBm_left = dBm_pan_volume[126-pan_ofs]; + premix_dBm_right = dBm_pan_volume[pan_ofs]; + + if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) { + premix_dBm = dBm_volume[vol_ofs]; + + premix_dBm_left += premix_dBm; + premix_dBm_right += premix_dBm; + + premix_left = (pow(10.0,(premix_dBm_left / 20.0))) * volume_adj; + premix_right = (pow(10.0,(premix_dBm_right / 20.0))) * volume_adj; + } else { + premix_lin = (float)(lin_volume[vol_ofs]) / 1024.0; + + premix_left = premix_lin * pow(10.0, (premix_dBm_left / 20)) * volume_adj; + premix_right = premix_lin * pow(10.0, (premix_dBm_right / 20)) * volume_adj; + } + nte->left_mix_volume = (int)(premix_left * 1024.0); + nte->right_mix_volume = (int)(premix_right * 1024.0); +} + +/* Should be called in any function that effects channel volumes */ +/* Calling this function with a value > 15 will make it adjust notes on all channels */ +void _WM_AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch) { + struct _note *nte = mdi->note; + if (nte != NULL) { + do { + if (ch <= 15) { + if ((nte->noteid >> 8) == ch) { + goto _DO_ADJUST; + } + } else { + _DO_ADJUST: + _WM_AdjustNoteVolumes(mdi, ch, nte); + if (nte->replay) _WM_AdjustNoteVolumes(mdi, ch, nte->replay); + } + nte = nte->next; + } while (nte != NULL); + } +} + +static void do_note_off_extra(struct _note *nte) { + + nte->is_off = 0; + + + if (!(nte->modes & SAMPLE_ENVELOPE)) { + if (nte->modes & SAMPLE_LOOP) { + nte->modes ^= SAMPLE_LOOP; + } + nte->env_inc = 0; + + } else if (nte->hold) { + nte->hold |= HOLD_OFF; + + } else if (nte->modes & SAMPLE_SUSTAIN) { + if (nte->env < 3) { + nte->env = 3; + if (nte->env_level > nte->sample->env_target[3]) { + nte->env_inc = -nte->sample->env_rate[3]; + } else { + nte->env_inc = nte->sample->env_rate[3]; + } + } + + } else if (nte->modes & SAMPLE_CLAMPED) { + if (nte->env < 5) { + nte->env = 5; + if (nte->env_level > nte->sample->env_target[5]) { + nte->env_inc = -nte->sample->env_rate[5]; + } else { + nte->env_inc = nte->sample->env_rate[5]; + } + } + } else if (nte->env < 4) { + nte->env = 4; + if (nte->env_level > nte->sample->env_target[4]) { + nte->env_inc = -nte->sample->env_rate[4]; + } else { + nte->env_inc = nte->sample->env_rate[4]; + } + } +} + +static void do_note_off(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + + if ((mdi->channel[ch].isdrum) && (!(nte->modes & SAMPLE_LOOP))) { + return; + } + + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env == 0)) { + // This is a fix for notes that end before the + // initial step of the envelope has completed + // making it impossible to hear them at times. + nte->is_off = 1; + } else { + do_note_off_extra(nte); + } +} + +static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) { + int ch = nte->noteid >> 8; + signed long int note_f; + unsigned long int freq; + + if (nte->patch->note != 0) { + note_f = nte->patch->note * 100; + } else { + note_f = (nte->noteid & 0x7f) * 100; + } + note_f += mdi->channel[ch].pitch_adjust; + if (note_f < 0) { + note_f = 0; + } else if (note_f > 12700) { + note_f = 12700; + } + freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200)); + return (((freq / ((_WM_SampleRate * 100) / 1024)) * 1024 + / nte->sample->inc_div)); +} + +static void do_note_on(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + struct _note *prev_nte; + struct _note *nte_array; + unsigned long int freq = 0; + struct _patch *patch; + struct _sample *sample; + unsigned char ch = data->channel; + unsigned char note = (unsigned char)(data->data >> 8); + unsigned char velocity = (unsigned char)(data->data & 0xFF); + + if (velocity == 0x00) { + do_note_off(mdi, data); + return; + } + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (!mdi->channel[ch].isdrum) { + patch = mdi->channel[ch].patch; + if (patch == NULL) { + return; + } + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } else { + patch = get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80)); + if (patch == NULL) { + return; + } + if (patch->note) { + freq = freq_table[(patch->note % 12) * 100] + >> (10 - (patch->note / 12)); + } else { + freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); + } + } + + sample = get_sample_data(patch, (freq / 100)); + if (sample == NULL) { + return; + } + + nte = &mdi->note_table[0][ch][note]; + + if (nte->active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + nte->replay = &mdi->note_table[1][ch][note]; + nte->env = 6; + nte->env_inc = -nte->sample->env_rate[6]; + nte = nte->replay; + } else { + if (mdi->note_table[1][ch][note].active) { + if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) + && (!(nte->hold & HOLD_OFF))) + return; + mdi->note_table[1][ch][note].replay = nte; + mdi->note_table[1][ch][note].env = 6; + mdi->note_table[1][ch][note].env_inc = + -mdi->note_table[1][ch][note].sample->env_rate[6]; + } else { + nte_array = mdi->note; + if (nte_array == NULL) { + mdi->note = nte; + } else { + do { + prev_nte = nte_array; + nte_array = nte_array->next; + } while (nte_array); + prev_nte->next = nte; + } + nte->active = 1; + nte->next = NULL; + } + } + nte->noteid = (ch << 8) | note; + nte->patch = patch; + nte->sample = sample; + nte->sample_pos = 0; + nte->sample_inc = get_inc(mdi, nte); + nte->velocity = velocity; + nte->env = 0; + nte->env_inc = nte->sample->env_rate[0]; + nte->env_level = 0; + nte->modes = sample->modes; + nte->hold = mdi->channel[ch].hold; + nte->replay = NULL; + nte->is_off = 0; + _WM_AdjustNoteVolumes(mdi, ch, nte); +} + +static void do_aftertouch(struct _mdi *mdi, struct _event_data *data) { + struct _note *nte; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + nte = &mdi->note_table[0][ch][(data->data >> 8)]; + if (!nte->active) { + nte = &mdi->note_table[1][ch][(data->data >> 8)]; + if (!nte->active) { + return; + } + } + + nte->velocity = (unsigned char)data->data; + _WM_AdjustNoteVolumes(mdi, ch, nte); + if (nte->replay) { + nte->replay->velocity = (unsigned char)data->data; + _WM_AdjustNoteVolumes(mdi, ch, nte->replay); + } +} + +static void do_control_bank_select(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].bank = (unsigned char)data->data; +} + +static void do_control_data_entry_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range % 100; + mdi->channel[ch].pitch_range = short(data->data * 100 + data_tmp); + /* printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Course: data %li\n\r",data->data);*/ + } +} + +static void do_control_channel_volume(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].volume = (unsigned char)data->data; + _WM_AdjustChannelVolumes(mdi, ch); +} + +static void do_control_channel_balance(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + mdi->channel[ch].balance = (signed char)(data->data); + _WM_AdjustChannelVolumes(mdi, ch); +} + +static void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + + mdi->channel[ch].pan = (signed char)(data->data); + _WM_AdjustChannelVolumes(mdi, ch); +} + +static void do_control_channel_expression(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].expression = (unsigned char)data->data; + _WM_AdjustChannelVolumes(mdi, ch); +} + +static void do_control_data_entry_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + int data_tmp; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + data_tmp = mdi->channel[ch].pitch_range / 100; + mdi->channel[ch].pitch_range = short((data_tmp * 100) + data->data); + /* printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Fine: data: %li\n\r", data->data);*/ + } + +} + +static void do_control_channel_hold(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (data->data > 63) { + mdi->channel[ch].hold = 1; + } else { + mdi->channel[ch].hold = 0; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (note_data->hold & HOLD_OFF) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->modes & SAMPLE_CLAMPED) { + if (note_data->env < 5) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + } + } else if (note_data->env < 4) { + note_data->env = 4; + if (note_data->env_level + > note_data->sample->env_target[4]) { + note_data->env_inc = + -note_data->sample->env_rate[4]; + } else { + note_data->env_inc = + note_data->sample->env_rate[4]; + } + } + } else { + if (note_data->modes & SAMPLE_LOOP) { + note_data->modes ^= SAMPLE_LOOP; + } + note_data->env_inc = 0; + } + } + note_data->hold = 0x00; + } + note_data = note_data->next; + } while (note_data); + } + } +} + +static void do_control_data_increment(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range < 0x3FFF) + mdi->channel[ch].pitch_range++; + } +} + +static void do_control_data_decrement(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + if ((mdi->channel[ch].reg_non == 0) + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ + if (mdi->channel[ch].pitch_range > 0) + mdi->channel[ch].pitch_range--; + } +} +static void do_control_non_registered_param_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0x3F80) + | data->data; + mdi->channel[ch].reg_non = 1; +} + +static void do_control_non_registered_param_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0x7F) + | (data->data << 7); + mdi->channel[ch].reg_non = 1; +} + +static void do_control_registered_param_fine(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x3F80) + | data->data); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_registered_param_course(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + mdi->channel[ch].reg_data = (unsigned short) ((mdi->channel[ch].reg_data & 0x7F) + | (data->data << 7)); + mdi->channel[ch].reg_non = 0; +} + +static void do_control_channel_sound_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->active = 0; + if (note_data->replay) { + note_data->replay = NULL; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_control_channel_controllers_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + mdi->channel[ch].expression = 127; + mdi->channel[ch].pressure = 127; + mdi->channel[ch].reg_data = 0xffff; + mdi->channel[ch].pitch_range = 200; + mdi->channel[ch].pitch = 0; + mdi->channel[ch].pitch_adjust = 0; + mdi->channel[ch].hold = 0; + + _WM_AdjustChannelVolumes(mdi, ch); +} + +static void do_control_channel_notes_off(struct _mdi *mdi, + struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + if (mdi->channel[ch].isdrum) + return; + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + if (!note_data->hold) { + if (note_data->modes & SAMPLE_ENVELOPE) { + if (note_data->env < 5) { + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + note_data->env = 5; + } + } + } else { + note_data->hold |= HOLD_OFF; + } + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_patch(struct _mdi *mdi, struct _event_data *data) { + unsigned char ch = data->channel; + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + if (!mdi->channel[ch].isdrum) { + mdi->channel[ch].patch = get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data))); + } else { + mdi->channel[ch].bank = (unsigned char)data->data; + } +} + +static void do_channel_pressure(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + while (note_data) { + if ((note_data->noteid >> 8) == ch) { + note_data->velocity = (unsigned char)data->data; + _WM_AdjustNoteVolumes(mdi, ch, note_data); + if (note_data->replay) { + note_data->replay->velocity = (unsigned char)data->data; + _WM_AdjustNoteVolumes(mdi, ch, note_data->replay); + } + } + note_data = note_data->next; + } +} + +static void do_pitch(struct _mdi *mdi, struct _event_data *data) { + struct _note *note_data = mdi->note; + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + mdi->channel[ch].pitch = short(data->data - 0x2000); + + if (mdi->channel[ch].pitch < 0) { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8192; + } else { + mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range + * mdi->channel[ch].pitch / 8191; + } + + if (note_data) { + do { + if ((note_data->noteid >> 8) == ch) { + note_data->sample_inc = get_inc(mdi, note_data); + } + note_data = note_data->next; + } while (note_data); + } +} + +static void do_sysex_roland_drum_track(struct _mdi *mdi, + struct _event_data *data) { + unsigned char ch = data->channel; + + MIDI_EVENT_DEBUG(__FUNCTION__,ch); + + if (data->data > 0) { + mdi->channel[ch].isdrum = 1; + mdi->channel[ch].patch = NULL; + } else { + mdi->channel[ch].isdrum = 0; + mdi->channel[ch].patch = get_patch_data(0); + } +} + +static void do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data) { + int i; + for (i = 0; i < 16; i++) { + mdi->channel[i].bank = 0; + if (i != 9) { + mdi->channel[i].patch = get_patch_data(0); + } else { + mdi->channel[i].patch = NULL; + } + mdi->channel[i].hold = 0; + mdi->channel[i].volume = 100; + mdi->channel[i].pressure = 127; + mdi->channel[i].expression = 127; + mdi->channel[i].balance = 64; + mdi->channel[i].pan = 64; + mdi->channel[i].pitch = 0; + mdi->channel[i].pitch_range = 200; + mdi->channel[i].reg_data = 0xFFFF; + mdi->channel[i].isdrum = 0; + } + /* I would not expect notes to be active when this event + triggers but we'll adjust active notes as well just in case */ + _WM_AdjustChannelVolumes(mdi,16); // A setting > 15 adjusts all channels + + mdi->channel[9].isdrum = 1; + UNUSED(data); /* NOOP, to please the compiler gods */ +} + +static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) { + do_sysex_gm_reset(mdi, data); +} + +static void do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data) { + do_sysex_gm_reset(mdi, data); +} + +static int add_handle(void * handle) { + struct _hndl *tmp_handle = NULL; + + if (first_handle == NULL) { + first_handle = (struct _hndl*)malloc(sizeof(struct _hndl)); + if (first_handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); + return -1; + } + first_handle->handle = handle; + first_handle->prev = NULL; + first_handle->next = NULL; + } else { + tmp_handle = first_handle; + if (tmp_handle->next) { + while (tmp_handle->next) + tmp_handle = tmp_handle->next; + } + tmp_handle->next = (struct _hndl*)malloc(sizeof(struct _hndl)); + if (tmp_handle->next == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); + return -1; + } + tmp_handle->next->prev = tmp_handle; + tmp_handle = tmp_handle->next; + tmp_handle->next = NULL; + tmp_handle->handle = handle; + } + return 0; +} + +static struct _mdi * +Init_MDI(void) { + struct _mdi *mdi; + + mdi = new _mdi; + + mdi->info.copyright = NULL; + mdi->info.mixer_options = WM_MixerOptions; + + load_patch(mdi, 0x0000); + + mdi->samples_to_mix = 0; + mdi->info.current_sample = 0; + mdi->info.total_midi_time = 0; + mdi->info.approx_total_samples = 0; + + do_sysex_roland_reset(mdi, NULL); + + return mdi; +} + +static void freeMDI(struct _mdi *mdi) { + struct _sample *tmp_sample; + unsigned long int i; + + if (mdi->patch_count != 0) { + patch_lock.Enter(); + for (i = 0; i < mdi->patch_count; i++) { + mdi->patches[i]->inuse_count--; + if (mdi->patches[i]->inuse_count == 0) { + /* free samples here */ + while (mdi->patches[i]->first_sample) { + tmp_sample = mdi->patches[i]->first_sample->next; + free(mdi->patches[i]->first_sample->data); + free(mdi->patches[i]->first_sample); + mdi->patches[i]->first_sample = tmp_sample; + } + mdi->patches[i]->loaded = 0; + } + } + patch_lock.Leave(); + free(mdi->patches); + } + + free(mdi->tmp_info); + _WM_free_reverb(mdi->reverb); + free(mdi->mix_buffer); + free(mdi); +} + +static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count) +{ + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + struct _note *note_data = NULL; + + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (note_data != NULL) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + premix = ((note_data->sample->data[data_pos] + (((note_data->sample->data[data_pos + 1] - note_data->sample->data[data_pos]) * (int)(note_data->sample_pos & FPMASK)) / 1024)) * (note_data->env_level >> 12)) / 1024; + + + left_mix += (premix * (int)note_data->left_mix_volume) / 1024; + right_mix += (premix * (int)note_data->right_mix_volume) / 1024; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (note_data->modes & SAMPLE_LOOP) { + if (note_data->sample_pos > note_data->sample->loop_end) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } + } else if (note_data->sample_pos >= note_data->sample->data_length) { + goto END_THIS_NOTE; + } + + if (note_data->env_inc == 0) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (note_data->env_inc < 0) { + if (note_data->env_level > note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } else if (note_data->env_inc > 0) { + if (note_data->env_level < note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } + + // Yes could have a condition here but + // it would create another bottleneck + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN /*|| note_data->hold*/) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (note_data->env_level == 0) { + goto END_THIS_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + END_THIS_NOTE: + if (note_data->replay != NULL) { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + } + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + +static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count) +{ + if (!gauss_table) init_gauss(); + + struct _mdi *mdi = (struct _mdi *)handle; + unsigned long int data_pos; + signed int premix, left_mix, right_mix; + struct _note *note_data = NULL; + signed short int *sptr; + double y, xd; + double *gptr, *gend; + int left, right, temp_n; + int ii, jj; + + do { + note_data = mdi->note; + left_mix = right_mix = 0; + if (note_data != NULL) { + while (note_data) { + /* + * =================== + * resample the sample + * =================== + */ + data_pos = note_data->sample_pos >> FPBITS; + + /* check to see if we're near one of the ends */ + left = data_pos; + right = (note_data->sample->data_length >> FPBITS) - left + - 1; + temp_n = (right << 1) - 1; + if (temp_n <= 0) + temp_n = 1; + if (temp_n > (left << 1) + 1) + temp_n = (left << 1) + 1; + + /* use Newton if we can't fill the window */ + if (temp_n < gauss_n) { + xd = note_data->sample_pos & FPMASK; + xd /= (1L << FPBITS); + xd += temp_n >> 1; + y = 0; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (temp_n >> 1); + for (ii = temp_n; ii;) { + for (jj = 0; jj <= ii; jj++) + y += sptr[jj] * newt_coeffs[ii][jj]; + y *= xd - --ii; + } + y += *sptr; + } else { /* otherwise, use Gauss as usual */ + y = 0; + gptr = &gauss_table[(note_data->sample_pos & FPMASK) * + (gauss_n + 1)]; + gend = gptr + gauss_n; + sptr = note_data->sample->data + + (note_data->sample_pos >> FPBITS) + - (gauss_n >> 1); + do { + y += *(sptr++) * *(gptr++); + } while (gptr <= gend); + } + + premix = (int)((y * (note_data->env_level >> 12)) / 1024); + + left_mix += (premix * (int)note_data->left_mix_volume) / 1024; + right_mix += (premix * (int)note_data->right_mix_volume) / 1024; + + /* + * ======================== + * sample position checking + * ======================== + */ + note_data->sample_pos += note_data->sample_inc; + if (note_data->sample_pos > note_data->sample->loop_end) + { + if (note_data->modes & SAMPLE_LOOP) { + note_data->sample_pos = + note_data->sample->loop_start + + ((note_data->sample_pos + - note_data->sample->loop_start) + % note_data->sample->loop_size); + } else if (note_data->sample_pos >= note_data->sample->data_length) { + goto END_THIS_NOTE; + } + } + + if (note_data->env_inc == 0) { + note_data = note_data->next; + continue; + } + + note_data->env_level += note_data->env_inc; + if (note_data->env_inc < 0) { + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } else if (note_data->env_inc > 0) { + if (note_data->env_level + < note_data->sample->env_target[note_data->env]) { + note_data = note_data->next; + continue; + } + } + + // Yes could have a condition here but + // it would create another bottleneck + + note_data->env_level = + note_data->sample->env_target[note_data->env]; + switch (note_data->env) { + case 0: + if (!(note_data->modes & SAMPLE_ENVELOPE)) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } + break; + case 2: + if (note_data->modes & SAMPLE_SUSTAIN) { + note_data->env_inc = 0; + note_data = note_data->next; + continue; + } else if (note_data->modes & SAMPLE_CLAMPED) { + note_data->env = 5; + if (note_data->env_level + > note_data->sample->env_target[5]) { + note_data->env_inc = + -note_data->sample->env_rate[5]; + } else { + note_data->env_inc = + note_data->sample->env_rate[5]; + } + continue; + } + break; + case 5: + if (note_data->env_level == 0) { + goto END_THIS_NOTE; + } + /* sample release */ + if (note_data->modes & SAMPLE_LOOP) + note_data->modes ^= SAMPLE_LOOP; + note_data->env_inc = 0; + note_data = note_data->next; + continue; + case 6: + END_THIS_NOTE: + if (note_data->replay != NULL) { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while (nte_array != note_data); + } + if (prev_note) { + prev_note->next = note_data->replay; + } else { + mdi->note = note_data->replay; + } + note_data->replay->next = note_data->next; + note_data = note_data->replay; + note_data->active = 1; + } + } else { + note_data->active = 0; + { + struct _note *prev_note = NULL; + struct _note *nte_array = mdi->note; + + if (nte_array != note_data) { + do { + prev_note = nte_array; + nte_array = nte_array->next; + } while ((nte_array != note_data) + && (nte_array)); + } + if (prev_note) { + prev_note->next = note_data->next; + } else { + mdi->note = note_data->next; + } + note_data = note_data->next; + } + } + continue; + } + note_data->env++; + + if (note_data->is_off == 1) { + do_note_off_extra(note_data); + } + + if (note_data->env_level + > note_data->sample->env_target[note_data->env]) { + note_data->env_inc = + -note_data->sample->env_rate[note_data->env]; + } else { + note_data->env_inc = + note_data->sample->env_rate[note_data->env]; + } + note_data = note_data->next; + continue; + } + + /* + * ========================= + * mix the channels together + * ========================= + */ + } + *buffer++ = left_mix; + *buffer++ = right_mix; + } while (--count); + return buffer; +} + +int *WM_Mix(midi *handle, int *buffer, unsigned long count) +{ + if (((struct _mdi *)handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) + { + return WM_Mix_Gauss(handle, buffer, count); + } + else + { + return WM_Mix_Linear(handle, buffer, count); + } +} + +/* + * ========================= + * External Functions + * ========================= + */ + +WM_SYMBOL const char * +WildMidi_GetString(unsigned short int info) { + switch (info) { + case WM_GS_VERSION: + return WM_Version; + } + return NULL; +} + +WM_SYMBOL int WildMidi_Init(const char * config_file, unsigned short int rate, + unsigned short int options) { + if (WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0); + return -1; + } + + if (config_file == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(NULL config file pointer)", 0); + return -1; + } + WM_InitPatches(); + if (WM_LoadConfig(config_file) == -1) { + return -1; + } + + if (options & 0x5FF8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", + 0); + WM_FreePatches(); + return -1; + } + WM_MixerOptions = options; + + if (rate < 11025) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(rate out of bounds, range is 11025 - 65535)", 0); + WM_FreePatches(); + return -1; + } + _WM_SampleRate = rate; + WM_Initialized = 1; + + return 0; +} + +WM_SYMBOL int WildMidi_GetSampleRate(void) +{ + return _WM_SampleRate; +} + +WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) { + struct _mdi *mdi = NULL; + struct _hndl * tmp_handle = first_handle; + int i = 0; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (master_volume > 127) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(master volume out of range, range is 0-127)", 0); + return -1; + } + + WM_MasterVolume = lin_volume[master_volume]; + + return 0; +} + +WM_SYMBOL int WildMidi_Close(midi * handle) { + struct _mdi *mdi = (struct _mdi *) handle; + struct _hndl * tmp_handle; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + if (first_handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)", + 0); + return -1; + } + mdi->lock.Enter(); + if (first_handle->handle == handle) { + tmp_handle = first_handle->next; + free(first_handle); + first_handle = tmp_handle; + if (first_handle) + first_handle->prev = NULL; + } else { + tmp_handle = first_handle; + while (tmp_handle->handle != handle) { + tmp_handle = tmp_handle->next; + if (tmp_handle == NULL) { + break; + } + } + if (tmp_handle) { + tmp_handle->prev->next = tmp_handle->next; + if (tmp_handle->next) { + tmp_handle->next->prev = tmp_handle->prev; + } + free(tmp_handle); + } + } + + freeMDI(mdi); + + return 0; +} + +midi *WildMidi_NewMidi() { + midi * ret = NULL; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + ret = Init_MDI(); + if (ret) { + if (add_handle(ret) != 0) { + WildMidi_Close(ret); + ret = NULL; + } + } + + if ((((_mdi*)ret)->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width, + reverb_room_length, reverb_listen_posx, reverb_listen_posy)) + == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); + WildMidi_Close(ret); + ret = NULL; + } + + + return ret; +} + +WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options, + unsigned short int setting) { + struct _mdi *mdi; + + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return -1; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return -1; + } + + mdi = (struct _mdi *) handle; + mdi->lock.Enter(); + if ((!(options & 0x0007)) || (options & 0xFFF8)) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", + 0); + mdi->lock.Leave(); + return -1; + } + if (setting & 0xFFF8) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(invalid setting)", 0); + mdi->lock.Leave(); + return -1; + } + + mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) + | (options & setting)); + + if (options & WM_MO_LOG_VOLUME) { + _WM_AdjustChannelVolumes(mdi, 16); // Settings greater than 15 + // adjust all channels + } else if (options & WM_MO_REVERB) { + _WM_reset_reverb(mdi->reverb); + } + + mdi->lock.Leave(); + return 0; +} + +WM_SYMBOL struct _WM_Info * +WildMidi_GetInfo(midi * handle) { + struct _mdi *mdi = (struct _mdi *) handle; + if (!WM_Initialized) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); + return NULL; + } + if (handle == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", + 0); + return NULL; + } + mdi->lock.Enter(); + if (mdi->tmp_info == NULL) { + mdi->tmp_info = (struct _WM_Info*)malloc(sizeof(struct _WM_Info)); + if (mdi->tmp_info == NULL) { + _WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0); + mdi->lock.Leave(); + return NULL; + } + mdi->tmp_info->copyright = NULL; + } + mdi->tmp_info->current_sample = mdi->info.current_sample; + mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples; + mdi->tmp_info->mixer_options = mdi->info.mixer_options; + if (mdi->info.copyright) { + free(mdi->tmp_info->copyright); + mdi->tmp_info->copyright = (char*)malloc(strlen(mdi->info.copyright) + 1); + strcpy(mdi->tmp_info->copyright, mdi->info.copyright); + } else { + mdi->tmp_info->copyright = NULL; + } + mdi->lock.Leave(); + return mdi->tmp_info; +} + +WM_SYMBOL int WildMidi_Shutdown(void) { + if (!WM_Initialized) { + // No error if trying to shut down an uninitialized device. + return 0; + } + while (first_handle) { + /* closes open handle and rotates the handles list. */ + WildMidi_Close((struct _mdi *) first_handle->handle); + } + WM_FreePatches(); + free_gauss(); + + /* reset the globals */ + WM_MasterVolume = 948; + WM_MixerOptions = 0; + fix_release = 0; + auto_amp = 0; + auto_amp_with_amp = 0; + reverb_room_width = 16.875f; + reverb_room_length = 22.5f; + reverb_listen_posx = 8.4375f; + reverb_listen_posy = 16.875f; + + WM_Initialized = 0; + + return 0; +} + +WildMidi_Renderer::WildMidi_Renderer() +{ + handle = WildMidi_NewMidi(); +} + +WildMidi_Renderer::~WildMidi_Renderer() +{ + WildMidi_Close((midi *)handle); +} + +void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2) +{ + _mdi *mdi = (_mdi *)handle; + _event_data ev; + + ev.channel = status & 0x0F; + switch ((status & 0xF0) >> 4) // command + { + case 0x8: + ev.data = (parm1 << 8) | parm2; + do_note_off(mdi, &ev); + break; + + case 0x9: + ev.data = (parm1 << 8) | parm2; + do_note_on(mdi, &ev); + break; + + case 0xA: + ev.data = (parm1 << 8) | parm2; + do_aftertouch(mdi, &ev); + break; + + case 0xC: + ev.data = parm1; + do_patch(mdi, &ev); + break; + + case 0xD: + ev.data = parm1; + do_channel_pressure(mdi, &ev); + break; + + case 0xE: + ev.data = parm1 | (parm2 << 7); + do_pitch(mdi, &ev); + break; + + case 0xB: // Controllers + ev.data = parm2; + switch (parm1) + { + case 0: do_control_bank_select(mdi, &ev); break; + case 6: do_control_data_entry_course(mdi, &ev); break; // [sic] + case 7: do_control_channel_volume(mdi, &ev); break; + case 8: do_control_channel_balance(mdi, &ev); break; + case 10: do_control_channel_pan(mdi, &ev); break; + case 11: do_control_channel_expression(mdi, &ev); break; + case 38: do_control_data_entry_fine(mdi, &ev); break; + case 64: do_control_channel_hold(mdi, &ev); break; + case 96: do_control_data_increment(mdi, &ev); break; + case 97: do_control_data_decrement(mdi, &ev); break; + case 98: do_control_non_registered_param_fine(mdi, &ev); break; + case 99: do_control_non_registered_param_course(mdi, &ev); break; // [sic] + case 100: do_control_registered_param_fine(mdi, &ev); break; + case 101: do_control_registered_param_course(mdi, &ev); break; // [sic] + case 120: do_control_channel_sound_off(mdi, &ev); break; + case 121: do_control_channel_controllers_off(mdi, &ev); break; + case 123: do_control_channel_notes_off(mdi, &ev); break; + } + } +} + +void WildMidi_Renderer::LongEvent(const unsigned char *data, int len) +{ + // Check for Roland SysEx + if (len >= 11 && // Must be at least 11 bytes + data[len-1] == 0xF7 && // SysEx end + data[0] == 0xF0 && // SysEx + data[1] == 0x41 && // Roland + data[2] == 0x10 && // Device ID, defaults to 0x10 + data[3] == 0x42 && // Model ID, 0x42 indicates a GS synth + data[4] == 0x12 && // The other end is sending data to us + data[5] == 0x40) // We only care about addresses with this first byte + { + // Calculate checksum + int cksum = 0; + for (int i = 5; i < len - 2; ++i) + { + cksum += data[i]; + } + cksum = 128 - (cksum & 0x7F); + if (data[len-2] == cksum) + { // Check destination address + if (((data[6] & 0xF0) == 0x10) && data[7] == 0x15) + { // Roland drum track setting + unsigned char sysex_ch = data[6] & 0x0F; + if (sysex_ch == 0) + { + sysex_ch = 9; + } + else if (sysex_ch <= 9) + { + sysex_ch -= 1; + } + _event_data ev = { sysex_ch, data[8] }; + do_sysex_roland_drum_track((_mdi *)handle, &ev); + } + else if (data[6] == 0x00 && data[7] == 0x7F && data[8] == 0x00) + { // Roland GS reset + do_sysex_roland_reset((_mdi *)handle, NULL); + } + } + } + // For non-Roland Sysex messages */ + else + { + const unsigned char gm_reset[] = { 0xf0, 0x7e, 0x7f, 0x09, 0x01, 0xf7 }; + const unsigned char yamaha_reset[] = { 0xf0, 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00, 0xf7}; + if (len == 6 && memcmp(gm_reset, data, 6) == 0) + { + do_sysex_gm_reset((_mdi *)handle, NULL); + } + else if (len == 9 && memcmp(yamaha_reset, data, 9) == 0) + { + do_sysex_yamaha_reset((_mdi *)handle, NULL); + } + } +} + +void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len) +{ + _mdi *mdi = (_mdi *)handle; + int *buffer = (int *)fbuffer; + int *newbuf = WM_Mix(handle, buffer, len); +// assert(newbuf - buffer == len); + if (mdi->info.mixer_options & WM_MO_REVERB) { + _WM_do_reverb(mdi->reverb, buffer, len * 2); + } + for (; buffer < newbuf; ++buffer) + { + *(float *)buffer = (float)*buffer / 32768.f; + } +} + +void WildMidi_Renderer::LoadInstrument(int bank, int percussion, int instr) +{ + load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0)); +} + +int WildMidi_Renderer::GetVoiceCount() +{ + int count = 0; + for (_note *note_data = ((_mdi *)handle)->note; note_data != NULL; note_data = note_data->next) + { + count++; + } + return count; +} + +void WildMidi_Renderer::SetOption(int opt, int set) +{ + WildMidi_SetOption((_mdi*)handle, (unsigned short)opt, (unsigned short)set); +} diff --git a/src/wildmidi/wildmidi_lib.h b/src/wildmidi/wildmidi_lib.h new file mode 100644 index 0000000000..b5aa798496 --- /dev/null +++ b/src/wildmidi/wildmidi_lib.h @@ -0,0 +1,87 @@ +/* + wildmidi_lib.h + + Midi Wavetable Processing library + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef WILDMIDI_LIB_H +#define WILDMIDI_LIB_H + +#define WM_MO_LOG_VOLUME 0x0001 +#define WM_MO_ENHANCED_RESAMPLING 0x0002 +#define WM_MO_REVERB 0x0004 +#define WM_MO_WHOLETEMPO 0x8000 +#define WM_MO_ROUNDTEMPO 0x2000 + +#define WM_GS_VERSION 0x0001 + +#define WM_SYMBOL // we do not need this in ZDoom + +/* +#if defined(__cplusplus) +extern "C" { +#endif +*/ + +struct _WM_Info { + char *copyright; + unsigned long int current_sample; + unsigned long int approx_total_samples; + unsigned short int mixer_options; + unsigned long int total_midi_time; +}; + +typedef void midi; + +WM_SYMBOL const char * WildMidi_GetString (unsigned short int info); +WM_SYMBOL int WildMidi_Init (const char * config_file, unsigned short int rate, unsigned short int options); +WM_SYMBOL int WildMidi_MasterVolume (unsigned char master_volume); +WM_SYMBOL int WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting); +WM_SYMBOL int WildMidi_Close (midi * handle); +WM_SYMBOL int WildMidi_Shutdown (void); +WM_SYMBOL int WildMidi_GetSampleRate (void); + +/* +#if defined(__cplusplus) +} +#endif +*/ + +class WildMidi_Renderer +{ +public: + WildMidi_Renderer(); + ~WildMidi_Renderer(); + + void ShortEvent(int status, int parm1, int parm2); + void LongEvent(const unsigned char *data, int len); + void ComputeOutput(float *buffer, int len); + void LoadInstrument(int bank, int percussion, int instr); + int GetVoiceCount(); + void SetOption(int opt, int set); +private: + void *handle; +}; + +#endif /* WILDMIDI_LIB_H */ + diff --git a/src/wildmidi/wm_error.cpp b/src/wildmidi/wm_error.cpp new file mode 100644 index 0000000000..eab56c437a --- /dev/null +++ b/src/wildmidi/wm_error.cpp @@ -0,0 +1,86 @@ +/* + wm_error.c + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +//#include "config.h" + +#include +#include +#include +#include "wm_error.h" +#include "doomtype.h" +#include "v_text.h" + +void _WM_ERROR_NEW(const char * wmfmt, ...) { + va_list args; + fprintf(stderr, "\r"); + va_start(args, wmfmt); + vfprintf(stderr, wmfmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error) { + + static const char *errors[WM_ERR_MAX+1] = { + "No error", + "Unable to obtain memory", + "Unable to stat", + "Unable to load", + "Unable to open", + "Unable to read", + "Invalid or Unsuported file format", + "File corrupt", + "Library not Initialized", + "Invalid argument", + "Library Already Initialized", + "Not a midi file", + "Refusing to load unusually long file", + + "Invalid error code" + }; + + if (wmerno < 0 || wmerno > WM_ERR_MAX) + wmerno = WM_ERR_MAX; + + if (wmfor != NULL) { + if (error != 0) { + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s %s (%s)\n", func, + lne, errors[wmerno], wmfor, strerror(error)); + } else { + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s %s\n", func, lne, + errors[wmerno], wmfor); + } + } else { + if (error != 0) { + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s (%s)\n", func, lne, + errors[wmerno], strerror(error)); + } else { + Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s\n", func, lne, + errors[wmerno]); + } + } +} + diff --git a/src/wildmidi/wm_error.h b/src/wildmidi/wm_error.h new file mode 100644 index 0000000000..3169246482 --- /dev/null +++ b/src/wildmidi/wm_error.h @@ -0,0 +1,56 @@ +/* + wm_error.h + + error reporting + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . + */ + +#ifndef __WM_ERROR_H +#define __WM_ERROR_H + +enum { + WM_ERR_NONE = 0, + WM_ERR_MEM, + WM_ERR_STAT, + WM_ERR_LOAD, + WM_ERR_OPEN, + WM_ERR_READ, + WM_ERR_INVALID, + WM_ERR_CORUPT, + WM_ERR_NOT_INIT, + WM_ERR_INVALID_ARG, + WM_ERR_ALR_INIT, + WM_ERR_NOT_MIDI, + WM_ERR_LONGFIL, + + WM_ERR_MAX +}; + + extern void _WM_ERROR_NEW(const char * wmfmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + ; + extern void _WM_ERROR(const char * func, unsigned int lne, int wmerno, + const char * wmfor, int error); + +#endif /* __WM_ERROR_H */ diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index e232757344..4e3a14c183 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -72,6 +72,7 @@ #include "v_palette.h" #include "w_wad.h" #include "r_data/colormaps.h" +#include "SkylineBinPack.h" // MACROS ------------------------------------------------------------------ @@ -84,15 +85,19 @@ // The number of quads we can batch together. #define MAX_QUAD_BATCH (NUM_INDEXES / 6) +// The default size for a texture atlas. +#define DEF_ATLAS_WIDTH 512 +#define DEF_ATLAS_HEIGHT 512 + // TYPES ------------------------------------------------------------------- IMPLEMENT_CLASS(D3DFB) struct D3DFB::PackedTexture { - D3DFB::PackingTexture *Owner; + D3DFB::Atlas *Owner; - PackedTexture *Next, **Prev; + PackedTexture **Prev, *Next; // Pixels this image covers RECT Area; @@ -104,23 +109,19 @@ struct D3DFB::PackedTexture bool Padded; }; -struct D3DFB::PackingTexture +struct D3DFB::Atlas { - PackingTexture(D3DFB *fb, int width, int height, D3DFORMAT format); - ~PackingTexture(); + Atlas(D3DFB *fb, int width, int height, D3DFORMAT format); + ~Atlas(); - PackedTexture *GetBestFit(int width, int height, int &area); - void AllocateImage(PackedTexture *box, int width, int height); - PackedTexture *AllocateBox(); - void AddEmptyBox(int left, int top, int right, int bottom); + PackedTexture *AllocateImage(const Rect &rect, bool padded); void FreeBox(PackedTexture *box); - PackingTexture *Next; + SkylineBinPack Packer; + Atlas *Next; IDirect3DTexture9 *Tex; D3DFORMAT Format; PackedTexture *UsedList; // Boxes that contain images - PackedTexture *EmptyList; // Boxes that contain empty space - PackedTexture *FreeList; // Boxes that are just waiting to be used int Width, Height; bool OneUse; }; @@ -283,7 +284,7 @@ D3DFB::D3DFB (UINT adapter, int width, int height, bool fullscreen) ScreenWipe = NULL; InScene = false; QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; - Packs = NULL; + Atlases = NULL; PixelDoubling = 0; SkipAt = -1; CurrRenderTexture = 0; @@ -496,7 +497,7 @@ void D3DFB::FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, b bool D3DFB::CreateResources() { - Packs = NULL; + Atlases = NULL; if (!Windowed) { // Remove the window border in fullscreen mode @@ -624,8 +625,8 @@ void D3DFB::ReleaseResources () delete ScreenWipe; ScreenWipe = NULL; } - PackingTexture *pack, *next; - for (pack = Packs; pack != NULL; pack = next) + Atlas *pack, *next; + for (pack = Atlases; pack != NULL; pack = next) { next = pack->Next; delete pack; @@ -1826,8 +1827,9 @@ IDirect3DTexture9 *D3DFB::GetCurrentScreen(D3DPOOL pool) // // D3DFB :: DrawPackedTextures // -// DEBUG: Draws the packing textures to the screen, starting with the -// 1-based packnum. +// DEBUG: Draws the texture atlases to the screen, starting with the +// 1-based packnum. Ignores atlases that are flagged for use by one +// texture only. // //========================================================================== @@ -1835,38 +1837,53 @@ void D3DFB::DrawPackedTextures(int packnum) { D3DCOLOR empty_colors[8] = { - 0xFFFF9999, 0xFF99FF99, 0xFF9999FF, 0xFFFFFF99, - 0xFFFF99FF, 0xFF99FFFF, 0xFFFFCC99, 0xFF99CCFF + 0x50FF0000, 0x5000FF00, 0x500000FF, 0x50FFFF00, + 0x50FF00FF, 0x5000FFFF, 0x50FF8000, 0x500080FF }; - PackingTexture *pack; + Atlas *pack; int x = 8, y = 8; if (packnum <= 0) { return; } - pack = Packs; + pack = Atlases; + // Find the first texture atlas that is an actual atlas. while (pack != NULL && pack->OneUse) - { // Skip textures that aren't used as packing containers + { // Skip textures that aren't used as atlases pack = pack->Next; } + // Skip however many atlases we would have otherwise drawn + // until we've skipped of them. while (pack != NULL && packnum != 1) { if (!pack->OneUse) - { // Skip textures that aren't used as packing containers + { // Skip textures that aren't used as atlases packnum--; } pack = pack->Next; } + // Draw atlases until we run out of room on the screen. while (pack != NULL) { if (pack->OneUse) - { // Skip textures that aren't used as packing containers + { // Skip textures that aren't used as atlases pack = pack->Next; continue; } - AddColorOnlyQuad(x-1, y-1-LBOffsetI, 258, 258, D3DCOLOR_XRGB(255,255,0)); + AddColorOnlyRect(x-1, y-1-LBOffsetI, 258, 258, D3DCOLOR_XRGB(255,255,0)); + int back = 0; + for (PackedTexture *box = pack->UsedList; box != NULL; box = box->Next) + { + AddColorOnlyQuad( + x + box->Area.left * 256 / pack->Width, + y + box->Area.top * 256 / pack->Height, + (box->Area.right - box->Area.left) * 256 / pack->Width, + (box->Area.bottom - box->Area.top) * 256 / pack->Height, empty_colors[back]); + back = (back + 1) & 7; + } +// AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, D3DCOLOR_ARGB(180,0,0,0)); CheckQuadBatch(); @@ -1876,12 +1893,12 @@ void D3DFB::DrawPackedTextures(int packnum) quad->Group1 = 0; if (pack->Format == D3DFMT_L8/* && !tex->IsGray*/) { - quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest; + quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/; quad->ShaderNum = BQS_PalTex; } else { - quad->Flags = BQF_WrapUV | BQF_DisableAlphaTest; + quad->Flags = BQF_WrapUV/* | BQF_DisableAlphaTest*/; quad->ShaderNum = BQS_Plain; } quad->Palette = NULL; @@ -1941,16 +1958,6 @@ void D3DFB::DrawPackedTextures(int packnum) VertexPos += 4; IndexPos += 6; - // Draw entries in the empty list. - PackedTexture *box; - int emptynum; - for (box = pack->EmptyList, emptynum = 0; box != NULL; box = box->Next, emptynum++) - { - AddColorOnlyQuad(x + box->Area.left, y + box->Area.top - LBOffsetI, - box->Area.right - box->Area.left, box->Area.bottom - box->Area.top, - empty_colors[emptynum & 7]); - } - x += 256 + 8; if (x > Width - 256) { @@ -1969,82 +1976,76 @@ void D3DFB::DrawPackedTextures(int packnum) // // D3DFB :: AllocPackedTexture // -// Finds space to pack an image inside a packing texture and returns it. +// Finds space to pack an image inside a texture atlas and returns it. // Large images and those that need to wrap always get their own textures. // //========================================================================== D3DFB::PackedTexture *D3DFB::AllocPackedTexture(int w, int h, bool wrapping, D3DFORMAT format) { - PackingTexture *bestpack; - PackedTexture *bestbox; - int area; + Atlas *pack; + Rect box; + bool padded; - // check for 254 to account for padding - if (w > 254 || h > 254 || wrapping) - { // Create a new packing texture. - bestpack = new PackingTexture(this, w, h, format); - bestpack->OneUse = true; - bestbox = bestpack->GetBestFit(w, h, area); - bestbox->Padded = false; + // The - 2 to account for padding + if (w > 256 - 2 || h > 256 - 2 || wrapping) + { // Create a new texture atlas. + pack = new Atlas(this, w, h, format); + pack->OneUse = true; + box = pack->Packer.Insert(w, h); + padded = false; } else - { // Try to find space in an existing packing texture. + { // Try to find space in an existing texture atlas. w += 2; // Add padding h += 2; - int bestarea = INT_MAX; - int bestareaever = w * h; - bestpack = NULL; - bestbox = NULL; - for (PackingTexture *pack = Packs; pack != NULL; pack = pack->Next) + for (pack = Atlases; pack != NULL; pack = pack->Next) { + // Use the first atlas it fits in. if (pack->Format == format) { - PackedTexture *box = pack->GetBestFit(w, h, area); - if (area == bestareaever) - { // An exact fit! Use it! - bestpack = pack; - bestbox = box; - break; - } - if (area < bestarea) + box = pack->Packer.Insert(w, h); + if (box.width != 0) { - bestarea = area; - bestpack = pack; - bestbox = box; + break; } } } - if (bestpack == NULL) - { // Create a new packing texture. - bestpack = new PackingTexture(this, 256, 256, format); - bestbox = bestpack->GetBestFit(w, h, bestarea); + if (pack == NULL) + { // Create a new texture atlas. + pack = new Atlas(this, DEF_ATLAS_WIDTH, DEF_ATLAS_HEIGHT, format); + box = pack->Packer.Insert(w, h); } - bestbox->Padded = true; + padded = true; } - bestpack->AllocateImage(bestbox, w, h); - return bestbox; + assert(box.width != 0 && box.height != 0); + return pack->AllocateImage(box, padded); } //========================================================================== // -// PackingTexture Constructor +// Atlas Constructor // //========================================================================== -D3DFB::PackingTexture::PackingTexture(D3DFB *fb, int w, int h, D3DFORMAT format) +D3DFB::Atlas::Atlas(D3DFB *fb, int w, int h, D3DFORMAT format) + : Packer(w, h, true) { Tex = NULL; Format = format; UsedList = NULL; - EmptyList = NULL; - FreeList = NULL; OneUse = false; Width = 0; Height = 0; + Next = NULL; - Next = fb->Packs; - fb->Packs = this; + // Attach to the end of the atlas list + Atlas **prev = &fb->Atlases; + while (*prev != NULL) + { + prev = &((*prev)->Next); + } + *prev = this; #if 1 if (FAILED(fb->D3DDevice->CreateTexture(w, h, 1, 0, format, D3DPOOL_MANAGED, &Tex, NULL))) @@ -2061,18 +2062,15 @@ D3DFB::PackingTexture::PackingTexture(D3DFB *fb, int w, int h, D3DFORMAT format) } Width = w; Height = h; - - // The whole texture is initially empty. - AddEmptyBox(0, 0, w, h); } //========================================================================== // -// PackingTexture Destructor +// Atlas Destructor // //========================================================================== -D3DFB::PackingTexture::~PackingTexture() +D3DFB::Atlas::~Atlas() { PackedTexture *box, *next; @@ -2082,85 +2080,36 @@ D3DFB::PackingTexture::~PackingTexture() next = box->Next; delete box; } - for (box = EmptyList; box != NULL; box = next) - { - next = box->Next; - delete box; - } - for (box = FreeList; box != NULL; box = next) - { - next = box->Next; - delete box; - } } //========================================================================== // -// PackingTexture :: GetBestFit -// -// Returns the empty box that provides the best fit for the requested -// dimensions, or NULL if none of them are large enough. -// -//========================================================================== - -D3DFB::PackedTexture *D3DFB::PackingTexture::GetBestFit(int w, int h, int &area) -{ - PackedTexture *box; - int smallestarea = INT_MAX; - PackedTexture *smallestbox = NULL; - - for (box = EmptyList; box != NULL; box = box->Next) - { - int boxw = box->Area.right - box->Area.left; - int boxh = box->Area.bottom - box->Area.top; - if (boxw >= w && boxh >= h) - { - int boxarea = boxw * boxh; - if (boxarea < smallestarea) - { - smallestarea = boxarea; - smallestbox = box; - if (boxw == w && boxh == h) - { // An exact fit! Use it! - break; - } - } - } - } - area = smallestarea; - return smallestbox; -} - -//========================================================================== -// -// PackingTexture :: AllocateImage +// Atlas :: AllocateImage // // Moves the box from the empty list to the used list, sizing it to the // requested dimensions and adding additional boxes to the empty list if // needed. // -// The passed box *MUST* be in this packing texture's empty list. +// The passed box *MUST* be in this texture atlas's empty list. // //========================================================================== -void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int h) +D3DFB::PackedTexture *D3DFB::Atlas::AllocateImage(const Rect &rect, bool padded) { - RECT start = box->Area; + PackedTexture *box = new PackedTexture; - box->Area.right = box->Area.left + w; - box->Area.bottom = box->Area.top + h; + box->Owner = this; + box->Area.left = rect.x; + box->Area.top = rect.y; + box->Area.right = rect.x + rect.width; + box->Area.bottom = rect.y + rect.height; - box->Left = float(box->Area.left + box->Padded) / Width; - box->Right = float(box->Area.right - box->Padded) / Width; - box->Top = float(box->Area.top + box->Padded) / Height; - box->Bottom = float(box->Area.bottom - box->Padded) / Height; + box->Left = float(box->Area.left + padded) / Width; + box->Right = float(box->Area.right - padded) / Width; + box->Top = float(box->Area.top + padded) / Height; + box->Bottom = float(box->Area.bottom - padded) / Height; - // Remove it from the empty list. - *(box->Prev) = box->Next; - if (box->Next != NULL) - { - box->Next->Prev = box->Prev; - } + box->Padded = padded; // Add it to the used list. box->Next = UsedList; @@ -2171,158 +2120,36 @@ void D3DFB::PackingTexture::AllocateImage(D3DFB::PackedTexture *box, int w, int UsedList = box; box->Prev = &UsedList; - // If we didn't use the whole box, split the remainder into the empty list. - if (box->Area.bottom + 7 < start.bottom && box->Area.right + 7 < start.right) - { - // Split like this: - // +---+------+ - // |###| | - // +---+------+ - // | | - // | | - // +----------+ - if (box->Area.bottom < start.bottom) - { - AddEmptyBox(start.left, box->Area.bottom, start.right, start.bottom); - } - if (box->Area.right < start.right) - { - AddEmptyBox(box->Area.right, start.top, start.right, box->Area.bottom); - } - } - else - { - // Split like this: - // +---+------+ - // |###| | - // +---+ | - // | | | - // | | | - // +---+------+ - if (box->Area.bottom < start.bottom) - { - AddEmptyBox(start.left, box->Area.bottom, box->Area.right, start.bottom); - } - if (box->Area.right < start.right) - { - AddEmptyBox(box->Area.right, start.top, start.right, start.bottom); - } - } -} - -//========================================================================== -// -// PackingTexture :: AddEmptyBox -// -// Adds a box with the specified dimensions to the empty list. -// -//========================================================================== - -void D3DFB::PackingTexture::AddEmptyBox(int left, int top, int right, int bottom) -{ - PackedTexture *box = AllocateBox(); - box->Area.left = left; - box->Area.top = top; - box->Area.right = right; - box->Area.bottom = bottom; - box->Next = EmptyList; - if (box->Next != NULL) - { - box->Next->Prev = &box->Next; - } - box->Prev = &EmptyList; - EmptyList = box; -} - -//========================================================================== -// -// PackingTexture :: AllocateBox -// -// Returns a new PackedTexture box, either by retrieving one off the free -// list or by creating a new one. The box is not linked into a list. -// -//========================================================================== - -D3DFB::PackedTexture *D3DFB::PackingTexture::AllocateBox() -{ - PackedTexture *box; - - if (FreeList != NULL) - { - box = FreeList; - FreeList = box->Next; - if (box->Next != NULL) - { - box->Next->Prev = &FreeList; - } - } - else - { - box = new PackedTexture; - box->Owner = this; - } return box; } //========================================================================== // -// PackingTexture :: FreeBox +// Atlas :: FreeBox // -// Removes a box from its current list and adds it to the empty list, -// updating EmptyArea. If there are no boxes left in the used list, then -// the empty list is replaced with a single box, so the texture can be -// subdivided again. +// Removes a box from the used list and deletes it. Space is returned to the +// waste list. Once all boxes for this atlas are freed, the entire bin +// packer is reinitialized for maximum efficiency. // //========================================================================== -void D3DFB::PackingTexture::FreeBox(D3DFB::PackedTexture *box) +void D3DFB::Atlas::FreeBox(D3DFB::PackedTexture *box) { *(box->Prev) = box->Next; if (box->Next != NULL) { box->Next->Prev = box->Prev; } - box->Next = EmptyList; - box->Prev = &EmptyList; - if (EmptyList != NULL) - { - EmptyList->Prev = &box->Next; - } - EmptyList = box; + Rect waste; + waste.x = box->Area.left; + waste.y = box->Area.top; + waste.width = box->Area.right - box->Area.left; + waste.height = box->Area.bottom - box->Area.top; + box->Owner->Packer.AddWaste(waste); + delete box; if (UsedList == NULL) - { // No more space in use! Move all but this into the free list. - if (box->Next != NULL) - { - D3DFB::PackedTexture *lastbox; - - // Find the last box in the free list. - lastbox = FreeList; - if (lastbox != NULL) - { - while (lastbox->Next != NULL) - { - lastbox = lastbox->Next; - } - } - // Chain the empty list to the end of the free list. - if (lastbox != NULL) - { - lastbox->Next = box->Next; - box->Next->Prev = &lastbox->Next; - } - else - { - FreeList = box->Next; - box->Next->Prev = &FreeList; - } - box->Next = NULL; - } - // Now this is the only box in the empty list, so it should - // contain the whole texture. - box->Area.left = 0; - box->Area.top = 0; - box->Area.right = Width; - box->Area.bottom = Height; + { + Packer.Init(Width, Height, true); } } @@ -2407,6 +2234,7 @@ bool D3DTex::CheckWrapping(bool wrapping) bool D3DTex::Create(D3DFB *fb, bool wrapping) { + assert(Box == NULL); if (Box != NULL) { Box->Owner->FreeBox(Box); @@ -3436,6 +3264,22 @@ void D3DFB::AddColorOnlyQuad(int left, int top, int width, int height, D3DCOLOR IndexPos += 6; } +//========================================================================== +// +// D3DFB :: AddColorOnlyRect +// +// Like AddColorOnlyQuad, except it's hollow. +// +//========================================================================== + +void D3DFB::AddColorOnlyRect(int left, int top, int width, int height, D3DCOLOR color) +{ + AddColorOnlyQuad(left, top, width - 1, 1, color); // top + AddColorOnlyQuad(left + width - 1, top, 1, height - 1, color); // right + AddColorOnlyQuad(left + 1, top + height - 1, width - 1, 1, color); // bottom + AddColorOnlyQuad(left, top + 1, 1, height - 1, color); // left +} + //========================================================================== // // D3DFB :: CheckQuadBatch @@ -4004,7 +3848,7 @@ void D3DFB::SetPaletteTexture(IDirect3DTexture9 *texture, int count, D3DCOLOR bo SetTexture(1, texture); } -void D3DFB::SetPalTexBilinearConstants(PackingTexture *tex) +void D3DFB::SetPalTexBilinearConstants(Atlas *tex) { #if 0 float con[8]; diff --git a/src/win32/i_dijoy.cpp b/src/win32/i_dijoy.cpp index ccbc7ecdc0..439e0fda7c 100644 --- a/src/win32/i_dijoy.cpp +++ b/src/win32/i_dijoy.cpp @@ -309,7 +309,7 @@ FDInputJoystick::~FDInputJoystick() { Joy_GenerateButtonEvents(Axes[0].ButtonValue, 0, 2, KEY_JOYAXIS1PLUS); } - else + else if (Axes.Size() > 1) { Joy_GenerateButtonEvents(Axes[1].ButtonValue, 0, 4, KEY_JOYAXIS1PLUS); for (i = 2; i < Axes.Size(); ++i) diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index a26ff320e6..941fc15de1 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -110,6 +110,11 @@ #include "rawinput.h" +// Compensate for w32api's lack +#ifndef GET_XBUTTON_WPARAM +#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) +#endif + #ifdef _DEBUG #define INGAME_PRIORITY_CLASS NORMAL_PRIORITY_CLASS diff --git a/src/win32/i_input.h b/src/win32/i_input.h index b0d409844d..c0de97e63b 100644 --- a/src/win32/i_input.h +++ b/src/win32/i_input.h @@ -47,6 +47,15 @@ FString I_GetFromClipboard (bool windows_has_no_selection_clipboard); void I_GetEvent(); +enum +{ + INPUT_DIJoy, + INPUT_XInput, + INPUT_RawPS2, + NUM_JOYDEVICES +}; + + #ifdef USE_WINDOWS_DWORD #include "m_joy.h" @@ -121,14 +130,6 @@ public: virtual IJoystickConfig *Rescan() = 0; }; -enum -{ - INPUT_DIJoy, - INPUT_XInput, - INPUT_RawPS2, - NUM_JOYDEVICES -}; - extern FJoystickCollection *JoyDevices[NUM_JOYDEVICES]; void I_StartupMouse(); diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index ea167428fc..8b6521ad57 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -106,6 +106,7 @@ LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); void CreateCrashLog (char *custominfo, DWORD customsize, HWND richedit); void DisplayCrashLog (); extern BYTE *ST_Util_BitsForBitmap (BITMAPINFO *bitmap_info); +void I_FlushBufferedConsoleStuff(); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -128,6 +129,7 @@ HANDLE MainThread; DWORD MainThreadID; HANDLE StdOut; bool FancyStdOut, AttachedStdOut; +bool ConWindowHidden; // The main window HWND Window; @@ -644,6 +646,7 @@ void I_SetWndProc() SetWindowLongPtr (Window, GWLP_USERDATA, 1); SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)WndProc); ShowWindow (ConWindow, SW_HIDE); + ConWindowHidden = true; ShowWindow (GameTitleWindow, SW_HIDE); I_InitInput (Window); } @@ -675,8 +678,10 @@ void RestoreConView() SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)LConProc); ShowWindow (ConWindow, SW_SHOW); + ConWindowHidden = false; ShowWindow (GameTitleWindow, SW_SHOW); I_ShutdownInput (); // Make sure the mouse pointer is available. + I_FlushBufferedConsoleStuff(); // Make sure the progress bar isn't visible. if (StartScreen != NULL) { diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 1d2bae8e1e..133337af5d 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -132,10 +132,12 @@ extern bool FancyStdOut; extern HINSTANCE g_hInst; extern FILE *Logfile; extern bool NativeMouse; +extern bool ConWindowHidden; // PUBLIC DATA DEFINITIONS ------------------------------------------------- CVAR (String, queryiwad_key, "shift", CVAR_GLOBALCONFIG|CVAR_ARCHIVE); +CVAR (Bool, con_debugoutput, false, 0); double PerfToSec, PerfToMillisec; UINT TimerPeriod; @@ -912,12 +914,11 @@ void ToEditControl(HWND edit, const char *buf, wchar_t *wbuf, int bpos) // //========================================================================== -void I_PrintStr(const char *cp) +static void DoPrintStr(const char *cp, HWND edit, HANDLE StdOut) { - if (ConWindow == NULL && StdOut == NULL) + if (edit == NULL && StdOut == NULL) return; - HWND edit = ConWindow; char buf[256]; wchar_t wbuf[countof(buf)]; int bpos = 0; @@ -1049,6 +1050,55 @@ void I_PrintStr(const char *cp) } } +static TArray bufferedConsoleStuff; + +void I_PrintStr(const char *cp) +{ + if (con_debugoutput) + { + // Strip out any color escape sequences before writing to debug output + char * copy = new char[strlen(cp)+1]; + const char * srcp = cp; + char * dstp = copy; + + while (*srcp != 0) + { + if (*srcp!=0x1c && *srcp!=0x1d && *srcp!=0x1e && *srcp!=0x1f) + { + *dstp++=*srcp++; + } + else + { + if (srcp[1]!=0) srcp+=2; + else break; + } + } + *dstp=0; + + OutputDebugStringA(copy); + delete [] copy; + } + + if (ConWindowHidden) + { + bufferedConsoleStuff.Push(cp); + DoPrintStr(cp, NULL, StdOut); + } + else + { + DoPrintStr(cp, ConWindow, StdOut); + } +} + +void I_FlushBufferedConsoleStuff() +{ + for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++) + { + DoPrintStr(bufferedConsoleStuff[i], ConWindow, NULL); + } + bufferedConsoleStuff.Clear(); +} + //========================================================================== // // SetQueryIWAD @@ -1480,30 +1530,83 @@ int I_FindClose(void *handle) static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FString &value) { - HKEY steamkey; + HKEY pathkey; DWORD pathtype; DWORD pathlen; LONG res; - if(ERROR_SUCCESS == RegOpenKeyEx(key, keypath, 0, KEY_QUERY_VALUE, &steamkey)) + if(ERROR_SUCCESS == RegOpenKeyEx(key, keypath, 0, KEY_QUERY_VALUE, &pathkey)) { - if (ERROR_SUCCESS == RegQueryValueEx(steamkey, valname, 0, &pathtype, NULL, &pathlen) && + if (ERROR_SUCCESS == RegQueryValueEx(pathkey, valname, 0, &pathtype, NULL, &pathlen) && pathtype == REG_SZ && pathlen != 0) { // Don't include terminating null in count char *chars = value.LockNewBuffer(pathlen - 1); - res = RegQueryValueEx(steamkey, valname, 0, NULL, (LPBYTE)chars, &pathlen); + res = RegQueryValueEx(pathkey, valname, 0, NULL, (LPBYTE)chars, &pathlen); value.UnlockBuffer(); if (res != ERROR_SUCCESS) { value = ""; } } - RegCloseKey(steamkey); + RegCloseKey(pathkey); } return value.IsNotEmpty(); } +//========================================================================== +// +// I_GetGogPaths +// +// Check the registry for GOG installation paths, so we can search for IWADs +// that were bought from GOG.com. This is a bit different from the Steam +// version because each game has its own independent installation path, no +// such thing as /SteamApps/common/. +// +//========================================================================== + +TArray I_GetGogPaths() +{ + TArray result; + FString path; + FString gamepath; + +#ifdef _WIN64 + FString gogregistrypath = "Software\\Wow6432Node\\GOG.com\\Games"; +#else + // If a 32-bit ZDoom runs on a 64-bit Windows, this will be transparently and + // automatically redirected to the Wow6432Node address instead, so this address + // should be safe to use in all cases. + FString gogregistrypath = "Software\\GOG.com\\Games"; +#endif + + // Look for Ultimate Doom + gamepath = gogregistrypath + "\\1435827232"; + if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path)) + { + result.Push(path); // directly in install folder + } + + // Look for Doom II + gamepath = gogregistrypath + "\\1435848814"; + if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path)) + { + result.Push(path + "/doom2"); // in a subdirectory + // If direct support for the Master Levels is ever added, they are in path + /master/wads + } + + // Look for Final Doom + gamepath = gogregistrypath + "\\1435848742"; + if (QueryPathKey(HKEY_LOCAL_MACHINE, gamepath.GetChars(), "Path", path)) + { + // in subdirectories + result.Push(path + "/TNT"); + result.Push(path + "/Plutonia"); + } + + return result; +} + //========================================================================== // // I_GetSteamPath @@ -1623,3 +1726,36 @@ FString I_GetLongPathName(FString shortpath) delete[] buff; return longpath; } + +#if _MSC_VER == 1900 && defined(_USING_V110_SDK71_) +//========================================================================== +// +// VS14Stat +// +// Work around an issue where stat doesn't work with v140_xp. This was +// supposedly fixed, but as of Update 1 continues to not function on XP. +// +//========================================================================== + +#include + +int VS14Stat(const char *path, struct _stat64i32 *buffer) +{ + WIN32_FILE_ATTRIBUTE_DATA data; + if(!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) + return -1; + + buffer->st_ino = 0; + buffer->st_mode = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR : S_IFREG)| + ((data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD|S_IWRITE); + buffer->st_dev = buffer->st_rdev = 0; + buffer->st_nlink = 1; + buffer->st_uid = 0; + buffer->st_gid = 0; + buffer->st_size = data.nFileSizeLow; + buffer->st_atime = (*(QWORD*)&data.ftLastAccessTime) / 10000000 - 11644473600LL; + buffer->st_mtime = (*(QWORD*)&data.ftLastWriteTime) / 10000000 - 11644473600LL; + buffer->st_ctime = (*(QWORD*)&data.ftCreationTime) / 10000000 - 11644473600LL; + return 0; +} +#endif diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 566ca19778..03f4f3c0ff 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -168,6 +168,9 @@ void I_SetWndProc(); // directories for IWADs if the user purchased any through Steam. TArray I_GetSteamPath(); +// [GZ] Same deal for GOG paths +TArray I_GetGogPaths(); + // Damn Microsoft for doing Get/SetWindowLongPtr half-assed. Instead of // giving them proper prototypes under Win32, they are just macros for // Get/SetWindowLong, meaning they take LONGs and not LONG_PTRs. diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index 5a02d7af4b..3d85f48a0d 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -789,6 +789,8 @@ void I_StartupXInput() #else // NO_XINPUT +#include "i_input.h" + void I_StartupXInput() { JoyDevices[INPUT_XInput] = NULL; diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 934931ea01..8b0c88f45a 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -279,7 +279,7 @@ private: friend class D3DPal; struct PackedTexture; - struct PackingTexture; + struct Atlas; struct FBVERTEX { @@ -375,6 +375,7 @@ private: static void SetColorOverlay(DWORD color, float alpha, D3DCOLOR &color0, D3DCOLOR &color1); void DoWindowedGamma(); void AddColorOnlyQuad(int left, int top, int width, int height, D3DCOLOR color); + void AddColorOnlyRect(int left, int top, int width, int height, D3DCOLOR color); void CheckQuadBatch(int numtris=2, int numverts=4); void BeginQuadBatch(); void EndQuadBatch(); @@ -392,7 +393,7 @@ private: void SetPixelShader(IDirect3DPixelShader9 *shader); void SetTexture(int tnum, IDirect3DTexture9 *texture); void SetPaletteTexture(IDirect3DTexture9 *texture, int count, D3DCOLOR border_color); - void SetPalTexBilinearConstants(PackingTexture *texture); + void SetPalTexBilinearConstants(Atlas *texture); BOOL AlphaTestEnabled; BOOL AlphaBlendEnabled; @@ -431,7 +432,7 @@ private: BYTE BlockNum; D3DPal *Palettes; D3DTex *Textures; - PackingTexture *Packs; + Atlas *Atlases; HRESULT LastHR; UINT Adapter; diff --git a/src/xs_Float.h b/src/xs_Float.h index 19300b2fc9..1f57f9205a 100644 --- a/src/xs_Float.h +++ b/src/xs_Float.h @@ -209,7 +209,7 @@ finline static uint32 xs_FloorToUInt(real64 val) finline static uint32 xs_CeilToUInt(real64 val) { - return (uint32)xs_CeilToUInt(val); + return (uint32)xs_CeilToInt(val); } finline static uint32 xs_RoundToUInt(real64 val) diff --git a/src/zstring.cpp b/src/zstring.cpp index 510ff19d7e..1aa388da91 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -583,6 +583,7 @@ void FString::SwapCase () void FString::StripLeft () { size_t max = Len(), i, j; + if (max == 0) return; for (i = 0; i < max; ++i) { if (!isspace(Chars[i])) @@ -613,6 +614,7 @@ void FString::StripLeft (const FString &charset) void FString::StripLeft (const char *charset) { size_t max = Len(), i, j; + if (max == 0) return; for (i = 0; i < max; ++i) { if (!strchr (charset, Chars[i])) @@ -665,6 +667,7 @@ void FString::StripRight (const FString &charset) void FString::StripRight (const char *charset) { size_t max = Len(), i; + if (max == 0) return; for (i = max; i-- > 0; ) { if (!strchr (charset, Chars[i])) @@ -687,6 +690,7 @@ void FString::StripRight (const char *charset) void FString::StripLeftRight () { size_t max = Len(), i, j, k; + if (max == 0) return; for (i = 0; i < max; ++i) { if (!isspace(Chars[i])) @@ -723,6 +727,7 @@ void FString::StripLeftRight (const FString &charset) void FString::StripLeftRight (const char *charset) { size_t max = Len(), i, j, k; + if (max == 0) return; for (i = 0; i < max; ++i) { if (!strchr (charset, Chars[i])) diff --git a/tools/lemon/lemon.c b/tools/lemon/lemon.c index 5e8f03a8bc..651e43f208 100644 --- a/tools/lemon/lemon.c +++ b/tools/lemon/lemon.c @@ -3061,6 +3061,7 @@ struct lemon *lemp; FILE *in; char *tpltname; char *cp; + Boolean tpltnameinbuf; cp = strrchr(lemp->filename,'.'); if( cp ){ @@ -3070,10 +3071,13 @@ struct lemon *lemp; } if( access(buf,004)==0 ){ tpltname = buf; + tpltnameinbuf = LEMON_TRUE; }else if( access(templatename,004)==0 ){ tpltname = templatename; + tpltnameinbuf = LEMON_TRUE; }else{ tpltname = pathsearch(lemp->argv0,templatename,0); + tpltnameinbuf = LEMON_FALSE; } if( tpltname==0 ){ fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", @@ -3084,11 +3088,11 @@ struct lemon *lemp; in = fopen(tpltname,"rb"); if( in==0 ){ fprintf(stderr,"Can't open the template file \"%s\".\n",templatename); - free(tpltname); + if (tpltnameinbuf == LEMON_FALSE) free(tpltname); lemp->errorcnt++; return 0; } - free(tpltname); + if (tpltnameinbuf == LEMON_FALSE) free(tpltname); return in; } diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index b7fea41898..5158aa7b5a 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -212,9 +212,9 @@ ACTOR Actor native //: Thinker action native A_CustomBulletAttack(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", float range = 0, int flags = 0, int ptr = AAPTR_TARGET); action native A_CustomRailgun(int damage, int spawnofs_xy = 0, color color1 = "", color color2 = "", int flags = 0, bool aim = false, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); action native A_JumpIfHealthLower(int health, state label, int ptr_selector = AAPTR_DEFAULT); - action native A_JumpIfCloser(float distance, state label); - action native A_JumpIfTracerCloser(float distance, state label); - action native A_JumpIfMasterCloser(float distance, state label); + action native A_JumpIfCloser(float distance, state label, bool noz = false); + action native A_JumpIfTracerCloser(float distance, state label, bool noz = false); + action native A_JumpIfMasterCloser(float distance, state label, bool noz = false); action native A_JumpIfTargetOutsideMeleeRange(state label); action native A_JumpIfTargetInsideMeleeRange(state label); action native A_JumpIfInventory(class itemtype, int itemamount, state label, int owner = AAPTR_DEFAULT); @@ -249,7 +249,7 @@ ACTOR Actor native //: Thinker action native A_PlayerSkinCheck(state label); action native A_BasicAttack(int meleedamage, sound meleesound, class missiletype, float missileheight); action native A_Teleport(state teleportstate = "", class targettype = "BossSpot", class fogtype = "TeleportFog", int flags = 0, float mindist = 0, float maxdist = 0, int ptr = AAPTR_DEFAULT); - action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = ""); + action native A_Warp(int ptr_destination, float xofs = 0, float yofs = 0, float zofs = 0, float angle = 0, int flags = 0, state success_state = "", float heightoffset = 0, float radiusoffset = 0, float pitch = 0); action native A_ThrowGrenade(class itemtype, float zheight = 0, float xyvel = 0, float zvel = 0, bool useammo = true); action native A_Weave(int xspeed, int yspeed, float xdist, float ydist); @@ -257,7 +257,7 @@ ACTOR Actor native //: Thinker action native A_JumpIfInTargetInventory(class itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT); action native A_GiveToTarget(class itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT); action native A_TakeFromTarget(class itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT); - action native A_RadiusGive(class itemtype, int distance, int flags, int amount = 0); + action native A_RadiusGive(class itemtype, int distance, int flags, int amount = 0, class filter = "None", name species = "None", int mindist = 0); action native A_CountdownArg(int argnum, state targstate = ""); action native A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); action native A_CustomComboAttack(class missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); @@ -304,6 +304,8 @@ ACTOR Actor native //: Thinker action native A_SetDamageType(name damagetype); action native A_DropItem(class item, int dropamount = -1, int chance = 256); action native A_SetSpeed(float speed, int ptr = AAPTR_DEFAULT); + action native A_SetFloatSpeed(float speed, int ptr = AAPTR_DEFAULT); + action native A_SetPainThreshold(int threshold, int ptr = AAPTR_DEFAULT); action native A_DamageSelf(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageTarget(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); action native A_DamageMaster(int amount, name damagetype = "none", int flags = 0, class filter = "None", name species = "None"); @@ -331,10 +333,12 @@ ACTOR Actor native //: Thinker action native A_SetHealth(int health, int ptr = AAPTR_DEFAULT); action native A_ResetHealth(int ptr = AAPTR_DEFAULT); action native A_JumpIfHigherOrLower(state high, state low, float offsethigh = 0, float offsetlow = 0, bool includeHeight = true, int ptr = AAPTR_TARGET); + action native A_SetSpecies(name species, int ptr = AAPTR_DEFAULT); action native A_SetRipperLevel(int level); action native A_SetRipMin(int min); action native A_SetRipMax(int max); - + action native A_CheckProximity(state jump, class classname, float distance, int count = 1, int flags = 0, int ptr = AAPTR_DEFAULT); + action native A_CheckBlock(state block, int flags = 0, int ptr = AAPTR_DEFAULT); action native A_CheckSightOrRange(float distance, state label, bool two_dimension = false); action native A_CheckRange(float distance, state label, bool two_dimension = false); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 6612dd9f84..213e2109db 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -74,6 +74,9 @@ const int SXF_NOPOINTERS = 1 << 22; const int SXF_ORIGINATOR = 1 << 23; const int SXF_TRANSFERSPRITEFRAME = 1 << 24; const int SXF_TRANSFERROLL = 1 << 25; +const int SXF_ISTARGET = 1 << 26; +const int SXF_ISMASTER = 1 << 27; +const int SXF_ISTRACER = 1 << 28; // Flags for A_Chase const int CHF_FASTCHASE = 1; @@ -126,6 +129,10 @@ const int WRF_NOFIRE = WRF_NOPRIMARY | WRF_NOSECONDARY; const int WRF_ALLOWRELOAD = 16; const int WRF_ALLOWZOOM = 32; const int WRF_DISABLESWITCH = 64; +const int WRF_ALLOWUSER1 = 128; +const int WRF_ALLOWUSER2 = 256; +const int WRF_ALLOWUSER3 = 512; +const int WRF_ALLOWUSER4 = 1024; // Morph constants const int MRF_ADDSTAMINA = 1; @@ -185,6 +192,7 @@ const int CPF_STEALARMOR = 32; // Flags for A_CustomMissile const int FPF_AIMATANGLE = 1; const int FPF_TRANSFERTRANSLATION = 2; +const int FPF_NOAUTOAIM = 4; // Flags for A_Teleport enum @@ -224,6 +232,12 @@ enum RGF_CUBE = 1 << 9, RGF_NOSIGHT = 1 << 10, RGF_MISSILES = 1 << 11, + RGF_INCLUSIVE = 1 << 12, + RGF_ITEMS = 1 << 13, + RGF_KILLED = 1 << 14, + RGF_EXFILTER = 1 << 15, + RGF_EXSPECIES = 1 << 16, + RGF_EITHER = 1 << 17, }; // Activation flags @@ -351,6 +365,9 @@ Const Int WAPRF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_ABSOLUTEPOSITION = 0x400; Const Int WARPF_BOB = 0x800; Const Int WARPF_MOVEPTR = 0x1000; +Const Int WARPF_USETID = 0x2000; +Const Int WARPF_COPYVELOCITY = 0x4000; +Const Int WARPF_COPYPITCH = 0x8000; // flags for A_SetPitch/SetAngle/SetRoll const int SPF_FORCECLAMP = 1; @@ -471,5 +488,28 @@ enum QF_WAVE = 1 << 5, }; +// A_CheckProximity flags +enum +{ + CPXF_ANCESTOR = 1, + CPXF_LESSOREQUAL = 1 << 1, + CPXF_NOZ = 1 << 2, + CPXF_COUNTDEAD = 1 << 3, + CPXF_DEADONLY = 1 << 4, + CPXF_EXACT = 1 << 5, +}; + +// Flags for A_CheckBlock +// These flags only affect the calling actor('s pointer), not the ones being searched. +enum +{ + CBF_NOLINES = 1 << 0, //Don't check actors. + CBF_SETTARGET = 1 << 1, //Sets the caller/pointer's target to the actor blocking it. Actors only. + CBF_SETMASTER = 1 << 2, //^ but with master. + CBF_SETTRACER = 1 << 3, //^ but with tracer. + CBF_SETONPTR = 1 << 4, //Sets the pointer change on the actor doing the checking instead of self. +}; + // This is only here to provide one global variable for testing. native int testglobalvar; + diff --git a/wadsrc/static/actors/doom/fatso.txt b/wadsrc/static/actors/doom/fatso.txt index 1ad40fd936..0b30393257 100644 --- a/wadsrc/static/actors/doom/fatso.txt +++ b/wadsrc/static/actors/doom/fatso.txt @@ -30,9 +30,9 @@ ACTOR Fatso Missile: FATT G 20 A_FatRaise FATT H 10 BRIGHT A_FatAttack1 - FATT IG 5 + FATT IG 5 A_FaceTarget FATT H 10 BRIGHT A_FatAttack2 - FATT IG 5 + FATT IG 5 A_FaceTarget FATT H 10 BRIGHT A_FatAttack3 FATT IG 5 Goto See diff --git a/wadsrc/static/actors/doom/keen.txt b/wadsrc/static/actors/doom/keen.txt index 2c07e5b327..93d43764f7 100644 --- a/wadsrc/static/actors/doom/keen.txt +++ b/wadsrc/static/actors/doom/keen.txt @@ -28,7 +28,7 @@ ACTOR CommanderKeen KEEN AB 6 KEEN C 6 A_Scream KEEN DEFGH 6 - KEEN I 6 A_NoBlocking + KEEN I 6 KEEN J 6 KEEN K 6 A_KeenDie KEEN L -1 diff --git a/wadsrc/static/actors/doom/spidermaster.txt b/wadsrc/static/actors/doom/spidermaster.txt index 97ebbf1435..c2a742f1eb 100644 --- a/wadsrc/static/actors/doom/spidermaster.txt +++ b/wadsrc/static/actors/doom/spidermaster.txt @@ -6,7 +6,7 @@ ACTOR SpiderMastermind { Health 3000 - Radius 100 + Radius 128 Height 100 Mass 1000 Speed 12 diff --git a/wadsrc/static/actors/heretic/hereticweaps.txt b/wadsrc/static/actors/heretic/hereticweaps.txt index 711c3fd292..f0db7caf2d 100644 --- a/wadsrc/static/actors/heretic/hereticweaps.txt +++ b/wadsrc/static/actors/heretic/hereticweaps.txt @@ -802,6 +802,7 @@ ACTOR BlasterPuff +NOGRAVITY +PUFFONACTORS RenderStyle Add + SeeSound "weapons/blasterhit" States { Crash: diff --git a/wadsrc/static/actors/heretic/mummy.txt b/wadsrc/static/actors/heretic/mummy.txt index 46e9deaba2..c06f519f67 100644 --- a/wadsrc/static/actors/heretic/mummy.txt +++ b/wadsrc/static/actors/heretic/mummy.txt @@ -121,12 +121,11 @@ ACTOR MummyFX1 States { Spawn: - FX15 A 1 Bright FX15 A 5 Bright A_PlaySound("mummy/head") FX15 B 5 Bright A_SeekerMissile(10,20) FX15 C 5 Bright FX15 B 5 Bright A_SeekerMissile(10,20) - Goto Spawn+1 + Loop Death: FX15 DEFG 5 Bright Stop diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index 0c106417b0..a44515efb5 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -8,9 +8,9 @@ ACTOR Inventory native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG" action native A_JumpIfNoAmmo(state label); - action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus"); + action native A_CustomPunch(int damage, bool norandom = false, int flags = CPF_USEAMMO, class pufftype = "BulletPuff", float range = 0, float lifesteal = 0, int lifestealmax = 0, class armorbonustype = "ArmorBonus", sound MeleeSound = "", sound MissSound = ""); action native A_FireBullets(float spread_xy, float spread_z, int numbullets, int damageperbullet, class pufftype = "BulletPuff", int flags = 1, float range = 0); - action native A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, bool aimatangle = false, float pitch = 0); + action native A_FireCustomMissile(class missiletype, float angle = 0, bool useammo = true, int spawnofs_xy = 0, float spawnheight = 0, int flags = 0, float pitch = 0); action native A_RailAttack(int damage, int spawnofs_xy = 0, int useammo = true, color color1 = "", color color2 = "", int flags = 0, float maxdiff = 0, class pufftype = "BulletPuff", float spread_xy = 0, float spread_z = 0, float range = 0, int duration = 0, float sparsity = 1.0, float driftspeed = 1.0, class spawnclass = "none", float spawnofs_z = 0, int spiraloffset = 270); action native A_Light(int extralight); action native A_Light0(); @@ -225,6 +225,7 @@ ACTOR PowerWeaponLevel2 : Powerup native { Powerup.Duration -40 Inventory.Icon "SPINBK0" + +INVENTORY.NOTELEPORTFREEZE } ACTOR PowerSpeed : Powerup native @@ -232,6 +233,7 @@ ACTOR PowerSpeed : Powerup native Powerup.Duration -45 Speed 1.5 Inventory.Icon "SPBOOT0" + +INVENTORY.NOTELEPORTFREEZE } // Player Speed Trail (used by the Speed Powerup) ---------------------------- diff --git a/wadsrc/static/actors/strife/macil.txt b/wadsrc/static/actors/strife/macil.txt index 24af35ada8..7b001e778b 100644 --- a/wadsrc/static/actors/strife/macil.txt +++ b/wadsrc/static/actors/strife/macil.txt @@ -44,7 +44,7 @@ ACTOR Macil1 Death: LEAD E 2 A_FaceTarget LEAD F 2 BRIGHT A_ShootGun - LEAD E 2 A_SentinelRefire + LEAD E 1 A_SentinelRefire Loop Pain: LEAD Y 3 @@ -80,7 +80,7 @@ ACTOR Macil2 : Macil1 LEAD K 3 LEAD L 3 A_NoBlocking LEAD MNOPQRSTUV 3 - LEAD W 4 Bright A_SpawnItemEx("AlienSpectre4", 0, 0, 0, 0, 0, random[spectrespawn](0,255)*0.0078125, 0, SXF_NOCHECKPOSITION) + LEAD W 3 A_SpawnItemEx("AlienSpectre4", 0, 0, 0, 0, 0, random[spectrespawn](0,255)*0.0078125, 0, SXF_NOCHECKPOSITION) LEAD X -1 Stop } diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 3a675f275a..13f0d22a70 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -55,6 +55,8 @@ A80E7EE40E0D0C76A6FBD242BE29FE27 // map15 { stairs maskedmidtex + corpsegibs + vileghosts } 10E1E2B36302D31AC4AE68C84B5DC457 // Eternal Doom MAP28 @@ -125,7 +127,6 @@ BA530202AF0BA0C6CBAE6A0C7076FB72 // Requiem map04 CBDFEFAC579A62DE8F1B48CA4A09D381 // gather2.wad map05 and darkside.wad map01 C7A2FAFB0AFB2632C50AD625CDB50E51 // Reverie map18 9E5724BC6135AA6F86EE54FD4D91F1E2 // Project X map14 -6DA6FCBA8089161BDEC6A1D3F6C8D60F // Eternal Doom map25 01899825FFEAE016D39C02A7DA4B218F // Archie map01 1D9F3AFDC2517C2E450491ED13896712 // Seej map01 0AE745A3AB86D15FB2FB74489962C421 // 6pack2 map02 @@ -320,8 +321,7 @@ F481922F4881F74760F3C0437FD5EDD0 // map03 7C1913DEE396BA26CFF22A0E9369B7D2 // Nuke Mine, e1m2 { - clearlinespecial 1107 - clearlinespecial 1108 + pointonline } 5B862477519B21B30059A466F2FF6460 // Khorus, map08 @@ -386,6 +386,7 @@ D62DCA9EC226DE49108D5DD9271F7631 // Cheogsh 2 map04 setthingz 1649 528 } +E89CCC7E155F1032F693359CC219BE6C // hexen.wad map30 B9DFF13207EACAC675C71D82624D0007 // XtheaterIII map01 { DisablePushWindowCheck @@ -395,3 +396,14 @@ A53AE580A4AF2B5D0B0893F86914781E // TNT: Evilution map31 { setthingflags 470 2016 } + +D0139194F7817BF06F3988DFC47DB38D // Whispers of Satan map29 +{ + nopassover +} + +D7F6E9F08C39A17026349A04F8C0B0BE // Return to Hadron, e1m9 +19D03FFC875589E21EDBB7AB74EF4AEF // Return to Hadron, e1m9, 2016.01.03 update +{ + pointonline +} diff --git a/wadsrc/static/dehsupp.txt b/wadsrc/static/dehsupp.txt index 51db22e911..ae31f68302 100644 --- a/wadsrc/static/dehsupp.txt +++ b/wadsrc/static/dehsupp.txt @@ -534,7 +534,13 @@ SoundMap "skeleton/active", "skeleton/sight", "skeleton/attack", - "misc/chat" + "misc/chat", + "dog/sight", + "dog/attack", + "dog/active", + "dog/death", + "dog/pain", + }; // Names of different actor types, in original Doom 2 order diff --git a/wadsrc/static/filter/doom.freedoom/decaldef.z b/wadsrc/static/filter/doom.freedoom/decaldef.z new file mode 100644 index 0000000000..68b85a8485 --- /dev/null +++ b/wadsrc/static/filter/doom.freedoom/decaldef.z @@ -0,0 +1,29 @@ + +/***** BFG Scorches ********************************************************/ + +decal BFGLightning1 +{ + pic BFGLITE1 + shade "80 80 ff" + fullbright + randomflipx + animator GoAway2 + lowerdecal BFGScorch +} + +decal BFGLightning2 +{ + pic BFGLITE2 + shade "80 80 ff" + fullbright + randomflipy + animator GoAway2 + lowerdecal BFGScorch +} + +decalgroup BFGLightning +{ + BFGLightning1 1 + BFGLightning2 1 +} + diff --git a/wadsrc/static/filter/game-strife/sndinfo.txt b/wadsrc/static/filter/game-strife/sndinfo.txt index 22f9b8356b..f602a969b4 100644 --- a/wadsrc/static/filter/game-strife/sndinfo.txt +++ b/wadsrc/static/filter/game-strife/sndinfo.txt @@ -8,7 +8,7 @@ $rolloff * 200 1200 $playersound player male *death dspldeth -$playersound player male *xdeath dspdiehi +$playersound player male *xdeath dsplxdth $playersound player male *gibbed dsslop $playersound player male *pain100 dsplpain $playersounddup player male *pain75 *pain100 diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 6c958ddfa2..401f3ca4c0 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -370,6 +370,7 @@ Names "doom.wad" "doom1.wad" "heretic.wad" + "hereticsr.wad" "heretic1.wad" "hexen.wad" "hexdd.wad" diff --git a/wadsrc/static/language.eng b/wadsrc/static/language.eng new file mode 100644 index 0000000000..172cd2b4f9 --- /dev/null +++ b/wadsrc/static/language.eng @@ -0,0 +1,25 @@ +/* British English */ + +[eng] + +GOTARMOR = "Picked up the armour."; +GOTMEGA = "Picked up the MegaArmour!"; +GOTARMBONUS = "Picked up an armour bonus."; + +SCORE_COLOR = "COLOUR"; + +TAG_SHADOWARMOR = "Shadow Armour"; +TAG_METALARMOR = "Metal Armour"; +TAG_LEATHER = "Leather Armour"; +TAG_ARMORER = "Armourer"; + +TXT_ARMOR1 = "MESH ARMOUR"; + +TXT_METALARMOR = "You picked up the Metal Armour."; +TXT_LEATHERARMOR = "You picked up the Leather Armour."; +TXT_SHADOWARMOR = "You picked up the Shadow armour."; + +GOTCHEXARMOR = "Picked up the Chex(R) Armour."; +GOTSUPERCHEXARMOR = "Picked up the Super Chex(R) Armour!"; + +OB_BIPEDICUS2 = "%o was slimed by an armoured bipedicus."; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index a01a03559d..005a3f57db 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1281,6 +1281,14 @@ TXT_LEADBOOTSOFF = "LEAD BOOTS OFF"; TXT_LIGHTER = "You feel lighter"; TXT_GRAVITY = "Gravity weighs you down"; +// Raven intermission + +TXT_IMKILLS = "KILLS"; +TXT_IMITEMS = "ITEMS"; +TXT_IMSECRETS = "SECRETS"; +TXT_IMTIME = "TIME"; + + RAVENQUITMSG = "ARE YOU SURE YOU WANT TO QUIT?"; // Hexen strings @@ -1542,6 +1550,10 @@ TXT_RANDOMGOODBYE_3 = "See you later!"; TXT_HAVEENOUGH = "You seem to have enough!"; TXT_GOAWAY = "Go away!"; +TXT_COMM0 = "Incoming Message"; +TXT_COMM1 = "Incoming Message from BlackBird"; + + // Skills: SKILL_BABY = "I'm too young to die"; diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt index f26ca67315..4b663047ce 100644 --- a/wadsrc/static/mapinfo/common.txt +++ b/wadsrc/static/mapinfo/common.txt @@ -118,70 +118,70 @@ DoomEdNums 9997 = SecActExit 9998 = SecActEnter 9999 = SecActHitFloor - 14001 = AmbientSound, 1 - 14002 = AmbientSound, 2 - 14003 = AmbientSound, 3 - 14004 = AmbientSound, 4 - 14005 = AmbientSound, 5 - 14006 = AmbientSound, 6 - 14007 = AmbientSound, 7 - 14008 = AmbientSound, 8 - 14009 = AmbientSound, 9 - 14010 = AmbientSound, 10 - 14011 = AmbientSound, 11 - 14012 = AmbientSound, 12 - 14013 = AmbientSound, 13 - 14014 = AmbientSound, 14 - 14015 = AmbientSound, 15 - 14016 = AmbientSound, 16 - 14017 = AmbientSound, 17 - 14018 = AmbientSound, 18 - 14019 = AmbientSound, 19 - 14020 = AmbientSound, 20 - 14021 = AmbientSound, 21 - 14022 = AmbientSound, 22 - 14023 = AmbientSound, 23 - 14024 = AmbientSound, 24 - 14025 = AmbientSound, 25 - 14026 = AmbientSound, 26 - 14027 = AmbientSound, 27 - 14028 = AmbientSound, 28 - 14029 = AmbientSound, 29 - 14030 = AmbientSound, 30 - 14031 = AmbientSound, 31 - 14032 = AmbientSound, 32 - 14033 = AmbientSound, 33 - 14034 = AmbientSound, 34 - 14035 = AmbientSound, 35 - 14036 = AmbientSound, 36 - 14037 = AmbientSound, 37 - 14038 = AmbientSound, 38 - 14039 = AmbientSound, 39 - 14040 = AmbientSound, 40 - 14041 = AmbientSound, 41 - 14042 = AmbientSound, 42 - 14043 = AmbientSound, 43 - 14044 = AmbientSound, 44 - 14045 = AmbientSound, 45 - 14046 = AmbientSound, 46 - 14047 = AmbientSound, 47 - 14048 = AmbientSound, 48 - 14049 = AmbientSound, 49 - 14050 = AmbientSound, 50 - 14051 = AmbientSound, 51 - 14052 = AmbientSound, 52 - 14053 = AmbientSound, 53 - 14054 = AmbientSound, 54 - 14055 = AmbientSound, 55 - 14056 = AmbientSound, 56 - 14057 = AmbientSound, 57 - 14058 = AmbientSound, 58 - 14059 = AmbientSound, 59 - 14060 = AmbientSound, 60 - 14061 = AmbientSound, 61 - 14062 = AmbientSound, 62 - 14063 = AmbientSound, 63 - 14064 = AmbientSound, 64 + 14001 = AmbientSound, 1, + + 14002 = AmbientSound, 2, + + 14003 = AmbientSound, 3, + + 14004 = AmbientSound, 4, + + 14005 = AmbientSound, 5, + + 14006 = AmbientSound, 6, + + 14007 = AmbientSound, 7, + + 14008 = AmbientSound, 8, + + 14009 = AmbientSound, 9, + + 14010 = AmbientSound, 10, + + 14011 = AmbientSound, 11, + + 14012 = AmbientSound, 12, + + 14013 = AmbientSound, 13, + + 14014 = AmbientSound, 14, + + 14015 = AmbientSound, 15, + + 14016 = AmbientSound, 16, + + 14017 = AmbientSound, 17, + + 14018 = AmbientSound, 18, + + 14019 = AmbientSound, 19, + + 14020 = AmbientSound, 20, + + 14021 = AmbientSound, 21, + + 14022 = AmbientSound, 22, + + 14023 = AmbientSound, 23, + + 14024 = AmbientSound, 24, + + 14025 = AmbientSound, 25, + + 14026 = AmbientSound, 26, + + 14027 = AmbientSound, 27, + + 14028 = AmbientSound, 28, + + 14029 = AmbientSound, 29, + + 14030 = AmbientSound, 30, + + 14031 = AmbientSound, 31, + + 14032 = AmbientSound, 32, + + 14033 = AmbientSound, 33, + + 14034 = AmbientSound, 34, + + 14035 = AmbientSound, 35, + + 14036 = AmbientSound, 36, + + 14037 = AmbientSound, 37, + + 14038 = AmbientSound, 38, + + 14039 = AmbientSound, 39, + + 14040 = AmbientSound, 40, + + 14041 = AmbientSound, 41, + + 14042 = AmbientSound, 42, + + 14043 = AmbientSound, 43, + + 14044 = AmbientSound, 44, + + 14045 = AmbientSound, 45, + + 14046 = AmbientSound, 46, + + 14047 = AmbientSound, 47, + + 14048 = AmbientSound, 48, + + 14049 = AmbientSound, 49, + + 14050 = AmbientSound, 50, + + 14051 = AmbientSound, 51, + + 14052 = AmbientSound, 52, + + 14053 = AmbientSound, 53, + + 14054 = AmbientSound, 54, + + 14055 = AmbientSound, 55, + + 14056 = AmbientSound, 56, + + 14057 = AmbientSound, 57, + + 14058 = AmbientSound, 58, + + 14059 = AmbientSound, 59, + + 14060 = AmbientSound, 60, + + 14061 = AmbientSound, 61, + + 14062 = AmbientSound, 62, + + 14063 = AmbientSound, 63, + + 14064 = AmbientSound, 64, + 14065 = AmbientSound 14066 = SoundSequence 14067 = AmbientSoundNoGravity diff --git a/wadsrc/static/mapinfo/doom2bfg.txt b/wadsrc/static/mapinfo/doom2bfg.txt index cc82c4e191..28bea129d7 100644 --- a/wadsrc/static/mapinfo/doom2bfg.txt +++ b/wadsrc/static/mapinfo/doom2bfg.txt @@ -3,7 +3,7 @@ include "mapinfo/doom2.txt" gameinfo { - titlepage = "INTERPIC" + titlepage = "DMENUPIC" } clearepisodes diff --git a/wadsrc/static/mapinfo/mindefaults.txt b/wadsrc/static/mapinfo/mindefaults.txt index cbb3fa06b4..0d7b162091 100644 --- a/wadsrc/static/mapinfo/mindefaults.txt +++ b/wadsrc/static/mapinfo/mindefaults.txt @@ -56,4 +56,12 @@ gameinfo statscreen_enteringpatch = "WIENTER" } +DoomEdNums +{ + 4001 = "$Player5Start" + 4002 = "$Player6Start" + 4003 = "$Player7Start" + 4004 = "$Player8Start" +} + include "mapinfo/common.txt" diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index f15f91dff3..add8444557 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -435,8 +435,12 @@ OptionMenu "CustomizeControls" StaticText "Controls", 1 Control "Fire", "+attack" Control "Secondary Fire", "+altattack" - Control "Weapon Reload", "+reload" - Control "Weapon Zoom", "+zoom" + Control "Weapon Reload", "+reload" + Control "Weapon Zoom", "+zoom" + Control "Weapon State 1", "+user1" + Control "Weapon State 2", "+user2" + Control "Weapon State 3", "+user3" + Control "Weapon State 4", "+user4" Control "Use / Open", "+use" Control "Move forward", "+forward" Control "Move backward", "+back" @@ -444,6 +448,7 @@ OptionMenu "CustomizeControls" Control "Strafe right", "+moveright" Control "Turn left", "+left" Control "Turn right", "+right" + Control "Quick Turn", "turn180" Control "Jump", "+jump" Control "Crouch", "+crouch" Control "Crouch Toggle", "crouch" @@ -456,6 +461,7 @@ OptionMenu "CustomizeControls" Control "Look down", "+lookdown" Control "Center view", "centerview" Control "Run", "+speed" + Control "Toggle Run", "toggle cl_run" Control "Strafe", "+strafe" Control "Show Scoreboard", "+showscores" Control "Toggle Scoreboard", "togglescoreboard" @@ -1303,6 +1309,7 @@ OptionMenu "CompatibilityOptions" Option "Find shortest textures like Doom", "compat_SHORTTEX", "YesNo" Option "Use buggier stair building", "compat_stairs", "YesNo" Option "Use Doom's floor motion behavior", "compat_floormove", "YesNo" + Option "Use Doom's point-on-line algorithm", "compat_pointonline", "YesNo" StaticText " " StaticText "Physics Behavior",1 @@ -1588,10 +1595,27 @@ OptionMenu AdvSoundOptions Option "OPL Emulator Core", "opl_core", "OplCores" StaticText " " StaticText "GUS Emulation", 1 + TextField "GUS config file", "midi_config" Slider "MIDI voices", "midi_voices", 16, 256, 4, 0 - Option "Emulate TiMidity", "midi_timiditylike", "OnOff" Option "Read DMXGUS lumps", "midi_dmxgus", "OnOff" Option "GUS memory size", "gus_memsize", "GusMemory" + StaticText " " + StaticText "FluidSynth", 1 + TextField "Patch set", "fluid_patchset" + Slider "Gain", "fluid_gain", 0, 10, 0.5, 1 + Option "Reverb", "fluid_reverb", "OnOff" + Slider "MIDI voices", "fluid_voices", 16, 4096, 16, 1 + // Leaving out the more advanced stuff for now. + StaticText " " + StaticText "Timidity++", 1 + TextField "Path for executable", "timidity_exe" + Option "Reverb", "timidity_reverb", "OnOff" + Option "Chorus", "timidity_chorus", "OnOff" + Slider "Relative volume", "timidity_mastervolume", 0, 4, 0.2, 1 + StaticText " " + StaticText "WildMidi", 1 + TextField "WildMidi config file", "wildmidi_config" + Option "Reverb", "wildmidi_reverb", "OnOff" } /*======================================= diff --git a/wadsrc/static/xlat/eternity.txt b/wadsrc/static/xlat/eternity.txt index ad270beb45..7b4367ee33 100644 --- a/wadsrc/static/xlat/eternity.txt +++ b/wadsrc/static/xlat/eternity.txt @@ -170,7 +170,7 @@ define Unsupported (0) 384 = 0, Static_Init(0, 3, 0, 10) // "Attach_MirrorCeilingToControl" // Attach tagged portal to front sector -385 = 0, Sector_SetPortal(0, 1, 2, tag) // "Apply_PortalToFrontsector" +385 = 0, Sector_SetPortal(0, 1, 3, tag) // "Apply_PortalToFrontsector" // Slopes! 386 = 0, Plane_Align (1, 0) // "Slope_FrontsectorFloor" diff --git a/zdoom.vcproj b/zdoom.vcproj index d57b2d4d8f..2c88f856d5 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -106,6 +106,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -223,6 +224,7 @@ MapExports="true" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" OptimizeReferences="2" EnableCOMDATFolding="2" @@ -332,6 +334,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="0" @@ -440,6 +443,7 @@ ProgramDatabaseFile=".\Debug/zdoomd.pdb" SubSystem="2" StackReserveSize="0" + LargeAddressAware="2" TerminalServerAware="2" SetChecksum="false" TargetMachine="17" @@ -662,6 +666,10 @@ RelativePath=".\src\gitinfo.cpp" > + + @@ -938,6 +946,10 @@ RelativePath=".\src\parsecontext.cpp" > + + @@ -1018,6 +1030,10 @@ RelativePath=".\src\skins.cpp" > + + @@ -1303,6 +1319,10 @@ RelativePath=".\src\gstrings.h" > + + @@ -1475,10 +1495,18 @@ RelativePath=".\src\parsecontext.h" > + + + + @@ -1503,6 +1531,10 @@ RelativePath=".\src\skins.h" > + + @@ -2589,6 +2621,10 @@ RelativePath=".\src\sound\music_timidity_mididevice.cpp" > + + @@ -2785,6 +2821,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +