diff --git a/.circleci/config.yml b/.circleci/config.yml index ca910568..3129c3bd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,12 +36,15 @@ jobs: - v1-SRB2-APT - run: name: Install SDK - command: apt-get -qq -y install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx + command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx - save_cache: key: v1-SRB2-APT paths: - /var/cache/apt/archives - checkout + - run: + name: Compile without network support + command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 - run: name: Clean build command: make -C src LINUX=1 clean diff --git a/libs/dll-binaries/i686/libgme.dll b/libs/dll-binaries/i686/libgme.dll index ddf8b0d8..9a31bc4d 100644 Binary files a/libs/dll-binaries/i686/libgme.dll and b/libs/dll-binaries/i686/libgme.dll differ diff --git a/libs/dll-binaries/x86_64/libgme.dll b/libs/dll-binaries/x86_64/libgme.dll index 2ba99450..598c2d71 100644 Binary files a/libs/dll-binaries/x86_64/libgme.dll and b/libs/dll-binaries/x86_64/libgme.dll differ diff --git a/libs/gme/CMakeLists.txt b/libs/gme/CMakeLists.txt index 8beee872..392b0185 100644 --- a/libs/gme/CMakeLists.txt +++ b/libs/gme/CMakeLists.txt @@ -4,7 +4,7 @@ project(libgme) include (CheckCXXCompilerFlag) # When version is changed, also change the one in gme/gme.h to match -set(GME_VERSION 0.6.0 CACHE INTERNAL "libgme Version") +set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version") # 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't. # Of course, 2.4 might work, in which case you're welcome to drop @@ -57,6 +57,8 @@ if (USE_GME_NSFE AND NOT USE_GME_NSF) SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE) endif() +option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON) + # Check for GCC "visibility" support. if (CMAKE_COMPILER_IS_GNUCXX) check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY) @@ -79,10 +81,10 @@ if (CMAKE_COMPILER_IS_GNUCXX) endif() endif() endif() # test visibility -endif (CMAKE_COMPILER_IS_GNUCXX) -# Cache this result -set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") + # Cache this result + set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") +endif (CMAKE_COMPILER_IS_GNUCXX) # Shared library defined here add_subdirectory(gme) diff --git a/libs/gme/changes.txt b/libs/gme/changes.txt index 62391ebb..034ba482 100644 --- a/libs/gme/changes.txt +++ b/libs/gme/changes.txt @@ -1,262 +1,5 @@ Game_Music_Emu Change Log ------------------------- -Game_Music_Emu 0.6.0 --------------------- - -- Note: A 0.5.6 release was referenced but never tagged or packaged. - -- SPC improvements: - - Switched to newer snes_spc 0.9.0 for SPC emulation. Uses fast DSP. - - Fixed Spc_Emu::gain(). - - Fixed support for files <0x10200 bytes. - -- Other bugfixes: - - Fixed a couple of GBS bugs, one involving access of memory after - realloc. - - Blip_Buffer works on systems where 'double' is a single-precision - floating-point type. - - Fix uninitialized buffer size in dual_resampler. - - Compilation warnings squashed out as of clang 3.3-pre and gcc 4.7.2. - -- API changes/additions: - - Removed documentation of C++ interface, as the C interface in gme.h is - the only supported one. - - Added gme_enable_accuracy() for enabling more accurate sound emulation - options (currently affects SPC only). - -- Build system improvements: - - Add pkg_config support. - - Fix build on case-insensitive systems. - - Allow for install on Cygwin. - - Fix install on multilib systems, such as many 64-bit distros (CMake must - be able to figure out your system's libsuffix, if any). - - C++ implementation symbols are not leaked into the resultant library - file (requires symbol visibility support). - -- Sample player improvements: - - Can toggle fast/accurate emulation (with the 'A' key). - -Game_Music_Emu 0.5.5 --------------------- -- CMake build support has been added. You can build Game_Music_Emu as -a shared library and install it so that you do not have to include your -own copy if you know libgme will be present on your target system. -Requires CMake 2.6 or higher. - - -Game_Music_Emu 0.5.2 --------------------- -- *TONS* of changes and improvements. You should re-read the new header -files and documentation as the changes will allow you to simplify your -code a lot (it might even be simpler to just rewrite it). Existing code -should continue to work without changes in most cases (see Deprecated -features in gme.txt). - -- New file formats: AY, HES, KSS, SAP, NSFE - -- All-new comprehensive C interface (also usable from C++). Simplifies -many things, especially file loading, and brings everything together in -one header file (gme.h). - -- Information tags and track names and times can be accessed for all -game music formats - -- New features supported by all emulators: end of track fading, -automatic silence detection, adjustable song tempo, seek to new time in -track - -- Updated mini player example to support track names and times, echo, -tempo, and channel muting, and added visual waveform display - -- Improved configuration to use blargg_config.h, which you can modify -and keep when you update to a newer libary version. Includes flag for -library to automatically handle gzipped files using zlib (so you don't -need to use Gzip_File_Reader anymore). - -- GBS: Fixed wave channel to not reset waveform when APU is powered off -(affected Garfield). Also improved invalid bank selection (affected Game -& Watch and others). - -- VGM: Added support for alternate noise shifter register -configurations, used by other systems like the BBC Micro. - -- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I -scanned needed it, and an SPC file can include a copy if necessary. Also -re-enabled supposed clamping in gaussian interpolation between the third -and fourth lookups, though I don't know whether it matters - -- Added Music_Emu::load_mem() to use music data already in memory -(without copying it) - -- Added Music_Emu::warning(), which reports minor problems when loading -and playing a music file - -- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only -be set during initialization, so not useful as a general volume control. - -- Added custom operator new to ensure that no exceptions are thrown in -the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++) - -- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality -bandlimited synthesis in "classic" emulators, which might help -performance on ancient processors (measure first!). Don't use this -unless absolutely necessary, as quality suffers. - -- Improved performance a bit for x86 platforms - -- Text files now in DOS newline format so they will open in Notepad -properly - -- Removed requirement that file header structures not have any padding -added to the end - -- Fixed common bug in all CPU emulators where negative program counter -could crash emulator (occurred during a negative branch from the -beginning of memory). Also fixed related bug in Z80 emulator for -IX/IY+displacement mode. - -- Eliminated all warnings when compiling on gcc 4.0. The following -generates no diagnostics: - - gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords - -fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align - -Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror - -Winline -Wlong-long -Wmultichar -Winvalid-offsetof - -Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses - -Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare - -Wsign-promo -Wunknown-pragmas -Wwrite-strings - - -Game_Music_Emu 0.3.0 --------------------- -- Added more demos, including music player using the SDL multimedia -library for sound, and improved documentation - -- All: Improved interface to emulators to allow simpler setup and -loading. Instead of various init() functions, all now support -set_sample_rate( long rate ) and load( const char* file_path ). - -- All: Removed error return from start_track() and play(), and added -error_count() to get the total number of emulation errors since the -track was last started. See demos for examples of new usage. - -- All: Fixed mute_voices() muting to be preserved after loading files -and starting tracks, instead of being cleared as it was whenever a track -was started - -- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at -any sample rate with optional FM oversampling, support for alternate -YM2612 sound cores, and support for optional YM2413 - -- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis -music to 50Hz PAL - -- VGM: Removed Vgm_Emu::track_data(), since I realized that this -information is already present in the VGM header (oops!) - -- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h) - -- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick -soundtrack - -- NSF: Fixed Namco 106 problems with Final Lap and others - -- Moved library sources to gme/ directory to reduce clutter, and merged -boost/ functionality into blargg_common.h - -- Added Gzip_File_Reader for transparently using gzipped files - - -Game_Music_Emu 0.2.4 --------------------- -- Created a discussion forum for problems and feedback: -http://groups-beta.google.com/group/blargg-sound-libs - -- Changed error return value of Blip_Buffer::sample_rate() (also for -Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in -blargg_common.h), to make error reporting consistent with other -functions. This means the "no error" return value is the opposite of -what it was before, which will break current code which checks the error -return value: - - // current code (broken) - if ( !buf.sample_rate( samples_per_sec ) ) - out_of_memory(); - - // quick-and-dirty fix (just remove the ! operation) - if ( buf.sample_rate( samples_per_sec ) ) - out_of_memory(); - - // proper fix - blargg_err_t error = buf.sample_rate( samples_per_sec ); - if ( error ) - report_error( error ); - -- Implemented workaround for MSVC++ 6 compiler limitations, allowing it -to work on that compiler again - -- Added sample clamping to avoid wrap-around at high volumes, allowing -higher volume with little distortion - -- Added to-do list and design notes - -- Added Music_Emu::skip( long sample_count ) to skip ahead in current -track - -- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for -determining the length of non-looped GYM and VGM files - -- Partially implemented DMC non-linearity when its value is directly set -using $4011, which reduces previously over-emphasized "popping" of -percussion on some games (TMNT II in particular) - -- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly -using abs() instead of fabs()...argh) - -- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and -now stops sample slightly earlier than the end, as the SNES does. Fixed -a totally broken CPU addressing mode. - -- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music -sounds decent - -- Fixed a minor GBS emulation bug - -- Fixed GYM loop point bug when track was restarted before loop point -had been reached - -- Made default GBS frequency equalization less muffled - -- Added pseudo-surround effect removal for SPC files - -- Added Music_Emu::voice_names() which returns names for each voice. - -- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be -easily set for library sources - -- Changed assignment of expansion sound chips in Nsf_Emu to be spread -more evenly when using Effects_Buffer - -- Changed 'size_t' values in Blip_Buffer interface to 'long' - -- Changed demo to generate a WAVE sound file rather than an AIFF file - - -Game_Music_Emu 0.2.0 --------------------- -- Redid framework and rewrote/cleaned up emulators - -- Changed licensing to GNU Lesser General Public License (LGPL) - -- Added Sega Genesis GYM and Super Nintendo SPC emulators - -- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator - -- Eliminated use of static mutable data in emulators, allowing -multi-instance safety - - -Game_Music_Emu 0.1.0 --------------------- -- First release +Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1) +for the accurate change log. diff --git a/libs/gme/demo/basics.c b/libs/gme/demo/basics.c index 55178251..741574af 100644 --- a/libs/gme/demo/basics.c +++ b/libs/gme/demo/basics.c @@ -1,7 +1,5 @@ /* C example that opens a game music file and records 10 seconds to "out.wav" */ -static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ - #include "gme/gme.h" #include "Wave_Writer.h" /* wave_ functions for writing sound file */ @@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type) void handle_error( const char* str ); -int main() +int main(int argc, char *argv[]) { + const char *filename = "test.nsf"; /* Default file to open */ + if ( argc >= 2 ) + filename = argv[1]; + long sample_rate = 44100; /* number of samples per second */ - int track = 0; /* index of track to play (0 = first) */ + /* index of track to play (0 = first) */ + int track = argc >= 3 ? atoi(argv[2]) : 0; /* Open music file in new emulator */ Music_Emu* emu; diff --git a/libs/gme/demo/cpp_basics.cpp b/libs/gme/demo/cpp_basics.cpp index 53fab418..5222fe27 100644 --- a/libs/gme/demo/cpp_basics.cpp +++ b/libs/gme/demo/cpp_basics.cpp @@ -1,7 +1,5 @@ // C++ example that opens a game music file and records 10 seconds to "out.wav" -static char filename [] = "test.nsf"; /* opens this file (can be any music type) */ - #include "gme/Music_Emu.h" #include "Wave_Writer.h" @@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type) void handle_error( const char* str ); -int main() +int main(int argc, char *argv[]) { + const char *filename = "test.nsf"; /* Default file to open */ + if ( argc >= 2 ) + filename = argv[1]; + long sample_rate = 44100; // number of samples per second - int track = 0; // index of track to play (0 = first) + // index of track to play (0 = first) + int track = argc >= 3 ? atoi(argv[2]) : 0; // Determine file type gme_type_t file_type; diff --git a/libs/gme/gme.txt b/libs/gme/gme.txt index d9a2452c..5a7d2f56 100644 --- a/libs/gme/gme.txt +++ b/libs/gme/gme.txt @@ -1,10 +1,10 @@ -Game_Music_Emu 0.6.0 +Game_Music_Emu 0.6.2 -------------------- -Author : Shay Green -Website: http://www.slack.net/~ant/libs/ -Forum : http://groups.google.com/group/blargg-sound-libs -Source : https://code.google.com/p/game-music-emu/ -License: GNU Lesser General Public License (LGPL) +Author : Shay Green +Maintainer : Michael Pyne +Website : https://bitbucket.org/mpyne/game-music-emu/ +Source : https://bitbucket.org/mpyne/game-music-emu/ +License : GNU Lesser General Public License (LGPL), see LICENSE.txt Contents -------- diff --git a/libs/gme/gme/CMakeLists.txt b/libs/gme/gme/CMakeLists.txt index 3c6464fc..534be8a8 100644 --- a/libs/gme/gme/CMakeLists.txt +++ b/libs/gme/gme/CMakeLists.txt @@ -143,7 +143,7 @@ add_definitions(-DBLARGG_BUILD_DLL) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Add library to be compiled. -add_library(gme SHARED ${libgme_SRCS}) +add_library(gme ${libgme_SRCS}) # The version is the release. The "soversion" is the API version. As long # as only build fixes are performed (i.e. no backwards-incompatible changes @@ -159,4 +159,4 @@ install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib) # DLL platforms install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib/pkgconfig) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) diff --git a/libs/gme/gme/Data_Reader.cpp b/libs/gme/gme/Data_Reader.cpp index 5bbfbf55..f18928f4 100644 --- a/libs/gme/gme/Data_Reader.cpp +++ b/libs/gme/gme/Data_Reader.cpp @@ -22,8 +22,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ const char Data_Reader::eof_error [] = "Unexpected end of file"; +#define RETURN_VALIDITY_CHECK( cond ) \ + do { if ( unlikely( !(cond) ) ) return "Corrupt file"; } while(0) + blargg_err_t Data_Reader::read( void* p, long s ) { + RETURN_VALIDITY_CHECK( s > 0 ); + long result = read_avail( p, s ); if ( result != s ) { @@ -38,6 +43,8 @@ blargg_err_t Data_Reader::read( void* p, long s ) blargg_err_t Data_Reader::skip( long count ) { + RETURN_VALIDITY_CHECK( count >= 0 ); + char buf [512]; while ( count ) { @@ -54,7 +61,8 @@ long File_Reader::remain() const { return size() - tell(); } blargg_err_t File_Reader::skip( long n ) { - assert( n >= 0 ); + RETURN_VALIDITY_CHECK( n >= 0 ); + if ( !n ) return 0; return seek( tell() + n ); @@ -67,13 +75,14 @@ Subset_Reader::Subset_Reader( Data_Reader* dr, long size ) in = dr; remain_ = dr->remain(); if ( remain_ > size ) - remain_ = size; + remain_ = max( 0l, size ); } long Subset_Reader::remain() const { return remain_; } long Subset_Reader::read_avail( void* p, long s ) { + s = max( 0l, s ); if ( s > remain_ ) s = remain_; remain_ -= s; @@ -85,7 +94,7 @@ long Subset_Reader::read_avail( void* p, long s ) Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r ) { header = (char const*) h; - header_end = header + size; + header_end = header + max( 0l, size ); in = r; } @@ -93,6 +102,7 @@ long Remaining_Reader::remain() const { return header_end - header + in->remain( long Remaining_Reader::read_first( void* out, long count ) { + count = max( 0l, count ); long first = header_end - header; if ( first ) { @@ -107,8 +117,9 @@ long Remaining_Reader::read_first( void* out, long count ) long Remaining_Reader::read_avail( void* out, long count ) { + count = max( 0l, count ); long first = read_first( out, count ); - long second = count - first; + long second = max( 0l, count - first ); if ( second ) { second = in->read_avail( (char*) out + first, second ); @@ -120,8 +131,9 @@ long Remaining_Reader::read_avail( void* out, long count ) blargg_err_t Remaining_Reader::read( void* out, long count ) { + count = max( 0l, count ); long first = read_first( out, count ); - long second = count - first; + long second = max( 0l, count - first ); if ( !second ) return 0; return in->read( (char*) out + first, second ); @@ -131,7 +143,7 @@ blargg_err_t Remaining_Reader::read( void* out, long count ) Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : begin( (const char*) p ), - size_( s ) + size_( max( 0l, s ) ) { pos = 0; } @@ -141,6 +153,7 @@ long Mem_File_Reader::size() const { return size_; } long Mem_File_Reader::read_avail( void* p, long s ) { long r = remain(); + s = max( 0l, s ); if ( s > r ) s = r; memcpy( p, begin + pos, s ); @@ -152,6 +165,7 @@ long Mem_File_Reader::tell() const { return pos; } blargg_err_t Mem_File_Reader::seek( long n ) { + RETURN_VALIDITY_CHECK( n >= 0 ); if ( n > size_ ) return eof_error; pos = n; @@ -164,7 +178,7 @@ Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : callback( c ), data( d ) { - remain_ = size; + remain_ = max( 0l, size ); } long Callback_Reader::remain() const { return remain_; } @@ -173,13 +187,14 @@ long Callback_Reader::read_avail( void* out, long count ) { if ( count > remain_ ) count = remain_; - if ( Callback_Reader::read( out, count ) ) + if ( count < 0 || Callback_Reader::read( out, count ) ) count = -1; return count; } blargg_err_t Callback_Reader::read( void* out, long count ) { + RETURN_VALIDITY_CHECK( count >= 0 ); if ( count > remain_ ) return eof_error; return callback( data, out, count ); @@ -210,11 +225,12 @@ long Std_File_Reader::size() const long Std_File_Reader::read_avail( void* p, long s ) { - return fread( p, 1, s, (FILE*) file_ ); + return fread( p, 1, max( 0l, s ), (FILE*) file_ ); } blargg_err_t Std_File_Reader::read( void* p, long s ) { + RETURN_VALIDITY_CHECK( s > 0 ); if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) return 0; if ( feof( (FILE*) file_ ) ) diff --git a/libs/gme/gme/Data_Reader.h b/libs/gme/gme/Data_Reader.h index acf571f6..6c22b678 100644 --- a/libs/gme/gme/Data_Reader.h +++ b/libs/gme/gme/Data_Reader.h @@ -129,6 +129,8 @@ private: }; #ifdef HAVE_ZLIB_H +#include + // Gzip compressed file reader class Gzip_File_Reader : public File_Reader { public: @@ -143,7 +145,7 @@ public: long tell() const; blargg_err_t seek( long ); private: - void* file_; + gzFile file_; long size_; }; #endif diff --git a/libs/gme/gme/Music_Emu.cpp b/libs/gme/gme/Music_Emu.cpp index 30b25dcf..942e86e2 100644 --- a/libs/gme/gme/Music_Emu.cpp +++ b/libs/gme/gme/Music_Emu.cpp @@ -178,6 +178,11 @@ blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; } +long Music_Emu::tell_samples() const +{ + return out_time; +} + long Music_Emu::tell() const { blargg_long rate = sample_rate() * stereo; @@ -185,14 +190,18 @@ long Music_Emu::tell() const return sec * 1000 + (out_time - sec * rate) * 1000 / rate; } -blargg_err_t Music_Emu::seek( long msec ) +blargg_err_t Music_Emu::seek_samples( long time ) { - blargg_long time = msec_to_samples( msec ); if ( time < out_time ) RETURN_ERR( start_track( current_track_ ) ); return skip( time - out_time ); } +blargg_err_t Music_Emu::seek( long msec ) +{ + return seek_samples( msec_to_samples( msec ) ); +} + blargg_err_t Music_Emu::skip( long count ) { require( current_track() >= 0 ); // start_track() must have been called already diff --git a/libs/gme/gme/Music_Emu.h b/libs/gme/gme/Music_Emu.h index b96f4b61..d98f7ce7 100644 --- a/libs/gme/gme/Music_Emu.h +++ b/libs/gme/gme/Music_Emu.h @@ -41,9 +41,15 @@ public: // Number of milliseconds (1000 msec = 1 second) played since beginning of track long tell() const; + // Number of samples generated since beginning of track + long tell_samples() const; + // Seek to new time in track. Seeking backwards or far forward can take a while. blargg_err_t seek( long msec ); + // Equivalent to restarting track then skipping n samples + blargg_err_t seek_samples( long n ); + // Skip n samples blargg_err_t skip( long n ); diff --git a/libs/gme/gme/Nsfe_Emu.cpp b/libs/gme/gme/Nsfe_Emu.cpp index 824a1a24..55ac4688 100644 --- a/libs/gme/gme/Nsfe_Emu.cpp +++ b/libs/gme/gme/Nsfe_Emu.cpp @@ -134,6 +134,9 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) RETURN_ERR( in.read( block_header, sizeof block_header ) ); blargg_long size = get_le32( block_header [0] ); blargg_long tag = get_le32( block_header [1] ); + + if ( size <= 0 ) + return "Corrupt file"; //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); diff --git a/libs/gme/gme/Spc_Cpu.cpp b/libs/gme/gme/Spc_Cpu.cpp index 90f60ed2..19aae113 100644 --- a/libs/gme/gme/Spc_Cpu.cpp +++ b/libs/gme/gme/Spc_Cpu.cpp @@ -433,9 +433,7 @@ void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) #endif // Registers other than $F2 and $F4-$F7 - //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) - // TODO: this is a bit on the fragile side - if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% + if ( reg != 2 && (reg < 4 || reg > 7) ) // 36% cpu_write_smp_reg( data, time, reg ); } // High mem/address wrap-around diff --git a/libs/gme/gme/Spc_Cpu.h b/libs/gme/gme/Spc_Cpu.h index 4742e099..10c24509 100644 --- a/libs/gme/gme/Spc_Cpu.h +++ b/libs/gme/gme/Spc_Cpu.h @@ -76,8 +76,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // TODO: remove non-wrapping versions? #define SPC_NO_SP_WRAPAROUND 0 -#define SET_SP( v ) (sp = ram + 0x101 + (v)) -#define GET_SP() (sp - 0x101 - ram) +#define SET_SP( v ) (sp = ram + 0x101 + ((uint8_t) v)) +#define GET_SP() (uint8_t (sp - 0x101 - ram)) #if SPC_NO_SP_WRAPAROUND #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) @@ -485,7 +485,7 @@ loop: case 0xAF: // MOV (X)+,A WRITE_DP( 0, x, a + no_read_before_write ); - x++; + x = (uint8_t) (x + 1); goto loop; // 5. 8-BIT LOGIC OPERATION COMMANDS @@ -808,7 +808,7 @@ loop: unsigned temp = y * a; a = (uint8_t) temp; nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; + y = (uint8_t) (temp >> 8); nz |= y; goto loop; } @@ -838,6 +838,7 @@ loop: nz = (uint8_t) a; a = (uint8_t) a; + y = (uint8_t) y; goto loop; } @@ -1004,7 +1005,7 @@ loop: case 0x7F: // RET1 temp = *sp; SET_PC( GET_LE16( sp + 1 ) ); - sp += 3; + SET_SP( GET_SP() + 3 ); goto set_psw; case 0x8E: // POP PSW POP( temp ); diff --git a/libs/gme/gme/blargg_source.h b/libs/gme/gme/blargg_source.h index b011777a..b65afd30 100644 --- a/libs/gme/gme/blargg_source.h +++ b/libs/gme/gme/blargg_source.h @@ -18,6 +18,19 @@ all other #include lines. */ #undef require #define require( expr ) assert( expr ) +// Use to provide hints to compiler for optimized code layout in situations where we +// can almost always expect a conditional to go one way or the other. Should only be +// used in situations where an unexpected branch is truly exceptional though! +#undef likely +#undef unlikely +#ifdef __GNUC__ + #define likely( x ) __builtin_expect(x, 1) + #define unlikely( x ) __builtin_expect(x, 0) +#else + #define likely( x ) (x) + #define unlikely( x ) (x) +#endif + // Like printf() except output goes to debug log file. Might be defined to do // nothing (not even evaluate its arguments). // void debug_printf( const char* format, ... ); diff --git a/libs/gme/gme/gme.cpp b/libs/gme/gme/gme.cpp index c05f25eb..47709840 100644 --- a/libs/gme/gme/gme.cpp +++ b/libs/gme/gme/gme.cpp @@ -337,7 +337,9 @@ BLARGG_EXPORT gme_err_t gme_play ( Music_Emu* me, int n, short* p ) BLARGG_EXPORT void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); } BLARGG_EXPORT int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } BLARGG_EXPORT int gme_tell ( Music_Emu const* me ) { return me->tell(); } +BLARGG_EXPORT int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); } BLARGG_EXPORT gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); } +BLARGG_EXPORT gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); } BLARGG_EXPORT int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } BLARGG_EXPORT void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } diff --git a/libs/gme/gme/gme.h b/libs/gme/gme/gme.h index 1f2a2d15..cb07061b 100644 --- a/libs/gme/gme/gme.h +++ b/libs/gme/gme/gme.h @@ -1,6 +1,6 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.6.0 */ +/* Game_Music_Emu 0.6.1 */ #ifndef GME_H #define GME_H @@ -8,7 +8,7 @@ extern "C" { #endif -#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */ +#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */ /* Error string returned by library functions, or NULL if no error (success) */ typedef const char* gme_err_t; @@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* ); /* Number of milliseconds (1000 = one second) played since beginning of track */ int gme_tell( Music_Emu const* ); +/* Number of samples generated since beginning of track */ +int gme_tell_samples( Music_Emu const* ); + /* Seek to new time in track. Seeking backwards or far forward can take a while. */ gme_err_t gme_seek( Music_Emu*, int msec ); +/* Equivalent to restarting track then skipping n samples */ +gme_err_t gme_seek_samples( Music_Emu*, int n ); + /******** Informational ********/ diff --git a/libs/gme/gme/libgme.pc.in b/libs/gme/gme/libgme.pc.in index 4f420d9e..49fd5b1d 100644 --- a/libs/gme/gme/libgme.pc.in +++ b/libs/gme/gme/libgme.pc.in @@ -3,7 +3,7 @@ # later are used by pkg-config. prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} -lib_suffix= +lib_suffix=@LIB_SUFFIX@ libdir=${exec_prefix}/lib${lib_suffix} includedir=${prefix}/include @@ -13,3 +13,4 @@ URL: http://code.google.com/p/game-music-emu/ Version: @GME_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lgme +Libs.private: -lstdc++ diff --git a/libs/gme/include/gme/gme.h b/libs/gme/include/gme/gme.h index 1f2a2d15..cb07061b 100644 --- a/libs/gme/include/gme/gme.h +++ b/libs/gme/include/gme/gme.h @@ -1,6 +1,6 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.6.0 */ +/* Game_Music_Emu 0.6.1 */ #ifndef GME_H #define GME_H @@ -8,7 +8,7 @@ extern "C" { #endif -#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */ +#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */ /* Error string returned by library functions, or NULL if no error (success) */ typedef const char* gme_err_t; @@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* ); /* Number of milliseconds (1000 = one second) played since beginning of track */ int gme_tell( Music_Emu const* ); +/* Number of samples generated since beginning of track */ +int gme_tell_samples( Music_Emu const* ); + /* Seek to new time in track. Seeking backwards or far forward can take a while. */ gme_err_t gme_seek( Music_Emu*, int msec ); +/* Equivalent to restarting track then skipping n samples */ +gme_err_t gme_seek_samples( Music_Emu*, int n ); + /******** Informational ********/ diff --git a/libs/gme/readme.txt b/libs/gme/readme.txt index 82a501db..4cfe5e7a 100644 --- a/libs/gme/readme.txt +++ b/libs/gme/readme.txt @@ -1,4 +1,4 @@ -Game_Music_Emu 0.6.0: Game Music Emulators +Game_Music_Emu 0.6.2: Game Music Emulators ------------------------------------------ Game_Music_Emu is a collection of video game music file emulators that support the following formats and systems: @@ -34,30 +34,45 @@ several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable, GP2X, and Nintendo DS. Author : Shay Green -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs +Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home License: GNU Lesser General Public License (LGPL) +Current Maintainer: Michael Pyne Getting Started --------------- Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and -all source files in gme/. If you have CMake 2.6 or later, execute +all source files in gme/. - run cmake - cd demo - run make +Or, if you have CMake 2.6 or later, execute at a command prompt (from the +extracted source directory): -Be sure "test.nsf" is in the same directory as the program. Running it + mkdir build + cd build + cmake ../ # <-- Pass any needed CMake flags here + make # To build the library + cd demo + make # To build the demo itself + +Be sure "test.nsf" is in the same directory as the demo program. Running it should generate the recording "out.wav". +You can use "make install" to install the library. To choose where to install +the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local" +(and replace /usr/local with the base path you wish to use). Alternately, you +can specify the base path to install to when you run "make install" by passing +'DESTDIR=/usr/local' on the make install command line (again, replace +/usr/local as appropriate). + +To build a static library instead of shared (the default), pass +-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake. + A slightly more extensive demo application is available in the player/ directory. It requires SDL to build. Read gme.txt for more information. Post to the discussion forum for assistance. - Files ----- gme.txt General notes about the library diff --git a/libs/gme/win32/libgme.dll.a b/libs/gme/win32/libgme.dll.a index d56d8739..2c5e9585 100644 Binary files a/libs/gme/win32/libgme.dll.a and b/libs/gme/win32/libgme.dll.a differ diff --git a/libs/gme/win64/libgme.dll.a b/libs/gme/win64/libgme.dll.a index 38079dc2..8348f12d 100644 Binary files a/libs/gme/win64/libgme.dll.a and b/libs/gme/win64/libgme.dll.a differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index faa860d4..e93777cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -379,6 +379,12 @@ if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB}) set(SRB2_HAVE_PNG ON) add_definitions(-DHAVE_PNG) add_definitions(-D_LARGEFILE64_SOURCE) + set(SRB2_PNG_SOURCES apng.c) + set(SRB2_PNG_HEADERS apng.h) + prepend_sources(SRB2_PNG_SOURCES) + prepend_sources(SRB2_PNG_HEADERS) + source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS} + ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS}) else() message(WARNING "You have specified that PNG is available but it was not found. SRB2Kart may not compile correctly.") endif() diff --git a/src/Makefile b/src/Makefile index 6c686c2d..b84a06a4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -341,6 +341,8 @@ endif LIBS+=$(PNG_LDFLAGS) CFLAGS+=$(PNG_CFLAGS) + +OBJS+=$(OBJDIR)/apng.o endif ifdef HAVE_LIBGME diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 1238050b..b6faf446 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -189,12 +189,6 @@ ifdef GCC46 WFLAGS+=-Wno-suggest-attribute=noreturn endif -ifndef MINGW -ifdef GCC45 -WFLAGS+=-Wunsuffixed-float-constants -endif -endif - ifdef NOLDWARNING LDFLAGS+=-Wl,--as-needed endif @@ -208,6 +202,9 @@ WFLAGS+=$(OLDWFLAGS) ifdef GCC43 #WFLAGS+=-Wno-error=clobbered endif +ifdef GCC44 + WFLAGS+=-Wno-error=array-bounds +endif ifdef GCC46 WFLAGS+=-Wno-error=suggest-attribute=noreturn endif @@ -228,6 +225,7 @@ ifdef GCC80 WFLAGS+=-Wno-format-overflow WFLAGS+=-Wno-stringop-truncation WFLAGS+=-Wno-stringop-overflow + WFLAGS+=-Wno-error=multistatement-macros endif diff --git a/src/apng.c b/src/apng.c new file mode 100644 index 00000000..694b3d1e --- /dev/null +++ b/src/apng.c @@ -0,0 +1,289 @@ +/* +Copyright 2019, James R. +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 + +#include "apng.h" + +#define APNG_INFO_acTL 0x20000U + +#define APNG_WROTE_acTL 0x10000U + +struct apng_info_def +{ + png_uint_32 mode; + png_uint_32 valid; + + png_uint_32 num_frames; + png_uint_32 num_plays; + + long start_acTL;/* acTL is written here */ + + png_flush_ptr output_flush_fn; + apng_seek_ptr output_seek_fn; + apng_tell_ptr output_tell_fn; + + apng_set_acTL_ptr set_acTL_fn; +}; + +/* PROTOS (FUCK COMPILER) */ +void apng_seek (png_structp, apng_const_infop, size_t); +size_t apng_tell (png_structp, apng_const_infop); +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void apng_flush (png_structp, apng_infop); +#ifdef PNG_STDIO_SUPPORTED +void apng_default_flush (png_structp); +#endif/* PNG_STDIO_SUPPORTED */ +#endif/* PNG_WRITE_FLUSH_SUPPORTED */ +#ifdef PNG_STDIO_SUPPORTED +void apng_default_seek (png_structp, size_t); +size_t apng_default_tell (png_structp); +#endif/* PNG_STDIO_SUPPORTED */ +void apng_write_IEND (png_structp); +void apng_write_acTL (png_structp, png_uint_32, png_uint_32); +#ifndef PNG_WRITE_APNG_SUPPORTED +png_uint_32 apng_set_acTL_dummy (png_structp, png_infop, + png_uint_32, png_uint_32); +#endif/* PNG_WRITE_APNG_SUPPORTED */ + +apng_infop +apng_create_info_struct (png_structp pngp) +{ + apng_infop ainfop; + (void)pngp; + if (( ainfop = calloc(sizeof (apng_info),1) )) + { + apng_set_write_fn(pngp, ainfop, 0, 0, 0, 0, 0); + apng_set_set_acTL_fn(pngp, ainfop, 0); + } + return ainfop; +} + +void +apng_destroy_info_struct (png_structp pngp, apng_infopp ainfopp) +{ + (void)pngp; + if (!( pngp && ainfopp )) + return; + + free((*ainfopp)); +} + +void +apng_seek (png_structp pngp, apng_const_infop ainfop, size_t l) +{ + (*(ainfop->output_seek_fn))(pngp, l); +} + +size_t +apng_tell (png_structp pngp, apng_const_infop ainfop) +{ + return (*(ainfop->output_tell_fn))(pngp); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void +apng_flush (png_structp pngp, apng_infop ainfop) +{ + if (ainfop->output_flush_fn) + (*(ainfop->output_flush_fn))(pngp); +} + +#ifdef PNG_STDIO_SUPPORTED +void +apng_default_flush (png_structp pngp) +{ + if (!( pngp )) + return; + + fflush((png_FILE_p)png_get_io_ptr); +} +#endif/* PNG_STDIO_SUPPORTED */ +#endif/* PNG_WRITE_FLUSH_SUPPORTED */ + +#ifdef PNG_STDIO_SUPPORTED +void +apng_default_seek (png_structp pngp, size_t l) +{ + if (!( pngp )) + return; + + if (fseek((png_FILE_p)png_get_io_ptr(pngp), (long)l, SEEK_SET) == -1) + png_error(pngp, "Seek Error"); +} + +size_t +apng_default_tell (png_structp pngp) +{ + long l; + + if (!( pngp )) + { + png_error(pngp, "Call to apng_default_tell with NULL pngp failed"); + } + + if (( l = ftell((png_FILE_p)png_get_io_ptr(pngp)) ) == -1) + png_error(pngp, "Tell Error"); + + return (size_t)l; +} +#endif/* PNG_STDIO_SUPPORTED */ + +void +apng_set_write_fn (png_structp pngp, apng_infop ainfop, png_voidp iop, + png_rw_ptr write_f, png_flush_ptr flush_f, + apng_seek_ptr seek_f, apng_tell_ptr tell_f) +{ + if (!( pngp && ainfop )) + return; + + png_set_write_fn(pngp, iop, write_f, flush_f); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED + if (!flush_f) + ainfop->output_flush_fn = &apng_default_flush; + else +#endif/* PNG_STDIO_SUPPORTED */ + ainfop->output_flush_fn = flush_f; +#endif/* PNG_WRITE_FLUSH_SUPPORTED */ +#ifdef PNG_STDIO_SUPPORTED + if (!seek_f) + ainfop->output_seek_fn = &apng_default_seek; + else +#endif/* PNG_STDIO_SUPPORTED */ + ainfop->output_seek_fn = seek_f; +#ifdef PNG_STDIO_SUPPORTED + if (!seek_f) + ainfop->output_tell_fn = apng_default_tell; + else +#endif/* PNG_STDIO_SUPPORTED */ + ainfop->output_tell_fn = tell_f; +} + +void +apng_write_IEND (png_structp pngp) +{ + png_byte chunkc[] = "IEND"; + png_write_chunk(pngp, chunkc, 0, 0); +} + +void +apng_write_acTL (png_structp pngp, png_uint_32 frames, png_uint_32 plays) +{ + png_byte chunkc[] = "acTL"; + png_byte buf[8]; + png_save_uint_32(buf, frames); + png_save_uint_32(buf + 4, plays); + png_write_chunk(pngp, chunkc, buf, 8); +} + +png_uint_32 +apng_set_acTL (png_structp pngp, png_infop infop, apng_infop ainfop, + png_uint_32 frames, png_uint_32 plays) +{ + (void)pngp; + (void)infop; + if (!( pngp && infop && ainfop )) + return 0; + + ainfop->num_frames = frames; + ainfop->num_plays = plays; + + ainfop->valid |= APNG_INFO_acTL; + + return 1; +} + +void +apng_write_info_before_PLTE (png_structp pngp, png_infop infop, + apng_infop ainfop) +{ + if (!( pngp && infop && ainfop )) + return; + + png_write_info_before_PLTE(pngp, infop); + + if (( ainfop->valid & APNG_INFO_acTL )&&!( ainfop->mode & APNG_WROTE_acTL )) + { + ainfop->start_acTL = apng_tell(pngp, ainfop); + + apng_write_acTL(pngp, 0, 0); + /* modified for runtime dynamic linking */ + (*(ainfop->set_acTL_fn))(pngp, infop, PNG_UINT_31_MAX, 0); + + ainfop->mode |= APNG_WROTE_acTL; + } +} + +void +apng_write_info (png_structp pngp, png_infop infop, + apng_infop ainfop) +{ + apng_write_info_before_PLTE(pngp, infop, ainfop); + png_write_info(pngp, infop); +} + +void +apng_write_end (png_structp pngp, png_infop infop, apng_infop ainfop) +{ + (void)infop; + apng_write_IEND(pngp); + apng_seek(pngp, ainfop, ainfop->start_acTL); + apng_write_acTL(pngp, ainfop->num_frames, ainfop->num_plays); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + apng_flush(pngp, infop); +#endif/* PNG_WRITE_FLUSH_SUPPORTED */ +#endif/* PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED */ +} + +#ifndef PNG_WRITE_APNG_SUPPORTED +png_uint_32 +apng_set_acTL_dummy (png_structp pngp, png_infop infop, + png_uint_32 frames, png_uint_32 plays) +{ + (void)pngp; + (void)infop; + (void)frames; + (void)plays; + return 0; +} +#endif/* PNG_WRITE_APNG_SUPPORTED */ + +/* Dynamic runtime linking capable! (Hopefully.) */ +void +apng_set_set_acTL_fn (png_structp pngp, apng_infop ainfop, + apng_set_acTL_ptr set_acTL_f) +{ + (void)pngp; + if (!ainfop->set_acTL_fn) +#ifndef PNG_WRITE_APNG_SUPPORTED + ainfop->set_acTL_fn = &apng_set_acTL_dummy; +#else + ainfop->set_acTL_fn = &png_set_acTL; +#endif/* PNG_WRITE_APNG_SUPPORTED */ + else + ainfop->set_acTL_fn = set_acTL_f; +} diff --git a/src/apng.h b/src/apng.h new file mode 100644 index 00000000..aa7fac3d --- /dev/null +++ b/src/apng.h @@ -0,0 +1,82 @@ +/* +Copyright 2019, James R. +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 APNG_H +#define APNG_H + +#ifndef _MSC_VER +#ifndef _WII +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#endif +#endif + +#ifndef _LFS64_LARGEFILE +#define _LFS64_LARGEFILE +#endif + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 +#endif + +#include + +typedef struct apng_info_def apng_info; +typedef apng_info * apng_infop; +typedef const apng_info * apng_const_infop; +typedef apng_info * * apng_infopp; + +typedef void (*apng_seek_ptr)(png_structp, size_t); +typedef size_t (*apng_tell_ptr)(png_structp); + +typedef png_uint_32 (*apng_set_acTL_ptr)(png_structp, png_infop, + png_uint_32, png_uint_32); + +apng_infop apng_create_info_struct (png_structp png_ptr); + +void apng_destroy_info_struct (png_structp png_ptr, + apng_infopp info_ptr_ptr); + +/* Call the following functions in place of the libpng counterparts. */ + +png_uint_32 apng_set_acTL (png_structp png_ptr, png_infop info_ptr, + apng_infop ainfo_ptr, + png_uint_32 num_frames, png_uint_32 num_plays); + +void apng_write_info_before_PLTE (png_structp png_ptr, png_infop info_ptr, + apng_infop ainfo_ptr); +void apng_write_info (png_structp png_ptr, png_infop info_ptr, + apng_infop ainfo_ptr); + +void apng_write_end (png_structp png_ptr, png_infop info_ptr, + apng_infop ainfo_ptr); + +void apng_set_write_fn (png_structp png_ptr, apng_infop ainfo_ptr, + png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn, + apng_seek_ptr output_seek_fn, apng_tell_ptr output_tell_fn); + +void apng_set_set_acTL_fn (png_structp png_ptr, apng_infop ainfo_ptr, + apng_set_acTL_ptr set_acTL_fn); + +#endif/* APNG_H */ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 8ed33d52..370b2dea 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2211,8 +2211,10 @@ static void CL_ConnectToServer(boolean viams) } while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); +#ifndef NONET if (netgame) F_StartWaitingPlayers(); +#endif DEBFILE(va("Synchronisation Finished\n")); displayplayer = consoleplayer; @@ -2542,6 +2544,8 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason) #ifdef HAVE_BLUA LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting +#else + (void)reason; #endif // Reset player data @@ -2563,8 +2567,6 @@ void CL_RemovePlayer(INT32 playernum, INT32 reason) if (playernum == displayplayer && !demoplayback) displayplayer = consoleplayer; // don't look through someone's view who isn't there - else - G_ResetViews(); #ifdef HAVE_BLUA LUA_InvalidatePlayer(&players[playernum]); diff --git a/src/d_main.c b/src/d_main.c index 5cf95f4b..28f89f4f 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1325,10 +1325,6 @@ void D_SRB2Main(void) midi_disabled = true; #endif } - else - { - CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n"); - } if (M_CheckParm("-nosound")) sound_disabled = true; if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic @@ -1347,10 +1343,18 @@ void D_SRB2Main(void) if (M_CheckParm("-nodigmusic")) digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound } - I_StartupSound(); - I_InitMusic(); - S_InitSfxChannels(cv_soundvolume.value); - S_InitMusicDefs(); + if (!( sound_disabled && digital_disabled +#ifndef NO_MIDI + && midi_disabled +#endif + )) + { + CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n"); + I_StartupSound(); + I_InitMusic(); + S_InitSfxChannels(cv_soundvolume.value); + S_InitMusicDefs(); + } CONS_Printf("ST_Init(): Init status bar.\n"); ST_Init(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 80203789..a59d535b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -130,6 +130,7 @@ static void Command_Map_f(void); static void Command_ResetCamera_f(void); static void Command_View_f (void); +static void Command_SetViews_f(void); static void Command_Addfile(void); static void Command_ListWADS_f(void); @@ -719,6 +720,8 @@ void D_RegisterClientCommands(void) COM_AddCommand("view3", Command_View_f); COM_AddCommand("view4", Command_View_f); + COM_AddCommand("setviews", Command_SetViews_f); + COM_AddCommand("setcontrol", Command_Setcontrol_f); COM_AddCommand("setcontrol2", Command_Setcontrol2_f); COM_AddCommand("setcontrol3", Command_Setcontrol3_f); @@ -793,6 +796,8 @@ void D_RegisterClientCommands(void) COM_AddCommand("displayplayer", Command_Displayplayer_f); + CV_RegisterVar(&cv_recordmultiplayerdemos); + // FIXME: not to be here.. but needs be done for config loading CV_RegisterVar(&cv_usegamma); @@ -1912,11 +1917,9 @@ static INT32 LookupPlayer(const char *s) for (playernum = 0; playernum < MAXPLAYERS; ++playernum) { - /* Consider strcasestr? */ - /* Match name (case-insensitively) fully, or partially the start. */ + /* Match name case-insensitively: fully, or partially the start. */ if (playeringame[playernum]) - if (stricmp(player_names[playernum], s) == 0 || - strstr(player_names[playernum], s) == player_names[playernum] ) + if (strnicmp(player_names[playernum], s, strlen(s)) == 0) { return playernum; } @@ -1924,6 +1927,44 @@ static INT32 LookupPlayer(const char *s) return -1; } +static INT32 FindPlayerByPlace(INT32 place) +{ + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].kartstuff[k_position] == place) + { + return playernum; + } + } + return -1; +} + +// +// GetViewablePlayerPlaceRange +// Return in first and last, that player available to view, sorted by placement +// in the race. +// +static void GetViewablePlayerPlaceRange(INT32 *first, INT32 *last) +{ + INT32 i; + INT32 place; + + (*first) = MAXPLAYERS; + (*last) = 0; + + for (i = 0; i < MAXPLAYERS; ++i) + if (G_CouldView(i)) + { + place = players[i].kartstuff[k_position]; + if (place < (*first)) + (*first) = place; + if (place > (*last)) + (*last) = place; + } +} + #define PRINTVIEWPOINT( pre,suf ) \ CONS_Printf(pre"viewing \x84(%d) \x83%s\x80"suf".\n",\ (*displayplayerp), player_names[(*displayplayerp)]); @@ -1932,7 +1973,10 @@ static void Command_View_f(void) INT32 *displayplayerp; INT32 olddisplayplayer; int viewnum; + const char *playerparam; + INT32 placenum; INT32 playernum; + INT32 firstplace, lastplace; char c; /* easy peasy */ c = COM_Argv(0)[strlen(COM_Argv(0))-1];/* may be digit */ @@ -1946,7 +1990,8 @@ static void Command_View_f(void) if (viewnum > 1 && !( multiplayer && demoplayback )) { - CONS_Alert(CONS_NOTICE, "You must be viewing a multiplayer replay.\n"); + CONS_Alert(CONS_NOTICE, + "You must be viewing a multiplayer replay to use this.\n"); return; } @@ -1954,20 +1999,45 @@ static void Command_View_f(void) if (COM_Argc() > 1)/* switch to player */ { - if (( playernum = LookupPlayer(COM_Argv(1)) ) == -1) + playerparam = COM_Argv(1); + if (playerparam[0] == '#')/* search by placement */ { - CONS_Alert(CONS_WARNING, "There is no player by that name!\n"); - return; + placenum = atoi(&playerparam[1]); + playernum = FindPlayerByPlace(placenum); + if (playernum == -1 || !G_CouldView(playernum)) + { + GetViewablePlayerPlaceRange(&firstplace, &lastplace); + if (playernum == -1) + { + CONS_Alert(CONS_WARNING, "There is no player in that place! "); + } + else + { + CONS_Alert(CONS_WARNING, + "That player cannot be viewed currently! " + "The first player that you can view is \x82#%d\x80; ", + firstplace); + } + CONS_Printf("Last place is \x82#%d\x80.\n", lastplace); + return; + } } - if (!playeringame[playernum]) + else { - CONS_Alert(CONS_WARNING, "There is no player using that slot!\n"); - return; + if (( playernum = LookupPlayer(COM_Argv(1)) ) == -1) + { + CONS_Alert(CONS_WARNING, "There is no player by that name!\n"); + return; + } + if (!playeringame[playernum]) + { + CONS_Alert(CONS_WARNING, "There is no player using that slot!\n"); + return; + } } olddisplayplayer = (*displayplayerp); - (*displayplayerp) = playernum; - G_ResetView(viewnum); + G_ResetView(viewnum, playernum, false); /* The player we wanted was corrected to who it already was. */ if ((*displayplayerp) == olddisplayplayer) @@ -1976,8 +2046,7 @@ static void Command_View_f(void) if ((*displayplayerp) != playernum)/* differ parameter */ { /* skipped some */ - CONS_Alert(CONS_NOTICE, - "Another viewpoint is already set to that player.\n"); + CONS_Alert(CONS_NOTICE, "That player cannot be viewed currently.\n"); PRINTVIEWPOINT ("Now "," instead") } else @@ -1992,6 +2061,37 @@ static void Command_View_f(void) } #undef PRINTVIEWPOINT +static void Command_SetViews_f(void) +{ + UINT8 splits; + UINT8 newsplits; + + if (!( demoplayback && multiplayer )) + { + CONS_Alert(CONS_NOTICE, + "You must be viewing a multiplayer replay to use this.\n"); + return; + } + + if (COM_Argc() != 2) + { + CONS_Printf("setviews : set the number of split screens\n"); + return; + } + + splits = splitscreen+1; + + newsplits = atoi(COM_Argv(1)); + newsplits = min(max(newsplits, 1), 4); + if (newsplits > splits) + G_AdjustView(newsplits, 0, true); + else + { + splitscreen = newsplits-1; + R_ExecuteSetViewSize(); + } +} + // ======================================================================== // play a demo, add .lmp for external demos @@ -2002,9 +2102,14 @@ static void Command_Playdemo_f(void) { char name[256]; - if (COM_Argc() != 2) + if (COM_Argc() < 2) { - CONS_Printf(M_GetText("playdemo : playback a demo\n")); + CONS_Printf("playdemo [-addfiles / -force]:\n"); + CONS_Printf(M_GetText( + "Play back a demo file. The full path from your Kart directory must be given.\n\n" + + "* With \"-addfiles\", any required files are added from a list contained within the demo file.\n" + "* With \"-force\", the demo is played even if the necessary files have not been added.\n")); return; } @@ -2026,6 +2131,9 @@ static void Command_Playdemo_f(void) CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name); + demo_loadfiles = strcmp(COM_Argv(2), "-addfiles") == 0; + demo_ignorefiles = strcmp(COM_Argv(2), "-force") == 0; + // Internal if no extension, external if one exists // If external, convert the file name to a path in SRB2's home directory if (FIL_CheckExtension(name)) @@ -2502,6 +2610,8 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) LUAh_MapChange(mapnumber); #endif*/ + demosaved = demodefersave = false; + demosavebutton = 0; G_InitNew(pencoremode, mapname, resetplayer, skipprecutscene); if (demoplayback && !timingdemo) precache = true; diff --git a/src/dehacked.c b/src/dehacked.c index 11aed24d..9e229f7e 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8254,6 +8254,7 @@ static const char *const POWERS_LIST[] = { "INGOOP" // In goop }; +#ifdef HAVE_BLUA static const char *const KARTSTUFF_LIST[] = { "POSITION", "OLDPOSITION", @@ -8337,6 +8338,7 @@ static const char *const KARTSTUFF_LIST[] = { "JAWZTARGETDELAY", "SPECTATEWAIT" }; +#endif static const char *const HUDITEMS_LIST[] = { "LIVESNAME", @@ -9042,20 +9044,6 @@ static powertype_t get_power(const char *word) return pw_invulnerability; } -static kartstufftype_t get_kartstuff(const char *word) -{ // Returns the vlaue of k_ enumerations - kartstufftype_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("K_",word,2)) - word += 2; // take off the k_ - for (i = 0; i < NUMKARTSTUFF; i++) - if (fastcmp(word, KARTSTUFF_LIST[i])) - return i; - deh_warning("Couldn't find power named 'k_%s'",word); - return k_position; -} - /// \todo Make ANY of this completely over-the-top math craziness obey the order of operations. static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; } static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; } @@ -9812,7 +9800,7 @@ static inline int lib_getenum(lua_State *L) lua_pushinteger(L, mapmusflags); return 1; } else if (fastcmp(word,"server")) { - if ((!multiplayer || !netgame) && !playeringame[serverplayer]) + if ((!multiplayer || !(netgame || demoplayback)) && !playeringame[serverplayer]) return 0; LUA_PushUserdata(L, &players[serverplayer], META_PLAYER); return 1; diff --git a/src/doomdef.h b/src/doomdef.h index ab863c6f..3126a7ca 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -151,8 +151,8 @@ extern FILE *logstream; #else #define VERSION 100 // Game version #define SUBVERSION 3 // more precise version number -#define VERSIONSTRING "v1.0.3" -#define VERSIONSTRINGW L"v1.0.3" +#define VERSIONSTRING "v1.0.3 Netreplays" +#define VERSIONSTRINGW L"v1.0.3 Netreplays" // Hey! If you change this, add 1 to the MODVERSION below! // Otherwise we can't force updates! #endif diff --git a/src/f_finale.c b/src/f_finale.c index b863ea74..1426e50d 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1024,11 +1024,29 @@ void F_TitleScreenTicker(boolean run) // is it time? if (!(--demoIdleLeft)) { + //static boolean use_netreplay = false; + char dname[9]; lumpnum_t l; const char *mapname; UINT8 numstaff; + //@TODO uncomment this when this goes into vanilla + /*if ((use_netreplay = !use_netreplay))*/ + { + numstaff = 1; + while ((l = W_CheckNumForName(va("TDEMO%03u", numstaff))) != LUMPERROR) + numstaff++; + numstaff--; + + if (numstaff) + { + numstaff = M_RandomKey(numstaff)+1; + snprintf(dname, 9, "TDEMO%03u", numstaff); + goto loadreplay; + } + } + // prevent console spam if failed demoIdleLeft = demoIdleTime; @@ -1079,7 +1097,10 @@ void F_TitleScreenTicker(boolean run) return; }*/ +loadreplay: titledemo = fromtitledemo = true; + demo_ignorefiles = true; + demo_loadfiles = false; G_DoPlayDemo(dname); } } diff --git a/src/g_game.c b/src/g_game.c index 53a48a9b..10774621 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -287,9 +287,11 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; //UINT32 timesBeatenUltimate; +//@TODO put these all in a struct for namespacing purposes? static char demoname[64]; -boolean demorecording; -boolean demoplayback; +boolean demorecording, demosaved, demodefersave, demoplayback; +boolean demo_loadfiles, demo_ignorefiles; // Demo file loading options +tic_t demosavebutton; boolean titledemo; // Title Screen demo can be cancelled by any key boolean fromtitledemo; // SRB2Kart: Don't stop the music static UINT8 *demobuffer = NULL; @@ -317,10 +319,14 @@ static struct { // EZT_SCALE fixed_t scale, lastscale; + // EZT_KART + INT32 kartitem, kartamount, kartbumpers; + boolean kartresync; //@TODO backwards compat with old replays. remove eventually + // EZT_HIT UINT16 hits; mobj_t **hitlist; -} ghostext; +} ghostext[MAXPLAYERS]; // Your naming conventions are stupid and useless. // There is no conflict here. @@ -330,6 +336,9 @@ boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; +static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; +consvar_t cv_recordmultiplayerdemos = {"recordmultiplayerdemos", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static UINT8 *savebuffer; // Analog Control @@ -1823,50 +1832,6 @@ static INT32 spectatedelay, spectatedelay2, spectatedelay3, spectatedelay4 = 0; // boolean G_Responder(event_t *ev) { - // allow spy mode changes even during the demo - if (gamestate == GS_LEVEL && ev->type == ev_keydown - && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1])) - { - if (!demoplayback && (splitscreen || !netgame)) - displayplayer = consoleplayer; - else - { - displayplayer++; - G_ResetView(1); - - // change statusbar also if playing back demo - if (singledemo) - ST_changeDemoView(); - - return true; - } - } - - if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demoplayback) - { - if (ev->data1 == gamecontrolbis[gc_viewpoint][0] || ev->data1 == gamecontrolbis[gc_viewpoint][1]) - { - secondarydisplayplayer++; - G_ResetView(2); - - return true; - } - else if (ev->data1 == gamecontrol3[gc_viewpoint][0] || ev->data1 == gamecontrol3[gc_viewpoint][1]) - { - thirddisplayplayer++; - G_ResetView(3); - - return true; - } - else if (ev->data1 == gamecontrol4[gc_viewpoint][0] || ev->data1 == gamecontrol4[gc_viewpoint][1]) - { - fourthdisplayplayer++; - G_ResetView(4); - - return true; - } - } - // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && ((demoplayback && !modeattacking && !titledemo && !multiplayer) || gamestate == GS_TITLESCREEN)) @@ -1944,6 +1909,46 @@ boolean G_Responder(event_t *ev) if (HU_Responder(ev)) return true; // chat ate the event + // allow spy mode changes even during the demo + if (gamestate == GS_LEVEL && ev->type == ev_keydown + && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1])) + { + if (!demoplayback && (splitscreen || !netgame)) + displayplayer = consoleplayer; + else + { + G_AdjustView(1, 1, true); + + // change statusbar also if playing back demo + if (singledemo) + ST_changeDemoView(); + + return true; + } + } + + if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demoplayback) + { + if (ev->data1 == gamecontrolbis[gc_viewpoint][0] || ev->data1 == gamecontrolbis[gc_viewpoint][1]) + { + G_AdjustView(2, 1, true); + + return true; + } + else if (ev->data1 == gamecontrol3[gc_viewpoint][0] || ev->data1 == gamecontrol3[gc_viewpoint][1]) + { + G_AdjustView(3, 1, true); + + return true; + } + else if (ev->data1 == gamecontrol4[gc_viewpoint][0] || ev->data1 == gamecontrol4[gc_viewpoint][1]) + { + G_AdjustView(4, 1, true); + + return true; + } + } + // update keys current state G_MapEventsToControls(ev); @@ -2066,81 +2071,131 @@ boolean G_Responder(event_t *ev) return false; } -static INT32 G_FindView(INT32 startview) +// +// G_CouldView +// Return whether a player could be viewed by any means. +// +boolean G_CouldView(INT32 playernum) { - UINT8 i = 0; // spy mode + player_t *player; - startview--; // Ensures view doesn't move if the current view is valid - for (i = 0; i < MAXPLAYERS; i++) + if (playernum < 0 || playernum > MAXPLAYERS-1) + return false; + + if (!playeringame[playernum]) + return false; + + player = &players[playernum]; + + if (player->spectator) + return false; + + // SRB2Kart: Only go through players who are actually playing + if (player->exiting) + return false; + if (( player->pflags & PF_TIMEOVER )) + return false; + + // I don't know if we want this actually, but I'll humor the suggestion anyway + if (G_BattleGametype() && !demoplayback) { - startview++; - if (startview == MAXPLAYERS) - startview = 0; - - if (!demoplayback && startview == consoleplayer) - break; // End loop - - if (startview == displayplayer) - continue; - - if (splitscreen && startview == secondarydisplayplayer) - continue; - - if (splitscreen >= 2 && startview == thirddisplayplayer) - continue; - - if (splitscreen == 3 && startview == fourthdisplayplayer) - continue; - - if (!playeringame[startview]) - continue; - - if (players[startview].spectator) - continue; - - // SRB2Kart: Only go through players who are actually playing - if (players[startview].exiting) - continue; - - if (players[startview].pflags & PF_TIMEOVER) - continue; - - // I don't know if we want this actually, but I'll humor the suggestion anyway - if (G_BattleGametype()) - { - if (players[startview].kartstuff[k_bumper] <= 0) - continue; - } - - // SRB2Kart: we have no team-based modes, YET... - /*if (G_GametypeHasTeams()) - { - if (players[consoleplayer].ctfteam - && players[startview].ctfteam != players[consoleplayer].ctfteam) - continue; - } - else if (gametype == GT_HIDEANDSEEK) - { - if (players[consoleplayer].pflags & PF_TAGIT) - continue; - } - // Other Tag-based gametypes? - else if (G_TagGametype()) - { - if (!players[consoleplayer].spectator - && (players[consoleplayer].pflags & PF_TAGIT) != (players[startview].pflags & PF_TAGIT)) - continue; - } - else if (G_GametypeHasSpectators() && G_BattleGametype()) - { - if (!players[consoleplayer].spectator) - continue; - }*/ - - break; + if (player->kartstuff[k_bumper] <= 0) + return false; } - return startview; + // SRB2Kart: we have no team-based modes, YET... + /*if (G_GametypeHasTeams()) + { + if (players[consoleplayer].ctfteam + && player->ctfteam != players[consoleplayer].ctfteam) + return false; + } + else if (gametype == GT_HIDEANDSEEK) + { + if (players[consoleplayer].pflags & PF_TAGIT) + return false; + } + // Other Tag-based gametypes? + else if (G_TagGametype()) + { + if (!players[consoleplayer].spectator + && (players[consoleplayer].pflags & PF_TAGIT) != (player->pflags & PF_TAGIT)) + return false; + } + else if (G_GametypeHasSpectators() && G_BattleGametype()) + { + if (!players[consoleplayer].spectator) + return false; + }*/ + + return true; +} + +// +// G_CanView +// Return whether a player can be viewed on a particular view (splitscreen). +// +boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive) +{ + UINT8 splits; + UINT8 viewd; + INT32 *displayplayerp; + + if (!(onlyactive ? G_CouldView(playernum) : (playeringame[playernum] && !players[playernum].spectator))) + return false; + + splits = splitscreen+1; + if (viewnum > splits) + viewnum = splits; + + for (viewd = 1; viewd < viewnum; ++viewd) + { + displayplayerp = (G_GetDisplayplayerPtr(viewd)); + if ((*displayplayerp) == playernum) + return false; + } + for (viewd = viewnum + 1; viewd <= splits; ++viewd) + { + displayplayerp = (G_GetDisplayplayerPtr(viewd)); + if ((*displayplayerp) == playernum) + return false; + } + + return true; +} + +// +// G_FindView +// Return the next player that can be viewed on a view, wraps forward. +// An out of range startview is corrected. +// +INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive) +{ + INT32 i; + startview = min(max(startview, 0), MAXPLAYERS); + for (i = startview; i < MAXPLAYERS; ++i) + { + if (G_CanView(i, viewnum, onlyactive)) + return i; + } + for (i = 0; i < startview; ++i) + { + if (G_CanView(i, viewnum, onlyactive)) + return i; + } + return -1; +} + +INT32 G_CountPlayersPotentiallyViewable(boolean active) +{ + INT32 total = 0; + INT32 i; + for (i = 0; i < MAXPLAYERS; ++i) + { + if (active ? G_CouldView(i) : (playeringame[i] && !players[i].spectator)) + total++; + } + return total; } INT32 *G_GetDisplayplayerPtr(UINT8 viewnum) @@ -2156,64 +2211,119 @@ INT32 *G_GetDisplayplayerPtr(UINT8 viewnum) // // G_ResetView -// Ensures a viewpoint is valid. +// Correct a viewpoint to playernum or the next available, wraps forward. +// Also promotes splitscreen up to available viewable players. +// An out of range playernum is corrected. // -void G_ResetView(UINT8 viewnum) +void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) { + UINT8 splits; + UINT8 viewd; + INT32 *displayplayerp; camera_t *camerap; - INT32 newdisplayplayer; - UINT8 viewd, splits; + INT32 olddisplayplayer; + INT32 playersviewable; splits = splitscreen+1; + /* Promote splits */ + if (viewnum > splits) + { + playersviewable = G_CountPlayersPotentiallyViewable(onlyactive); + if (playersviewable < splits)/* do not demote */ + return; + + if (viewnum > playersviewable) + viewnum = playersviewable; + splitscreen = viewnum-1; + + /* Prepare extra views for G_FindView to pass. */ + for (viewd = splits+1; viewd < viewnum; ++viewd) + { + displayplayerp = (G_GetDisplayplayerPtr(viewd)); + (*displayplayerp) = INT32_MAX; + } + + R_ExecuteSetViewSize(); + } + + /* Focus our target view first so that we don't take its player. */ displayplayerp = (G_GetDisplayplayerPtr(viewnum)); - newdisplayplayer = (*displayplayerp); - (*displayplayerp) = INT32_MAX; - (*displayplayerp) = G_FindView(newdisplayplayer); + olddisplayplayer = (*displayplayerp); + (*displayplayerp) = G_FindView(playernum, viewnum, onlyactive); + if ((*displayplayerp) != olddisplayplayer) + { + camerap = (P_GetCameraPtr(viewnum)); + P_ResetCamera(&players[(*displayplayerp)], camerap); + } if (viewnum > splits) { - splitscreen = viewnum-1; - for (viewd = splits+1; viewd < viewnum; ++viewd) - { - (*(G_GetDisplayplayerPtr(viewd))) = INT32_MAX;/* ensure clean */ - } - /* Initialise views up from current splitscreen. */ - for (viewd = splits+1 ;; ) { displayplayerp = (G_GetDisplayplayerPtr(viewd)); camerap = (P_GetCameraPtr(viewd)); + (*displayplayerp) = G_FindView(0, viewd, onlyactive); + P_ResetCamera(&players[(*displayplayerp)], camerap); - - if (++viewd > viewnum) - break; - - /* Correct up to but viewnum */ - (*displayplayerp) = G_FindView(displayplayer); } - R_ExecuteSetViewSize(); } if (viewnum == 1 && demoplayback) consoleplayer = displayplayer; } +// +// G_AdjustView +// Increment a viewpoint by offset from the current player. A negative value +// decrements. +// +void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive) +{ + INT32 *displayplayerp, oldview; + displayplayerp = G_GetDisplayplayerPtr(viewnum); + oldview = (*displayplayerp); + G_ResetView(viewnum, ( (*displayplayerp) + offset ), onlyactive); + + // If no other view could be found, go back to what we had. + if ((*displayplayerp) == -1) + (*displayplayerp) = oldview; +} + // // G_ResetViews // Ensures all viewpoints are valid +// Also demotes splitscreen down to one player. // void G_ResetViews(void) { - UINT8 viewnum = splitscreen+1; - do + UINT8 splits; + UINT8 viewd; + + INT32 playersviewable; + + splits = splitscreen+1; + + playersviewable = G_CountPlayersPotentiallyViewable(false); + /* Demote splits */ + if (playersviewable < splits) { - G_ResetView(viewnum); + splits = playersviewable; + splitscreen = max(splits-1, 0); + R_ExecuteSetViewSize(); + } + + /* + Consider installing a method to focus the last + view elsewhere if all players spectate? + */ + for (viewd = 1; viewd <= splits; ++viewd) + { + G_AdjustView(viewd, 0, false); } - while (--viewnum > 0) ; } // @@ -3202,6 +3312,9 @@ void G_ExitLevel(void) // Remove CEcho text on round end. HU_ClearCEcho(); + + if (multiplayer && demorecording && cv_recordmultiplayerdemos.value == 2) + G_SaveDemo(); } } @@ -4634,6 +4747,7 @@ char *G_BuildMapTitle(INT32 mapnum) #define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! #define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? #define DF_ATTACKSHIFT 1 +#define DF_FILELIST 0x08 // This demo contains an extra files list #define DF_GAMETYPEMASK 0x30 #define DF_GAMESHIFT 4 #define DF_ENCORE 0x40 @@ -4652,8 +4766,14 @@ char *G_BuildMapTitle(INT32 mapnum) #define DEMOMARKER 0x80 // demoend UINT8 demo_extradata[MAXPLAYERS]; +UINT8 demo_writerng; // 0=no, 1=yes, 2=yes but on a timeout static ticcmd_t oldcmd[MAXPLAYERS]; +#define DW_END 0xFF // End of extradata block +#define DW_RNG 0xFE // Check RNG seed! + +#define DW_EXTRASTUFF 0xFE // Numbers below this are reserved for writing player slot data + // For Metal Sonic and time attack ghosts #define GZT_XYZ 0x01 #define GZT_MOMXY 0x02 @@ -4674,8 +4794,9 @@ static ticcmd_t oldcmd[MAXPLAYERS]; #define EZT_SCALE 0x10 // Changed size #define EZT_HIT 0x20 // Damaged a mobj #define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever) +#define EZT_KART 0x80 // SRB2Kart: Changed current held item/quantity and bumpers for battle -static mobj_t oldmetal, oldghost; +static mobj_t oldmetal, oldghost[MAXPLAYERS]; void G_SaveMetal(UINT8 **buffer) { @@ -4722,7 +4843,7 @@ void G_ReadDemoExtraData(void) p = READUINT8(demo_p); - while (p != 0xFF) + while (p < DW_EXTRASTUFF) { extradata = READUINT8(demo_p); @@ -4762,14 +4883,13 @@ void G_ReadDemoExtraData(void) { extradata = READUINT8(demo_p); - // @TODO uhhhhh do something here - switch (extradata) { case DXD_PST_PLAYING: players[p].pflags |= PF_WANTSTOJOIN; // fuck you break; case DXD_PST_SPECTATING: + players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you if (!playeringame[p]) { CL_ClearPlayer(p); @@ -4796,6 +4916,8 @@ void G_ReadDemoExtraData(void) break; } + G_ResetViews(); + // maybe these are necessary? if (G_BattleGametype()) K_CheckBumpers(); // SRB2Kart @@ -4807,6 +4929,27 @@ void G_ReadDemoExtraData(void) p = READUINT8(demo_p); } + while (p != DW_END) + { + INT32 rng; + + switch (p) + { + case DW_RNG: + rng = READUINT32(demo_p); + if (P_GetRandSeed() != rng) + { + P_SetRandSeed(rng); + + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + } + } + + p = READUINT8(demo_p); + } + if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) { // end of demo data stream @@ -4854,6 +4997,7 @@ void G_WriteDemoExtraData(void) } if (demo_extradata[i] & DXD_PLAYSTATE) { + demo_writerng = 1; if (!playeringame[i]) WRITEUINT8(demo_p, DXD_PST_LEFT); else if ( @@ -4869,7 +5013,25 @@ void G_WriteDemoExtraData(void) demo_extradata[i] = 0; } - WRITEUINT8(demo_p, 0xFF); + // May not be necessary, but might as well play it safe... + if ((leveltime & 255) == 128) + demo_writerng = 1; + + { + static UINT8 timeout = 0; + + if (timeout) timeout--; + + if (demo_writerng == 1 || (demo_writerng == 2 && timeout == 0)) + { + demo_writerng = 0; + timeout = 16; + WRITEUINT8(demo_p, DW_RNG); + WRITEUINT32(demo_p, P_GetRandSeed()); + } + } + + WRITEUINT8(demo_p, DW_END); } void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) @@ -4982,71 +5144,88 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) } } -void G_GhostAddThok(void) +void G_GhostAddThok(INT32 playernum) { if (!demorecording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_THOK; } -void G_GhostAddSpin(void) +void G_GhostAddSpin(INT32 playernum) { if (!demorecording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_SPIN; } -void G_GhostAddRev(void) +void G_GhostAddRev(INT32 playernum) { if (!demorecording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_REV; } -void G_GhostAddFlip(void) +void G_GhostAddFlip(INT32 playernum) { if (!demorecording || !(demoflags & DF_GHOST)) return; - ghostext.flags |= EZT_FLIP; + ghostext[playernum].flags |= EZT_FLIP; } -void G_GhostAddColor(ghostcolor_t color) +void G_GhostAddColor(INT32 playernum, ghostcolor_t color) { if (!demorecording || !(demoflags & DF_GHOST)) return; - if (ghostext.lastcolor == (UINT8)color) + if (ghostext[playernum].lastcolor == (UINT8)color) { - ghostext.flags &= ~EZT_COLOR; + ghostext[playernum].flags &= ~EZT_COLOR; return; } - ghostext.flags |= EZT_COLOR; - ghostext.color = (UINT8)color; + ghostext[playernum].flags |= EZT_COLOR; + ghostext[playernum].color = (UINT8)color; } -void G_GhostAddScale(fixed_t scale) +void G_GhostAddScale(INT32 playernum, fixed_t scale) { if (!demorecording || !(demoflags & DF_GHOST)) return; - if (ghostext.lastscale == scale) + if (ghostext[playernum].lastscale == scale) { - ghostext.flags &= ~EZT_SCALE; + ghostext[playernum].flags &= ~EZT_SCALE; return; } - ghostext.flags |= EZT_SCALE; - ghostext.scale = scale; + ghostext[playernum].flags |= EZT_SCALE; + ghostext[playernum].scale = scale; } -void G_GhostAddHit(mobj_t *victim) +void G_GhostAddHit(INT32 playernum, mobj_t *victim) { if (!demorecording || !(demoflags & DF_GHOST)) return; - ghostext.flags |= EZT_HIT; - ghostext.hits++; - ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL); - ghostext.hitlist[ghostext.hits-1] = victim; + ghostext[playernum].flags |= EZT_HIT; + ghostext[playernum].hits++; + ghostext[playernum].hitlist = Z_Realloc(ghostext[playernum].hitlist, ghostext[playernum].hits * sizeof(mobj_t *), PU_LEVEL, NULL); + ghostext[playernum].hitlist[ghostext[playernum].hits-1] = victim; } -void G_WriteGhostTic(mobj_t *ghost) +void G_WriteAllGhostTics(void) +{ + INT32 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + WRITEUINT8(demo_p, i); + G_WriteGhostTic(players[i].mo, i); + } + WRITEUINT8(demo_p, 0xFF); +} + +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) { char ziptic = 0; UINT8 *ziptic_p; @@ -5071,38 +5250,38 @@ void G_WriteGhostTic(mobj_t *ghost) #define MAXMOM (0xFFFF<<8) // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(ghost->x-oldghost.x) > MAXMOM - || abs(ghost->y-oldghost.y) > MAXMOM - || abs(ghost->z-oldghost.z) > MAXMOM - || ( leveltime & 255 ) == 1) // Hack to enable slightly nicer resyncing + if (abs(ghost->x-oldghost[playernum].x) > MAXMOM + || abs(ghost->y-oldghost[playernum].y) > MAXMOM + || abs(ghost->z-oldghost[playernum].z) > MAXMOM + || (leveltime & 255) == 1) // Hack to enable slightly nicer resyncing { - oldghost.x = ghost->x; - oldghost.y = ghost->y; - oldghost.z = ghost->z; + oldghost[playernum].x = ghost->x; + oldghost[playernum].y = ghost->y; + oldghost[playernum].z = ghost->z; ziptic |= GZT_XYZ; - WRITEFIXED(demo_p,oldghost.x); - WRITEFIXED(demo_p,oldghost.y); - WRITEFIXED(demo_p,oldghost.z); + WRITEFIXED(demo_p,oldghost[playernum].x); + WRITEFIXED(demo_p,oldghost[playernum].y); + WRITEFIXED(demo_p,oldghost[playernum].z); } else { // For moving normally: // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((ghost->x-oldghost.x + (1<<4))>>8); - INT16 momy = (INT16)((ghost->y-oldghost.y + (1<<4))>>8); - if (momx != oldghost.momx - || momy != oldghost.momy) + INT16 momx = (INT16)((ghost->x-oldghost[playernum].x + (1<<4))>>8); + INT16 momy = (INT16)((ghost->y-oldghost[playernum].y + (1<<4))>>8); + if (momx != oldghost[playernum].momx + || momy != oldghost[playernum].momy) { - oldghost.momx = momx; - oldghost.momy = momy; + oldghost[playernum].momx = momx; + oldghost[playernum].momy = momy; ziptic |= GZT_MOMXY; WRITEINT16(demo_p,momx); WRITEINT16(demo_p,momy); } - momx = (INT16)((ghost->z-oldghost.z + (1<<4))>>8); - if (momx != oldghost.momz) + momx = (INT16)((ghost->z-oldghost[playernum].z + (1<<4))>>8); + if (momx != oldghost[playernum].momz) { - oldghost.momz = momx; + oldghost[playernum].momz = momx; ziptic |= GZT_MOMZ; WRITEINT16(demo_p,momx); } @@ -5110,9 +5289,9 @@ void G_WriteGhostTic(mobj_t *ghost) // This SHOULD set oldghost.x/y/z to match ghost->x/y/z // but it keeps the fractional loss of one byte, // so it will hopefully be made up for in future tics. - oldghost.x += oldghost.momx<<8; - oldghost.y += oldghost.momy<<8; - oldghost.z += oldghost.momz<<8; + oldghost[playernum].x += oldghost[playernum].momx<<8; + oldghost[playernum].y += oldghost[playernum].momy<<8; + oldghost[playernum].z += oldghost[playernum].momz<<8; } #undef MAXMOM @@ -5120,56 +5299,72 @@ void G_WriteGhostTic(mobj_t *ghost) // Only store the 8 most relevant bits of angle // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites // and it does not affect this mode of movement at all anyway. - if (ghost->angle>>24 != oldghost.angle) + if (ghost->angle>>24 != oldghost[playernum].angle) { - oldghost.angle = ghost->angle>>24; + oldghost[playernum].angle = ghost->angle>>24; ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldghost.angle); + WRITEUINT8(demo_p,oldghost[playernum].angle); } // Store the sprite frame. frame = ghost->frame & 0xFF; - if (frame != oldghost.frame) + if (frame != oldghost[playernum].frame) { - oldghost.frame = frame; + oldghost[playernum].frame = frame; ziptic |= GZT_SPRITE; - WRITEUINT8(demo_p,oldghost.frame); + WRITEUINT8(demo_p,oldghost[playernum].frame); } // Check for sprite set changes sprite = ghost->sprite; - if (sprite != oldghost.sprite) + if (sprite != oldghost[playernum].sprite) { - oldghost.sprite = sprite; - ghostext.flags |= EZT_SPRITE; + oldghost[playernum].sprite = sprite; + ghostext[playernum].flags |= EZT_SPRITE; } - if (ghostext.flags) + if (ghost->player) + { + if ( + ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || + ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || + ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] + ) + { + ghostext[playernum].flags |= EZT_KART; + ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; + ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; + ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; + + } + } + + if (ghostext[playernum].color == ghostext[playernum].lastcolor) + ghostext[playernum].flags &= ~EZT_COLOR; + if (ghostext[playernum].scale == ghostext[playernum].lastscale) + ghostext[playernum].flags &= ~EZT_SCALE; + + if (ghostext[playernum].flags) { ziptic |= GZT_EXTRA; + WRITEUINT8(demo_p,ghostext[playernum].flags); - if (ghostext.color == ghostext.lastcolor) - ghostext.flags &= ~EZT_COLOR; - if (ghostext.scale == ghostext.lastscale) - ghostext.flags &= ~EZT_SCALE; - - WRITEUINT8(demo_p,ghostext.flags); - if (ghostext.flags & EZT_COLOR) + if (ghostext[playernum].flags & EZT_COLOR) { - WRITEUINT8(demo_p,ghostext.color); - ghostext.lastcolor = ghostext.color; + WRITEUINT8(demo_p,ghostext[playernum].color); + ghostext[playernum].lastcolor = ghostext[playernum].color; } - if (ghostext.flags & EZT_SCALE) + if (ghostext[playernum].flags & EZT_SCALE) { - WRITEFIXED(demo_p,ghostext.scale); - ghostext.lastscale = ghostext.scale; + WRITEFIXED(demo_p,ghostext[playernum].scale); + ghostext[playernum].lastscale = ghostext[playernum].scale; } - if (ghostext.flags & EZT_HIT) + if (ghostext[playernum].flags & EZT_HIT) { - WRITEUINT16(demo_p,ghostext.hits); - for (i = 0; i < ghostext.hits; i++) + WRITEUINT16(demo_p,ghostext[playernum].hits); + for (i = 0; i < ghostext[playernum].hits; i++) { - mobj_t *mo = ghostext.hitlist[i]; + mobj_t *mo = ghostext[playernum].hitlist[i]; WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) WRITEUINT32(demo_p,mo->type); WRITEUINT16(demo_p,(UINT16)mo->health); @@ -5178,13 +5373,19 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEFIXED(demo_p,mo->z); WRITEANGLE(demo_p,mo->angle); } - Z_Free(ghostext.hitlist); - ghostext.hits = 0; - ghostext.hitlist = NULL; + Z_Free(ghostext[playernum].hitlist); + ghostext[playernum].hits = 0; + ghostext[playernum].hitlist = NULL; } - if (ghostext.flags & EZT_SPRITE) + if (ghostext[playernum].flags & EZT_SPRITE) WRITEUINT8(demo_p,sprite); - ghostext.flags = 0; + if (ghostext[playernum].flags & EZT_KART) + { + WRITEINT32(demo_p, ghostext[playernum].kartitem); + WRITEINT32(demo_p, ghostext[playernum].kartamount); + WRITEINT32(demo_p, ghostext[playernum].kartbumpers); + } + ghostext[playernum].flags = 0; } *ziptic_p = ziptic; @@ -5198,9 +5399,27 @@ void G_WriteGhostTic(mobj_t *ghost) } } +void G_ConsAllGhostTics(void) +{ + UINT8 p = READUINT8(demo_p); + + while (p != 0xFF) + { + G_ConsGhostTic(p); + p = READUINT8(demo_p); + } + + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + // Uses ghost data to do consistency checks on your position. // This fixes desynchronising demos when fighting eggman. -void G_ConsGhostTic(void) +void G_ConsGhostTic(INT32 playernum) { UINT8 ziptic; UINT32 px,py,pz,gx,gy,gz; @@ -5213,29 +5432,29 @@ void G_ConsGhostTic(void) if (!(demoflags & DF_GHOST)) return; // No ghost data to use. - testmo = players[0].mo; + testmo = players[playernum].mo; // Grab ghost data. ziptic = READUINT8(demo_p); if (ziptic & GZT_XYZ) { - oldghost.x = READFIXED(demo_p); - oldghost.y = READFIXED(demo_p); - oldghost.z = READFIXED(demo_p); + oldghost[playernum].x = READFIXED(demo_p); + oldghost[playernum].y = READFIXED(demo_p); + oldghost[playernum].z = READFIXED(demo_p); syncleeway = 0; } else { if (ziptic & GZT_MOMXY) { - oldghost.momx = READINT16(demo_p)<<8; - oldghost.momy = READINT16(demo_p)<<8; + oldghost[playernum].momx = READINT16(demo_p)<<8; + oldghost[playernum].momy = READINT16(demo_p)<<8; } if (ziptic & GZT_MOMZ) - oldghost.momz = READINT16(demo_p)<<8; - oldghost.x += oldghost.momx; - oldghost.y += oldghost.momy; - oldghost.z += oldghost.momz; + oldghost[playernum].momz = READINT16(demo_p)<<8; + oldghost[playernum].x += oldghost[playernum].momx; + oldghost[playernum].y += oldghost[playernum].momy; + oldghost[playernum].z += oldghost[playernum].momz; syncleeway = FRACUNIT; } if (ziptic & GZT_ANGLE) @@ -5243,7 +5462,7 @@ void G_ConsGhostTic(void) if (ziptic & GZT_SPRITE) demo_p++; if(ziptic & GZT_NIGHTS) { - if (!testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) + if (!testmo || !testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) nightsfail = true; else testmo = testmo->tracer; @@ -5299,27 +5518,53 @@ void G_ConsGhostTic(void) } if (ziptic & EZT_SPRITE) demo_p++; + if (ziptic & EZT_KART) + { + ghostext[playernum].kartitem = READINT32(demo_p); + ghostext[playernum].kartamount = READINT32(demo_p); + ghostext[playernum].kartbumpers = READINT32(demo_p); + ghostext[playernum].kartresync = true; + } } - // Re-synchronise - px = testmo->x; - py = testmo->y; - pz = testmo->z; - gx = oldghost.x; - gy = oldghost.y; - gz = oldghost.z; - - if (nightsfail || abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway) + if (testmo) { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; + // Re-synchronise + px = testmo->x; + py = testmo->y; + pz = testmo->z; + gx = oldghost[playernum].x; + gy = oldghost[playernum].y; + gz = oldghost[playernum].z; - P_UnsetThingPosition(testmo); - testmo->x = oldghost.x; - testmo->y = oldghost.y; - P_SetThingPosition(testmo); - testmo->z = oldghost.z; + if (nightsfail || abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + P_UnsetThingPosition(testmo); + testmo->x = oldghost[playernum].x; + testmo->y = oldghost[playernum].y; + P_SetThingPosition(testmo); + testmo->z = oldghost[playernum].z; + } + + if ( + ghostext[playernum].kartresync && ( + players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem || + players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount || + players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers) + ) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + players[playernum].kartstuff[k_itemtype] = ghostext[playernum].kartitem; + players[playernum].kartstuff[k_itemamount] = ghostext[playernum].kartamount; + players[playernum].kartstuff[k_bumper] = ghostext[playernum].kartbumpers; + } } if (*demo_p == DEMOMARKER) @@ -5491,6 +5736,8 @@ void G_GhostTicker(void) } if (ziptic & EZT_SPRITE) g->mo->sprite = READUINT8(g->p); + if (ziptic & EZT_KART) + g->p += 12; // kartitem, kartamount, kartbumpers } // Tick ghost colors (Super and Mario Invincibility flashing) @@ -5740,7 +5987,8 @@ void G_RecordDemo(const char *name) strcpy(demoname, name); strcat(demoname, ".lmp"); - maxsize = 1024*1024; + //@TODO make a maxdemosize cvar + maxsize = 1024*1024*2; if (M_CheckParm("-maxdemo") && M_IsNextParm()) maxsize = atoi(M_GetNextParm()) * 1024; // if (demobuffer) @@ -5770,15 +6018,24 @@ void G_BeginRecording(void) char name[16]; player_t *player = &players[consoleplayer]; + char *filename; + UINT8 totalfiles; + UINT8 *m; + if (demo_p) return; memset(name,0,sizeof(name)); demo_p = demobuffer; - demoflags = multiplayer ? DF_MULTIPLAYER : (DF_GHOST|(modeattacking<important) + { + nameonly(( filename = va("%s", wadfiles[i]->filename) )); + WRITESTRINGN(demo_p, filename, 64); + WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); + + totalfiles++; + } + + WRITEUINT8(m, totalfiles); + } + switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { case ATTACKING_NONE: // 0 @@ -5853,13 +6131,7 @@ void G_BeginRecording(void) WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing - memset(&oldcmd,0,sizeof(oldcmd)); - memset(&demo_extradata, 0, sizeof(demo_extradata)); - // Lower two lines aren't useful until ghost replays for mp are implemented, but eh - memset(&oldghost,0,sizeof(oldghost)); - memset(&ghostext,0,sizeof(ghostext)); - - return; + goto initcmdandghost; } // Name @@ -5909,22 +6181,27 @@ void G_BeginRecording(void) // Save netvar data (SONICCD, etc) CV_SaveNetVars(&demo_p, false); //@TODO can this be true? it's not necessary for now but would be nice for consistency +initcmdandghost: memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); memset(&ghostext,0,sizeof(ghostext)); - ghostext.lastcolor = ghostext.color = GHC_NORMAL; - ghostext.lastscale = ghostext.scale = FRACUNIT; - if (player->mo) + for (i = 0; i < MAXPLAYERS; i++) { - oldghost.x = player->mo->x; - oldghost.y = player->mo->y; - oldghost.z = player->mo->z; - oldghost.angle = player->mo->angle; + ghostext[i].lastcolor = ghostext[i].color = GHC_NORMAL; + ghostext[i].lastscale = ghostext[i].scale = FRACUNIT; - // preticker started us gravity flipped - if (player->mo->eflags & MFE_VERTICALFLIP) - ghostext.flags |= EZT_FLIP; + if (players[i].mo) + { + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + oldghost[i].angle = players[i].mo->angle; + + // preticker started us gravity flipped + if (players[i].mo->eflags & MFE_VERTICALFLIP) + ghostext[i].flags |= EZT_FLIP; + } } } @@ -5974,6 +6251,162 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap) }*/ } +static void G_LoadDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + filestatus_t ncs; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + + for (j = 0; j < numwadfiles; ++j) + { + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + break; + } + } + + if (alreadyloaded) + continue; + + if (numwadfiles >= MAX_WADFILES) + toomany = true; + else + ncs = findfile(filename, md5sum, false); + + if (toomany || ncs != FS_FOUND) + { + if (toomany) + CONS_Printf("Too many files loaded\n"); + else if (ncs == FS_NOTFOUND) + CONS_Printf("You do not have a copy of %s\n", filename); + else if (ncs == FS_MD5SUMBAD) + CONS_Printf("Checksum mismatch on %s\n", filename); + else + CONS_Printf("Unknown error finding file (%s)\n", filename); + } + else + { + P_AddWadFile(filename); + } + } + } +} + +static void G_SkipDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + UINT8 i; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + SKIPSTRING((*pp));// file name + (*pp) += 16;// md5 + } +} + +// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. +#define DFILE_ERROR_NOTLOADED 0x01 // Files are not loaded, but can be without a restart. +#define DFILE_ERROR_OUTOFORDER 0x02 // Files are loaded, but out of order. +#define DFILE_ERROR_INCOMPLETEOUTOFORDER 0x03 // Some files are loaded out of order, but others are not. +#define DFILE_ERROR_CANNOTLOAD 0x04 // Files are missing and cannot be loaded. +#define DFILE_ERROR_EXTRAFILES 0x05 // Extra files outside of the replay's file list are loaded. +static UINT8 G_CheckDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles, filesloaded, nmusfilecount; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + UINT8 error = 0; + + totalfiles = READUINT8((*pp)); + filesloaded = 0; + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + { + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + else + continue; + + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + + if (i != nmusfilecount-1 && error < DFILE_ERROR_OUTOFORDER) + error |= DFILE_ERROR_OUTOFORDER; + + break; + } + } + + if (alreadyloaded) + { + filesloaded++; + continue; + } + + if (numwadfiles >= MAX_WADFILES) + error = DFILE_ERROR_CANNOTLOAD; + else if (findfile(filename, md5sum, false) != FS_FOUND) + error = DFILE_ERROR_CANNOTLOAD; + else if (error < DFILE_ERROR_INCOMPLETEOUTOFORDER) + error |= DFILE_ERROR_NOTLOADED; + } else + error = DFILE_ERROR_CANNOTLOAD; + } + + // Get final file count + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + + if (!error && filesloaded < nmusfilecount) + error = DFILE_ERROR_EXTRAFILES; + + return error; +} + // Returns bitfield: // 1 == new demo has lower time // 2 == new demo has higher score @@ -6010,6 +6443,10 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p += 2; // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags + if (flags & DF_FILELIST) // file list + { + G_SkipDemoExtraFiles(&p); + } aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); I_Assert(aflags); @@ -6067,6 +6504,10 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p += 2; // gamemap p += 16; // mapmd5 flags = READUINT8(p); + if (flags & DF_FILELIST) // file list + { + G_SkipDemoExtraFiles(&p); + } if (!(flags & aflags)) { CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); @@ -6105,7 +6546,7 @@ void G_DeferedPlayDemo(const char *name) { COM_BufAddText("playdemo \""); COM_BufAddText(name); - COM_BufAddText("\"\n"); + COM_BufAddText("\" -addfiles\n"); } // @@ -6217,6 +6658,65 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); + if (demoflags & DF_FILELIST) + { + if (titledemo) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. + G_SkipDemoExtraFiles(&demo_p); + else if (demo_loadfiles) + G_LoadDemoExtraFiles(&demo_p); + else if (demo_ignorefiles) + G_SkipDemoExtraFiles(&demo_p); + else + { + UINT8 error = G_CheckDemoExtraFiles(&demo_p); + + if (error) + { + switch (error) + { + case DFILE_ERROR_NOTLOADED: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_OUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_CANNOTLOAD: + snprintf(msg, 1024, + M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_EXTRAFILES: + snprintf(msg, 1024, + M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + } + + CONS_Alert(CONS_ERROR, "%s", msg); + if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demoplayback = false; + titledemo = false; + return; + } + } + } + modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; multiplayer = !!(demoflags & DF_MULTIPLAYER); gametype = (demoflags & DF_GAMETYPEMASK)>>DF_GAMESHIFT; @@ -6320,6 +6820,7 @@ void G_DoPlayDemo(char *defdemoname) memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); + memset(&ghostext,0,sizeof(ghostext)); #if defined(SKIPERRORS) && !defined(DEVELOP) if ((VERSION != version || SUBVERSION != subversion) && !skiperrors) @@ -6347,10 +6848,13 @@ void G_DoPlayDemo(char *defdemoname) if (multiplayer) { boolean spectator; + UINT8 slots[MAXPLAYERS], numslots = 0; // Load players that were in-game when the map started p = READUINT8(demo_p); + secondarydisplayplayer = thirddisplayplayer = fourthdisplayplayer = INT32_MAX; + while (p != 0xFF) { spectator = false; @@ -6358,21 +6862,11 @@ void G_DoPlayDemo(char *defdemoname) spectator = true; p &= ~DEMO_SPECTATOR; } + slots[numslots] = p; numslots++; if (!playeringame[displayplayer] || players[displayplayer].spectator) - displayplayer = consoleplayer = secondarydisplayplayer = thirddisplayplayer = fourthdisplayplayer = p; - /*else if (!spectator && splitscreen < 3) { - if (splitscreen == 0) { - splitscreen = 1; - secondarydisplayplayer = p; - } else if (splitscreen == 1) { - splitscreen = 2; - thirddisplayplayer = p; - } else { - splitscreen = 3; - fourthdisplayplayer = p; - } - }*/ + displayplayer = consoleplayer = serverplayer = p; + playeringame[p] = true; players[p].spectator = spectator; @@ -6403,21 +6897,35 @@ void G_DoPlayDemo(char *defdemoname) } splitscreen = 0; + + if (titledemo) + { + splitscreen = M_RandomKey(6)-1; + splitscreen = min(min(3, numslots-1), max(0, splitscreen)); // Bias toward 1p and 4p views + + for (p = 0; p <= splitscreen; p++) + G_ResetView(p+1, slots[M_RandomKey(numslots)], false); + } + R_ExecuteSetViewSize(); } P_SetRandSeed(randseed); G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer. + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].mo) + { + players[i].mo->color = players[i].skincolor; + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + } + } + if (!multiplayer) { //CV_StealthSetValue(&cv_playercolor, players[0].skincolor); -- as far as I can tell this is more trouble than it's worth - if (players[0].mo) - { - players[0].mo->color = players[0].skincolor; - oldghost.x = players[0].mo->x; - oldghost.y = players[0].mo->y; - oldghost.z = players[0].mo->z; - } // Set saved attribute values // No cheat checking here, because even if they ARE wrong... @@ -6517,6 +7025,10 @@ void G_AddGhost(char *defdemoname) p += 2; // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); + if (flags & DF_FILELIST) + { + G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. + } if (!(flags & DF_GHOST)) { CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname); @@ -6595,6 +7107,7 @@ void G_AddGhost(char *defdemoname) if (i == numskins) { + //@TODO nah this should fallback CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid character.\n"), pdemoname); Z_Free(pdemoname); Z_Free(buffer); @@ -6669,6 +7182,9 @@ void G_UpdateStaffGhostName(lumpnum_t l) UINT16 ghostversion; UINT8 flags; + UINT8 totalfiles; + UINT8 md5sum[16]; + buffer = p = W_CacheLumpNum(l, PU_CACHE); // read demo header @@ -6695,6 +7211,15 @@ void G_UpdateStaffGhostName(lumpnum_t l) p += 2; // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); + if (flags & DF_FILELIST) // file list + { + totalfiles = READUINT8(p); + for (; totalfiles > 0; --totalfiles) + { + SKIPSTRING(p); + READMEM(p, md5sum, 16); + } + } if (!(flags & DF_GHOST)) { goto fail; // we don't NEED to do it here, but whatever @@ -6892,8 +7417,6 @@ void G_StopDemo(void) boolean G_CheckDemoStatus(void) { - boolean saved; - while (ghosts) { demoghost *next = ghosts->next; @@ -6927,7 +7450,7 @@ boolean G_CheckDemoStatus(void) if (singledemo) I_Quit(); - if (multiplayer) + if (multiplayer && !titledemo) G_ExitLevel(); else { @@ -6942,35 +7465,41 @@ boolean G_CheckDemoStatus(void) return true; } - if (demorecording) + if (demorecording && (!multiplayer || cv_recordmultiplayerdemos.value == 2)) { - UINT8 *p = demobuffer+16; // checksum position -#ifdef NOMD5 - UINT8 i; - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - for (i = 0; i < 16; i++, p++) - *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. -#else - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. -#endif - saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. - free(demobuffer); - demorecording = false; - - if (modeattacking != ATTACKING_RECORD) - { - if (saved) - CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); - else - CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); - } + G_SaveDemo(); return true; } + demorecording = false; return false; } +void G_SaveDemo(void) +{ + UINT8 *p = demobuffer+16; // checksum position +#ifdef NOMD5 + UINT8 i; + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + for (i = 0; i < 16; i++, p++) + *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. +#else + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. +#endif + demosaved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. + free(demobuffer); + demorecording = false; + + if (modeattacking != ATTACKING_RECORD) + { + if (demosaved) + CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); + else + CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); + } +} + // // G_SetGamestate // diff --git a/src/g_game.h b/src/g_game.h index 9d9a4755..4549044b 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -36,7 +36,9 @@ extern boolean playeringame[MAXPLAYERS]; // ====================================== // demoplaying back and demo recording -extern boolean demoplayback, titledemo, fromtitledemo, demorecording, timingdemo; +extern boolean demoplayback, titledemo, fromtitledemo, demorecording, timingdemo, demosaved, demodefersave, demo_loadfiles, demo_ignorefiles; +extern tic_t demosavebutton; +extern consvar_t cv_recordmultiplayerdemos; // Quit after playing a demo from cmdline. extern boolean singledemo; @@ -155,6 +157,7 @@ typedef enum } ghostcolor_t; extern UINT8 demo_extradata[MAXPLAYERS]; +extern UINT8 demo_writerng; #define DXD_RESPAWN 0x01 // "respawn" command in console #define DXD_SKIN 0x02 // skin changed #define DXD_NAME 0x04 // name changed @@ -170,15 +173,17 @@ void G_ReadDemoExtraData(void); void G_WriteDemoExtraData(void); void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_GhostAddThok(void); -void G_GhostAddSpin(void); -void G_GhostAddRev(void); -void G_GhostAddColor(ghostcolor_t color); -void G_GhostAddFlip(void); -void G_GhostAddScale(fixed_t scale); -void G_GhostAddHit(mobj_t *victim); -void G_WriteGhostTic(mobj_t *ghost); -void G_ConsGhostTic(void); +void G_GhostAddThok(INT32 playernum); +void G_GhostAddSpin(INT32 playernum); +void G_GhostAddRev(INT32 playernum); +void G_GhostAddColor(INT32 playernum, ghostcolor_t color); +void G_GhostAddFlip(INT32 playernum); +void G_GhostAddScale(INT32 playernum, fixed_t scale); +void G_GhostAddHit(INT32 playernum, mobj_t *victim); +void G_WriteAllGhostTics(void); +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum); +void G_ConsAllGhostTics(void); +void G_ConsGhostTic(INT32 playernum); void G_GhostTicker(void); void G_ReadMetalTic(mobj_t *metal); void G_WriteMetalTic(mobj_t *metal); @@ -206,6 +211,7 @@ void G_StopMetalDemo(void); ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void); void G_StopDemo(void); boolean G_CheckDemoStatus(void); +void G_SaveDemo(void); boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); @@ -228,8 +234,15 @@ boolean G_Responder(event_t *ev); INT32 *G_GetDisplayplayerPtr(UINT8 viewnum); +boolean G_CouldView(INT32 playernum); +boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive); + +INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive); +INT32 G_CountPlayersPotentiallyViewable(boolean active); + void G_ResetViews(void); -void G_ResetView(UINT8 viewnum); +void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); +void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); void G_AddPlayer(INT32 playernum); diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c index 6d120efe..2397ce08 100644 --- a/src/hardware/hw_clip.c +++ b/src/hardware/hw_clip.c @@ -72,6 +72,7 @@ #include "../v_video.h" #include "hw_clip.h" #include "hw_glob.h" +#include "../r_main.h" #include "../r_state.h" #include "../tables.h" #include "r_opengl/r_opengl.h" @@ -328,7 +329,7 @@ angle_t gld_FrustumAngle(void) // NEWCLIP TODO: SRB2CBTODO: make a global render_fov for this function - float render_fov = FIXED_TO_FLOAT(cv_grfov.value); + float render_fov = FIXED_TO_FLOAT(cv_fov.value); float render_fovratio = (float)BASEVIDWIDTH / (float)BASEVIDHEIGHT; // SRB2CBTODO: NEWCLIPTODO: Is this right? float render_multiplier = 64.0f / render_fovratio / RMUL; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c45814bb..4fcef218 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -86,12 +86,10 @@ static UINT32 atohex(const char *s); static void CV_filtermode_ONChange(void); static void CV_anisotropic_ONChange(void); static void CV_FogDensity_ONChange(void); -static void CV_grFov_OnChange(void); // ========================================================================== // 3D ENGINE COMMANDS & CONSOLE VARS // ========================================================================== -static CV_PossibleValue_t grfov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}}; static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"}, {HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"}, {HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"}, @@ -112,7 +110,6 @@ static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NUL static float HWRWipeCounter = 1.0f; consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grFov_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned, CV_FogDensity_ONChange, 0, NULL, NULL, 0, 0, NULL}; @@ -5907,7 +5904,7 @@ void HWR_SetViewSize(void) // ========================================================================== void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player) { - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); postimg_t *type; UINT8 ssplayer = 0; @@ -6073,7 +6070,7 @@ if (0) viewangle = localaiming4; // Handle stuff when you are looking farther up or down. - if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT)) + if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT)) { dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); @@ -6151,7 +6148,7 @@ if (0) // ========================================================================== void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) { - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); postimg_t *type; UINT8 ssplayer = 0; @@ -6332,7 +6329,7 @@ if (0) viewangle = localaiming4; // Handle stuff when you are looking farther up or down. - if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT)) + if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT)) { dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); @@ -6455,11 +6452,6 @@ static void HWR_FoggingOn(void) // ========================================================================== -static void CV_grFov_OnChange(void) -{ - if ((netgame || multiplayer) && !cv_debug && cv_grfov.value != 90*FRACUNIT) - CV_Set(&cv_grfov, cv_grfov.defaultvalue); -} static void Command_GrStats_f(void) { @@ -6482,7 +6474,6 @@ static void Command_GrStats_f(void) void HWR_AddCommands(void) { CV_RegisterVar(&cv_grrounddown); - CV_RegisterVar(&cv_grfov); CV_RegisterVar(&cv_grfogdensity); CV_RegisterVar(&cv_grfiltermode); CV_RegisterVar(&cv_granisotropicmode); diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 198780f9..6978856e 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -80,7 +80,6 @@ extern consvar_t cv_grstaticlighting; extern consvar_t cv_grcoronas; extern consvar_t cv_grcoronasize; #endif -extern consvar_t cv_grfov; extern consvar_t cv_grmd2; extern consvar_t cv_grfog; extern consvar_t cv_grfogcolor; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 84835389..3a8be6a0 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2056,10 +2056,11 @@ EXPORT void HWRAPI(DrawMD2) (INT32 *gl_cmd_buffer, md2_frame_t *frame, FTransfor EXPORT void HWRAPI(SetTransform) (FTransform *stransform) { static boolean special_splitscreen; + float used_fov; pglLoadIdentity(); if (stransform) { - boolean fovx90; + used_fov = stransform->fovxangle; // keep a trace of the transformation for md2 memcpy(&md2_transform, stransform, sizeof (md2_transform)); @@ -2074,36 +2075,29 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform) pglRotatef(stransform->angley+270.0f, 0.0f, 1.0f, 0.0f); pglTranslatef(-stransform->x, -stransform->z, -stransform->y); - pglMatrixMode(GL_PROJECTION); - pglLoadIdentity(); - fovx90 = stransform->fovxangle > 0.0f && fabsf(stransform->fovxangle - 90.0f) < 0.5f; - special_splitscreen = (stransform->splitscreen == 1 && fovx90); - if (special_splitscreen) - GLPerspective(53.13l, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - GLPerspective(stransform->fovxangle, ASPECT_RATIO); -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) -#endif - pglMatrixMode(GL_MODELVIEW); + special_splitscreen = (stransform->splitscreen == 1); } else { + //Hurdler: is "fov" correct? + used_fov = fov; pglScalef(1.0f, 1.0f, -1.0f); - - pglMatrixMode(GL_PROJECTION); - pglLoadIdentity(); - if (special_splitscreen) - GLPerspective(53.13l, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - //Hurdler: is "fov" correct? - GLPerspective(fov, ASPECT_RATIO); -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) -#endif - pglMatrixMode(GL_MODELVIEW); } + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + if (special_splitscreen) + { + used_fov = atan(tan(used_fov*M_PI/360)*0.8)*360/M_PI; + GLPerspective(used_fov, 2*ASPECT_RATIO); + } + else + GLPerspective(used_fov, ASPECT_RATIO); +#ifndef MINI_GL_COMPATIBILITY + pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) +#endif + pglMatrixMode(GL_MODELVIEW); + #ifndef MINI_GL_COMPATIBILITY pglGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer) #endif diff --git a/src/k_kart.c b/src/k_kart.c index e5cbc9c5..1732f69c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1891,8 +1891,10 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto } } +#ifdef HAVE_BLUA if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. return; +#endif if (source && source != player->mo && source->player) K_PlayHitEmSound(source); @@ -2022,8 +2024,10 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) } } +#ifdef HAVE_BLUA if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. return; +#endif player->kartstuff[k_sneakertimer] = 0; player->kartstuff[k_driftboost] = 0; @@ -2136,8 +2140,10 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b } } +#ifdef HAVE_BLUA if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah return; +#endif if (source && source != player->mo && source->player) K_PlayHitEmSound(source); @@ -8107,19 +8113,12 @@ static void K_drawInput(void) if (timeinmap < 113) { INT32 count = ((INT32)(timeinmap) - 105); - offs = (titledemo ? 128 : 64); + offs = 64; while (count-- > 0) offs >>= 1; x += offs; } - if (titledemo) - { - V_DrawTinyScaledPatch(x-54, 128, splitflags, W_CachePatchName("TTKBANNR", PU_CACHE)); - V_DrawTinyScaledPatch(x-54, 128+25, splitflags, W_CachePatchName("TTKART", PU_CACHE)); - return; - } - #define BUTTW 8 #define BUTTH 11 @@ -8413,7 +8412,7 @@ void K_drawKartHUD(void) && comeback && stplyr->playerstate == PST_LIVE))); - if (!battlefullscreen || splitscreen) + if (!titledemo && (!battlefullscreen || splitscreen)) { // Draw the CHECK indicator before the other items, so it's overlapped by everything else if (cv_kartcheck.value && !splitscreen && !players[displayplayer].exiting) @@ -8428,7 +8427,7 @@ void K_drawKartHUD(void) K_drawKartWanted(); } - if (cv_kartminimap.value && !titledemo) + if (cv_kartminimap.value) { #ifdef HAVE_BLUA if (LUA_HudEnabled(hud_minimap)) @@ -8469,26 +8468,45 @@ void K_drawKartHUD(void) } if (!stplyr->spectator) // Bottom of the screen elements, don't need in spectate mode - { - if (G_RaceGametype()) // Race-only elements - { - if (!titledemo) - { - // Draw the lap counter -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartLaps(); + { + if (titledemo) // Draw title logo instead in titledemos + { + INT32 x = BASEVIDWIDTH - 32, y = 128, offs; + + if (splitscreen == 3) + { + x = BASEVIDWIDTH/2 + 10; + y = BASEVIDHEIGHT/2 - 30; + } - if (!splitscreen) - { - // Draw the speedometer - // TODO: Make a better speedometer. + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 104); + offs = 256; + while (count-- > 0) + offs >>= 1; + x += offs; + } + + V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); + V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); + } + else if (G_RaceGametype()) // Race-only elements + { + // Draw the lap counter #ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) + if (LUA_HudEnabled(hud_gametypeinfo)) #endif - K_drawKartSpeedometer(); - } + K_drawKartLaps(); + + if (!splitscreen) + { + // Draw the speedometer + // TODO: Make a better speedometer. +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_speedometer)) +#endif + K_drawKartSpeedometer(); } if (isfreeplay) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 78f972f8..36becc56 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -163,6 +163,7 @@ static int lib_pRandomFixed(lua_State *L) { NOHUD lua_pushfixed(L, P_RandomFixed()); + demo_writerng = 2; return 1; } @@ -170,6 +171,7 @@ static int lib_pRandomByte(lua_State *L) { NOHUD lua_pushinteger(L, P_RandomByte()); + demo_writerng = 2; return 1; } @@ -181,6 +183,7 @@ static int lib_pRandomKey(lua_State *L) if (a > 65536) LUA_UsageWarning(L, "P_RandomKey: range > 65536 is undefined behavior"); lua_pushinteger(L, P_RandomKey(a)); + demo_writerng = 2; return 1; } @@ -198,6 +201,7 @@ static int lib_pRandomRange(lua_State *L) if ((b-a+1) > 65536) LUA_UsageWarning(L, "P_RandomRange: range > 65536 is undefined behavior"); lua_pushinteger(L, P_RandomRange(a, b)); + demo_writerng = 2; return 1; } @@ -207,6 +211,7 @@ static int lib_pRandom(lua_State *L) NOHUD LUA_Deprecated(L, "P_Random", "P_RandomByte"); lua_pushinteger(L, P_RandomByte()); + demo_writerng = 2; return 1; } @@ -214,6 +219,7 @@ static int lib_pSignedRandom(lua_State *L) { NOHUD lua_pushinteger(L, P_SignedRandom()); + demo_writerng = 2; return 1; } @@ -222,6 +228,7 @@ static int lib_pRandomChance(lua_State *L) fixed_t p = luaL_checkfixed(L, 1); NOHUD lua_pushboolean(L, P_RandomChance(p)); + demo_writerng = 2; return 1; } diff --git a/src/m_menu.c b/src/m_menu.c index 5e86a092..b0b8a1e5 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -438,7 +438,7 @@ static CV_PossibleValue_t serversort_cons_t[] = { {1,"Modified State"}, {2,"Most Players"}, {3,"Least Players"}, - {4,"Max Players"}, + {4,"Max Player Slots"}, {5,"Gametype"}, {0,NULL} }; @@ -1249,7 +1249,7 @@ static menuitem_t OP_OpenGLOptionsMenu[] = {IT_SUBMENU|IT_STRING, NULL, "Fog...", &OP_OpenGLFogDef, 10}, {IT_SUBMENU|IT_STRING, NULL, "Gamma...", &OP_OpenGLColorDef, 20}, - {IT_STRING|IT_CVAR, NULL, "Field of View", &cv_grfov, 35}, + {IT_STRING|IT_CVAR, NULL, "Field of View", &cv_fov, 35}, {IT_STRING|IT_CVAR, NULL, "Quality", &cv_scr_depth, 45}, {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 55}, {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode, 65}, @@ -1830,7 +1830,6 @@ static menu_t SP_NightsGhostDef = NULL };*/ -#ifndef NONET // Multiplayer menu_t MP_MainDef = { @@ -1841,12 +1840,18 @@ menu_t MP_MainDef = M_DrawMPMainMenu, 42, 30, 0, - M_CancelConnect -}; -menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef); -#endif -menu_t MP_OfflineServerDef = MAPICONMENUSTYLE("M_MULTI", MP_OfflineServerMenu, &MP_MainDef); #ifndef NONET + M_CancelConnect +#else + NULL +#endif +}; + +menu_t MP_OfflineServerDef = MAPICONMENUSTYLE("M_MULTI", MP_OfflineServerMenu, &MP_MainDef); + +#ifndef NONET +menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef); + menu_t MP_ConnectDef = { "M_MULTI", @@ -6897,6 +6902,7 @@ static void M_HandleStaffReplay(INT32 choice) break; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; + demo_loadfiles = false; demo_ignorefiles = true; // Just assume that record attack replays have the files needed G_DoPlayDemo(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); break; default: @@ -6917,6 +6923,7 @@ static void M_ReplayTimeAttack(INT32 choice) const char *which; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows + demo_loadfiles = false; demo_ignorefiles = true; // Just assume that record attack replays have the files needed if (currentMenu == &SP_ReplayDef) { @@ -7113,7 +7120,7 @@ static void M_ExitGameResponse(INT32 ch) static void M_EndGame(INT32 choice) { (void)choice; - if (demoplayback || demorecording) + if (demoplayback) return; if (!Playing()) diff --git a/src/m_misc.c b/src/m_misc.c index 53b63e75..c95aa392 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -93,9 +93,8 @@ typedef off_t off64_t; #ifdef PNG_WRITE_SUPPORTED #define USE_PNG // Only actually use PNG if write is supported. #if defined (PNG_WRITE_APNG_SUPPORTED) //|| !defined(PNG_STATIC) - #if (PNG_LIBPNG_VER_MAJOR) == 1 && (PNG_LIBPNG_VER_MINOR <= 4) // Supposedly, the current APNG code can't work on newer versions as is + #include "apng.h" #define USE_APNG - #endif #endif // See hardware/hw_draw.c for a similar check to this one. #endif @@ -795,13 +794,13 @@ static inline void M_PNGImage(png_structp png_ptr, png_infop png_info_ptr, PNG_C #ifdef USE_APNG static png_structp apng_ptr = NULL; static png_infop apng_info_ptr = NULL; +static apng_infop apng_ainfo_ptr = NULL; static png_FILE_p apng_FILE = NULL; static png_uint_32 apng_frames = 0; -static png_byte acTL_cn[5] = { 97, 99, 84, 76, '\0'}; #ifdef PNG_STATIC // Win32 build have static libpng -#define apng_set_acTL png_set_acTL -#define apng_write_frame_head png_write_frame_head -#define apng_write_frame_tail png_write_frame_tail +#define aPNG_set_acTL png_set_acTL +#define aPNG_write_frame_head png_write_frame_head +#define aPNG_write_frame_tail png_write_frame_tail #else // outside libpng may not have apng support #ifndef PNG_WRITE_APNG_SUPPORTED // libpng header may not have apng patch @@ -838,20 +837,20 @@ static png_byte acTL_cn[5] = { 97, 99, 84, 76, '\0'}; #endif #endif -typedef PNG_EXPORT(png_uint_32, (*P_png_set_acTL)) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays)); -typedef PNG_EXPORT (void, (*P_png_write_frame_head)) PNGARG((png_structp png_ptr, +typedef png_uint_32 (*P_png_set_acTL) (png_structp png_ptr, + png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays); +typedef void (*P_png_write_frame_head) (png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers, png_uint_32 width, png_uint_32 height, png_uint_32 x_offset, png_uint_32 y_offset, png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op, - png_byte blend_op)); + png_byte blend_op); -typedef PNG_EXPORT (void, (*P_png_write_frame_tail)) PNGARG((png_structp png_ptr, - png_infop info_ptr)); -static P_png_set_acTL apng_set_acTL = NULL; -static P_png_write_frame_head apng_write_frame_head = NULL; -static P_png_write_frame_tail apng_write_frame_tail = NULL; +typedef void (*P_png_write_frame_tail) (png_structp png_ptr, + png_infop info_ptr); +static P_png_set_acTL aPNG_set_acTL = NULL; +static P_png_write_frame_head aPNG_write_frame_head = NULL; +static P_png_write_frame_tail aPNG_write_frame_tail = NULL; #endif static inline boolean M_PNGLib(void) @@ -860,7 +859,7 @@ static inline boolean M_PNGLib(void) return true; #else static void *pnglib = NULL; - if (apng_set_acTL && apng_write_frame_head && apng_write_frame_tail) + if (aPNG_set_acTL && aPNG_write_frame_head && aPNG_write_frame_tail) return true; if (pnglib) return false; @@ -880,16 +879,16 @@ static inline boolean M_PNGLib(void) if (!pnglib) return false; #ifdef HAVE_SDL - apng_set_acTL = hwSym("png_set_acTL", pnglib); - apng_write_frame_head = hwSym("png_write_frame_head", pnglib); - apng_write_frame_tail = hwSym("png_write_frame_tail", pnglib); + aPNG_set_acTL = hwSym("png_set_acTL", pnglib); + aPNG_write_frame_head = hwSym("png_write_frame_head", pnglib); + aPNG_write_frame_tail = hwSym("png_write_frame_tail", pnglib); #endif #ifdef _WIN32 - apng_set_acTL = GetProcAddress("png_set_acTL", pnglib); - apng_write_frame_head = GetProcAddress("png_write_frame_head", pnglib); - apng_write_frame_tail = GetProcAddress("png_write_frame_tail", pnglib); + aPNG_set_acTL = GetProcAddress("png_set_acTL", pnglib); + aPNG_write_frame_head = GetProcAddress("png_write_frame_head", pnglib); + aPNG_write_frame_tail = GetProcAddress("png_write_frame_tail", pnglib); #endif - return (apng_set_acTL && apng_write_frame_head && apng_write_frame_tail); + return (aPNG_set_acTL && aPNG_write_frame_head && aPNG_write_frame_tail); #endif } @@ -903,11 +902,6 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn apng_frames++; -#ifndef PNG_STATIC - if (apng_set_acTL) -#endif - apng_set_acTL(apng_ptr, apng_info_ptr, apng_frames, 0); - for (y = 0; y < height; y++) { row_pointers[y] = png_buf; @@ -915,9 +909,9 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn } #ifndef PNG_STATIC - if (apng_write_frame_head) + if (aPNG_write_frame_head) #endif - apng_write_frame_head(apng_ptr, apng_info_ptr, row_pointers, + aPNG_write_frame_head(apng_ptr, apng_info_ptr, row_pointers, vid.width, /* width */ height, /* height */ 0, /* x offset */ @@ -930,57 +924,21 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn png_write_image(png_ptr, row_pointers); #ifndef PNG_STATIC - if (apng_write_frame_tail) + if (aPNG_write_frame_tail) #endif - apng_write_frame_tail(apng_ptr, apng_info_ptr); + aPNG_write_frame_tail(apng_ptr, apng_info_ptr); png_free(png_ptr, (png_voidp)row_pointers); } -static inline boolean M_PNGfind_acTL(void) +static void M_PNGfix_acTL(png_structp png_ptr, png_infop png_info_ptr, + apng_infop png_ainfo_ptr) { - png_byte cn[8]; // 4 bytes for len then 4 byes for name - long endpos = ftell(apng_FILE); // not the real end of file, just what of libpng wrote - for (fseek(apng_FILE, 0, SEEK_SET); // let go to the start of the file - ftell(apng_FILE)+12 < endpos; // let not go over the file bound - fseek(apng_FILE, 1, SEEK_CUR) // we went 8 steps back and now we go 1 step forward - ) - { - if (fread(cn, sizeof(cn), 1, apng_FILE) != 1) // read 8 bytes - return false; // failed to read data - if (fseek(apng_FILE, -8, SEEK_CUR) != 0) //rewind 8 bytes - return false; // failed to rewird - if (!png_memcmp(cn+4, acTL_cn, 4)) //cmp for chuck header - return true; // found it - } - return false; // acTL chuck not found -} - -static void M_PNGfix_acTL(png_structp png_ptr, png_infop png_info_ptr) -{ - png_byte data[16]; - long oldpos; - -#ifndef PNG_STATIC - if (apng_set_acTL) -#endif - apng_set_acTL(png_ptr, png_info_ptr, apng_frames, 0); + apng_set_acTL(png_ptr, png_info_ptr, png_ainfo_ptr, apng_frames, 0); #ifndef NO_PNG_DEBUG png_debug(1, "in png_write_acTL\n"); #endif - - png_ptr->num_frames_to_write = apng_frames; - - png_save_uint_32(data, apng_frames); - png_save_uint_32(data + 4, 0); - - oldpos = ftell(apng_FILE); - - if (M_PNGfind_acTL()) - png_write_chunk(png_ptr, (png_bytep)acTL_cn, data, (png_size_t)8); - - fseek(apng_FILE, oldpos, SEEK_SET); } static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal) @@ -1012,6 +970,16 @@ static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal) return false; } + apng_ainfo_ptr = apng_create_info_struct(apng_ptr); + if (!apng_ainfo_ptr) + { + CONS_Debug(DBG_RENDER, "M_StartMovie: Error on allocate for apng\n"); + png_destroy_write_struct(&apng_ptr, &apng_info_ptr); + fclose(apng_FILE); + remove(filename); + return false; + } + png_init_io(apng_ptr, apng_FILE); #ifdef PNG_SET_USER_LIMITS_SUPPORTED @@ -1029,12 +997,11 @@ static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal) M_PNGText(apng_ptr, apng_info_ptr, true); -#ifndef PNG_STATIC - if (apng_set_acTL) -#endif - apng_set_acTL(apng_ptr, apng_info_ptr, PNG_UINT_31_MAX, 0); + apng_set_set_acTL_fn(apng_ptr, apng_ainfo_ptr, aPNG_set_acTL); - png_write_info(apng_ptr, apng_info_ptr); + apng_set_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr, PNG_UINT_31_MAX, 0); + + apng_write_info(apng_ptr, apng_info_ptr, apng_ainfo_ptr); apng_frames = 0; @@ -1237,8 +1204,8 @@ void M_StopMovie(void) if (apng_frames) { - M_PNGfix_acTL(apng_ptr, apng_info_ptr); - png_write_end(apng_ptr, apng_info_ptr); + M_PNGfix_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr); + apng_write_end(apng_ptr, apng_info_ptr, apng_ainfo_ptr); } png_destroy_write_struct(&apng_ptr, &apng_info_ptr); diff --git a/src/p_enemy.c b/src/p_enemy.c index 9d3aa951..b01f9be6 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3079,7 +3079,7 @@ void A_Invincibility(mobj_t *actor) { S_StopMusic(); if (mariomode) - G_GhostAddColor(GHC_INVINCIBLE); + G_GhostAddColor((INT32) (player - players), GHC_INVINCIBLE); S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false); } } diff --git a/src/p_inter.c b/src/p_inter.c index dd27858f..425461d5 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1424,7 +1424,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; player->powers[pw_shield] |= SH_FIREFLOWER; toucher->color = SKINCOLOR_WHITE; - G_GhostAddColor(GHC_FIREFLOWER); + G_GhostAddColor(player - players, GHC_FIREFLOWER); break; // *************** // @@ -3028,7 +3028,7 @@ void P_RemoveShield(player_t *player) if (!player->powers[pw_super]) { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } } else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) // Give them what's coming to them! @@ -3459,7 +3459,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da target->health -= damage; if (source && source->player && target) - G_GhostAddHit(target); + G_GhostAddHit((INT32) (source->player - players), target); if (target->health <= 0) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 302f50a1..4be4afd9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1220,7 +1220,7 @@ static void P_PlayerFlip(mobj_t *mo) if (!mo->player) return; - G_GhostAddFlip(); + G_GhostAddFlip((INT32) (mo->player - players)); // Flip aiming to match! if (mo->player->pflags & PF_NIGHTSMODE) // NiGHTS doesn't use flipcam @@ -6010,7 +6010,7 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale) if (player) { - G_GhostAddScale(newscale); + G_GhostAddScale((INT32) (player - players), newscale); player->viewheight = FixedMul(FixedDiv(player->viewheight, oldscale), newscale); // Nonono don't calculate viewheight elsewhere, this is the best place for it! player->dashspeed = FixedMul(FixedDiv(player->dashspeed, oldscale), newscale); // Prevents the player from having to re-charge up spindash if the player grew in size } diff --git a/src/p_setup.c b/src/p_setup.c index 08bcfd2f..de250b1f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3228,10 +3228,9 @@ boolean P_SetupLevel(boolean skipprecip) if (!cv_analog4.changed) CV_SetValue(&cv_analog4, 0);*/ -#ifdef HWRENDER - if (rendermode != render_soft && rendermode != render_none) - CV_Set(&cv_grfov, cv_grfov.defaultvalue); -#endif + // Shouldn't be necessary with render parity? + /*if (rendermode != render_none) + CV_Set(&cv_fov, cv_fov.defaultvalue);*/ displayplayer = consoleplayer; // Start with your OWN view, please! } diff --git a/src/p_tick.c b/src/p_tick.c index 8b99c5aa..b422e9d2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -13,6 +13,7 @@ #include "doomstat.h" #include "g_game.h" +#include "g_input.h" #include "p_local.h" #include "z_zone.h" #include "s_sound.h" @@ -722,10 +723,28 @@ void P_Ticker(boolean run) G_ReadMetalTic(metalplayback); if (metalrecording) G_WriteMetalTic(players[consoleplayer].mo); - if (demorecording) - G_WriteGhostTic(players[consoleplayer].mo); - if (demoplayback) // Use Ghost data for consistency checks. - G_ConsGhostTic(); + + if (multiplayer) + { + if (demorecording) + { + G_WriteAllGhostTics(); + + if (demosavebutton && demosavebutton + 3*TICRATE < leveltime && InputDown(gc_lookback, 1)) + demodefersave = true; + } + if (demoplayback) // Use Ghost data for consistency checks. + { + G_ConsAllGhostTics(); + } + } + else + { + if (demorecording) + G_WriteGhostTic(players[consoleplayer].mo, consoleplayer); + if (demoplayback) // Use Ghost data for consistency checks. + G_ConsGhostTic(0); + } if (modeattacking) G_GhostTicker(); diff --git a/src/p_user.c b/src/p_user.c index 0c1fb11a..180a35b0 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -169,7 +169,7 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move) boolean P_AutoPause(void) { // Don't pause even on menu-up or focus-lost in netgames or record attack - if (netgame || modeattacking) + if (netgame || modeattacking || titledemo) return false; return (menuactive || window_notinfocus); @@ -1708,7 +1708,7 @@ void P_SpawnThokMobj(player_t *player) P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do if (demorecording) - G_GhostAddThok(); + G_GhostAddThok((INT32) (player - players)); } // @@ -1835,6 +1835,9 @@ void P_DoPlayerExit(player_t *player) player->powers[pw_spacetime] = 0; player->kartstuff[k_cardanimation] = 0; // srb2kart: reset battle animation + if (player == &players[consoleplayer]) + demosavebutton = leveltime; + /*if (playeringame[player-players] && netgame && !circuitmap) CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/ } @@ -2416,12 +2419,12 @@ static void P_CheckInvincibilityTimer(player_t *player) //if (player->powers[pw_shield] & SH_FIREFLOWER) //{ // player->mo->color = SKINCOLOR_WHITE; - // G_GhostAddColor(GHC_FIREFLOWER); + // G_GhostAddColor((INT32) (player - players), GHC_FIREFLOWER); //} //else { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } } @@ -3673,12 +3676,12 @@ static void P_DoSuperStuff(player_t *player) if (player->powers[pw_shield] & SH_FIREFLOWER) { player->mo->color = SKINCOLOR_WHITE; - G_GhostAddColor(GHC_FIREFLOWER); + G_GhostAddColor((INT32) (player - players), GHC_FIREFLOWER); } else { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } if (gametype != GT_COOP) @@ -3714,7 +3717,7 @@ static void P_DoSuperStuff(player_t *player) P_SetScale(spark, player->mo->scale); } - G_GhostAddColor(GHC_SUPER); + G_GhostAddColor((INT32) (player - players), GHC_SUPER); // Ran out of rings while super! if (player->health <= 1 || player->exiting) @@ -3728,12 +3731,12 @@ static void P_DoSuperStuff(player_t *player) if (player->powers[pw_shield] & SH_FIREFLOWER) { player->mo->color = SKINCOLOR_WHITE; - G_GhostAddColor(GHC_FIREFLOWER); + G_GhostAddColor((INT32) (player - players), GHC_FIREFLOWER); } else { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } if (gametype != GT_COOP) @@ -4035,7 +4038,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. // Now spawn the color thok circle. P_SpawnSpinMobj(player, player->revitem); if (demorecording) - G_GhostAddRev(); + G_GhostAddRev((INT32) (player - players)); } } // If not moving up or down, and travelling faster than a speed of four while not holding @@ -7017,7 +7020,7 @@ static void P_MovePlayer(player_t *player) { P_SpawnSpinMobj(player, player->spinitem); if (demorecording) - G_GhostAddSpin(); + G_GhostAddSpin((INT32) (player - players)); } */ @@ -8309,9 +8312,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (mo->eflags & MFE_VERTICALFLIP) camheight += thiscam->height; - if (splitscreen == 1) - camspeed = (3*camspeed)/4; - if (camspeed > FRACUNIT) camspeed = FRACUNIT; @@ -8365,13 +8365,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall height -= FixedMul(height, player->kartstuff[k_boostcam]); } - // in splitscreen modes, mess with the camera distances to make it feel proportional to how it feels normally - if (splitscreen == 1) // widescreen splits should get x1.5 distance - { - dist = FixedMul(dist, 3*FRACUNIT/2); - height = FixedMul(height, 3*FRACUNIT/2); - } - x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); @@ -8637,10 +8630,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall { thiscam->momx = x - thiscam->x; thiscam->momy = y - thiscam->y; - if (splitscreen == 1) // Wide-screen needs to follow faster, due to a smaller vertical:horizontal ratio of screen space - thiscam->momz = FixedMul(z - thiscam->z, (3*camspeed)/4); - else - thiscam->momz = FixedMul(z - thiscam->z, camspeed/2); + thiscam->momz = FixedMul(z - thiscam->z, camspeed/2); } thiscam->pan = pan; diff --git a/src/r_main.c b/src/r_main.c index 8b070e9e..36182d0e 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -56,6 +56,7 @@ INT32 centerx, centery; fixed_t centerxfrac, centeryfrac; fixed_t projection; fixed_t projectiony; // aspect ratio +fixed_t fovtan; // field of view // just for profiling purposes size_t framecount; @@ -134,11 +135,14 @@ static CV_PossibleValue_t drawdist_precip_cons_t[] = { {1024, "1024"}, {1536, "1536"}, {2048, "2048"}, {0, "None"}, {0, NULL}}; +static CV_PossibleValue_t fov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}}; + //static CV_PossibleValue_t precipdensity_cons_t[] = {{0, "None"}, {1, "Light"}, {2, "Moderate"}, {4, "Heavy"}, {6, "Thick"}, {8, "V.Thick"}, {0, NULL}}; static CV_PossibleValue_t translucenthud_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}}; static CV_PossibleValue_t maxportals_cons_t[] = {{0, "MIN"}, {12, "MAX"}, {0, NULL}}; // lmao rendering 32 portals, you're a card static CV_PossibleValue_t homremoval_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Flash"}, {0, NULL}}; +static void Fov_OnChange(void); static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); @@ -175,6 +179,7 @@ consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, //consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //consvar_t cv_precipdensity = {"precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_fov = {"fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange, 0, NULL, NULL, 0, 0, NULL}; // Okay, whoever said homremoval causes a performance hit should be shot. consvar_t cv_homremoval = {"homremoval", "Yes", CV_SAVE, homremoval_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -227,6 +232,14 @@ void SplitScreen_OnChange(void) } } } +static void Fov_OnChange(void) +{ + // Shouldn't be needed with render parity? + //if ((netgame || multiplayer) && !cv_debug && cv_fov.value != 90*FRACUNIT) + // CV_Set(&cv_fov, cv_fov.defaultvalue); + + R_SetViewSize(); +} static void ChaseCam_OnChange(void) { @@ -517,7 +530,7 @@ static void R_InitTextureMapping(void) // // Calc focallength // so FIELDOFVIEW angles covers SCREENWIDTH. - focallength = FixedDiv(centerxfrac, + focallength = FixedDiv(projection, FINETANGENT(FINEANGLES/4+/*cv_fov.value*/ FIELDOFVIEW/2)); #ifdef ESLOPE @@ -632,6 +645,7 @@ void R_ExecuteSetViewSize(void) INT32 j; INT32 level; INT32 startmapl; + angle_t fov; setsizeneeded = false; @@ -660,9 +674,12 @@ void R_ExecuteSetViewSize(void) centerxfrac = centerx<> ANGLETOFINESHIFT); + if (splitscreen == 1) // Splitscreen FOV should be adjusted to maintain expected vertical view + fovtan = 17*fovtan/10; + + projection = projectiony = FixedDiv(centerxfrac, fovtan); R_InitViewBuffer(scaledviewwidth, viewheight); @@ -688,7 +705,7 @@ void R_ExecuteSetViewSize(void) for (i = 0; i < j; i++) { dy = ((i - viewheight*8)<>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS) +#define AIMINGTODY(a) FixedDiv((FINETANGENT((2048+(((INT32)a)>>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS, fovtan) // recalc necessary stuff for mouseaiming // slopes are already calculated for the full possible view (which is 4*viewheight). @@ -1490,6 +1507,7 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_drawdist); //CV_RegisterVar(&cv_drawdist_nights); CV_RegisterVar(&cv_drawdist_precip); + CV_RegisterVar(&cv_fov); CV_RegisterVar(&cv_chasecam); CV_RegisterVar(&cv_chasecam2); diff --git a/src/r_main.h b/src/r_main.h index 7d3e26a8..38a58968 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -79,6 +79,7 @@ extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; extern consvar_t cv_shadow, cv_shadowoffs; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; +extern consvar_t cv_fov; extern consvar_t cv_skybox; extern consvar_t cv_tailspickup; diff --git a/src/r_things.c b/src/r_things.c index c1a847a1..9816ca22 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -925,6 +925,13 @@ static void R_DrawVisSprite(vissprite_t *vis) if (vis->x2 >= vid.width) vis->x2 = vid.width-1; +#if 1 + // Something is occasionally setting 1px-wide sprites whose frac is exactly the width of the sprite, causing crashes due to + // accessing invalid column info. Until the cause is found, let's try to correct those manually... + while (frac + vis->xiscale*(vis->x2-vis->x1) > SHORT(patch->width)<x2 >= vis->x1) + vis->x2--; +#endif + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) { if (vis->scalestep) // currently papersprites only diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index ec5d63ac..de215705 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -70,6 +70,8 @@ if(${SDL2_FOUND}) set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS} + ${SRB2_PNG_SOURCES} + ${SRB2_PNG_HEADERS} ${SRB2_CORE_RENDER_SOURCES} ${SRB2_CORE_GAME_SOURCES} ${SRB2_LUA_SOURCES} @@ -80,7 +82,8 @@ if(${SDL2_FOUND}) ${SRB2_SDL2_HEADERS} ) - source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}) + source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS} + ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS}) source_group("Renderer" FILES ${SRB2_CORE_RENDER_SOURCES}) source_group("Game" FILES ${SRB2_CORE_GAME_SOURCES}) source_group("Assembly" FILES ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES}) diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index bd964259..d2466bd5 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -164,6 +164,7 @@ + @@ -317,6 +318,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 09287436..daa13189 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -294,6 +294,9 @@ LUA + + M_Misc + M_Misc diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index d2a268f8..3898aeba 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -2834,6 +2834,50 @@ RelativePath="..\m_argv.h" > + + + + + + + + + + + + + + + + diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index f360072a..f92f1f14 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2974,8 +2974,8 @@ static void I_ShutdownTimer(void) // tic_t I_GetTime (void) { - static Uint32 basetime = 0; - Uint32 ticks = SDL_GetTicks(); + static Uint64 basetime = 0; + Uint64 ticks = SDL_GetTicks(); if (!basetime) basetime = ticks; diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj index 94a3fbb6..a8ecbf7f 100644 --- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj +++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 1E44AF0D0B67CDE900BAD059 /* m_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AEFE0B67CDE900BAD059 /* m_fixed.c */; }; 1E44AF0F0B67CDE900BAD059 /* m_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF000B67CDE900BAD059 /* m_menu.c */; }; 1E44AF110B67CDE900BAD059 /* m_misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* m_misc.c */; }; + 1E44AF110B67CDE900BAD059 /* apng.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* apng.c */; }; 1E44AF130B67CDE900BAD059 /* m_random.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF040B67CDE900BAD059 /* m_random.c */; }; 1E44AF1A0B67CE2A00BAD059 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF180B67CE2A00BAD059 /* info.c */; }; 1E44AF1E0B67CE3600BAD059 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF1C0B67CE3600BAD059 /* md5.c */; }; @@ -253,7 +254,9 @@ 1E44AF000B67CDE900BAD059 /* m_menu.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_menu.c; path = ../../m_menu.c; sourceTree = SOURCE_ROOT; }; 1E44AF010B67CDE900BAD059 /* m_menu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_menu.h; path = ../../m_menu.h; sourceTree = SOURCE_ROOT; }; 1E44AF020B67CDE900BAD059 /* m_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_misc.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; }; + 1E44AF020B67CDE900BAD059 /* apng.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; }; 1E44AF030B67CDE900BAD059 /* m_misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_misc.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; }; + 1E44AF020B67CDE900BAD059 /* apng.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.h; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; }; 1E44AF040B67CDE900BAD059 /* m_random.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_random.c; path = ../../m_random.c; sourceTree = SOURCE_ROOT; }; 1E44AF050B67CDE900BAD059 /* m_random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_random.h; path = ../../m_random.h; sourceTree = SOURCE_ROOT; }; 1E44AF060B67CDE900BAD059 /* m_swap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_swap.h; path = ../../m_swap.h; sourceTree = SOURCE_ROOT; }; @@ -679,6 +682,8 @@ 1E44AEFF0B67CDE900BAD059 /* m_fixed.h */, 1E44AF020B67CDE900BAD059 /* m_misc.c */, 1E44AF030B67CDE900BAD059 /* m_misc.h */, + 1E44AF020B67CDE900BAD059 /* apng.c */, + 1E44AF030B67CDE900BAD059 /* apng.h */, 676BB51C0E0DE06100C95963 /* m_queue.c */, 676BB51D0E0DE06100C95963 /* m_queue.h */, 1E44AF040B67CDE900BAD059 /* m_random.c */, diff --git a/src/sdl/srb2icon.png b/src/sdl/srb2icon.png new file mode 100644 index 00000000..2aca4b20 Binary files /dev/null and b/src/sdl/srb2icon.png differ diff --git a/src/sdl12/Srb2SDL-vc10.vcxproj b/src/sdl12/Srb2SDL-vc10.vcxproj index 958cd7d0..99916f58 100644 --- a/src/sdl12/Srb2SDL-vc10.vcxproj +++ b/src/sdl12/Srb2SDL-vc10.vcxproj @@ -835,6 +835,16 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -1293,6 +1303,7 @@ + diff --git a/src/sdl12/Srb2SDL-vc9.vcproj b/src/sdl12/Srb2SDL-vc9.vcproj index d2a268f8..fa386e38 100644 --- a/src/sdl12/Srb2SDL-vc9.vcproj +++ b/src/sdl12/Srb2SDL-vc9.vcproj @@ -2790,6 +2790,50 @@ + + + + + + + + + + + + + + + + diff --git a/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj index de12201f..1f8e3276 100644 --- a/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj +++ b/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj @@ -1365,6 +1365,20 @@ path = ../../m_misc.h; refType = 2; }; + 84177764085A10EB000C01D8 = { + fileEncoding = 30; + isa = PBXFileReference; + name = apng.c; + path = ../../apng.c; + refType = 2; + }; + 84177765085A10EB000C01D8 = { + fileEncoding = 30; + isa = PBXFileReference; + name = m_misc.h; + path = ../../apng.h; + refType = 2; + }; 84177766085A10EB000C01D8 = { fileEncoding = 30; isa = PBXFileReference; diff --git a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj index 0ab40c45..69c544c5 100644 --- a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj +++ b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 1E44AF0D0B67CDE900BAD059 /* m_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AEFE0B67CDE900BAD059 /* m_fixed.c */; }; 1E44AF0F0B67CDE900BAD059 /* m_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF000B67CDE900BAD059 /* m_menu.c */; }; 1E44AF110B67CDE900BAD059 /* m_misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* m_misc.c */; }; + 1E44AF110B67CDE900BAD059 /* apng.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* apng.c */; }; 1E44AF130B67CDE900BAD059 /* m_random.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF040B67CDE900BAD059 /* m_random.c */; }; 1E44AF1A0B67CE2A00BAD059 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF180B67CE2A00BAD059 /* info.c */; }; 1E44AF1E0B67CE3600BAD059 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF1C0B67CE3600BAD059 /* md5.c */; }; @@ -254,6 +255,8 @@ 1E44AF010B67CDE900BAD059 /* m_menu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_menu.h; path = ../../m_menu.h; sourceTree = SOURCE_ROOT; }; 1E44AF020B67CDE900BAD059 /* m_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_misc.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; }; 1E44AF030B67CDE900BAD059 /* m_misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_misc.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; }; + 1E44AF020B67CDE900BAD059 /* apng.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; }; + 1E44AF030B67CDE900BAD059 /* apng.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = apng.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; }; 1E44AF040B67CDE900BAD059 /* m_random.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_random.c; path = ../../m_random.c; sourceTree = SOURCE_ROOT; }; 1E44AF050B67CDE900BAD059 /* m_random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_random.h; path = ../../m_random.h; sourceTree = SOURCE_ROOT; }; 1E44AF060B67CDE900BAD059 /* m_swap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_swap.h; path = ../../m_swap.h; sourceTree = SOURCE_ROOT; }; @@ -679,6 +682,8 @@ 1E44AEFF0B67CDE900BAD059 /* m_fixed.h */, 1E44AF020B67CDE900BAD059 /* m_misc.c */, 1E44AF030B67CDE900BAD059 /* m_misc.h */, + 1E44AF020B67CDE900BAD059 /* apng.c */, + 1E44AF030B67CDE900BAD059 /* apng.h */, 676BB51C0E0DE06100C95963 /* m_queue.c */, 676BB51D0E0DE06100C95963 /* m_queue.h */, 1E44AF040B67CDE900BAD059 /* m_random.c */, diff --git a/src/st_stuff.c b/src/st_stuff.c index b9046abd..3899864f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1907,7 +1907,7 @@ static void ST_overlayDrawer(void) V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[stplyr-players]); } } - else + else if (!titledemo) { if (!splitscreen) @@ -2014,6 +2014,15 @@ static void ST_overlayDrawer(void) } } + // Replay manual-save stuff + if (demorecording && multiplayer && demosavebutton && demosavebutton + 3*TICRATE < leveltime) + { + if (demodefersave || cv_recordmultiplayerdemos.value == 2) + V_DrawCenteredString(BASEVIDWIDTH/2, 178, V_HUDTRANSHALF|V_ALLOWLOWERCASE|(G_BattleGametype() ? V_REDMAP : V_SKYMAP), "Replay will be saved."); + else + V_DrawCenteredString(BASEVIDWIDTH/2, 178, V_HUDTRANSHALF|V_ALLOWLOWERCASE|(G_BattleGametype() ? V_REDMAP : V_SKYMAP), "Press Look Backward to save the replay"); + } + ST_drawDebugInfo(); } diff --git a/src/w_wad.c b/src/w_wad.c index efa09ce4..d1c6d488 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -186,6 +186,7 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors) static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) { UINT16 posStart, posEnd; +#ifdef HAVE_BLUA posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0); if (posStart != INT16_MAX) { @@ -194,6 +195,7 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum) for (; posStart < posEnd; posStart++) LUA_LoadLump(wadnum, posStart); } +#endif posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0); if (posStart != INT16_MAX) { @@ -793,9 +795,11 @@ UINT16 W_InitFile(const char *filename) CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename); DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0); break; +#ifdef HAVE_BLUA case RET_LUA: LUA_LoadLump(numwadfiles - 1, 0); break; +#endif default: break; } diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index ca73b129..774ce5cb 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -182,6 +182,7 @@ + @@ -330,6 +331,7 @@ + diff --git a/src/win32/Srb2win-vc9.vcproj b/src/win32/Srb2win-vc9.vcproj index 24235bc7..a64b8638 100644 --- a/src/win32/Srb2win-vc9.vcproj +++ b/src/win32/Srb2win-vc9.vcproj @@ -2851,6 +2851,50 @@ RelativePath="..\m_misc.h" > + + + + + + + + + + + + + + + + diff --git a/src/y_inter.c b/src/y_inter.c index 0ca17507..24c4f93e 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -562,6 +562,11 @@ dotimer: string); } + if (demorecording && cv_recordmultiplayerdemos.value == 1) + V_DrawCenteredString(BASEVIDWIDTH/2, 178, V_ALLOWLOWERCASE|hilicol, "Press Look Backward to save the replay"); + else if (demosaved) + V_DrawCenteredString(BASEVIDWIDTH/2, 178, V_ALLOWLOWERCASE|hilicol, "Replay saved!"); + // Make it obvious that scrambling is happening next round. if (cv_scrambleonchange.value && cv_teamscramble.value && (intertic/TICRATE % 2 == 0)) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!")); @@ -577,6 +582,12 @@ void Y_Ticker(void) if (intertype == int_none) return; + if (demorecording && cv_recordmultiplayerdemos.value == 1 && (demodefersave || InputDown(gc_lookback, 1))) + { + demodefersave = false; + G_SaveDemo(); + } + // Check for pause or menu up in single player if (paused || P_AutoPause()) return; @@ -769,6 +780,8 @@ void Y_StartIntermission(void) { if (cv_inttime.value == 0 && gametype == GT_COOP) timer = 0; + else if (demoplayback) // Override inttime (which is pulled from the replay anyway + timer = 10*TICRATE; else { timer = cv_inttime.value*TICRATE;